Image Enhancement (Part 2)

Ralph Caubalejo
6 min readJan 28, 2021

--

Part 2

For our Part 2 of Image enhancement, we would be dealing with one of the most important enhancement techniques, and that would the White Balancing Method.

White Balancing

White Balancing Method is an image processing technique where we correct the brightness and darkness of a specific image into different kinds of ground truth values that can be seen on the image itself.

They are various kind of technique on how to white balancing, some them are as follows:

  1. Ground Truth Method/White Patch Method
  2. Percentile Method
  3. Gray World Algorithm

Let us try to show each one of them!

For this article, we will use a new image example! let us read and put it in a variable for use throughout the article.

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

Ground Truth Algorithm

GT Algorithm is done by defining a certain ground truth or certain patch in the image as the whitest patch. One we are able to define the whitest patch, then we can use this patch as the basis for the balancing of the darkness and brightness of the specific image.

For the GT Algorithm, let us follow the steps below:

  1. Define the Patch determined to be the whitest
  2. Show the Patch
  3. Perform Ground Truth Algorithm using the Patch Defined by normalizing to the maximum value of each channel of the patch
  4. Perform Ground Truth Algorithm using the Patch Defined by making the mean value of each channel of the original image match the mean value of each channel of the patch

The code are as follows:

from matplotlib.patches import Rectangle# Defining the patch using blue rectangle
fig, ax = plt.subplots(1,2,figsize=(15,15))
ax[0].imshow(Image_1)
ax[0].add_patch(Rectangle((970, 950), 50, 50, edgecolor='b', facecolor='none'));
ax[0].set_title('Patch Location')
#Showing Patch
Image_1_patch_a = Image_1[970:1000, 950:1000]
ax[1].imshow(Image_1_patch_a)
ax[1].set_title('Whitest Patch')
plt.show()
Figure 2: Night Example with Patch

As you can see, the blue box shows where the patch is located in the original image. From Visual inspection within the image. This patch serves as the whitest among the rest of the other parts. We will use this patch for the GT algorithm.

We can use then use Maximum Value of the Pixels of the Patch:

fig, ax = plt.subplots(1,2,figsize=(15,15))
ax[0].imshow(Image_1)
ax[0].set_title('Original Image')
#Perform Ground Truth Algorithm using the Patch Defined by normalizing to the maximum value of each channel of the patch
Image_1_norm_a = (Image_1*1.0 / Image_1_patch_a.max(axis=(0, 1))).clip(0, 1)
ax[1].imshow(Image_1_norm_a)
ax[1].set_title('Using Maximum Value of Patch Channels')
plt.show()
Figure 3: Night Example using Maximum Value

We can use also use the Mean Value of the Pixels of the Patch:

fig, ax = plt.subplots(1,2,figsize=(15,15))
ax[0].imshow(Image_1)
ax[0].set_title('Original Image')
#Perform Ground Truth Algorithm using the Patch Defined by making the mean value of each channel of the original image match
Image_1_norm_a = ((Image_1*(Image_1_patch_a.mean() / Image_1.mean(axis=(0, 1)))).clip(0, 255).astype(int))
ax[1].imshow(Image_1_norm_a)
ax[1].set_title('Using Mean Value of Patch Channels')
plt.show()
Figure 4: Night Example using Mean Value

From Figure 3 and Figure 4, we can see the difference when using the maximum value and the mean value. The mean value shows a brighter enhancement compared to the max value method.

Let us try a different random patch!

# Defining the patch using blue rectangle
fig, ax = plt.subplots(1,2,figsize=(15,15))
ax[0].imshow(Image_1)
ax[0].add_patch(Rectangle((500,500), 50, 50, edgecolor='r', facecolor='none'));
ax[0].set_title('Patch Location')
#Showing Patch
Image_1_patch_a = Image_1[500:550, 500:550]
ax[1].imshow(Image_1_patch_a)
ax[1].set_title('Whitest Patch #2')
plt.show()
Figure 5: Night Example with Patch #2
Figure 6: Using Maximum Value on Patch#2
Figure 7: Using Mean Value on Patch#2

We can see that the results of ground truth algorithm varies depending on the patch that chose. For the 1st Patch we have tried, it seems that it is the most whitest part in the picture, the resulting enhanced image was more brighter than the original, in fact it look though that the image is already at day time. You can see the difference on using the maximum and minimum value of the patch.

