3

I am trying to do some image processing for which I am given an 8-bit grayscale image. I am supposed to change the contrast of the image by generating a lookup table that increases the contrast for pixel values between 50 and 205. I have generated a look up table using the following MATLAB code.

a = 2;
x = 0:255;
lut = 255 ./ (1+exp(-a*(x-127)/32));

When I plot lut, I get a graph shown below:

enter image description here

So far so good, but how do I go about increasing the contrast for pixel values between 50 and 205? Final plot of the transform mapping should be something like:

enter image description here

David Norman
  • 301
  • 2
  • 10
  • 18
  • 1
    for each pixel intensity (x) assign the value in y. So for example a pixel with value 50 is assigned a value of 255./(1+exp(-2*(50-127)/32)) ~0 and a pixel of 205 a value of 255./(1+exp(-2*(205-127)/32)) ~255 – ASantosRibeiro Aug 07 '14 at 20:10
  • 1
    Y = lut(x+1) is the new value – nicolas Aug 07 '14 at 20:13
  • @ASantosRibeiro, Thanks. Also, when I increase the value of 'a' (which is currently 2) I see that the image goes brighter. Can you tell me why that is? or is 'a' supposed to be a fixed value which must never be changed? – David Norman Aug 07 '14 at 21:07
  • @DavidNorman - `a` controls the rate of change of the curve. When `a` increases, the rate of change increases, which means that the `exp` part of your equation will go towards zero more rapidly. By taking the inverse of this, you will obtain higher values more quickly as you increase the intensity when `a` is larger in comparison to it being smaller. As such, as `a -> infty` contrast gets higher. – rayryeng Aug 07 '14 at 21:21

2 Answers2

5

Judging from your comments, you simply want a linear map where intensities that are < 50 get mapped to 0, intensities that are > 205 get mapped to 255, and everything else is a linear mapping in between. You can simply do this by:

slope = 255 / (205 - 50); % // Generate equation of the line - 
                          % // y = mx + b - Solve for m
intercept = -50*slope; %// Solve for b --> b = y - m*x, y = 0, x = 50
LUT = uint8(slope*(0:255) + intercept); %// Generate points
LUT(1:51) = 0; %// Anything < intensity 50 set to 0
LUT(206:end) = 255; %// Anything > intensity 205 set to 255

The LUT now looks like:

plot(0:255, LUT);
axis tight;
grid;

enter image description here

Take note at how I truncated the intensities when they're < 50 and > 205. MATLAB starts indexing at index 1, and so we need to offset the intensities by 1 so that they correctly map to pixel intensities which start at 0.

To finally apply this to your image, all you have to do is:

out = LUT(img + 1); 

This is assuming that img is your input image. Again, take note that we had to offset the input by +1 as MATLAB starts indexing at location 1, while intensities start at 0.


Minor Note

You can easily do this by using imadjust, which basically does this for you under the hood. You call it like so:

outAdjust = imadjust(in, [low_in; high_in], [low_out; high_out]);

low_in and high_in represent the minimum and maximum input intensities that exist in your image. Note that these are normalized between [0,1]. low_out and high_out adjust the intensities of your image so that low_in maps to low_out, high_in maps to high_out, and everything else is contrast stretched in between. For your case, you would do:

outAdjust = imadjust(img, [0; 1], [50/255; 205/255]);

This should stretch the contrast such that the input intensity 50 maps to the output intensity 0 and the input intensity 205 maps to the output intensity 255. Any intensities < 50 and > 205 get automatically saturated to 0 and 255 respectively.

