Page 1 of 1

Set a limit on per-pixel contrast?

Posted: 2013-05-14T15:53:57-07:00
by meetar
I'm working with elevation data, and I want to set a limit on the allowable contrast between pixels, ideally something numeric I can refer to later.

I have an example working with Fx, which checks a basic plus-shaped kernel for a difference in value past some limit:

Code: Select all

#!/bin/bash runthis.sh
convert in.png  \
          -fx "lim = .25;
               mm = 0;
               mm = max(mm, p[-1,0]);
               mm = max(mm, p[0,-1]);
               mm = max(mm, p[0,1]);
               mm = max(mm, p[1,0]);
               mm-p[0] > lim ? p[0]+lim/2 : p[0]" \
          out.png

METRIC=`compare -metric AE in.png out.png null: 2>&1`
echo $METRIC

if [ $METRIC -ne "0" ]; then
  cp out.png in.png
  sh runthis.sh
fi
Input:
Image

Output:
Image

In this case, it brightens pixels near a peak until they're within the contrast limit, and loops until all areas of contrast over the limit are gone. I can also write it so it pulls down peaks, but this is better for my purposes (for now).

Of course Fx is extremely slow, and the simple unweighted kernel produces some very obvious artifacts. Is there a faster and better way to do this?

The old "inverse of unsharp mask" trick is similar, but doesn't seem to be very easy to control; some kind of convolution also seems likely; but in both cases, I can't work out how I'd specify a contrast threshold, or – even better – specify that the effect be proportional over some contrast threshold.

Thanks --

Re: Set a limit on per-pixel contrast?

Posted: 2013-05-14T16:02:55-07:00
by fmw42
Do the same thing with a shaped morphologic dilate rather than fx, then threshold the result to your given numeric value. You can create any custom shape you want or use the plus or cross (X) shape

see
ttp://www.imagemagick.org/Usage/morphology/#dilate
http://www.imagemagick.org/Usage/morphology/#plus
http://www.imagemagick.org/Usage/morphology/#cross

Re: Set a limit on per-pixel contrast?

Posted: 2013-05-14T16:16:29-07:00
by meetar
That sounds good, but I must not be doing it correctly:

Code: Select all

convert in.gif -morphology Dilate Cross out.gif
Input:
Image
Output:
Image

I'm also not sure what you mean by "threshold the result to your given numeric value" -- I don't want to set an upper or lower limit on values at all, only on the difference between values. In other words if the difference between a pixel and its neighbors is under a certain threshold, I want to leave it untouched.

What am I missing?

Re: Set a limit on per-pixel contrast?

Posted: 2013-05-14T16:54:59-07:00
by fmw42
Sorry dilate is not what you want.

Your -fx expression probably should be looking at the absolute (value) difference and not the signed difference.

In any case, what you can do is shift the image 8 times, once in each direction. Then use -compose minus or compose difference (the latter for the absolute value of the difference) to subtract two images at a time. Then use -evaluate-sequence max to get the largest difference. Then threshold the difference image and use it as a mask with -compose over to blend the original and the difference to achieve your by-pixel limit.

The shift can be done by compositing a subsection of the image onto the image in each gravity direction.

Code: Select all

# shifted images
shifted="$tmpdir/shifted"
shift1="$shifted-1.png"
shift2="$shifted-2.png"
shift3="$shifted-3.png"
shift4="$shifted-4.png"
shift5="$shifted-5.png"
shift6="$shifted-6.png"
shift7="$shifted-7.png"
shift8="$shifted-8.png"

# function to create 8 1-pixel shifted images in each direction from center
grav1="NorthWest"
grav2="North"
grav3="NorthEast"
grav4="West"
grav5="East"
grav6="SouthWest"
grav7="South"
grav8="SouthEast"

shift_size=`identify -format '%[fx:w-2]x%[fx:h-2]+1+1' $TMP`

create_shifted_images() {
  # create 8 shifted images from the given image
  for ((j=1; j<=8; j++)); do
    eval grav=\$grav$j
    eval img=\$shift$j
    convert "$1" "$1"[${shift_size}] -gravity $grav -composite $img
  done
}
see
http://www.imagemagick.org/Usage/compose/#minus
http://www.imagemagick.org/Usage/compose/#difference
http://www.imagemagick.org/script/comma ... e-sequence
http://www.imagemagick.org/Usage/compose/#compose
http://www.imagemagick.org/Usage/layers/#convert

Re: Set a limit on per-pixel contrast?

Posted: 2013-05-14T18:11:18-07:00
by meetar
Wow, thanks Fred; I see that this code is from your "morphology" script – I was trying to use that earlier today to replicate my Fx script. Is there any way you know of to use Morphology to do what you describe?

I expect I'll need to hack something together myself, but this is an excellent starting point.

Re: Set a limit on per-pixel contrast?

Posted: 2013-05-14T18:34:15-07:00
by fmw42
Yes it was from my morphology script. But I don't think there is anything pre-built by Anthony to do what you actually want. You probably could create all the shifted image differences by creating 8 2-pixe shaped kernels for -morphology convolve to match what you use in your fx. Then apply each one to your image. For example the kernel would be 3x1: 0,-1,+1 or something of that nature. Then after you get each of your 8 difference images you would then continue with my processing outline via -evaluate-sequency max on all 8 images to get the max difference image.