The second patch is on the bluer side of the color shade. Noticed that when we used this, the sky became more brighter since it has the almost the same pixel values of the patch, this way it is the most affected.

Percentile Method

The idea behind the percentile method is that we check the histogram of the pixel value first. From this histogram graph, we can actually see where the pixel values lies on the distribution graph. Using this graph, we can set different percentile value we can use to make sure the brightness is on the

We will be using the White Balancing Algorithm using different Percentiles namely:

A. 100th
B. 99th
C. 95th
D. 90th

In order to get the percentiles, let us show the histogram of the pixel value of the picture and get the different percentiles for to be our maximum value.

#percentile
for channel, color in enumerate('rgb'):
channel_values = Image_1[:,:,channel]
plt.step(np.arange(256),
np.bincount(channel_values.flatten(), minlength=256)*1.0/
channel_values.size,
c=color)
plt.xlim(0, 255)
plt.xlabel('channel value')
plt.ylabel('fraction of pixels')
plt.title('Histogram of Pixel Values')
plt.show()
list100 = []
list99 = []
list95 = []
list90 = []
#printing the maximum values per percentile
for channel, color in enumerate('RGB'):
list100.append(np.percentile(Image_1[:,:,channel], 100))
list99.append(np.percentile(Image_1[:,:,channel], 99))
list95.append(np.percentile(Image_1[:,:,channel], 95))
list90.append(np.percentile(Image_1[:,:,channel], 90))
print("100th percentile of channels[0,1,2] are: ",list100)
print("99th percentile of channels[0,1,2] are: ",list99)
print("95th percentile of channels[0,1,2] are: ",list95)
print("90th percentile of channels[0,1,2] are: ",list90)
Figure 8: Histogram of the Night Example

Using the percentiles:

from skimage import img_as_ubyte, img_as_float#showing the whitepatch per percentile
A100 = img_as_ubyte((Image_1*1.0 / np.percentile(Image_1, 100, axis=(0, 1))).clip(0, 1))
A99 = img_as_ubyte((Image_1*1.0 / np.percentile(Image_1, 99, axis=(0, 1))).clip(0, 1))
A95 = img_as_ubyte((Image_1*1.0 / np.percentile(Image_1, 95, axis=(0, 1))).clip(0, 1))
A90 = img_as_ubyte((Image_1*1.0 / np.percentile(Image_1, 90, axis=(0, 1))).clip(0, 1))
#plotting the percentiled images
fig, ax = plt.subplots(2,2,figsize=(15,10))
ax[0,0].imshow(A100)
ax[0,1].imshow(A99)
ax[1,0].imshow(A95)
ax[1,1].imshow(A90)
ax[0,0].set_title('Using 100th Percentile',fontsize=20)
ax[0,1].set_title('Using 99th Percentile',fontsize=20)
ax[1,0].set_title('Using 95th Percentile',fontsize=20)
ax[1,1].set_title('Using 90th Percentile',fontsize=20)
plt.tight_layout()
plt.show()
Figure 9: Night Example using different Percentiles

From the results of the percentile algorithm,as we decrease the percentile value, the brighter it becomes, the blue shade also become more lighter or whiter, this is because it goes to its minimum value.

Gray World

The gray world algorithm assumes that all image pixels has a gray value. useful codes are as:

Image_1_gw = ((Image_1 * (Image_1.mean() / Image_1.mean(axis=(0, 1)))).clip(0, 255).astype(int))fig, ax = plt.subplots(1,2,figsize=(15,15))
ax[0].imshow(Image_1)
ax[0].set_title('Original Image')
ax[1].imshow(Image_1_gw)
ax[1].set_title('Gray World Algorithm')
plt.show()
Figure 10: Using the Grayworld

From the results we can see that the effect is very minimal, this is because in grayworld algo, we only check the mean values.

Let us try a different image!

Very cool results right! We were able to use different mathematical methods to produce different kinds of white balancing.

Summary

We were able to do three different white Balancing Method for this article and based on the results, the are two possible techniques that works the best. First is using the Ground Truth Algorithm and choosing the whitest patch within the image. This shifts the range of pixel values to the highest value of the whitest pixel value in the image. The second technique would be using the Percentile Technique and choosing the best percentile value. Using this technique, we can actually normalize the whole picture to shift on the whitest pixel value in terms of the histogram and count of pixel values.

Stay tuned for Image Enhancement Part 3!! :)

--

--