Wednesday, July 3, 2013

Activity 6 - Enhancement by Histogram Manipulation

This activity, as the title suggests, involves playing around with the histogram of a grayscale image to correct or enhance the image. In our case, we chose a dark image and we want to enhance the image by correcting the brightness and contrast levels so that more detail will be visible.

So here's what I did. First I chose my image. I have a sunset image of Dresden, Germany (from my adviser Dr. Rene Batac who's taking up his postdoc there) as shown below:
Figure 1. Sunset in Dresden. My adviser sent this to me
because he knew how much I loved sunsets, and it was a
particularly beautiful sunset.

As you can see, the foreground is dark while the background is light, and so I cropped the image to only select the lower half of the sunset image,
Figure 2. Lower half of the sunset image above. The foliage
and a few buildings are barely visible in this half.
and used this image for the activity today. The goal now is to adjust the brightness and contrast levels to make the foreground visible.

To enhance my image, here are the steps I did. First, I converted the image to grayscale using Scilab and took the histogram of the gray values. Normalizing the histogram, I would obtain the probability density function (PDF) of my image. Strictly speaking, a PDF describes the probability that a variable may take on a specific value, but here the PDF of the image describes the likelihood that a gray value (from 0-255) will appear on the image. As we can see in the images below, the PDF peaks around the lower values, signifying that the image is mostly dark-pixeled. Another related graph is the cumulative distribution function (CDF) of the image, which describes the likelihood that a gray value will be less than or equal to some given gray value.
For my image, the CDF is rapidly increasing along low gray values and tapers of at 1. This is expected, as the probability cannot be more than 1.
Figure 3. from left to right: (a) histogram; (b) PDF; and (c) CDF of the grayscaled
image shown in Figure 2. The gray values are concentrated at the lower end.
The next task is to correct the image by applying a linear CDF onto the original image. To do this, I first created a uniformly distributed PDF and took the corresponding CDF using Scilab. For each CDF value in the image, I need to find the corresponding image pixel in the linear CDF. My first idea was to use find() and return the index of the CDF value. However this does not work, as the CDF values in the image and in the linear CDF I constructed do not correspond. The trick is to use the mathematical equations and treat the CDF as a continuous function, rather than a matrix.


Suppose I have my CDF values from the original image. Now, the linear CDF can be expressed by a linear equation of the form y = kx, where k is a constant, which is equal to 1/255. So, to obtain the grayscale values in the linear CDF, I only need to multiply the CDF by 255. For example, suppose I have a CDF value of 0.42 from the image. The corresponding gray value in the linear CDF is just 0.42*255 or 107.1. However, since gray values are discreet from 0-255, we need to round it off to the nearest integer.

In the script I wrote, after converting the image into grayscale, I replaced the gray values on the image matrix by the corresponding CDF values (using a for loop, since I could not find the proper matrix operation I needed). Then I multiplied the matrix by 255 to get the corresponding gray values of the linear CDF. There is a command in Scilab called uint8 that converts the matrix into an 8-bit image. Below is the actual code snippet I used.
path = "C:\my_img_path\";
stacksize('max');             //to increase the max size of a matrix
im = imread(path+'\pic.jpg');
im = rgb2gray(im);

//CDF OF ORIGINAL
hist = CreateHistogram(im);
pdf = hist/(1080*406);        //normalize by total number of pixels
cdf = cumsum(pdf);
im_new = zeros(im);
for r = 1:406
    for c = 1:1080
        im_new(r,c)=cdf(im(r,c)+1);    //replace gray values by CDF
    end                                  values
end

//CDF OF LINEAR
im_new = uint8(im_new*255);
hist2 = CreateHistogram(im_new);
pdf2 = hist2/(1080*406);
cdf2 = cumsum(pdf2);
The result is an adjusted image, as shown in Figure 4 below. Also, observe the histogram, PDF and CDF of the adjusted image. The gray values are now more spread out from 0-255, and the CDF approximates the shape of the linear CDF that I used as correction.
Figure 4. Corrected image using a linear CDF as correction.

Figure 5. from left to right: (a) histogram; (b) PDF; and (c) CDF of the corrected
image using the linear CDF.
Next, the human eye has a nonlinear response, and so the more appropriate correction to the image should be a nonlinear CDF. First I chose to use a gaussian PDF, which gives a sigmoidal CDF. At first I just chose a gaussian function out of a whim, since I was not really particular. The form of the function I used is
where x is the gray value and y is the PDF, with μ, σ and k are constants. While manipulating the constants, I realized that μ dictates at which gray value the gaussian is centered, which is related to the brightness level and σ dictates the width of the gaussian, which is related to the contrast of the image. And so I played around with different brightness and contrast levels, looking for the right combination for my image to be corrected.

I used the same code snippet above, but instead of multiplying 255 to the CDF, I solved for x in terms of y in the equation above to obtain the gray level in terms of the PDF and used cumsum to obtain the CDF. The result is this:
Figure 6. Corrected image using the nonlinear CDF, with
μ = 25 and σ = 175.

In addition, I chose different values of μ and σ and compared the results below:
Figure 7. Corrected image for different values of μ and σ.

Lastly, I tried histogram manipulation using different image processing software. The only ones I have in my laptop are GIMP, ImageJ and Adobe Photoshop. Below are the results when I tried histogram manipulation using the three software. Both GIMP and Photoshop allows freehand manipulation of the IO-response curve and not the histogram itself, while ImageJ only allows changing the minimum and maximum values of the gray color pallete, with separate controls for changing brightness and contrast.
Figure 8. Corrected image using GIMP. 
 Figure 9. Corrected image using Adobe Photoshop.
Figure 10. Corrected image using ImageJ.

Grade I give myself: 12/10. I actually enjoyed changing the brightness and contrast levels in the nonlinear correction, which was not required. Also, I completed the optional work by comparing histogram manipulation of three image processing software. Special acknowledgement for Wynn who gave me a tip about how to back-project the CDF values by just using the equations.

2 comments:

  1. 12 /10 is deserved. Good job.

    ReplyDelete
  2. Cool! Nice picture! :D I miss my old apartment... :( Oh, and nice job also on the image enhancement. :)

    ReplyDelete