I cannot seem to find where Anthony discusses the limitations of the kernels. I am not sure if they need to be odd or can be even, but you probably need to use an odd kernel with a zero on one side so that it knows which pixel is the middle and which neighbor to do the difference with. I am also not sure if the kernel needs to be square or not. If it does then make it 3x3 with all zeros except the middle and the one neighbor.

You might be able to do that all in one step using the flag ">" to generate all rotated kernels and applying it with -define morphology:compose=Lighten to get the max. There is a good example of that for the Sobel filter at http://www.imagemagick.org/Usage/convolve/#sobel

Perhaps Anthony can suggest something more elegant or elaborate further. He really know morphology better than I.

Re: Set a limit on per-pixel contrast?

Posted: 2013-05-14T22:39:50-07:00
by anthony
What you are wanting is some type of true 3D greyscale morphology. Specifically Greyscale Dilation
However it is currently not implemented, at least not as Greyscale Dialation!
See http://www.imagemagick.org/Usage/morphology/#greyscale

Currently the only 3D morphology of that type, that is implemented is Morphological Distance. Though it is implemented to take minimum rather than maximum, and adds rather than subtracts the kernel from the neighbouring pixels, when compared to 'Erode Greyscale Morphology'.

Note: Distance Kernel shapes are zero in the middle and positive around it, which is how people think of such kernels. True grayscale morphology uses a negative valued shape to define the 3-D shape used for erosion and dialation, and all zeros for a 'flat kernel' as using for binary morphology.

Still it should be possible to use it! Try using distance on the negative of the image, then negating the result. It has controlled for the kernel scaling either by value or by maximum distance over the color range.

Actually I get something like your example image you are after using...

Code: Select all

convert test01.gif -negate -morphology Distance Manhattan:1,8\! -negate show:
The '8\!' is your 'distance limit'. It scales the greyscale pixel to pixel distance to be QuantumRange/9
You may like to try other Distance kernels which are 'more round', lots to choose from (an a couple more planned).

I an not certain your '0.25' limit did not just directly translate to '25%' instead of '8!' in the above.
25% would mean it reaches zero after only 4 pixels form the edge (making 3 intermedite colors)
8! makes 8 intermediate colors from white to black.

Kernals can be ANY shape, odd or even in size. You can also specify the 'origin' anywhere within the defining rectangle, though the shape in the rectangle can be anything as you can mark points as 'ignore'.
See: User defined Kernels.
http://www.imagemagick.org/Usage/morphology/#user

Re: Set a limit on per-pixel contrast?

Posted: 2013-05-21T19:58:52-07:00
by meetar
This does seem to be exactly what I needed; amazing. Thanks for all the help!

Re: Set a limit on per-pixel contrast?

Posted: 2013-05-21T23:11:01-07:00
by anthony
dont forget to try other 'distance' metrics for better 'smoothing' of corners.

Re: Set a limit on per-pixel contrast?

Posted: 2013-05-22T08:44:15-07:00
by meetar
"Euclidean" looks perfect for my needs at the moment:

Code: Select all

convert test01.gif -negate -morphology Distance Euclidean:1,8\! -negate outbars2.gif
Image

Re: Set a limit on per-pixel contrast?

Posted: 2013-06-11T09:13:47-07:00
by meetar
Hello again – the Euclidean distance is working great for me, but as I've been learning more about distance transforms I'm increasingly curious about the ImageMagick implementation. I just found something in the docs which sounded familiar:
The distance kernel is applied to the image so that each pixel is assigned the smallest pixel value plus the kernel value for that distance. This is applied all over the image simultaneously, using an algorithm that does not require multiple iterations, as we saw in previous morphology methods.
So the "multiple iterations" (which I assume means IterativeDistance) turns out to be something very similar to my original Fx process – applying the kernel repeatedly until nothing changes. And I've been learning about the 2-pass exact distance transformation (the Meijster transform), which as far as I can tell doesn't use a kernel – but the IM -morphology Distance transform seems to both use a kernel and work in only two passes (unless you use IterativeDistance). Am I misunderstanding this?

If I have it mostly right, then my question is: what's the relationship between the Meijster two-pass exact distance transform and your two-pass kernel transform, if any?

Thanks!

Re: Set a limit on per-pixel contrast?

Posted: 2013-08-09T08:11:37-07:00
by meetar
Hey all - thought I'd check back in and show the results of my work.

I implemented a grayscale morphological dilate and erode and applied it to a heightmap based on data from the Shuttle Radar Topography Mission, using the three.js WebGL library.

Image

Here's an erosion demo:
http://meetar.github.io/heightmap-demos/erode.html

Here's a dilation demo:
http://meetar.github.io/heightmap-demos/dilate.html

Here's the repo with the code, and more demos:
https://github.com/meetar/heightmap-demos

And here's a semi-rambly post about the process that led me to your forum in the first place:
http://edgeca.se/the-lay-of-the-land/

Thanks again for your help --

Re: Set a limit on per-pixel contrast?

Posted: 2013-08-09T09:33:39-07:00
by snibgo
Fascinating! Thanks for sharing.