Image Pre-processing through OpenCV (Part-2)

Read Time: 6 min

In part-1, we learned basic terminology and operations to pre-process the image. In part-2, let’s check some other methods.

Table of content:

  1. Image Thresholding
  2. Image Smoothing
  3. Rescaling
  4. Denoising

Image Thresholding

Image thresholding is used to extract the desired information or pixels from the image.

There are 3 types thresholding can be applied using openCV

  • Simple Thresholding
    • Binary threshold
    • Inverted binary threshold
    • Truncated threshold
    • threshold set to zero
    • Inverted threshold set to zero
  • Adaptive Thresholding
    • Mean
    • Gaussian
  • Otsu Thresholding

Simple thresholding

Constant threshold is applied to whole image

Binary threshold

If pixel value > Threshold: Pixel value == 255

If pixel value < Threshold: Pixel value == 0

ret, thresh1 = cv2.threshold(img, 60, 255, cv2.THRESH_BINARY) ##threshold=60
cv2.imshow('zebra',thresh1)
cv2.waitKey(0)
cv2.destroyAllWindows()

If threshold value is changed to 60:

ret, thresh2 = cv2.threshold(img, 60, 255, cv2.THRESH_BINARY_INV) ##inverted_binary_threshold=60
cv2.imshow('zebra',thresh2)
cv2.waitKey(0)
cv2.destroyAllWindows()

Inverted binary threshold

It’s just opposite to binary threshold

If pixel value > Threshold: Pixel value == 0

If pixel value < Threshold: Pixel value ==255

ret, thresh3 = cv2.threshold(img, 60, 255, cv2.THRESH_BINARY_INV) ##inverted_binary_threshold=60
cv2.imshow('zebra',thresh3)
cv2.waitKey(0)
cv2.destroyAllWindows()

Truncated threshold

If pixel value > Threshold: pixel value == Threshold

If pixel value < threshold: pixel value == No change

ret, thresh4 = cv2.threshold(img, 90, 255, cv2.THRESH_TRUNC) ##thresh=90
cv2.imshow(‘zebra’,thresh4)
cv2.waitKey(0)
cv2.destroyAllWindows()

Threshold set to zero

If pixel value > Threshold: pixel value == No change

If pixel value < threshold: pixel value == 0

ret, thresh5 = cv2.threshold(img, 100, 255, cv2.THRESH_TOZERO) 
cv2.imshow('zebra',thresh5)
cv2.waitKey(0)
cv2.destroyAllWindows()

Inverted threshold set to zero

If pixel value > Threshold: pixel value == 0

If pixel value < threshold: pixel value == No change

ret, thresh6 = cv2.threshold(img, 200, 255, cv2.THRESH_TOZERO_INV) ## threshold=200
cv2.imshow('zebra',thresh6)
cv2.waitKey(0)
cv2.destroyAllWindows()

Adaptive threshold

Previously, in simple thresholding, threshold value was constant for all pixels. That is not very effective if image has variations in light, contrast.

So in that case, adaptive thresholding is used. There are two ways to implement adaptive thresholding.

  1. based on mean: In this case we take mean of pixels under kernel area and subtract it from a constant value (manually defined). Through this approach, threshold will be varied according to neighborhood pixels.
  2. Based on Gaussian weighted sum: In this case we take weighted sum of pixels under kernel area and then subtract it from a constant value.

which method will be suitable for application, that purely depends on what we want to extract from the image, image details.

Mean adaptive thresholding

img_t = cv2.imread("frag.png")
img1 = cv2.cvtColor(img_t, cv2.COLOR_BGR2GRAY) 
thresh7 = cv2.adaptiveThreshold(img1, 255, cv2.ADAPTIVE_THRESH_MEAN_C,cv2.THRESH_BINARY, 121,15) 
cv2.imshow('text',thresh7)
cv2.waitKey(0)
cv2.destroyAllWindows() 
Original Image
Mean adaptive thresholding

Gaussian Adaptive thresholding

thresh8 = cv2.adaptiveThreshold(img1, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 33,15) 
cv2.imshow('text',thresh8)
cv2.waitKey(0)
cv2.destroyAllWindows()  
Gaussian adaptive thresholding

