Activity 5: Enhancement by Histogram Manipulation

đź•‘08:55, 26 Aug 2019

For this activity, I chose a low-light portrait that I shot at the Light Bridge at the Mind Museum (Fig. 1) last April. I imported it as uint8 for ease in the histogram manipulation later on (something to do with indexing). Its histogram is shown in Fig. 1e. Notice that the histogram values range only from 0 to 226. The “spike” at the end most likely corresponds to the clipped highlights evident in the light stripes that can be seen in the original, unprocessed image. This spike is also reflected in its CDF, shown in Fig. 1i.

The first order of business is contrast stretching, which is essentially stretching the histogram so that it encompasses the entire data space available for the data type, in this case, 0 to 255. We first convert the image to float32 by dividing it by 255 in order to constrain the pixel values between 0 and 1. Since the global maximum of the original image is 226, no pixel will have an exact value of 1. We want to stretch the data so that its maximum is exactly 1, so we transform the pixel intensities [1] according to

We then cast the image back into uint8 by multiplying by 255 and rounding off. The result of this process is shown in Fig. 1b. At face value, there is no discernible change since the global maximum of the original image is fairly close to 255. However, in the eyes of a photographer, there are substantial contrast & detail enhancements, particularly in the regions with rapid light falloff, such as on the shirt. If you zoom in quite a bit, the side of the face which is better lit has more pronounced details. The light stripes also appear less clipped, and are now a tad brighter. Looking into its histogram (Fig.1f), we see that the brighter range of pixels are now more even, and now encompass the full range of values up to 255, and the “stretching” is evident by the lack of some pixels in the lower range. The CDF (Fig. 1j) also reflects a smoother increase in the upper range of values.

Next, we perform histogram equalization. Generating the desired CDF (Fig. 1k) is trivial: we just use in the range [0, 255] for both x and y axes since we intend to retain its data type. By importing the image as an unsigned integer type, it makes the job of backprojection much easier as we only need to take the pixel values of the original image and use that as an index to find the corresponding pixel value in the desired CDF. The resulting image is shown in Fig. 1c. We can see from its histogram in Fig. 1g that the pixel values are now uniformly spread out throughout the data range. Since the bulk of the pixel values were originally in the lower range, the image now looks more properly exposed, albeit less in contrast.

Finally, we try to simulate a nonlinear response by generating a sigmoid CDF (Fig. 1l) of the form

and scaling it to uint8 then rounding off. The resultant image can be seen in Fig. 1d, and its histogram in Fig. 1h. Once again, the image appears more properly exposed, but now is high in contrast. We can also see in the histogram that the middle values are stretched more than the outer values.

Original
(a) Original
Contrast-stretched (CS)
(b) Contrast-stretched (CS)
Histogram-equalized (HE)
(c) Histogram-equalized (HE)
Nonlinear response (NL)
(d) Nonlinear response (NL)
original histogram
(e) original histogram
CS histogram
(f) CS histogram
HE histogram
(g) HE histogram
NL histogram
(h) NL histogram
original CDF
(i) original CDF
CS CDF
(j) CS CDF
HE CDF
(k) HE CDF
NL CDF
(l) NL CDF

Figure 1: Grayscale-converted image and its histogram manipulations, their respective histograms, and their respective CDFs.

Upon further reading, this is actually the method used for a technique called adaptive equalization [2]. The CDF is a nonlinear response, usually of a sigmoid-like shape which is shifted along the x-axis and/or varied in slope, depending on where most of the original pixel values are clustered, and I generally use this to correct the exposure of images without blowing out the highlights or crushing the shadows. I also recognized the S-curve of the CDF and its effect on the image, and I realized that the CDF is actually what is called the tone curve in photo editing programs such as Lightroom and Photoshop. To test this, I tried to perform a technique called crushing the blacks/whites, which is used to simulate a faded film look, wherein the tone curve is shaped like a sigmoid, but the endpoints are clipped, i.e., the global minimum should be greater than 0 and the global maximum should be less than 255 sort of like the opposite of contrast stretching. To do this, I generated a sigmoid CDF (Fig. 2a) with a bias and scaled it to a value less than 255, so that its minimum and maximum y values are 42 and 197, respectively. Using the same process as before, lo-and-behold, I got the expected result in Fig.2b, and it definitely looks like it was taken with the classic Kodak Tri-X film. Its histogram in Fig.2c also shows the squeezing of the pixel values and con rms that the technique acts like contrast compressing.

desired CDF
(a) desired CDF
resultant image
(b) resultant image
histogram
(c) histogram

Figure 2: “Crushed blacks/whites” post-processing technique using histogram manipulation.

References

  1. M. N. Soriano, Enhancement by histogram manipulation (2019).

  2. S. van der Walt, J. L. Schonberger, J. Nunez-Iglesias, F. Boulogne, J. D. Warner, N. Yager, E. Gouillart, T. Yu, and the scikit-image contributors, Histogram equalization (2014).

Keywords

histogram manipulation
contrast stretching
histogram equalization
crushed blacks