Strange behavior in linear_stretch with HDRI (6.6.1-4)

Post any defects you find in the released or beta versions of the ImageMagick software here. Include the ImageMagick version, OS, and any command-line required to reproduce the problem. Got a patch for a bug? Post it here.
rnbc
Posts: 109
Joined: 2010-04-11T18:27:46-07:00
Authentication code: 8675308

Strange behavior in linear_stretch with HDRI (6.6.1-4)

Post by rnbc »

[reviewed below]

This is a strange behavior in linear stretch...

convert input.exr -auto-level -linear-stretch 0%x0% -gamma 2 -channel Red -gamma 1 -channel Green -gamma 0.9 -channel Blue -gamma 1.02 +channel -depth 16 -sigmoidal-contrast 5x27.5% -set option:modulate:colorspace hsb -modulate 100,500,100 -auto-level -linear-stretch 0%x0% -clamp -depth 8 -interlace line -quality 98 -sampling-factor 1x1 output_0.jpg

Image

Image

convert input.exr -auto-level -linear-stretch 0%x0% -gamma 2 -channel Red -gamma 1 -channel Green -gamma 0.9 -channel Blue -gamma 1.02 +channel -depth 16 -sigmoidal-contrast 5x27.5% -set option:modulate:colorspace hsb -modulate 100,500,100 -auto-level -linear-stretch 0.00001%x0.00001% -clamp -depth 8 -interlace line -quality 98 -sampling-factor 1x1 output_2.jpg

Image

Image

The only difference is the 0% --> 0.00001% in the final linear-stretch.

I know this might sound like a strange command, but it's part of an automated program... so it's perhaps a bit redundant.

Anyway, I think 0.00001% shouldn't definitely have such a large effect... notice how the histogram changes in the highlights!

The files for you to reproduce this are here:

http://rnbc.dyndns.org/pub/imagemagick_ ... r_stretch/
Last edited by rnbc on 2010-04-20T20:07:48-07:00, edited 1 time in total.
User avatar
fmw42
Posts: 25562
Joined: 2007-07-02T17:14:51-07:00
Authentication code: 1152
Location: Sunnyvale, California, USA

Re: Strange behavior in linear_stretch with HDRI (6.6.1-4)

Post by fmw42 »

linear stretch 0x0% does nothing. but once you give it some value however small, it will stretch/clip to the first non-empty bin in the histogram which may be quite far from the ends. Linear stretch is also NOT channel sensitive, but contrast-stretch is.

see difference between linear-stretch and contrast-stretch at

http://www.imagemagick.org/Usage/color/ ... st-stretch
http://www.imagemagick.org/Usage/color/#linear-stretch
rnbc
Posts: 109
Joined: 2010-04-11T18:27:46-07:00
Authentication code: 8675308

Re: Strange behavior in linear_stretch with HDRI (6.6.1-4)

Post by rnbc »

While it is true linear-stretch 0%x0% does not change the image there is an auto-level just before it, doing what linear-stretch 0%-0% should do in the first place (IMHO), so it's a wash: it does nothing but the work was already done anyway.

In an image as small as this one burning 0.0001% is nothing, unless you have some badly behaved single-pixel, and this is not such a case.

I had tried contrast-stretch and it does basically the same in this case: it clips badly! Try for yourself and see the histogram.

Contrary to what you say linear-stretch does use the -channel or +channel setting, just like contrast-stretch and auto-level operations.

Please note I'm using an HDRI-enabled version, not the integer version.

PS: I suspect the problem is with the HDRI-enabled histogram, since it affects both contrast-stretch and linear-stretch, but not auto-level (which only needs the min&max, not the histogram).
User avatar
fmw42
Posts: 25562
Joined: 2007-07-02T17:14:51-07:00
Authentication code: 1152
Location: Sunnyvale, California, USA

Re: Strange behavior in linear_stretch with HDRI (6.6.1-4)

Post by fmw42 »

If you read the page that I reference it says:

The third difference is that "-contrast-stretch" is channel sensitive, whereas "-linear-stretch" is not.

Unless this has been fixed since Anthony and I made this document, it is still in effect and the channel sensitivity is coming from auto-level.

-contrast-stretch 0 does something similar to -auto-level in that it will automatically stretch to min and max (but bins rather than gray levels)

Linear stretches from full black and white, so that is why it does nothing for -linear-stretch 0. If your min and max graylevels are far from black and white, any small clip amount will jump all the way to those graylevels for linear-stretch and will go a little further for -contrast-stretch as it starts at the min and max (bins).

It is quite possible that the histogramming has problems in HDRI. I do not know. But I still think you have to understand the difference between linear-stretch and contrast-stretch.

It might be better for the IM folks, if you can shorten your command line to find out just where the issue is coming from. Make it as short and simple as possible and still show the issue. Just a suggestion.
rnbc
Posts: 109
Joined: 2010-04-11T18:27:46-07:00
Authentication code: 8675308

Re: Strange behavior in linear_stretch with HDRI (6.6.1-4)