If batch size is chosen an even no, then error will be thrown as shown. So batch size is always chosen odd.

Ostu threshold

In otsu thresholding, value of threshold is decided automatically.

How does it work?

It assumes that every image contains two kinds of pixel intensities, one belongs to background class and one to foreground class. So it involves iterating process through all pixels, segregate pixels by keeping them into two classes. The goal is to find a threshold such that the resulting background and foreground distributions are maximally
separated.

ret, thresh9 = cv2.threshold(img1,100 , 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
cv2.imshow('otsu',thresh8)
cv2.waitKey(0)
cv2.destroyAllWindows()  

Image Blurring/smoothing

Image smoothing can be done in 4 ways:

  • Averaging
  • Median blur
  • Gaussian blur
  • Bilateral filtering

Averaging

It replaces the pixel value with average of all pixels under kernel area.

img = cv2.imread("zebra.jpg")
blur = cv2.blur(img,(5,5))
cv2.imshow('blur',blur)
cv2.waitKey(0)
cv2.destroyAllWindows() 

Median Blur

It’s non linear filter. It replaces the central element pixel values with the median value of pixels present in the kernel area. How kernel area pixels are taken into account, depends on kernel size e.g. (3×3),(5×5)

img = cv2.imread("zebra.jpg")
median = cv2.medianBlur(img,5)
cv2.imshow('median',median)
cv2.waitKey(0)
cv2.destroyAllWindows() 

Gaussian Blur

It replaces pixel values with weighted average of neighborhood pixels. It follows Gaussian distribution, that’s why more weight is given to the pixels which are nearer to central pixel of the kernel. It’s a linear filter. It does not preserves the edges because while taking weighted average, it does not check intensity or edges.

img = cv2.imread("zebra.jpg")
gblur = cv2.GaussianBlur(img,(5,5),0)
cv2.imshow('Gaussian',gblur)
cv2.waitKey(0)
cv2.destroyAllWindows() 

Bilateral Filtering

Bilateral filtering considers two Gaussian filters for blurring. One is used for weighted average, same as in Gaussian blurring while another Gaussian filter is function of intensity difference that makes sure that it will consider only pixels in kernel which are almost similar in intensity. So that edges can be preserved.

img = cv2.imread("zebra.jpg")
bi_lat = cv2.bilateralFilter(img,9,75,75)
cv2.imshow('Bi_lateral',bi_lat)
cv2.waitKey(0)
cv2.destroyAllWindows() 

Rescaling

Image resizing is known as Image rescaling. It helps to reduce or increase number of pixels in an image. Increase in nuumber of pixels is called zooming of the image. Reduction in size is helpful in many ways in image processing as by reducing number of pixels, less computation is required for neural network or any other machine learning model.

Reduction in number of pixels, may affect the image quality i.e. fine details or features.

img = cv2.imread("zebra.jpg")
#print(img.shape)
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
half = cv2.resize(img, (0,0), fx = 0.5, fy = 0.5) 
plt.imshow(half)

To make the image size larger:

img = cv2.imread("zebra.jpg")
#print(img.shape)
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
large = cv2.resize(img, (800, 500)) 
plt.imshow(large)

Denoising

Through image blurring, we filtered out noise on the basis of nearest pixels. So blurring is a local process to reduce noise.

If we take small patch in the image, find out similiar windows in the same image, take their average and then replace the pixel of the window with the average obtained, process will be non-local that denoising.

There are two functions available in opencv to denoise the image.

  1. cv2.fastNlMeansDenoising() – works with a single grayscale image
img = cv2.imread("frag.png")
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
denoised = cv2.fastNlMeansDenoising(img, denoised, 15, 10, 50)
plt.imshow(denoised)
denoised_image
plt.imshow(img)
original_image
  1. cv2.fastNlMeansDenoisingColored() – works with a color image.
img = cv2.imread("zebra.jpg")
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
denoised = cv2.fastNlMeansDenoisingColored(img, denoised, 15, 10, 50)
plt.imshow(denoised)

Here is the link for github code:

https://github.com/Appiiee/Image-Pre-processing-using-OpenCV

If want to know image pre-processing basic terms and operations, please check part-1 here: