Tuesday, June 30, 2009

Activity 4 - Enhancement by Histogram Manipulation

The program Adobe Photoshop is very well-known for easy handling of various images like adjusting the contrast, adding special effects, among many more. In this activity, we are rather to enhance an image through manipulating the histogram of the image's grayscale values using Scilab.

The first step of course is to find an image which has poor contrast. I have found this image from the web as shown below.


Source: http://www.american-indian-artwork.com/photograph-restoration-tutorial.htm

The image to be enhanced is strictly a grayscale type which possesses 0 to 255 grayscale values. The graylevel probability distribution function (PDF) of this image is what we manipulated using two main methods: first is through the backprojection using its original cumulative probability function (CDF) and the other is through backprojection via a desired CDF.

The PDF of my image under enhancement is illustrated below. The CDF is then acquired through the cumulative sum of the PDF. The PDF and CDF of the original image are shown as follows.



Based from its histogram, the image really has a poor contarst because the grayscale values are concentrated in the region near 0 or pure black.

In the backprojection of the grayscale values through the original CDF of the image, each pixel value is corresponded to its y-value in the original CDF and replaced by it. The effect of this first method is illustrated below.


The image is excellently enhanced in this method because it results to a more recognizable image where details are not sacrificed.

Then the PDF and CDF of this enhanced image are the following.



It can be observed from the enhanced CDF that it follows a staright line (y = x) thus distributing the pixel values from 0 to 255 linearly in the same form of the original CDF.

The second method which is the backprojection using a desired CDF is done initially by finding the original CDF value from the pixel grayscale. Then, this is traced in the value of the desired CDF. Finally, this pixel value is replaced by the pixel value at the desired CDF having the same CDF value. The same algorithm is applied to all the pixel values of the original CDF.

For a simple case, I used a parabolic function y = x^2 as a desired CDF. The effect of this CDF to the original image is shown below.


It can be observed that the contrast enhancement thorugh a parabolic CDF is somehow not applicable for this image since it is too bright.

The following are the PDF and CDF of this enhanced image via a parabolic CDF.



The PDF is similar to the first method except that in this parabolic CDF, the histogram is not well spreaded over the grayscale values. Apparently, the CDF follows the contour of a parabola which in turn make the image highly contrasted compared to the first method.

I have also tried when a placed a linear function (y = x). I anticipate that this will result to the first method. And I am correct. The first algorithm is exactly doing the second method but with a linear CDF.

Finally, using the same second method, I replaced the parabolic function into a nonlinear CDF which mimicks a nonlinear response like the human eye. Here, I have chosen a logarithmic one which is y = log(x).

The effect is as follows.


Some details of the image are lost in this kind of contrast enhacement which implies that the nonlinear CDF is not applicable for my image.

Meanwhile, its PDF and CDF are illustrated below.



The PDF for this nonlinear logarithmic CDF is not well-distributed. Due to this nonlinearity, the spacings along the grayscale values are not uniform and the probability values are not high. Like the parabolic CDF, the CDF of the enhanced image follows the graph of the desired CDF which is a logarithmic function here, and this makes the quality of the image not that good.

In summary, here are the original image and its enhanced images using the two methods (the second is composed of two kinds of functions).



The first one is the original image, the second is the enhanced image from its original CDF (linear backprojection), the third is used with a parabolic CDF, and finally with a logarithmic CDF. I can conclude that the first method or implementing a linear CDF is the best process for the contrast enhancement of my image.

I grade myself 10/10 in this activity since I am able to enhance a grayscale image through the manipulation of its PDF using two main procedures successfully.

The following are my collaborators in this activity: Gary, Ed, Raffy, Neil, and Earl.

Appendix
The whole code in Scilab utilized in this activity is as follows.

poor = imread('poor_contrast.jpg'); // For histogram (PDF) of the original image
imfinfo('poor_contrast.jpg');

a_poor = 1;
grayscale_poor = [];
pixels_poor = [];
for i = 0:255
[x_poor, y_poor] = find(poor == i);
grayscale_poor(a_poor) = i;
pixels_poor(a_poor) = length(x_poor);
a_poor = a_poor + 1;
end

scf(0);
imshow(poor/max(poor));

scf(1)
subplot(211)
plot(grayscale_poor, pixels_poor/max(pixels_poor));
title('PDF of Original Image');
xlabel('Grayscale Values');

cdf_0_poor = cumsum(pixels_poor); // For CDF of the original image
cdf_poor = cdf_0_poor/max(cdf_0_poor);
subplot(212)
plot(grayscale_poor, cdf_poor)
title('CDF of Original Image');
xlabel('Grayscale Values');

good1= []; // For backprojection of the image through the original CDF
for i = 1:size(poor,1)
for j = 1:size(poor,2)
good1(i,j) = cdf_poor(poor(i,j));
end
end

scf(2);
imshow(good1);

//imwrite(good1, 'good_contrast_linear.jpg');

good1 = round(good1*255); // For histogram (PDF) of the enhanced image through the original CDF
a_good1 = 1;
grayscale_good1 = [];
pixels_good1 = [];
for i = 0:255
[x_good1, y_good1] = find(good1 == i);
grayscale_good1(a_good1) = i;
pixels_good1(a_good1) = length(x_good1);
a_good1 = a_good1 + 1;
end

scf(3);
subplot(211);
plot(grayscale_good1, pixels_good1/max(pixels_good1));
title('PDF of Enhanced Image via Linear Function');
xlabel('Grayscale Values');

cdf_0_good1 = cumsum(pixels_good1); // For CDF of the enhanced image through the original CDF
cdf_good1 = cdf_0_good1/max(cdf_0_good1);
subplot(212);
plot(grayscale_good1, cdf_good1);
title('CDF of Enhanced Image via Linear Function');
xlabel('Grayscale Values');

// Image enhancement through a desired CDF

z = [0:255]; // CDF definition
parabola = z^2;
parabola = parabola/max(parabola);

good2 = []; // For backprojection through a desired CDF
for i = 1:size(poor, 1)
for j = 1:size(poor, 2)
dummy = abs(cdf_poor(poor(i, j)) - parabola);
dummy = find(dummy == min(dummy));
good2(i,j) = dummy;
end
end

good2 = good2/max(good2);
scf(4);
imshow(good2);

//imwrite(good2, 'good_parabola.jpg');

good2 = round(good2*255); // For histogram (PDF) of the enhanced image through a desired CDF
a_good2 = 1;
grayscale_good2 = [];
pixels_good2 = [];
for i = 0:255
[x_good2, y_good2] = find(good2 == i);
grayscale_good2(a_good2) = i;
pixels_good2(a_good2) = length(x_good2);
a_good2 = a_good2 + 1;
end

scf(5);
subplot(211);
plot(grayscale_good2, pixels_good2/max(pixels_good2));
title('PDF of Enhanced Image via Parabolic Function');
xlabel('Grayscale Values');

cdf_0_good2 = cumsum(pixels_good2); // For CDF of the enhanced image through a desired CDF
cdf_good2 = cdf_0_good2/max(cdf_0_good2);
subplot(212);
plot(grayscale_good2, cdf_good2);
title('CDF of Enhanced Image via Parabolic Function');
xlabel('Grayscale Values');

// Enhancement through a nonlinear function

v = [0:255]; // Nonlinear CDF definition
non = log(v + 1);
non = non/max(non);

good3 = []; // For backprojection of the image through a nonlinear CDF
for i = 1:size(poor, 1)
for j = 1:size(poor, 2)
dummy = abs(cdf_poor(poor(i, j)) - non);
dummy = find(dummy == min(dummy));
good3(i,j) = dummy;
end
end

good3 = good3/max(good3);
scf(6);
imshow(good3);

//imwrite(good3, 'good_nonlinear_log.jpg');

good3 = round(good3*255); // For histogram (PDF) of the enhanced image through a nonlinear CDF
a_good3 = 1;
grayscale_good3 = [];
pixels_good3 = [];
for i = 0:255
[x_good3, y_good3] = find(good3 == i);
grayscale_good3(a_good3) = i;
pixels_good3(a_good3) = length(x_good3);
a_good3 = a_good3 + 1;
end

scf(7);
subplot(211);
plot(grayscale_good3, pixels_good3/max(pixels_good3));
title('PDF of Enhanced Image via Nonlinear Function');
xlabel('Grayscale Values');

cdf_0_good3 = cumsum(pixels_good3); // For CDF of the image through a nonlinear CDF
cdf_good3 = cdf_0_good3/max(cdf_0_good3);
subplot(212);
plot(grayscale_good3, cdf_good3);
title('CDF of Enhanced Image via Nonlinear Function');
xlabel('Grayscale Values');

Thursday, June 25, 2009

Activity 2 - Area Estimation for Images with Defined Edges

In this activity, we are asked to obtain the area of some basic geometric shapes through the Green's theorem then this will be compared with its known analytic area.

Note that the first part of Activity 3 was done prior to this activity because the SIP Toolbox and ImageMagick needed for Scilab to handle images were not yet installed in our computer.

Green's theorem can compute the area of a closed contour through the relation of its double integral to the line integral of the closed curve. After manipulation of the Green's theorem, the resulting discrete or summation form for calculating the area of a closed contour is as follows.


Here, x and y are the pixel locations and N_b is the number of pixels along the contour.

All my geometric shapes under area estimation are created in Microsoft Paint. They are all 256 x 256 pixels in dimension and are saved as 24-bit Bitmap image. Then, using the programming software Scilab 4.1.2. with ImageMagick and SIP Toolbox installed, I managed to implement Green's method in area calculation.

It is important that the shapes created are white in color and black as their background since the follow command in Scilab gets the pixel locations along the border of the white before the transition to the black background. Incidentally, the default save format in Paint does not require to be converted into Binary-type because it already consists of only ones (white) and zeroes (black) when imported to Scilab using the command imread.

The first geometric shape is a rectangle.


Calculated Area using Green's Theorem = 13926.5 square pixels
Theoretical Area = 14016 square pixels
Percent Error = 0.64 %

The theoretical area is simply obtained by the formula A = lw (l = length, w = width) through subtracting the maximum pixel location in x to its minimum and multiplying it to the difference of extrema in y from the followed contour of the rectangle.

Next one is a right triangle.


Calculated Area using Green's Theorem = 6791.5 square pixels
Theoretical Area = 6960 square pixels
Percent Error = 2.42 %

The theoretical area is from A = (1/2)bh (b = base, h = height), where this is similar to the rectangle except for dividing it to 2. Of course, base and height are also from the differences of extrema in x and y of the followed contour of the triangle.

Finally, a circle is created for area calculation using Green's theorem.


Calculated Area using Green's Theorem = 16643.5 square pixels
Theoretical Area = 16741.547 square pixels
Percent Error = 0.59 %

From the theoretical equation A = pi*r^2 (pi = 3.14..., r = radius), the radius is determined from half of the difference of extrema in either x or y from the followed contour of the circle.

Since the shapes generated have pixelated edges, then errors cannot be prevented in area calculation using Green's theorem especially if they are diminutive which possess not-well-defined perimeter. However, I can say that the method is very reliable due to incredibly small percent errors I acquired hence I grade myself 10/10 for this activity.

I would like to thank Gary and Ed in helping me for loading the SIP Toolbox in Scilab of my desktop in our computer room. Raffy guided me for the correct syntax since I am not yet familiar with the Scilab environment.

Appendix
The Scilab code below is used in this activity.

rectangle = imread('rectangle.bmp');
triangle = imread('triangle.bmp');
circle = imread('circle.bmp');
[x_rectangle, y_rectangle] = follow(rectangle);
[x_triangle, y_triangle] = follow(triangle);
[x_circle, y_circle] = follow(circle);

n = length(x_rectangle);
o = length(x_triangle);
p = length(x_circle);

A_rectangle = [];
A_triangle = [];
A_circle = [];

for i = 1:n-1
A_rectangle(i) = x_rectangle(i)*y_rectangle(i+1) - y_rectangle(i)*x_rectangle(i+1);
end

for j = 1:o-1
A_triangle(j) = x_triangle(j)*y_triangle(j+1) - y_triangle(j)*x_triangle(j+1);
end

for k = 1:p-1
A_circle(k) = x_circle(k)*y_circle(k+1) - y_circle(k)*x_circle(k+1);
end

Theo_rectangle = (max(x_rectangle)-min(x_rectangle))*(max(y_rectangle)-min(y_rectangle))
Theo_triangle = (1/2)*(max(x_triangle)-min(x_triangle))*(max(y_triangle)-min(y_triangle))
Theo_circle = %pi*((max(x_circle)-min(x_circle))/2)^2

Area_rectangle = sum(A_rectangle)/2
Area_triangle = sum(A_triangle)/2
Area_circle = sum(A_circle)/2

percent_error_rectangle = abs(((Theo_rectangle - Area_rectangle)/Theo_rectangle))*100
percent_error_triangle = abs(((Theo_triangle - Area_triangle)/Theo_triangle))*100
percent_error_circle = abs(((Theo_circle - Area_circle)/Theo_circle))*100

Tuesday, June 23, 2009

Activity 3 - Image Types and Basic Image Enhancement

This activity has 2 major parts. First, different image types which are Binary, Grayscale, Truecolor, and Indexed image were collected. Their respective properties including the pixel dimensions, resolution, image type, file size file type were also determined. The second part involves the conversion of scanned image to grayscale for histogram and threshold determination. Then, the area of the region of interest (ROI) of the Binary-converted image from being a Grayscale type is calculated using Green's theorem which is similar to the method utilized in Activity 2.

Note that the second part was only started after the SIP Toolbox and ImageMagick which are needed for Scilab to handle images, were installed thus the first part was finished before Activity 2 was completed.

Using the internet, I searched some images then I was able to determine their image type and other image properties using the software GIMP 2.6.6.

1. Binary Image
The first image is somehow resembling an optical illusion. It is only composed of 2 colors by mere observation.


Width = 405 pixels
Height = 397 pixels
Horizontal Resolution = 72 dpi (dots per inch) or ppi (points per inch)
Vertical Resolution = 72 dpi
Color Space = Indexed (2 Colors)
File Size = 4.3 kb
File Type = GIF Image
Source: http://acjournal.org/holdings/vol5/iss1/articles/radwan.htm

Notice that the color space from the properties of the image above is indexed, but it only comprises 2 colors which represent pure black and white, thus it is a Binary image indeed.

2. Grayscale Image
I found a grayscale image of the lake at the summit of Mt. Pinatubo which is shown as follows.


Width = 640 pixels
Height = 433 pixels
Horizontal Resolution = 72 dpi
Vertical Resolution = 72 dpi
Color Space = Grayscale
File Size = 48.8 kb
File Type = JPEG Image
Source: http://www.volcano.si.edu/world/volcano.cfm?vnum=0703-083&volpage=var

Obviously, the image above is a grayscale one because it contains the different shades between black and white.

3. Truecolor Image
The next image is a panoramic view of Taal Volcano as seen from Tagaytay Ridge.


Width = 500 pixels
Height = 338 pixels
Horizontal Resolution = 96 dpi
Vertical Resolution = 96 dpi
Color Space = RGB Color
File Size = 105.9 kb
File Type = JPEG Image
Source: http://www.flickr.com/search/?q=taal+volcano

Since the image of Taal Volcano above has color space of Red-Green-Blue (RGB), then it is a Truecolor image.

4. Indexed Image
The following image illustrates the location of major volcanoes in the Philippines. This is apparently an Indexed image because there is less color information compared to a Truecolor image.


Width = 351 pixels
Height = 752 pixels
Horizontal Resolution = 96 dpi
Vertical Resolution = 96 dpi
Color Space = Indexed (51 Colors)
File Size = 23.8 kb
File Type = GIF Image
Source: http://www.absoluteastronomy.com/topics/Pacific_Ring_of_Fire

From the image property color space, the image above is an Indexed image type which has 51 representative colors independent from each other.

For the second part of this activity, I scanned an old 2-peso coin for its area calculation. The scanned image is shown below.


Using the command im2gray in Scilab, I was able to convert the scanned image to a Grayscale-type one. Then im2bw made it into a Binary image type. Finally, I used Microsoft Paint to make the text and image of Andres Bonifacio to black and invert them to white and the white background to black. The 3 images in order of Grayscale, Binary, and inverted Binary are displayed below.



The Grayscale image has the following properties.

Width = 476 pixels
Height = 520 pixels
Horizontal Resolution = 72 dpi
Vertical Resolution = 72 dpi
Color Space = Grayscale
File Size = 12.6 kb
File Type = JPEG Image

The histogram plot of the Grayscale image is as follows.



From the histogram plot above, the horizontal axis is the Grayscale values from 0 to 255 and the vertical axis is the number of pixels. The Grayscale range between 100 and 150 represents the ROI and the spike between 200 and 250 represents the background. I can infer from the histogram plot that the ROI is well separated from the background. A threshold of 0.7 for converting the Grayscale image to Binary is best suited based from this plot.

Finally, the following data for area calculation of the 2-peso coin are obtained.

Calculated Area using Green's Theorem = 46300 square pixels
Theoretical Area = 46005 square pixels
Percent Error = 0.64 %

Since the object under area calculation is no longer a basic geometric shape, hence the best way to get its theoretical area is simply summing all the elements of the imported Binary image which only consists of zeroes and ones. But due to the fact that the followed contour is not included to the Green's calculation of area (the followed contour is the white perimeter before the transition to the black background), then it is subtracted to the summation of ones for more accurate theoretical area.

I grade myself 10/10 here because first, I was able to complete all types of image asked in this activity and identify their properties, and second, because of the very small deviation of the calculated area of the 2-peso coin using Green's theorem from the theoretical value I got.

This activity is done successfully through the help of Gary and Ed in search of different image types and Raffy for the proper syntax in Scilab.

Appendix
The following Scilab code is implemented in converting the scanned image to Grayscale and Binary, acquiring the histogram plot of the Grayscale image, and calculating the theoretical and Green's area of the 2-peso coin.

coin = imread('coin.jpg'); // For Image Conversion
coin_gray = im2gray(coin);
imwrite(coin_gray, 'coin_gray.jpg');
coin_bw = im2bw(coin_gray, 0.7);
imwrite(coin_bw, 'coin_bw_inv.jpg');
imfinfo('coin_gray.jpg');

gray = imread('coin_gray.jpg'); // For Histogram

a = 1;
grayscale = [];
pixels = [];
for j = 0:255
[x_gray, y_gray] = find(gray == j);
grayscale(a) = j;
pixels(a) = length(x_gray);
a = a + 1;
end

plot(grayscale, pixels);

coin_pic = imread('coin_bw_inv.jpg'); // For Area Calculation
coin = im2bw(coin_pic, 0.5);
[x_coin, y_coin] = follow(coin);

n = length(x_coin);
A_coin = [];
for i = 1:n-1
A_coin(i) = x_coin(i)*y_coin(i+1) - y_coin(i)*x_coin(i+1);
end

Theo_coin = sum(coin) - length(x_coin)
Area_coin = sum(A_coin)/2
percent_error_coin = abs(((Theo_coin - Area_coin)/Theo_coin))*100

Thursday, June 18, 2009

Activity 1 - Digital Scanning

Our very first activity is all about reconstructing a hand-drawn graph from an old journal found in the College of Science (CS) library through digital scanning. Ratio and proportion is implemented for the reconstruction where the number of pixels for the scanned image is converted to the physical value of the plot.

Initially for the first day of classes (June 16, 2009 - Tuesday), all of us went to the CS library where . Here, I found a hand-drawn graph from a 1953 Botany journal which is about analysis of plant environment. Below is shown the scanned image.


On our second meeting (June 18, 2009 - Thursday), we started the reconstruction of the hand-drawn plot. Using the software Nero PhotoSnap Viewer Essentials, I cropped my image leaving only the graph so that the pixel locations will coincide directly with the reconstruction's physical values. I also adjusted the angle of its orientation so that error in reconstruction will be minimized. The following image is the cropped image of the graph.


I opened the cropped image using the software Microsoft Paint where I located the pixel locations (both X and Y) of my selected data along the plot, which appear at the lower right corner of its working window when the mouse pointer is directed to them. Then, I noted these pixel locations together with determining conversion factors which will turn pixel locations into the physical values of the plot. Since the pixel location of the origin in Paint is at the upper leftmost part, I subtracted the Y pixel location from the total vertical pixels of my cropped image.

In equations:
Light Intensity = X*(1000/318)
Count per Limit Time = Y prime*(200/219) ; where Y prime = 769 - Y

Thus, for the horizontal axis, there is 1000 units of Light Intensity in 318 pixels while there is 200 units of Count per Limit Time in 219 pixels for the vertical axis.

Next, using the software OpenOffice.org Calc, I tabulated my 14 data points and their corresponding physical values.


Finally, I have reconstructed the hand-drawn graph by plotting the physical values in the same program. Below is shown my reconstructed graph which has background of the cropped image.


Notice that there is quite some vertical displacement at high Light Intensity values and this is because the hand-drawn graph is not equally scaled especially in the Y direction. Meaning, there are significant differences in pixels between two ticks of Count per Limit Time. As an illustration, there are 219 pixels between 0 and 200 units of Count per Limit Time which served as my conversion factor for all data points in Y, but there are only 207 pixels between the ticks of 200 and 400 units of Count per Limit Time in the hand-drawn graph. However, since I fully understood and enjoyed the whole process of this activity plus my reconstruction of the hand-drawn graph is successful, I confidently grade myself 10/10.

This activity is completed with the help of Dr. Gay Jane Perez for scanning our images. In addition, Raffy, Ed, and Gary guided me with the techniques in plotting, scaling, and making an external image as background of a plot in OpenOffice.org Calc.