1. Code
  2. Python

Histogram Equalization in Python

Scroll to top

Remember when you saw a low-quality image and felt a bit disappointed? It wasn't clear enough, and the details were a bit fuzzy. What if you could enhance that image to a better version? Wouldn't that be great? Fortunately, there's a way to do that using Python!

One of the methods you can use to enhance an image is histogram equalization, which in particular enhances the contrast of the image. Almost all camera systems use histogram equalization to make our pictures look better, and at the end of the tutorial you will discover why this is so.

In this tutorial, we will being by learning what is meant by histogram and histogram equalization and what happens to the image when applying the method, and then we'll see how we can implement the method in Python. Ready?

What Does an Image Histogram Represent?

Histograms are used to represent the distribution of numerical data on a graph. The x-axis is divided into bins or intervals, while the y-axis is used to plot the frequency with which the values in a particular bin are encountered.

Now consider an image which is made up of pixels. Each pixel will have its own color and intensity. Colors are usually represented with separate values for red, green, and blue channels. Each of these channel values can range from 0 (no color) to 255 (full color).

Plotting the number of different pixels with the same value of the red channel that ranges from 0 to 255 will give you a histogram that represents that red channel of the image. Similarly, you can have different histograms for different channels.

Let's say you have a grayscale image. In this case, we will have the same values for the red, green, and blue channels for a particular pixel. This means that instead of having three different histograms because of three different channels, we can have a single histogram. The leftmost part on the x-axis will now represent black, while the rightmost part will represent white.

For some images, all the histogram bars will be confined to a small set of intensities. This can reduce the clarity of that image and make it difficult to see details. A good image will have a balanced histogram, where the color intensity is more spread out.

What Is Histogram Equalization?

Histogram equalization is the process of stretching the histogram of an image so that its bars reach either end of the spectrum. In other words, the histogram is stretched in such a manner that it will have some black as well as some white parts. The end result will have a higher contrast because the intensity value of the pixels is now spread over the whole range.

Histogram equalization might not always produce great results with regular photography, but it has scientific applications where you need better details such as satellite or thermal images.

We will be using this monkey image from Pixabay that I have converted to grayscale with reduced contrast. The image looks as follows:

Grayscale MonkeyGrayscale MonkeyGrayscale Monkey

Let's take a look at how we can access the pixel values of the image, referred to as intensities. I wrote this small Python script that we can use to do just that (notice that I'm using the OpenCV library):

1
import cv2, random
2
3
img = cv2.imread('monkey.jpg')
4
5
img_shape = img.shape
6
7
height = img_shape[0]
8
width = img_shape[1]
9
10
for row in range(width):
11
    for column in range(height):
12
        if random.randint(0, width) == row and row < 10:
13
            print(img[column][row])
14

What I'm doing here is reading our image and then investigating the shape (size) of the image. img_shape will return (1126, 850, 3). This means that our image is of height (number of columns) 1126, and of width (number of rows) 850, and has 3 channels (RGB). Notice that the first parameter in the result is the height, and the second parameter is the width. Finally, we loop through the rows and columns and print out the different pixel values (intensities) at each row/column pair.

One sample of the output is [113 113 113]. Yes, I know, you were expecting one value as a result for the pixel intensity. We actually have the value of the pixel intensity here, but what the output is showing us are the results of the red, green, and blue (RGB) channels. Please be aware, however, that in OpenCV the order is BGR, as this is how OpenCV loads the image. Thus, the above sample result contains the value 113 for each channel, in the order of B, G, and R, respectively.

The reason for the introduction is that histogram equalization is actually about the modification of pixel intensities for the sake of improving the image's contrast. Thus, our main work here will be at the pixel intensity level.

Since our pixels have three values, one for each of the BGR channels, one way to draw the histogram is to have three histograms, one for each channel, where the x-axis will have the different pixel values (intensities), and the y-axis will show how many times (frequency) that particular pixel value appeared among the different pixel values.

For instance, the red channel histogram can have a pixel value of 113 on the x-axis, and the y-axis can show how many pixels had this value for the red channel, e.g. 86. So the way we read that is by saying that the pixel value for the red channel of 113 showed up in 86 pixels or has repeated 86 times in our image.

Using the code from this Image Histogram article to draw the histogram for our image, we get the following:

Monkey Original HistogramMonkey Original HistogramMonkey Original Histogram

The histogram is actually for the red, green, and blue channels. Let's take a small sample of the output you would get from the previous code, as shown below. This shows that the channel values seem to always be the same, and the different three lines drawn will thus have the same values and will be drawn on top of each other, appearing as only one line.

1
[113 113 113]
2
[110 110 110]
3
[106 106 106]
4
[102 102 102]
5
[138 138 138]
6
[106 106 106]
7
[114 114 114]
8
[131 131 131]
9
[124 124 124]
10
[132 132 132]

What the histogram equalization method will do for the above histogram is transform the intensity values in a way that will make the histogram look flatter in the resulting image. In other words, histogram equalization is a method that adjusts image intensities in order to enhance the contrast of the image.

The above histogram looks a bit concentrated towards the middle of the figure, and histogram equalization will distribute the pixel intensity values further to get a more flattened histogram.

