5

Histogram Equalization in Python

 1 year ago
source link: https://code.tutsplus.com/histogram-equalization-in-python--cms-30202t
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Histogram Equalization in Python

Abder-Rahman Ali Last updated Jul 28, 2023

Read Time: 10 min

Remember when you saw that 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 actually 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 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 which means no color to 255 which means full color.

Plotting the number of different pixels with 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 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 left-most part on the x-axis will now represent the color black while the right-most part will represent the color white.

For some images, all the histogram bars will be confined to a small set of intensities. This can reduced 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 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 form Pixabay that I have converted to grayscale with reduced contrast. The image looks as follows:

Grayscale Monkey

Grayscale Monkey

Grayscale 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):

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
for row in range(width):
    for column in range(height):
        if random.randint(0, width) == row and row < 10:
            print(img[column][row])

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—say, for instance, 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 Histogram

Monkey Original Histogram

Monkey 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.

[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]
[132 132 132]

What the histogram equalization method will do for the above histogram is that it will 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 what histogram equalization will do is 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 gives us the values for the five most common intensities:

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 = {}
for row in range(width):
    for column in range(height):
        intensity = img[column][row][0]
        count = frequency.get(intensity, 0)
        frequency[intensity] = count + 1
print("Unique Intensities " + str(len(frequency)))
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:

Unique Intensities 129
2
3
4
5
6

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

I think that's sufficient about histogram equalization to discuss here, as we don't want to get more mathematical in this tutorial, especially since it is more about the implementation of the method in Python. However, you can check these notes that show the different formulas involved in the method: histogram equalization. So now let's dive in to 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:

import cv2
2
import numpy

After that, we simply need to read our image, pout.jpg:

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

The good news is that OpenCV provides us with a function through which we can apply histogram equalization on an image, namely equalizeHist(). It is straightforward to apply this function on 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 in a separate manner.

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:

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:

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

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

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

Congratulations! You have now applied histogram equalization to the image. In the next subsection, I will put all the code together and show you how 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:

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 Equalized

Monkey Grayscale Equalized

Monkey 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-Side

Monkeys Side-by-Side

Monkeys Side-by-Side

Did you notice the difference? The right image looks much clearer than the original image. No wonder why 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 Equalized

Monkey Grayscale Histogram Equalized

Monkey 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:

Unique Intensities 256
2
3
208 11863
4
5
222 10980
6

The intensities values are now distributed over the whole range and they are also less clumped together.

Advertisement

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 we have available for sale and for study in the Envato Market, and please ask any questions and provide your valuable feedback using the feed below.


Recommend

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK