Image Enhancement Techniques using OpenCV and Python | by Ashish Bansal | Towards Data Science

In this blog post, I would like to demonstrate how one can enhance the quality and extract meaningful information from a low resolution /blurred image/low contrast using image processing.
Let's begin the process :
I have a sample image of an LPG Cylinder which is taken from the warehouse running on a conveyor belt. My objective is to find out the Batch no of LPG Cylinder so that I could update how many LPG Cylinders have been tested for quality check.
Step 1: Import the necessary libraries
import cv2import numpy as npimport matplotlib.pyplot as plt
Step 2: Load the image and display the sample image.
img= cv2.imread('cylinder1.png')img1=cv2.imread('cylinder.png')images=np.concatenate(img(img,img1),axis=1)cv2.imshow("Images",images)cv2.waitKey(0)cv2.destroyAllWindows()
As you can see that the contrast of the image is very poor. We could hardly recognize the batch number. This is a common problem in the warehouse where the lightning conditions are inappropriate. Further will discuss Contrast Limited Adaptive Histogram Equalization and try to experiment with different algorithms on the data set.
Step 3: Convert the images into a grayscale image
gray_img=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)gray_img1=cv2.cvtColor(img1,cv2.COLOR_BGR2GRAY)
Step 4: Now we find out the histograms of the grayscale image and look for the distribution of intensities.
hist=cv2.calcHist(gray_img,[0],None,[256],[0,256])hist1=cv2.calcHist(gray_img1,[0],None,[256],[0,256])plt.subplot(121)plt.title("Image1")plt.xlabel('bins')plt.ylabel("No of pixels")plt.plot(hist)plt.subplot(122)plt.title("Image2")plt.xlabel('bins')plt.ylabel("No of pixels")plt.plot(hist1)plt.show()
Step 5: Now we will use cv2.equalizeHist() function with the purpose of equalizing the contrast of a given grayscale image. cv2.equalizeHist() function normalises the brightness and also increases the contrast.
gray_img_eqhist=cv2.equalizeHist(gray_img)gray_img1_eqhist=cv2.equalizeHist(gray_img1)hist=cv2.calcHist(gray_img_eqhist,[0],None,[256],[0,256])hist1=cv2.calcHist(gray_img1_eqhist,[0],None,[256],[0,256])plt.subplot(121)plt.plot(hist)plt.subplot(122)plt.plot(hist1)plt.show()
Step 6: Display the Gray Scale Histogram equalized images
eqhist_images=np.concatenate((gray_img_eqhist,gray_img1_eqhist),axis=1)cv2.imshow("Images",eqhist_images)cv2.waitKey(0)cv2.destroyAllWindows()
Let's drill down further with CLAHE
Step 7 :
Contrast Limited Adaptive Histogram Equalization
This algorithm can be applied to improve the contrast of the images. This algorithm works by creating several histograms of the image and uses all of these histograms to redistribute the lightness of the image.CLAHE can be applied to greyscale as well as colour images. There are 2 parameters to tune.
clip limit which sets the threshold for contrast limiting. The default value is 40tileGridsize which sets the number of titles in the row and column. While applying CLAHE image is divided into small blocks called tiles (8*8) in order to perform calculations.
clip limit which sets the threshold for contrast limiting. The default value is 40
tileGridsize which sets the number of titles in the row and column. While applying CLAHE image is divided into small blocks called tiles (8*8) in order to perform calculations.
clahe=cv2.createCLAHE(clipLimit=40)gray_img_clahe=clahe.apply(gray_img_eqhist)gray_img1_clahe=clahe.apply(gray_img1_eqhist)images=np.concatenate((gray_img_clahe,gray_img1_clahe),axis=1)cv2.imshow("Images",images)cv2.waitKey(0)cv2.destroyAllWindows()
Step 8:
Thresholding Techniques
Thresholding is a simple, yet effective method for image partitioning into a foreground and background. The simplest thresholding methods replace each pixel in the source image with a black pixel if the pixel intensity is less than some predefined constant(the threshold value)or a white pixel if the pixel intensity is greater than the threshold value. Different types of Thresholding are:-
cv2.THRESH_BINARY
cv2.THRESH_BINARY_INV
cv2.THRESH_TRUNC
cv2.THRESH_TOZERO
cv2.THRESH_TOZERO_INV
cv2.THRESH_OTSU
cv2.THRESH_TRIANGLE
Try changing the threshold and max_val to obtain different results.
th=80max_val=255ret, o1 = cv2.threshold(gray_img_clahe, th, max_val, cv2.THRESH_BINARY)cv2.putText(o1,"Thresh_Binary",(40,100),cv2.FONT_HERSHEY_SIMPLEX,2,(255,255,255),3,cv2.LINE_AA)ret, o2 = cv2.threshold(gray_img_clahe, th, max_val, cv2.THRESH_BINARY_INV)cv2.putText(o2,"Thresh_Binary_inv",(40,100),cv2.FONT_HERSHEY_SIMPLEX,2,(255,255,255),3,cv2.LINE_AA)ret, o3 = cv2.threshold(gray_img_clahe, th, max_val, cv2.THRESH_TOZERO)cv2.putText(o3,"Thresh_Tozero",(40,100),cv2.FONT_HERSHEY_SIMPLEX,2,(255,255,255),3,cv2.LINE_AA)ret, o4 = cv2.threshold(gray_img_clahe, th, max_val, cv2.THRESH_TOZERO_INV)cv2.putText(o4,"Thresh_Tozero_inv",(40,100),cv2.FONT_HERSHEY_SIMPLEX,2,(255,255,255),3,cv2.LINE_AA)ret, o5 = cv2.threshold(gray_img_clahe, th, max_val, cv2.THRESH_TRUNC)cv2.putText(o5,"Thresh_trunc",(40,100),cv2.FONT_HERSHEY_SIMPLEX,2,(255,255,255),3,cv2.LINE_AA)ret ,o6=  cv2.threshold(gray_img_clahe, th, max_val,  cv2.THRESH_OTSU)cv2.putText(o6,"Thresh_OSTU",(40,100),cv2.FONT_HERSHEY_SIMPLEX,2,(255,255,255),3,cv2.LINE_AA)final=np.concatenate((o1,o2,o3),axis=1)final1=np.concatenate((o4,o5,o6),axis=1)cv2.imwrite("Image1.jpg",final)cv2.imwrite("Image2.jpg",final1)
Step 9: Adaptive Thresholding
In the previous section, we have applied cv2.threshold() using a global threshold value. As we could see, the obtained results were not very good due to the different illumination conditions in the different areas of the image. In these cases, you can try adaptive thresholding. In OpenCV, the adaptive thresholding is performed by the cv2.adapativeThreshold() function
This function applies an adaptive threshold to the src array (8­bit single­channel image). The maxValue parameter sets the value for the pixels in the dst image for which the condition is satisfied. The adaptiveMethod parameter sets the adaptive thresholding algorithm to use .
cv2.ADAPTIVE_THRESH_MEAN_C: The T(x, y) threshold value is calculated as the mean of the blockSize x blockSize neighbourhood of (x, y) minus the C parameter. cv2.ADAPTIVE_THRESH_GAUSSIAN_C: The T(x, y) threshold value is calculated as the weighted sum of the blockSize x blockSize neighbourhood of (x, y) minus the C parameter.
The blockSize parameter sets the size of the neighbourhood area used to calculate a threshold value for the pixel, and it can take the values 3, 5, 7,... and so forth.
The C parameter is just a constant subtracted from the means or weighted means (depending on the adaptive method set by the adaptiveMethod parameter). Commonly, this value is positive, but it can be zero or negative.
gray_image = cv2.imread('cylinder1.png',0)gray_image1 = cv2.imread('cylinder.png',0)thresh1 = cv2.adaptiveThreshold(gray_image, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 11, 2)thresh2 = cv2.adaptiveThreshold(gray_image, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 31, 3)thresh3 = cv2.adaptiveThreshold(gray_image, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 13, 5)thresh4 = cv2.adaptiveThreshold(gray_image, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 31, 4)thresh11 = cv2.adaptiveThreshold(gray_image1, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 11, 2)thresh21 = cv2.adaptiveThreshold(gray_image1, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 31, 5)thresh31 = cv2.adaptiveThreshold(gray_image1, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 21,5 )thresh41 = cv2.adaptiveThreshold(gray_image1, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 31, 5)final=np.concatenate((thresh1,thresh2,thresh3,thresh4),axis=1)final1=np.concatenate((thresh11,thresh21,thresh31,thresh41),axis=1)cv2.imwrite('rect.jpg',final)cv2.imwrite('rect1.jpg',final1)
Step 10:
OTSU Binarization
Otsu’s binarization algorithm, which is a good approach when dealing with bimodal images. A bimodal image can be characterized by its histogram containing two peaks. Otsu’s algorithm automatically calculates the optimal threshold value that separates both peaks by maximizing the variance between two classes of pixels. Equivalently, the optimal threshold value minimizes the intra­class variance. Otsu’s binarization algorithm is a statistical method, because it relies on statistical information derived from the histogram (for example, mean, variance, or entropy)
gray_image = cv2.imread('cylinder1.png',0)gray_image1 = cv2.imread('cylinder.png',0)ret,thresh1 = cv2.threshold(gray_image,0, 255,  cv2.THRESH_BINARY+cv2.THRESH_OTSU)ret,thresh2 = cv2.threshold(gray_image1,0, 255,  cv2.THRESH_BINARY+cv2.THRESH_OTSU)cv2.imwrite('rect.jpeg',np.concatenate((thresh1,thresh2),axis=1))
Now we have clearly recognized the batch no from a low contrast image . Hope you find this blog informative and interesting.
Thank you !!
Stay Tuned for my next blog...
You can visit my previous blog on below links
Follow me on my Youtube Channel
https://www.youtube.com/channel/UCSp0BoeXI_EK2W0GzG7TxEw
Connect with me here:
Linkedin: https://www.linkedin.com/in/ashishban...
Github: https://github.com/Ashishb21
Medium: https://medium.com/@ashishb21
Website: http://techplanetai.com/
Email : ashishb21@gmail.com , techplanetai@gmail.com