Image Enhancement (Part 3)

Ralph Caubalejo
5 min readJan 28, 2021

Histogram Manipulation at its best!

Before we finish our discussion of image enhancements, it would be much perfect if we can discuss one of the widely used image enhancement technique which is Histogram Manipulation.

Normally an image can be seen either as super bright or super dark. A lot of image editing software can already do the adjustment of the exposure of the image itself, even before snapping that camera button, you can already use different filters to adjust the exposure of the camera shot/frame itself.

But what is the theory and mathematics behind it?

Histogram Manipulation

Histogram Manipulation is an image enhancement technique where an image’s histogram values are shifted to a specific distribution to bring the exposure of the brightness and darkness into a specific range.

It can be better shown on our example, let us load our night example!

import numpy as np
from skimage.io import imshow, imread
import matplotlib.pyplot as plt
Image_1 = imread('night.jpg')
imshow(Image_1);
Figure 1: Night Example

Let us first check the histogram graph of each color channel of our night example. From this histogram, we can actually see where our pixel values, is it in lower intensity or higher intensity values.

from skimage.exposure import histogram, cumulative_distribution
fig, ax = plt.subplots(2,3,figsize=(20,10),sharey = True,sharex=True)
## Checking the Histogram of the Pictures by Color Channel
for channel, color in enumerate('rgb'):
channel_values = Image_1[:,:,channel]

his = histogram(channel_values)
cum = cumulative_distribution(channel_values)

ax[0,channel].plot(his[1],his[0]/his[0].sum(),c=color)

ax[0,channel].set_title('Histogram of ' + str(channel) + ' channel', Fontsize=20)

ax[1,channel].plot(cum[1],cum[0],c=color)

ax[1,channel].set_title('CDF of ' + str(channel) + ' channel', Fontsize=20)
ax[1,channel].set_xlabel('Pixel Values',fontsize=15)
ax[0,0].set_ylabel('Bins',fontsize=15)
ax[1,0].set_ylabel('Cum. Fraction of Pixels',fontsize=15)
plt.xlim(0, 255)
plt.tight_layout
plt.show()
Figure 2: Histogram of PDF and CDF

Based on the graph, we can see that the pixel values are situated in the lower intensity value. We will try to transform the image by shifting the CDF of the color channel to a normal distribution and check the difference.

Let us first visualize the PDFand CDF function of several distributions

from scipy.stats import expon, gamma, maxwell,normfig, ax = plt.subplots(1,4,figsize=(20,5))
gaus = norm(128, 64)
exp = expon(128, 64)
gam = gamma(128,64)
mwell = maxwell(128,64)
## Graphing the PDF and CDF different functions
for x,y in enumerate([gaus,exp,gam,mwell]):
ax[x].plot(y.pdf(np.arange(0,256)),c='b')
ax[x].set_ylabel('PDF',c='b')
ax[x].set_xlabel('Pixel Values')
ax[x] = ax[x].twinx()
ax[x].plot(y.cdf(np.arange(0,256)), c='r')
ax[x].set_ylabel('CDF',c='r')
ax[0].set_title('Gaussian Function',fontsize=20)
ax[1].set_title('Exponential Function',fontsize=20)
ax[2].set_title('Gamma Function',fontsize=20)
ax[3].set_title('Maxwell Function',fontsize=20)
plt.tight_layout()
plt.show()
Figure 3: Different Distribution Functions

We can note that different distribution has different characteristics for the CDF and PDF. Some distributions like the gaussian show that their CDF is positive increasing at a steady pace while for other distributions like the exponential and gamma function, the increase is sudden in a specific pixel value. There are different effects on using different distribution.

Let us show how histogram manipulation is implemented using python:

Using Gaussian Distribution:

#get the image distribution
cum = cumulative_distribution(Image_1)
#interpolate the distribution to a function
new_vals = np.interp(cum[0], gaus.cdf(np.arange(0,256)), np.arange(0,256))
image_hist = img_as_ubyte(new_vals[Image_1].astype(int))
fig, ax = plt.subplots(1,2,figsize=(15,15))
ax[0].imshow(Image_1)
ax[0].set_title('Original',fontsize=15)
ax[1].imshow(image_hist)
ax[1].set_title('Using Gaussian',fontsize=15)
plt.show()
Figure 4: Using the Gaussian Function

From the results, we can see that the Image become brighter and more exposed on the whiter values, this is because when we check the new distribution of the color channels:

Figure 5: Histogram of the Gaussian Enhanced

We can see that the CDF is already shifted to a higher intensity which resulted in a brighter image.

Let us now try other functions:

Using Exponential Distribution

#get the image distribution
cum = cumulative_distribution(Image_1)
#interpolate the distribution to a function
new_vals = np.interp(cum[0], exp.cdf(np.arange(0,256)), np.arange(0,256))
image_hist = img_as_ubyte(new_vals[Image_1].astype(int))
fig, ax = plt.subplots(1,2,figsize=(15,15))
ax[0].imshow(Image_1)
ax[0].set_title('Original',fontsize=15)
ax[1].imshow(image_hist)
ax[1].set_title('Using Exponential',fontsize=15)
plt.show()
Figure 6:Using Exponential Distribution

Using the exponential function, the image had the effect of a white background filter of the sort, this is due to the fact the CDF of an exponential function is biased on the higher intensity value.

Using Gamma Distribution

#get the image distribution
cum = cumulative_distribution(Image_1)
#interpolate the distribution to a function
new_vals = np.interp(cum[0], gam.cdf(np.arange(0,256)), np.arange(0,256))
image_hist = img_as_ubyte(new_vals[Image_1].astype(int))
fig, ax = plt.subplots(1,2,figsize=(15,15))
ax[0].imshow(Image_1)
ax[0].set_title('Original',fontsize=15)
ax[1].imshow(image_hist)
ax[1].set_title('Using Gamma',fontsize=15)
plt.show()
Figure 7: Using Gamma Distribution

Using the gamma distribution resulted in an image where the pixel value has a sort of gray filter.

Using Maxwell Distribution

#get the image distribution
cum = cumulative_distribution(Image_1)
#interpolate the distribution to a function
new_vals = np.interp(cum[0], mwell.cdf(np.arange(0,256)), np.arange(0,256))
image_hist = img_as_ubyte(new_vals[Image_1].astype(int))
fig, ax = plt.subplots(1,2,figsize=(15,15))
ax[0].imshow(Image_1)
ax[0].set_title('Original',fontsize=15)
ax[1].imshow(image_hist)
ax[1].set_title('Using Maxwell',fontsize=15)
plt.show()
Figure 8: Using Maxwell Distribution

The result of the maxwell distribution Is almost the same as the exponential distribution with only a slight difference since the peak of the CDF of the two are different.

Summary

Before discussing the correction and enhancement, we should first check the Histogram of the pixel values. The pixel values histogram shows that most of the pixel values are really on the low sides which makes the image has a sort of dark color. Using Histogram manipulation, we were able to correct the image using the different functions.

It is also noted worthy that the histogram of the pixel value can direct us to the needed strategies and needed functions to use. There is also a possibility of being able to use different distribution on different channels to be able to arrive at the best version of the image.

Based on the results, the best function among the four would be the Gaussian Function. Using the function, we were able to get the clearest image of the image by depicting the distinct red, green, blue values of the image.

That concludes our 3 sets of articles for Image Enhancement!

--

--