The purpose of this lab is to implement an efficient histogramming
equalization algorithm for an input image. Like the image convolution
MP, the image is represented as RGB float
values. You will
convert that to GrayScale unsigned char
values and compute
the histogram. Based on the histogram, you will compute a histogram
equalization function which you will then apply to the original image to
get the color corrected image.
Before starting this lab, make sure that:
Edit the code in the code tab to perform the following:
unsigned char
In this section we discuss some of the background details of the
histogram equalization algorithm. For images that represent the full
color space, we expect an image’s histogram to be evenly distributed.
This means that we expect the bin values in the histogram to be
256/pixel_count
. This algorithm adjust an image’s histogram
so that all bins have equal probability.
We first need to convert the image to gray scale by computing it’s luminosity values. These represent the brightness of the image and would allow us to simplify the histogram computation.
The histogram computes the number of pixels having a specific brightness value. Dividing by the number of pixels (width * height) gives us the probability of a luminosity value to occur in an image.
A color balanced image is expected to have a uniform distribution of the luminosity values.
This means that if we compute the Cumulative Distribution Function (CDF) we expect a linear curve for a color equalized image. For images that are not color equalized, we expect the curve to be non-linear.
The algorithm equalizes the curve by computing a transformation function to map the original CDF to the desired CDF (the desired CDF being an almost linear function).
The computed transformation is applied to the original image to produce the equalized image.
Note that the CDF of the histogram of the new image has been transformed into an almost linear curve.
Here we show the steps to be performed. The computation to be performed by each kernel is illustrated with serial pseudo code.
float
to unsigned char
Implement a kernel that casts the image from float *
to
unsigned char *
.
for ii from 0 to (width * height * channels) do
[ii] = (unsigned char) (255 * inputImage[ii])
ucharImageend
Implement a kernel that converts the the RGB image to GrayScale
for ii from 0 to height do
for jj from 0 to width do
= ii * width + jj
idx # here channels is 3
= ucharImage[3*idx]
r = ucharImage[3*idx + 1]
g = ucharImage[3*idx + 2]
b [idx] = (unsigned char) (0.21*r + 0.71*g + 0.07*b)
grayImageend
end
grayImage
Implement a kernel that computes the histogram (like in the lectures) of the image.
= [0, ...., 0] # here len(histogram) = 256
histogram for ii from 0 to width * height do
[grayImage[idx]]++
histogramend
histogram
This is a scan operation like you have done in the previous lab
[0] = p(histogram[0])
cdffor ii from 1 to 256 do
[ii] = cdf[ii - 1] + p(histogram[ii])
cdfend
Where p
is the probability of a pixel to be in a
histogram bin
def p(x):
return x / (width * height)
end
This is a reduction operation using the min function
= cdf[0]
cdfmin for ii from 1 to 256 do
= min(cdfmin, cdf[ii])
cdfmin end
The histogram equalization function (correct
) remaps the
cdf of the histogram of the image to a linear function and is defined
as
def correct_color(val)
return clamp(255*(cdf[val] - cdfmin)/(1 - cdfmin), 0, 255)
end
Use the same clamp function you used in the Image Convolution MP.
def clamp(x, start, end)
return min(max(x, start), end)
end
Once you have implemented all of the above, then you are ready to correct the input image
for ii from 0 to (width * height * channels) do
[ii] = correct_color(ucharImage[ii])
ucharImageend
float
for ii from 0 to (width * height * channels) do
[ii] = (float) (ucharImage[ii]/255.0)
outputImageend
And you’re done
The most recent version of source code for this lab along with the build-scripts can be found on the Bitbucket repository. A description on how to use the CMake tool in along with how to build the labs for local development found in the README document in the root of the repository.
The executable generated as a result of compiling the lab can be run using the following command:
./ImageEqualization_Template -e <expected.ppm> \
-i <input.ppm> -o <output.ppm> -t image`.
where <expected.ppm>
is the expected output,
<input.ppm>
is the input dataset, and
<output.ppm>
is an optional path to store the
results. The datasets can be generated using the dataset generator built
as part of the compilation process.
The images are stored in PPM (P6
) format, this means
that you can (if you want) create your own input images. The easiest way
to create image is via external tools such as bmptoppm
. The
masks are stored in a CSV format. Since the input is small, it is best
to edit it by hand.