# Which values did ImageAdjust choose?

I have a picture obtained from a camera in an experiment. It's a .dat file, contains only numbers. I can re-create the image by importing it as data and using the Image[*data*] command.

However, I need to use ImageAdjust[Image[data]] to make something visible. Since I want to perform the same adjustment on several pictures, I need to know exactly which values ImageAdjust chooses for brightness, contrast, etc.

How to find out?

• According to the documentation ImageAdjust[Image[data]] is equivalent to Image[Rescale[data]]. If you want the same adjustment you'll have to specify it explicitly. – MikeLimaOscar Aug 25 '15 at 9:17
• I don't see where it says it is equivalent to Rescale, but it is clearly not true for multi channel images. For color images ImageAdjust works independently on each channel while Rescale applies a single transformation to the whole data array. Oddly ImageAdjust does not provide options to manually specify each channel range, so you cant use it directly even if you know the transform the auto setting used. – george2079 Aug 25 '15 at 14:01
• MikeLimaOscars answer wasn't directly what I wanted, but playing around with Rescale definitively got me some steps further! Thanks! – keyx Aug 26 '15 at 9:34
• A duplicate question: (14956). – Alexey Popkov Apr 4 '17 at 8:38

Here is a look at the default behavior of ImageAdjust on a color image:

 img = ExampleData[{"TestImage", "Lena"}];
Column[ Table[
inout  =
Transpose@(Flatten[ImageData@ColorSeparate[#][[channel]], 1] & /@
fit = LinearModelFit[ inout , x , x];
Row[{fit,
Show[{Plot[fit[x], {x, 0, 1}, PlotStyle -> Red],
ListPlot[inout]},
ImageSize -> 300]
}] , {channel, 3}]]


What you see is a simple linear transform on each channel. We could have got at those numbers just by looking at the data range for each channel:

Table[
range = Sort[
Flatten[ImageData@ColorSeparate[img][[channel]], 1]][[{1, -1}]];
({range[[1]] # , -#} &@ 1/(Subtract @@ range)) , {channel, 3}]


{{-0.268657, 1.26866}, {-0.0122449, 1.04082}, {-0.0368664, 1.17512}}

From this I suppose the answer to the question should be to first pass through all the images and find the absolute range over all the images for each channel, then use ImageApply to do the scaling.

• Thanks a lot, I think this one is exactly what I searched. – keyx Aug 26 '15 at 9:33

Assuming all your images are the same size and channel depth you can create a per pixel, per channel scale factor base on what ImageAdjust has done and multiply each image by it.

adjustImages[keyImage_, images_] :=
Image@ArrayReshape[scaleFactor Flatten@ImageData@#,
Dimensions@ImageData@keyImage]&/@images]


And apply so:

adjustImages[kim,{im1,im1,im3,...,imn}]


You may want to decide what the the result should be if rescaling using the key image pushes values beyond the normal range and saturates.

It may also be worth exploring what it actually is you want to adjust in the images to improve performance on whatever pipeline follows, rather than trusting ImageAdjust to optimise things to your requirements.

• ImageAdjust actually does a linear transform, so properly you need to fit to a scale + offset form. (this will work if keyimage has at least one completely black pixel.. ) – george2079 Aug 25 '15 at 17:51

Under the "Properties & Relations" on the Documentation page for ImageAdjust we read:

ImageAdjust[image] is equivalent to ImageAdjust[image,{0,0,1},{min,max},{0,1}] where {min,max} is the channel-wise pixel ranges in image:

ImageAdjust[img] ==
ImageAdjust[img, {0, 0, 1}, ImageMeasurements[img, {"Min", "Max"}], {0, 1}]

 True


So ImageAdjust[img] works channel-wise and by default simply rescales channel values on the base of MinMax ranges returned by ImageMeasurements.

Note however that due to a bug in ImageAdjust currently we have to unpack the output of ImageMeasurements manually:

img = ExampleData[{"TestImage", "Sailboat"}];
ImageAdjust[img, {0, 0, 1}, List @@@ ImageMeasurements[img, {"Min", "Max"}], {0, 1}]

True


The output of ImageMeasurements is just Transpose of MinMax applied separately to each channel:

Transpose[MinMax /@ ImageData[img, Interleaving -> False]] ==
ImageMeasurements[img, {"Min", "Max"}]

True


We can also understand the default behavior of ImageAdjust[img] as applying Rescale separately to each channel:

data = ImageData[img, Interleaving -> False];
res1 = ImageAdjust[Image[data, Interleaving -> False]];
res2 = Image[Rescale /@ data, Interleaving -> False];
Max@Abs[ImageData[res1] - ImageData[res2]]

1.11022*10^-16


It is worth to note here that ImageAdjust always returns an image with the same ImageType as the original image:

ImageType@ImageAdjust@Image[img, #] & /@ {"Bit", "Byte", "Bit16", "Real32", "Real"}

{"Bit", "Byte", "Bit16", "Real32", "Real"}


This explains significant difference which we get in the cases like the following (caused by rounding off the channel values in the case of the "Byte" image):

img = ExampleData[{"TestImage", "Sailboat"}];

"Byte"
0.0019607843137254832
`