How to retrieve the parameters calculated and used by ImageAdjust?


Major update at the bottom. First part may be obsolete.


A brute force approach:

Define a function that provides a measure of the difference between the automatically adjusted image and an image with given contrast, brightness and gamma adjustments (for now, this only works for images that are made of a raster of color triplets):

ClearAll[f];
f[c_?NumericQ, b_?NumericQ, γ_?NumericQ, im_Image] := 
    Total[Map[#.# &, ImageData@ImageAdjust[im] - ImageData@ImageAdjust[im, {c, b, γ}], {2}], 2]

Get an image:

img=Import["http://i.stack.imgur.com/nAaCm.png"]

Mathematica graphics

Now minimize the image differences:

adj = FindMinimum[f[c, b, γ, img], {{c, 0.1, 0, -1, 1}, {b, 0.1, 0, -1, 1}, {γ, 1.1, 1, 0.1, 3}}]

{14.19915417, {c -> -0.06143890985, b -> 0.09520280553, γ -> 1.094933574}}

Comparison of original image and automatically adjusted image:

Row[{img, ImageReflect[ImageAdjust[img], Left -> Right], img}]

Mathematica graphics

Comparison of the semi-automatically adjusted image and fully automatically adjusted image:

Row[{ImageAdjust[img, {c, b, γ} /. adj[[2]]],
     ImageReflect[ImageAdjust[img], Left -> Right], 
     ImageAdjust[img, {c, b, γ} /. adj[[2]]]}
]

Mathematica graphics

The differences are small:

ImageSubtract[ImageAdjust[img, {c, b, γ} /. adj[[2]]], ImageAdjust[img]]

Mathematica graphics

And 'small' is this:

ImageSubtract[ImageAdjust[img, {c, b, γ} /. adj[[2]]], ImageAdjust[img]] // ImageData // Max

0.0431372549

Using ImageAdjust itself to enhance those differences:

ImageSubtract[ImageAdjust[img, {c, b, γ} /. adj[[2]]], ImageAdjust[img]] // ImageAdjust

Mathematica graphics

Not quite an exact match but close.


EDIT

Originally, I believed that ImageAdjust does a simple histogram stretch, scaling the maximum pixel value to 1 and the minimum to 0, basically this:

imgd = img // ImageData;
min = Min[imgd];
max = Max[imgd];
Rescale[imgd, {min, max}, {0, 1}]

But that simply didn't work out. If I use the measure introduced in the beginning to determine the difference between the {c,b,γ}-adjusted pictures and the automatically ImageAdjusted pictures on the one hand and the rescaled pictures and the automatically ImageAdjusted pictures on the other hand, the {c,b,γ} seems to be better 4 out of the 5 test images I used it on.

(
   img = #;
   imgd = img // ImageData;
   min = Min[imgd];
   max = Max[imgd];
   adj = FindMinimum[f[c, b, γ, img], 
                     {{c, 0.1, 0, -1, 1}, {b, 0.1, 0, -1, 1}, {γ, 1.1, 1, 0.1, 3}}];
   {Total[Map[#.# &,
          ImageData@ImageAdjust[img]-ImageData@ImageAdjust[img, {c, b, γ} /. adj[[2]]], {2}],
      2],
    Total[Map[#.# &, ImageData@ImageAdjust[img]-Rescale[imgd, {min, max}, {0, 1}], {2}], 2]
    }
   ) & /@ { ...images...}

For the five pictures on the ImageAdjust doc page I get:

{{50.5394233, 81.76899059}, {14.19915417, 14.46839005}, {30.34660515, 46.0902785}, {6.459238754, 2.528418174}, {537.6580546, 1011.243348}}

It seems that ImageAdjust does more than a simple stretching of the values. It probably uses histogram equalization, which linearizes the cumulative distribution function of the pixel intensity values. This is a rather non-linear transformation. It probably requires a transformation to another color space first, to separate color values from luminance values (CIELab for instance). The transformation will then be done on the luminance component only after which an inverse color space transformation occurs.


Yet another edit

Trying to test the hypothesis Mathematica is using histogram equalization.

Load an image and turn it into grayscale (we won't do this colorspace hence and forth stuff here):

img = Import["http://i.stack.imgur.com/xGMge.png"]
imgG = ImageData[ColorConvert[img, "Grayscale"]];

Mathematica graphics

Do histogram equalization:

ClearAll[cdf];
SetAttributes[cdf, Listable]
Scan[(cdf[#[[1]]] = #[[2]]) &,
     Transpose[MapAt[Accumulate, Transpose[Sort@Tally[Flatten[imgG]]], 2]]]
imgH = (cdf[imgG] - cdf[Min[imgG]])/(Times @@ ImageDimensions[img] - cdf[Min[imgG]]);

Accumulated counts before equalization:

ListPlot@Transpose[MapAt[Accumulate, Transpose[Sort@Tally[Flatten[imgG]]], 2]]

Mathematica graphics

Accumulated counts after equalization:

ListPlot@Transpose[MapAt[Accumulate, Transpose[Sort@Tally[Flatten[imgH]]], 2]]

Mathematica graphics

Image comparisons:

Original grayscale image:

imgG // Image

Mathematica graphics

Automatically adjusted image:

imgG // Image // ImageAdjust

Mathematica graphics

Histogram equalized image:

imgH // Image

Mathematica graphics

Clearly, ImageAdjust does not do a histogram equalization.


Final edit

For grayscale images at least, Mathematica seems to do a simple rescaling after all. The rescaling I used above used the minimum and maximum values measured over all color channels. In a grayscale image, rescaling with

Rescale[imgG, {Min[imgG], Max[imgG]}, {0, 1}] 

yields an image that is virtually identical to the adjusted image:

(imgG // Image // ImageAdjust // ImageData) - 
 Rescale[imgG, {Min[imgG], Max[imgG]}, {0, 1}] // Abs // Max

2.220446049*10^-16

So, conclusion: there are no {c, b, γ} parameters to be estimated because ImageAdjust, called without additional parameters, doesn't use them.


With only one argument, ImageAdjust[img] performs histogram stretching, ie. pixel values, which run from min to max in img, are rescaled so they run from 0 to 1 in the ouput image.

The setting {contrast, brightness, gamma} = {0, 0, 1} does not change anything: ImageAdjust[img, {0, 0, 1}] === img gives True.