rayryeng
  • 102,964
  • 22
  • 184
  • 193
  • Thanks for your answer ray. When I plot this new transform of values between 50 and 205, I am supposed to see a ramp that stays at 0 from 0:50 on x-axis and increases linearly to 205 at 255 on x-axis? I have added a picture of the ramp in my question – David Norman Aug 07 '14 at 21:24
  • @DavidNorman Oh... so do you want a **linear** ramp, or an exponential ramp like the image you have provided? If you want a linear ramp, all you have to do is `outAdjust = imadjust(in, [0; 1], [50/255; 205/255]);`. This completely changes my answer. I'll modify my post. – rayryeng Aug 07 '14 at 21:27
  • Sorry for being misleading, the exponential plot was to show what I get when I generate the look up table. And also I should add that I am not allowed to use matlab functions. We have to learn to make our own functions which is where I am struggling currently. I do not understand the whole concept of 'mapping' and intensity and stuff – David Norman Aug 07 '14 at 21:32
  • Yes, the whole point of this exercise is to learn how linear ramp of this sort makes the picture more pleasing with bit more details emerging – David Norman Aug 07 '14 at 21:38
  • what I still don't understand is how a range of values from 0:255 increase the contrast of image that is 512x512 in size or any other size? How does mapping work? In order to increase the contrast of the image is to set the pixels to low value right? – David Norman Aug 08 '14 at 03:58
  • @DavidNorman - This is a pixel based transformation. One pixel of a certain intensity gets mapped to another pixel. This is independent of size. The ramp you see defines the input / output relationship. You take each pixel in your image, look at its intensity value on the `x`-axis, then consult a LUT and see what the output intensity is, the `y`-axis. Think of it as a black box, where an input intensity goes in, and an output intensity comes out. For example, if an input pixel has intensity `50`, the output will be zero. These transformations are one of the simplest in image processing. – rayryeng Aug 08 '14 at 04:29
  • @DavidNorman - We specify between `0-255` because images are usually unsigned 8-bit integer, and so the dynamic range of the intensities that occur in the image range from `0-255`. We specify this range because we want to cover all possibilities. We need to define an output intensity, given an input intensity `0`, same for `1`, `2`, etc. We have 256 possible intensities, covering 256 possible outputs. – rayryeng Aug 08 '14 at 04:32
  • @DavidNorman - What this transformation is specifically doing is increasing the contrast of your image. The higher the spread, the better the contrast. Contrast is defined as how well you can make out objects from the background. Loosely speaking, contrast talks about the spread of pixel intensities over your image. What this ramp is doing is that any intensities that range between `50-205` get a slight boost in intensity, where you're clipping anything below 50 and above 205. This effectively makes the background more subdued while the object pixels pop out more, thus increasing contrast. – rayryeng Aug 08 '14 at 04:36
  • let me get this right, so if I specify intensity values between 50 and 205 that means in my Image all the pixels that have a value of less than 50 get mapped to 0? similarly, all the pixel values in the image greater than 205 get overwritten to 255? – David Norman Aug 08 '14 at 04:37
  • @DavidNorman - Yes. Anything between 50 and 205... that's assuming that those pixels belong to objects, and so those pixel intensities will pop out more. Anything above 205 or below 50 get saturated to white or black. – rayryeng Aug 08 '14 at 04:37
  • But how does matlab know which index has what pixel value for it to map? – David Norman Aug 08 '14 at 04:38
  • @DavidNorman - That is through how MATLAB indexes the image. For example, supposing I have an array of `A = [1 1 1 2 2 2 3 3 3 4 4 4];` By doing `B = A([4 8 10 12]);`, this is the same as `B = [2 3 4 4];`. You can specify an array that references which part of `A` we want to grab. As such, the image pixels will naturally access the corresponding locations in the LUT and output an image of the same size that does this mapping exactly. Therefore, should an image have intensity 3, we will access what the LUT value is at input intensity 3 and we output this. Remember to offset by `1`! – rayryeng Aug 08 '14 at 04:39
  • Referring back to your comment. 'The higher the spread, the better the contrast.' I noticed that when I make the range narrower I get more details than having a higher range. What we are supposed to do it try out different range of values to get the best picture possible – David Norman Aug 08 '14 at 04:44
  • @DavidNorman - That's correct. When you restrict the range, you are increasing the rate of change, so you will get a higher boost in intensities. What I would suggest you do is take a look at your image, and see what the background intensities are... open up your image using `imshow`, then type in `impixelinfo`. You can then use your mouse and hover over the image, and it spits out what the intensities are over your mouse. Get an idea of what the average intensity is in the background... this would be your lower end (or your "50"). – rayryeng Aug 08 '14 at 04:46
  • @DavidNorman Next, take a look at your object and do the same thing. Find the average pixel intensity and this would be your higher end (or your "205"). Once you find these, go ahead and apply your ramp. I'm off to bed, so good luck! – rayryeng Aug 08 '14 at 04:47
2

You need to take each pixel in your image and replace it with the corresponding value in the lookup table. This can be done with some nested for loops, but it is not the most idiomatic way to do it. I would recommend using arrayfun with a function that replaces a pixel.

new_image = arrayfun(@(pixel) lut(pixel), image);

It might be more efficient to use the code that generates lut directly on the image. If performance is a concern and you don't need to use a lookup table, try comparing both methods.

new_image = 255 ./ (1 + exp(-image * (x-127) / 32));

Note that the new_image variable will no longer be of type uint8. If you need to display it again (say, with imshow) you will need to convert it back by writing uint8(new_image).

Andrew
  • 1,839
  • 14
  • 25
  • 2
    Just a minor comment. `arrayfun` in that capacity seems wasteful. You can simply do: `new_image = lut(image+1);`. The `+1` is to accommodate for the indexing start at 1, while image pixel intensities start from 0. – rayryeng Aug 07 '14 at 20:34