Here is some Python code that will calculate the frequency of all pixel intensities and give us the values for the five most common intensities:

1
import cv2
2
3
img = cv2.imread('monkey.jpg')
4
img_shape = img.shape
5
6
height = img_shape[0]
7
width = img_shape[1]
8
9
frequency = {}
10
11
for row in range(width):
12
    for column in range(height):
13
        intensity = img[column][row][0]
14
        count = frequency.get(intensity, 0)
15
        frequency[intensity] = count + 1
16
17
print("Unique Intensities " + str(len(frequency)))
18
19
most_frequent = dict(sorted(frequency.items(), key=lambda elem: elem[1], reverse=True))
20
21
intensity_values = most_frequent.keys()
22
23
i = 0
24
for intensity in intensity_values:
25
    i += 1
26
    if i <= 5:
27
        print(intensity, most_frequent[intensity])

When I execute the above code, I get the following output:

1
Unique Intensities 129
2
124 52816
3
131 42114
4
116 41339
5
128 37606
6
118 37276

As you can see, there are only 129 unique intensities, and the most common intensity is found in over 52,000 pixels.

I think that's enough discussion of histogram equalization as we don't want to get too mathematical in this tutorial, especially since it is more about the implementation of the method in Python. However, you can check these histogram equalization notes that show the different formulas involved in the method. Now let's dive into the implementation!

Histogram Equalization in Python

In this section, I will show you how to implement the histogram equalization method in Python. We will use the above image (pout.jpg) in our experiments. Let's go through the process step by step. The first thing we need to do is import the OpenCV and NumPy libraries, as follows:

1
import cv2
2
import numpy

After that, we simply need to read our image:

1
img = cv2.imread('monkey.jpg')

The good news is that OpenCV provides us with a function through which we can apply histogram equalization to an image, namely equalizeHist(). It is straightforward to apply this function to a grayscale image as the method actually equalizes the histogram of a grayscale image, but in our case we have three channels (RGB) for each pixel, and we cannot apply histogram equalization on the three channels separately.

A nice solution I came across in the book Python: Real World Machine Learning is to convert our image to the YUV color space, equalize the Y channel, and finally convert the result to RGB.

So the first thing we do is convert our image to YUV. This can be done using the cvtColor() method, which converts the image from one space color to another, as follows:

1
img_to_yuv = cv2.cvtColor(img,cv2.COLOR_BGR2YUV)

Notice that we use BGR instead of RGB here, since OpenCV (as mentioned before) loads the images in BGR format.

We now apply the histogram equalization method on the Y channel using the equalizeHist() method:

1
img_to_yuv[:,:,0] = cv2.equalizeHist(img_to_yuv[:,:,0])

Finally, we convert the Y channel to RGB (BGR in OpenCV), as follows:

1
hist_equalization_result = cv2.cvtColor(img_to_yuv, cv2.COLOR_YUV2BGR)

Congratulations! You have now applied histogram equalization to the image. In the next section, I will put all the code together and show you what our image will look like after applying histogram equalization.

Putting It All Together

Let's put everything we have learned together. The Python script for applying histogram equalization on pout.jpg looks as follows:

1
import cv2
2
import numpy
3
img = cv2.imread('monkey.jpg')
4
img_to_yuv = cv2.cvtColor(img,cv2.COLOR_BGR2YUV)
5
img_to_yuv[:,:,0] = cv2.equalizeHist(img_to_yuv[:,:,0])
6
hist_equalization_result = cv2.cvtColor(img_to_yuv, cv2.COLOR_YUV2BGR)
7
cv2.imwrite('result.jpg',hist_equalization_result)

The output of the above script is the following image:

Monkey Grayscale EqualizedMonkey Grayscale EqualizedMonkey Grayscale Equalized

To notice the difference better, I will put the two images beside each other (left: original image; right: result of histogram equalization):

Monkeys Side-by-SideMonkeys Side-by-SideMonkeys Side-by-Side

Did you notice the difference? The right image looks much clearer than the original image. No wonder almost all imaging systems perform histogram equalization!

Before we wrap up, let's see what the histogram of our result looks like:

Monkey Grayscale Histogram EqualizedMonkey Grayscale Histogram EqualizedMonkey Grayscale Histogram Equalized

If you compare the histogram of the resulting image with the histogram of the original image, you will notice that the histogram of the resulting image is flatter than the histogram of the original image, and this is exactly what the histogram equalization method does.

Running our intensity frequency calculator script from earlier gives me the following output:

1
Unique Intensities 256
2
160 13577
3
208 11863
4
159 11383
5
222 10980
6
102 10814

The intensity values are now distributed over the whole range, and they are less clumped together.

Conclusion

In this tutorial, we saw how we can enhance the contrast of an image using a method called histogram equalization, and how it is easy to implement using Python and OpenCV.

The result was very interesting as it was much clearer than the original image, and the histogram of the result was flatter than the histogram of the original image, showing a better distribution of pixel intensity values across the image.

Finally, don’t hesitate to see what Python scripts and add-ons we have available for sale and for study on Envato Market.

Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.