Post by rnbc »

When I read the current ImageMagick documentation what I see is that linear-stretch does the same as contrast-stretch, but instead of using the histogram as a lookup table it calculates the correct factors for use in "level" and then issues it internally.

All 3 operators are now -channel/+channel aware. As far as I understand that has not always been the case though...

Anyway, I've changed "bug" report, and here it is:

-linear-stretch 1 ---> works as advertised

Image
Image

-linear-stretch 0.00001%x0.00001% ---> clips

Image
Image

-contrast-stretch 0.0001%x0.0001% ---> clips

Image
Image

All the files are here:

http://rnbc.dyndns.org/pub/imagemagick_ ... r_stretch/

And the command now only does one operation (contrast-stretch or linear-stretch). The EXR has been pre-processed already, so that only one single operation is needed.

I know I tend to complain a lot... IIRC it was because I complained of not being able to adjust the black/white % in normalize a few years ago that contrast-stretch was originally developed...

I'm sorry about not being able to contribute more, and solve problems myself...
User avatar
fmw42
Posts: 25562
Joined: 2007-07-02T17:14:51-07:00
Authentication code: 1152
Location: Sunnyvale, California, USA

Re: Strange behavior in linear_stretch with HDRI (6.6.1-4)

Post by fmw42 »

contrast-stretch 0.00001%x0.00001% and linear-stretch 0.00001%x0.00001% stretch/clip differently as I explained before and as explained in the page on Anthony's site, which is more detailed about the difference than on the options page. The former clips/stretches relative to the min and max bin. The latter stretch/clips relative to black and white. If linear-stretch has been made channel sensitive, I am not aware of that.
rnbc
Posts: 109
Joined: 2010-04-11T18:27:46-07:00
Authentication code: 8675308

Re: Strange behavior in linear_stretch with HDRI (6.6.1-4)

Post by rnbc »

fmw42 wrote: Linear stretches from full black and white, so that is why it does nothing for -linear-stretch 0. If your min and max graylevels are far from black and white, any small clip amount will jump all the way to those graylevels for linear-stretch and will go a little further for -contrast-stretch as it starts at the min and max (bins).

It is quite possible that the histogramming has problems in HDRI. I do not know. But I still think you have to understand the difference between linear-stretch and contrast-stretch.
Now that I've read the code and documentation I think you're right, both contrast-stretch and linear-stretch go from full white to full black. They calculate their transformations based on luminance alone. "Clipping" for them means "clip all channels", not "clip one channel is enough".

Hum... maybe another operator, or an option? :oops:
rnbc
Posts: 109
Joined: 2010-04-11T18:27:46-07:00
Authentication code: 8675308

Re: Strange behavior in linear_stretch with HDRI (6.6.1-4)

Post by rnbc »

The thing is, in the docs it says:

"All the channels are normalized in concert by the came amount so as to preserve color integrity, when the default +channel setting is in use."

I can't see where color integrity is being preserved here at 0.0001% levels or whatever is specified... :?
User avatar
fmw42
Posts: 25562
Joined: 2007-07-02T17:14:51-07:00
Authentication code: 1152
Location: Sunnyvale, California, USA

Re: Strange behavior in linear_stretch with HDRI (6.6.1-4)

Post by fmw42 »

-contrast-stretch clips from min and max bins. -linear-stretch clips from black and white -- that is why -linear-stretch 0 does nothing and why -contrast-stretch 0 is like -auto-level and stretches from min and max bin to full black and white. Also -contrast-stretch can use -channel to select and stretch one channel at a time. Whereas (as far as I know) -linear-stretch does all channel together. That is it computes the full histogram from all channel combined and stretches relative to that. I don't think that is equivalent to luminance channel, but perhaps it is using that.
Hum... maybe another operator, or an option?
Can you explain what exactly you are trying to do. Perhaps I can point you to the right function. Perhaps it is just auto-level that you need. It is channel sensitive so you can stretch each channel separately with no clipping.
rnbc
Posts: 109
Joined: 2010-04-11T18:27:46-07:00
Authentication code: 8675308

Re: Strange behavior in linear_stretch with HDRI (6.6.1-4)

Post by rnbc »

In that specific operation I'm trying to stretch contrast while not burning more than X% pixels and blacking out more than Y% pixels.

My definition of "burn" and "black out" is, it seems, different from the current one used in ImageMagick: I would consider that if one channel burns the pixel has already burned, while it seems ImageMagick considers that the pixels has only burned if luminance burns. At least that's what I can seem in the histograms when I use 0.0001% as an input, both in contrast-stretch and in linear-stretch.

Why not just use auto-level? Well... because sometimes there are some outlier pixels (noise) that ruin contrast.

Why not stretch each channel separately? Because that changes color balance...
User avatar
fmw42
Posts: 25562
Joined: 2007-07-02T17:14:51-07:00
Authentication code: 1152
Location: Sunnyvale, California, USA

Re: Strange behavior in linear_stretch with HDRI (6.6.1-4)

Post by fmw42 »

