I was working with some less than ideal photographs, and wanted to adjust them before continuing. I wanted to raise the levels of the very dark areas and lower the levels of the very light areas. I couldn't find a function in Mathematica 8 that would allow me to do this. As a quick work-round, I wrote a function that quickly adjusted the levels. It looks terrible in this simplified example (I had more levels going, for one thing), but you get the idea (I hope!).

image1 = Image[
       Table[i - 3 Sin[i^2 + j^2], 
           {i, -4, 4, .03}, 
           {j, -4, 4, .03}]]]; 
tweakC = Compile[{pixel}, Module[{ p = pixel},
   m = Mean[p];
        m < 0.3, p = pixel * 1.5,
            m > 0.85, p = pixel * 0.8 ,
            m > 0, p = p ]]];
image2 = ImageAdjust[ImageApply[tweakC, image1]];
ImageAssemble[{image1, image2}]

before and after:

What's the best way to do this? I don't think ImageAdjust or ImageClip do what I want.


Two things.

First, a minor point: if you rewrite your compiled function as

tweakC = Compile[{{pixel, _Real, 1}},
   m = Mean[pixel];
    m <= 0.3, pixel*1.5,
    m >= 0.85, pixel*0.8,
    True, pixel]

then the ImageApply bit is 20 times faster (due to not having to use external calls). It's also a bit cleaner.

If you have v8 and a C compiler, you can speed it up by another factor of 2 by using CompilationTarget->"C".

Second, and more important, is that your tone curve looks like this:

enter image description here

the jumps at .35 and .8 lead to harsh transitions. So I thought I'd use a smoother curve which you can interactively manipulate (horrible code, but seems to do the job):

image1 = Image[
    Table[i - 3 Sin[i^2 + j^2], {i, -4, 4, .03}, {j, -4, 4, .03}]]];
 {pts = {{0, 0}, {.25, .25}, {.5, .5}, {.75, .75}, {1, 1}}}, Dynamic[];
   curve = InterpolatingPolynomial[pts, x];
   image2 = ImageAdjust[
     ImageApply[Function[{x}, Evaluate@curve], image1, 
      Interleaving -> False]];
    Plot[curve, {x, 0, 3}, PlotRange -> {{0, 1}, {0, 1}}]]
   LocatorAutoCreate -> True
 {{image1, Dynamic[image2]}}

it looks like this:

enter image description here

The idea is, you define a curve by moving the locators, and the image on the right bottom reflects that transformation. The whole thing is interactive. You may add more locators by alt-clicking on windows and linux, cmd-clicking on OS X.

Note that I have little understanding of Dynamic etc, so this is probably badly written in terms of dynamic interactivity.

  • $\begingroup$ snap! I used Erf though. $\endgroup$ – Verbeia Jan 18 '12 at 21:29
  • 1
    $\begingroup$ Basically, this works similarly to the curves tool in any image editor, see eg cambridgeincolour.com/tutorials/photoshop-curves.htm $\endgroup$ – acl Jan 18 '12 at 22:23
  • 1
    $\begingroup$ This is amazingly cool. It's cute how you implemented curves functionality using Locator and Dynamic. $\endgroup$ – Mike Bailey Jan 19 '12 at 3:20
  • 1
    $\begingroup$ @Mike much cheaper than photoshop! (and I lifted much of the code wholesale from the docs) $\endgroup$ – acl Jan 19 '12 at 3:24
  • 3
    $\begingroup$ Neat - this is basically "Image>> Adjustments>>Curves" menu in Photoshop. The only suggestion is to add LocatorAutoCreate -> True option to LocatorPane[...] function so by pressing ALT+CLICK you can add or remove locators. For precision-crafting of the curve. $\endgroup$ – Vitaliy Kaurov Jan 19 '12 at 14:59

I liked @acl implementation of "curves". Here is a note on simple and effective image improvement. These is built in functionality in ImageAdjust[] :

 Row[{image, ImageAdjust[image, {x, y, z}]}], {{x, 0, "contrast"}, 0, 
  1}, {{y, 0, "brightness"}, 0, 1}, {{z, 1, "gamma correction"}, .1, 

enter image description here

Also this interactive interface is built in in M. Right click on the image, in context menu choose "Adjust Image" - and you can do it quickly via popped up interface, good for workflow.

enter image description here

  • $\begingroup$ I've got a couple more hours before I can upvote again, but +1! $\endgroup$ – Verbeia Jan 18 '12 at 22:12
  • $\begingroup$ @vitaliy that looks promising. It might do the trick, but I'll have to se if it modifies the mid-range of the photos... $\endgroup$ – cormullion Jan 18 '12 at 22:14
  • $\begingroup$ @vitaliy But isn't this changing the whole image? Not just the badly exposed areas? $\endgroup$ – cormullion Jan 18 '12 at 22:37
  • $\begingroup$ Yes but mostly in such a way that whole image improves. You cannot sharply distinguish between "bad" and "good" areas, in a typical meaningful image even "bad" areas are correlated with their "better" neighborhood, so to have no abrupt lines you have to modify the neighborhood too a bit. For more advanced techniques you can see M8 function Inpaint[...]. Tone curve is also something to look at - bu this demonstration does not use Image Processing really, just shows general algorithm with pixels. I am sure it can be done better in M8: demonstrations.wolfram.com/ToneCurve $\endgroup$ – Vitaliy Kaurov Jan 18 '12 at 22:57

acl's answer addresses performance issue. I would like to address the design of your filter.

The fact that you are using a discrete function can't be helping. It will inherently create 'edges' in the graphic, even with more levels.

I'm also a bit confused by the fact that the output of your function is non-monotonic.

Plot[Which[m < 0.3, m*1.5, m > 0.85, m*0.8, m > 0, m], {m, 0, 1}]

enter image description here

EDIT In fact my experimentation suggests that any function with kinks, let alone discontinuities such as the one you have used, will have problems with colour shifting.

I would suggest using a function that looks more like this, if you want to make dark areas darker and light areas lighter. You can tweak the parameter 5 to taste.

Plot[0.5 Erf[5. (m - 0.5)] + 0.5, {m, 0, 1}]

enter image description here

tweakL = Compile[{pixel}, 
   Module[{p = pixel, m}, m = Mean[p]; 
    p = pixel *0.5 Erf[5. (m - 0.5)] + 0.5]];

image2 = ImageAdjust[
  ImageApply[tweakL, image1]]; ImageAssemble[{image1, image2}]

enter image description here

To reduce the contrast, you need to define a similar function that is smooth and has the desired slopes, much as acl has done

  • $\begingroup$ Thanks. I'll play with this and see if I can make the dark areas lighter and the light areas darker. But it looks like I didn't miss an obvious 'ImageExposure' function, which is something. $\endgroup$ – cormullion Jan 18 '12 at 22:09

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.