The main content of this article comes from the image processing part of OpenCV in the OpenCV-Python tutorial . The main content of this part is as follows:
-
Learn to change images between different color spaces. Also learn to track colorful objects in videos.
-
Geometric transformation of images
Learn to apply different geometric transformations to images, such as rotation, translation, etc.
-
Learn to convert images into binary images using global thresholding, adaptive thresholding, Otsu's binarization, and more.
-
Learn to blur images, filter images with custom kernels, and more.
-
Understand morphological transformations such as erosion, expansion, opening, closing, etc.
-
Learn to find image gradients, edges, and more.
-
Learn to find edges with Canny edge detection.
-
Learn about image pyramids and how to use them for image blending.
-
All about contours in OpenCV.
-
All about histograms in OpenCV.
-
Image transformation in OpenCV
Different image transformations are encountered in OpenCV, such as Fourier transform, cosine transform, etc.
-
Learn to use template matching to search for objects in images.
-
Learn to detect lines in an image.
-
Learn to detect circles in an image.
-
Image segmentation using watershed algorithm
Learn to segment images using the watershed segmentation algorithm.
-
Interactive foreground extraction using GrabCut algorithm
Learn to use GrabCut algorithm to extract foreground
Target
In this chapter:
- We will learn about different morphological operations such as erosion, dilation, opening, closing, etc.
- We will see different functions such as: cv.erode() , cv.dilate() , cv.morphologyEx() and many more.
theory
Morphological transformations are some simple operations based on the shape of the image. It usually operates on binary images. It takes two inputs, one is our original image and the second is called the structuring element or kernel , which determines the nature of the operation. Two basic morphological operations are erosion and dilation. Then its variants like Opening, Closing, Gradient, etc. also come into play. We will look at them one by one with the help of the image below:
1. Erosion
The basic idea of erosion is like soil erosion, it erodes the boundaries of foreground objects (always try to keep the foreground white). So what does it do? The kernel slides through the image (as in 2D convolution). A pixel (1 or 0) in the original image is considered to be 1 only if all pixels under the kernel are 1, otherwise it is eroded (becomes 0).
So what happens is that all pixels near the border will be discarded based on the size of the kernel. Therefore, the thickness or size of the foreground object is reduced, or just the white area in the image is reduced. It's useful for removing small white noise (as we saw in the color space chapter), separating two connected objects, etc.
Here, as an example, we use a 5x5 kernel with all ones. Let's see how it works:
import cv2 as cv
import numpy as np
def erosion():
img = cv.imread("/home/zhangsan/j.png", 0)
kernel = np.ones((5, 5), np.uint8)
erosion = cv.erode(img, kezhangsanrnel, iterations=1)
edge = np.full((img.shape[0], 3, 1), 255, np.uint8);
images = [img, edge, erosion]
dest = cv.hconcat(images)
cv.imshow("Image", dest)
cv.waitKey(-1)
if __name__ == "__main__":
erosion()
The result is as follows:
2. Expansion
It is the exact opposite of erosion. Here, a pixel element value is '1' if at least one of the elements under the kernel has a value of '1'. So it increases the white area in the image, or increases the size of the foreground object. Typically, in cases such as noise removal, erosion is followed by dilation. Because, erosion removes white noise, but it also shrinks our object. So we expand it. Since the noise is gone, they don't come back, but our object area increases. It can also be used to join damaged parts of an object.
dilation = cv.dilate(img, kernel, iterations=1)
The result is as follows:
3. open
Opening is just another name for expansion after erosion . As we explained above, it's useful in removing noise. Here we use the function cv.morphologyEx() .
def opening():
img = cv.imread("/home/hanpfei/j.png", 0)
for i in range(50):
row = random.randint(0, img.shape[0] - 1)
col = random.randint(0, img.shape[1] - 1)
img[row][col] = 255
kernel = np.ones((5, 5), np.uint8)
opening = cv.morphologyEx(img, cv.MORPH_OPEN, kernel)
edge = np.full((img.shape[0], 3, 1), 255, np.uint8);
images = [img, edge, opening]
dest = cv.hconcat(images)
cv.imshow("Image", dest)
cv.waitKey(-1)
If the image to be processed contains some white noise pixels, the effect will be more obvious. Here we first add some white noise pixels to the input image. The result is as follows:
4. close
Closing is the reverse operation of opening, expansion followed by erosion . It's useful for closing small holes within foreground objects, or small black spots on objects.
def closing():
img = cv.imread("/home/hanpfei/j.png", 0)
for i in range(5000):
row = random.randint(0, img.shape[0] - 1)
col = random.randint(0, img.shape[1] - 1)
img[row][col] = 0
kernel = np.ones((5, 5), np.uint8)
closing = cv.morphologyEx(img, cv.MORPH_CLOSE, kernel)
edge = np.full((img.shape[0], 3, 1), 255, np.uint8);
images = [img, edge, closing]
dest = cv.hconcat(images)
cv.imshow("Image", dest)
cv.waitKey(-1)
if __name__ == "__main__":
closing()
Here we also create some noise points on the input image, but this time they are black noise pixels. Since most black noise points will fall in the black background area and have no visible effect, more noise points are created here. The final result is as follows:
5. Morphological gradient
It is the difference between the dilation and erosion of an image.
The result will look like the outline of the object.
gradient = cv.morphologyEx(img, cv.MORPH_GRADIENT, kernel)
The result is as follows:
6. Top hat
It is an open interpolation of the input image and the image. The following example is done for the 9x9 kernel.
def top_hat():
img = cv.imread("/home/hanpfei/j.png", 0)
kernel = np.ones((9, 9), np.uint8)
tophat = cv.morphologyEx(img, cv.MORPH_TOPHAT, kernel)
edge = np.full((img.shape[0], 3, 1), 255, np.uint8);
images = [img, edge, tophat]
dest = cv.hconcat(images)
cv.imshow("Image", dest)
cv.waitKey(-1)
The result is as follows:
7. Black Hat
It is a closed interpolation of the input image and the image. The following example is done for the 9x9 kernel.
tophat = cv.morphologyEx(img, cv.MORPH_BLACKHAT, kernel)
The result is as follows:
Structural elements
We manually created the structural elements from the previous example with the help of Numpy. It is rectangular. But in some cases you may need an oval/round core. So for this purpose, OpenCV has a function, cv.getStructuringElement() . We just need to pass in the shape and size of the kernel to get the desired kernel.
# Rectangular Kernel
>>> cv.getStructuringElement(cv.MORPH_RECT,(5,5))
array([[1, 1, 1, 1, 1],
[1, 1, 1, 1, 1],
[1, 1, 1, 1, 1],
[1, 1, 1, 1, 1],
[1, 1, 1, 1, 1]], dtype=uint8)
# Elliptical Kernel
>>> cv.getStructuringElement(cv.MORPH_ELLIPSE,(5,5))
array([[0, 0, 1, 0, 0],
[1, 1, 1, 1, 1],
[1, 1, 1, 1, 1],
[1, 1, 1, 1, 1],
[0, 0, 1, 0, 0]], dtype=uint8)
# Cross-shaped Kernel
>>> cv.getStructuringElement(cv.MORPH_CROSS,(5,5))
array([[0, 0, 1, 0, 0],
[0, 0, 1, 0, 0],
[1, 1, 1, 1, 1],
[0, 0, 1, 0, 0],
[0, 0, 1, 0, 0]], dtype=uint8)
Other resources
- HIPR2 的 Morphological Operations
practise
Reference documentation
Done.