contrast-stretch with no -channel selection should not change color balance, neither should linear-stretch. at least it does not in non_hdri to my knowledge.

original:
Image

convert port.jpg -contrast-stretch .1x.1% port_cs0p1.jpg
Image


convert port.jpg -linear-stretch .1x.99% port_ls0p1.jpg
Image
rnbc
Posts: 109
Joined: 2010-04-11T18:27:46-07:00
Authentication code: 8675308

Re: Strange behavior in linear_stretch with HDRI (6.6.1-4)

Post by rnbc »

fmw42 wrote:contrast-stretch with no -channel selection should not change color balance, neither should linear-stretch. at least it does not in non_hdri to my knowledge.
That is what I said, with a caveat... if channels are changes in sync the color won't change but, OTOH if you do that you'll burn much more pixels than intended...

convert port.jpg -linear-stretch .1x.99% port_ls0p1.jpg
Image

... that image you posted has about ~50% of the pixels burned in the blue channel, not 1% as intended, effectively ruining color balance in the sky.
rnbc
Posts: 109
Joined: 2010-04-11T18:27:46-07:00
Authentication code: 8675308

Re: Strange behavior in linear_stretch with HDRI (6.6.1-4)

Post by rnbc »

My proposal for linear-stretch (or let's call it a new name, perhaps) is as follows...

Define 2 functions:

max(r,g,b){
if ((r >= g) && (r >= b)) return r;
if ((g >= r) && (g >= b)) return g;
if ((b >= r) && (b >= g)) return b;
}

min(r,g,b){
if ((r <= g) && (r <= b)) return r;
if ((g <= r) && (g <= b)) return g;
if ((b <= r) && (b <= g)) return b;
}

Generate 2 new pixel buffers (or one, used 2 times), with the max(r,g,b) and min(r,g,b) values for each image pixel.

max_rgb=map(max(r,g,b),image);

min_rgb=map(min(r,g,b),image);

Order them, ascending for example.

Take the correct % value from the min_rgb and max_rgb buffer.

For example, if you want to burn 1% and black 2% and your array contains 1000000 pixels:

min_value=min_rgb[10000];

max_value=max_rgb[999999-10000];

Let's imagine now that min_value = 0.0233 and max_value = 0.8888

Then apply the correct linear stretch to the original image:

new_pixel = (old_pixel - 0.0233) * (1/(0.8888-0.0233));

Was my explanation clear?

This guarantees that at most x% of the pixels get one of the channels blacked-out and one of the channels whited-out without otherwise compromising color in any of the other pixels.

PS: the reason I'm proposing an intermediate array with all pixels instead of an histogram it's because in HDR, since you're using floating point, it's unlikely any pixel will have an identical pair anyway.
User avatar
fmw42
Posts: 25562
Joined: 2007-07-02T17:14:51-07:00
Authentication code: 1152
Location: Sunnyvale, California, USA

Re: Strange behavior in linear_stretch with HDRI (6.6.1-4)

Post by fmw42 »

I believe that you want to find some min and max value such that you have increased or decreased the pixel count from the true min and max by some percent count (not graylevel). However, histograms work by bins and not exact counts, since the IM histogram is limited to 1024 bins which is much smaller than the number of pixels in the image. Thus bins contain many pixels. And this is what -contrast-stretch does using the combined histogram. Except it includes the bin containing the true min and max values for clipping and increments or decrements bins from those bins by the given percent count translated into bins.

Linear stretch finds some graylevel for each channel that is in those bins (not sure where in the bin) and stretches using -level between those graylevels rather than stretching bins. However, it burns in a closer amount as it starts from true black and true white and does not count bins from the min and max bin. This is what I think you want.

Note your example has an error as you wanted 1% and 2% and use the same number for each (10,000), but one should be 20,000.

Anthony and I have long discussed some new stretch that has the best properties of -contrast-stretch and -linear-stretch (and other variations). But he has not had time to implement it. Perhaps he will see this and add his comments or clarifications.

Your comments are welcome and I am sure he will add those to his notes before implementing something.

However, you are welcome to implement something yourself and contribute it to the IM library.
rnbc
Posts: 109
Joined: 2010-04-11T18:27:46-07:00
Authentication code: 8675308

Re: Strange behavior in linear_stretch with HDRI (6.6.1-4)

Post by rnbc »

Correct, it should be 999999-20000, not 999999-10000 !

Apart from that believe it's all correct, right?

I think for an exact algorithm one should not use histograms but go straight into an array the size of the image. Note that since the array would only contain 1 value per pixel, instead of 3, of would only increase memory into 133% of the original image anyway.

I would implement this. In fact I will give it a try, just give me some pointers... Is there a start point for would-be ImageMagick developers? The code looks rather convoluted to me :? I tried in the past with contrast-stretch, without too much success :P

An algorithm like this in ImageMagick framework would probably need to deal with alpha(don't know how...), and at least not crash on grayscale images (but it's useless on them anyway, same as linear-stretch conceptually).
Post Reply