Quick Start with OpenCV: Image Morphological Operations

Article directory


Preface

Image morphology is a powerful and interesting technique that performs morphological operations on images to make them more suitable for subsequent processing steps. In this article, we'll take a deep dive into image morphology operations in OpenCV to get started quickly in this key area.

Logo

1. Basics of image morphology

1.1 Background introduction

As a branch of digital image processing, image morphology is dedicated to extracting image features, removing noise, and segmenting targets through morphological operations. Research in this field provides powerful tools and methods for areas such as computer vision, image recognition, and medical image processing.

1.2 pixel distance

In image processing, pixel distance is a key concept, which involves the spatial relationship between pixels in the image. Understanding pixel distance is important for various image analysis tasks.

1.2.1 What is pixel distance?

Pixel distance refers to the distance or measurement between two pixel points in an image. This concept has wide applications in many image processing tasks, such as distance measurement of targets, contour analysis, etc.

1.2.2 Common pixel distance measurement methods

Please refer toIntroduction to Common Distance Formulas and Similarity Calculation Methods in Machine LearningCommon The distance formulaRelated content

1.2.3 Code implementation for calculating pixel distance

In OpenCV, calculating pixel distance usually uses the cv2.distanceTransform function. This function calculates the distance from each pixel in the image to the nearest zero-valued pixel.

import cv2
import numpy as np

# 生成简单的二值化图像
image = np.zeros((300, 300), dtype=np.uint8)+150
cv2.rectangle(image, (50, 50), (250, 250), 0, -1)
cv2.circle(image, (200, 200), 50, 255, -1)

margin = np.zeros((300, 10), dtype=np.uint8)
# 计算像素距离
distance_transform = cv2.distanceTransform(image, cv2.DIST_L2, 3)
distance_transform = cv2.normalize(distance_transform, None, 255,0, cv2.NORM_MINMAX, cv2.CV_8UC1)

# 显示像素距离图像
cv2.imshow('Distance Transform', cv2.hconcat([image, margin, distance_transform]))
cv2.waitKey(0)
cv2.destroyAllWindows()

Distance Transform

Through such calculations, we can obtain the distance from each pixel in the image to the nearest zero-value pixel, and display it visually on the image.

In image processing, pixel distance is an important concept, which provides us with an effective tool to measure the spatial relationship of images. The cv2.distanceTransform function in OpenCV makes calculating pixel distance simple and efficient. In practical applications, understanding and skillfully using the concept of pixel distance will help to better understand and process images and lay the foundation for subsequent image morphology operations.

1.3 Image connectivity

In image processing, image connectivity is a key concept, which describes the connection relationship between pixels in the image. Understanding the connectivity of images helps analyze and process objects, regions, and their structures in images. This section will discuss in depth the concept of image connectivity and its application in OpenCV.

1.3.1 What is image connectivity?

Image connectivity refers to the connection relationship or connectivity relationship between pixels in the image. Two pixels are considered connected when they are connected by adjacent horizontal, vertical, or diagonal positions. The concept of connectivity is crucial for analyzing the connections between objects, regions, or objects in an image.

1.3.2 Connection type

Image connectivity can be divided into different types according to the way pixels are connected. Common ones include:

  1. 4 connected: Pixels are considered connected when they are adjacent horizontally and vertically.

  2. 8 connected: Pixels are considered connected when they are adjacent horizontally, vertically and diagonally.

  3. D connected: A type of connectivity between 4-connected and 8-connected, usually when pixels are adjacent through part of the horizontal, vertical and diagonal directions considered connected.

In practical applications, choosing the appropriate connectivity type depends on the shape and characteristics of the objects in the image.

1.3.3 Connected component marking

In image processing, it is often necessary to label and identify different connected components in the image, that is, connected regions in the image. This process is called connected component labeling. OpenCV provides related functions to perform this task, such as cv2.connectedComponents.

import cv2
import numpy as np

# 生成简单的二值化图像
image = np.zeros((300, 300), dtype=np.uint8)+150
cv2.rectangle(image, (50, 50), (150, 150), 255, -1)
cv2.rectangle(image, (50, 50), (250, 250), 0, -1)
cv2.circle(image, (150, 150), 50, 255, -1)

# 连通组件标记
num_labels, labels = cv2.connectedComponents(image)

# 共享的参数
shared_params = {
    
    
    "org": (10, 30),
    "fontFace": cv2.FONT_HERSHEY_SIMPLEX,
    "fontScale": 1,
    "thickness": 2,
    "color": 255,
    "lineType": cv2.LINE_AA,
}
shared_params2 = {
    
    
    "org": (10, 30),
    "fontFace": cv2.FONT_HERSHEY_SIMPLEX,
    "fontScale": 1,
    "thickness": 10,
    "color": 0,
    "lineType": cv2.LINE_AA,
}
# 添加文字
cv2.putText(image, "Original Image",**shared_params)

connected_image = cv2.hconcat([image])
for label in range(0, num_labels):
    component = np.zeros_like(image)
    component[labels == label] = 255
    cv2.putText(component, f'Component {
      
      label+1}', **shared_params2)
    cv2.putText(component, f'Component {
      
      label+1}', **shared_params)
    connected_image = cv2.hconcat([connected_image,component])

cv2.imshow('Connected Components', connected_image)
cv2.waitKey(0)
cv2.destroyAllWindows()

Connected Components

In this example, we generate a simple binarized image and label the image with connected components using cv2.connectedComponents . By looping through the labeled results, we can display different connected components separately to better understand the connectivity in the image.

In some scenes, it is necessary to detect the connectivity of the image and mark it with a frame line in the original image. We can use OpenCV's cv2.connectedComponentsWithStats to obtain the connected components in the image. Then, each connected component is traversed, its bounding box information is obtained, and marked with a green rectangle in the original image.

import cv2
import numpy as np
import random

# 创建一个空白的图像
width, height = 500, 500
image = np.zeros((height, width, 3), dtype=np.uint8)

# 定义绘制椭圆的函数
def draw_random_ellipse(img):
    # 生成随机椭圆的参数
    center = (random.randint(0, width-1), random.randint(0, height-1))
    axes = (random.randint(10, 100), random.randint(10, 100))
    angle = random.randint(0, 360)
    color = (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))

    # 绘制椭圆
    cv2.ellipse(img, center, axes, angle, 0, 360, color, -1)

# 定义检查椭圆是否重叠的函数
def check_overlap(new_ellipse, existing_ellipses):
    for existing_ellipse in existing_ellipses:
        # 获取椭圆的掩码
        new_mask = np.zeros_like(image, dtype=np.uint8)
        existing_mask = np.zeros_like(image, dtype=np.uint8)
        cv2.ellipse(new_mask, new_ellipse[0], new_ellipse[1], new_ellipse[2], 0, 360, (255, 255, 255), -1)
        cv2.ellipse(existing_mask, existing_ellipse[0], existing_ellipse[1], existing_ellipse[2], 0, 360, (255, 255, 255), -1)

        # 检查是否有重叠的部分
        overlap = cv2.bitwise_and(new_mask, existing_mask)
        if np.sum(overlap) > 0:
            return True
    return False

# 生成不重叠的椭圆
num_ellipses = 10
ellipses = []
for _ in range(num_ellipses):
    while True:
        new_ellipse = (
            (random.randint(0, width-1), random.randint(0, height-1)),
            (random.randint(10, 100), random.randint(10, 100)),
            random.randint(0, 360),
            (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
        )
        if not check_overlap(new_ellipse, ellipses):
            ellipses.append(new_ellipse)
            break

# 绘制不重叠的椭圆
for ellipse in ellipses:
    cv2.ellipse(image, ellipse[0], ellipse[1], ellipse[2], 0, 360, ellipse[3], -1)


# 将图像转换为灰度
gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

# 进行连通组件标记
_, labels, stats, centroids = cv2.connectedComponentsWithStats(gray_image)

# 获取连通组件的数量(排除背景)
num_labels = len(stats) - 1

# 遍历每个连通组件并在原图中用矩形标出
for i in range(1, num_labels + 1):
    x, y, w, h, _ = stats[i]
    cv2.rectangle(image, (x, y), (x + w, y + h), (0, 255, 0), 2)

# 显示图像
cv2.imshow("Connected Components", image)
cv2.waitKey(0)
cv2.destroyAllWindows()

Some operational renderings:

1.3.4 Application of connectivity in image processing

  • Object Counting: Connectivity analysis allows you to count objects in an image, such as particles or cells in the image.

  • Region segmentation: Connectivity information helps to segment the image so that different parts of the image can be accurately extracted and analyzed.

  • Target recognition: In the field of computer vision, targets in images, such as faces, vehicles, etc., can be identified and tracked through connectivity.

Image connectivity is a basic and important concept in image processing, which provides us with an effective tool for understanding and analyzing image structure. In OpenCV, the support of related functions makes the calculation of connectivity more convenient. By deeply understanding image connectivity, we can better process image data and lay a solid foundation for subsequent image analysis and processing work.

1.4 Structural elements

In image morphology operations, structural elements are a key concept, which provide templates for performing operations such as dilation and erosion. This section will delve into the concept of structural elements and the application of different types of structural elements in OpenCV.

1.4.1 What are structural elements?

Structural elements are small, slideable templates across the image that define the shape and size of morphological operations. It is the basis for operations such as dilation, erosion, opening and closing operations.

1.4.2 Types of structural elements

In OpenCV, there are three common types of structural elements: rectangular structural elements, circular structural elements and cross-shaped structural elements.

1.4.2.1 Rectangular structural elements

The rectangular structural element is a basic structural element whose shape is rectangular. In image processing, rectangular structural elements are often used to perform basic dilation and erosion operations. Its definition can be achieved through OpenCV's cv2.getStructuringElement function:

import cv2
# 定义矩形结构元素
rectangle_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))
# 打印矩形结构元素
print(rectangle_kernel)

operation result

[[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]]
1.4.2.2 Circular structural elements

The shape of the circular structure element is circular, which is suitable for processing situations where a wider range around the pixel needs to be considered. Likewise, circular structural elements can be easily created via the cv2.getStructuringElement function:

import cv2
# 定义圆形结构元素
circle_kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5))
# 打印圆形结构元
print(circle_kernel)
[[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]]
1.4.2.3 Cross-shaped structural elements

The cruciform structural element is a structural element similar to the shape of a cross, suitable for morphological operations that emphasize diagonal directions. Here's how to create a cross-shaped structural element:

import cv2
# 定义十字形结构元素
cross_kernel = cv2.getStructuringElement(cv2.MORPH_CROSS, (5, 5))
# 打印十字形结构元素
print(cross_kernel)
[[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]]
1.4.2.4 Custom structural elements

Sometimes we may need to define our own structural elements based on specific tasks, such as using a specific shape or size. You can directly create a NumPy array as a custom structural element and then pass it to the morphological operation function:

import cv2
import numpy as np

# 定义自定义结构元素
custom_kernel = np.array([[0, 1, 0],
                          [1, 1, 1],
                          [0, 1, 0]], dtype=np.uint8)

# 显示自定义结构元素
cv2.imshow('Custom Kernel', custom_kernel * 255)
cv2.waitKey(0)
cv2.destroyAllWindows()

2. Expansion and corrosion

In image processing, dilation and corrosion are two commonly used image morphological operations. They are used to expand and reduce the area of ​​objects in the image respectively. They are indispensable basic operations in image processing.

2.1 Expansion operation

Dilation is an operation that increases the area of ​​an object in an image by adding pixels. The core idea of ​​the dilation operation is to use a structuring element (usually a rectangle, circle, or cross), slide it over the image, and match the center of the structuring element to a pixel in the image. If the center of the structural element matches a pixel in the image, then the area covered by the structural element is set to white (255).

2.1.1 Principle of expansion operation

The mathematical expression of the expansion operation can be expressed in the following form:

( A ⊕ B ) ( x , y ) = ⋃ ( i , j ) ∈ B A ( x + i , y + j ) (A \oplus B)(x, y) = \bigcup_{(i, j) \in B} A(x+i, y+j)(AB)(x,y)=(i,j)BA(x+i,and+j)

Among, A A A This is the imported statue, B B B is a constituent element, ( A ⊕ B ) ( x , y ) (A \oplus B)(x, y) < /span>(AB)(x,y) Displayed image A A A Upper structure element B B B The result of the expansion operation. In the expansion operation, for the structural element B B Each element in B ( i , j ) (i, j) (i,j), General Statue A A The corresponding pixel in A is set to white. The final dilation result is the union of all pixels set to white.

2.1.2 Application of expansion operation

Dilation operations have many application scenarios in image processing:

  1. Connect Objects: Connect adjacent object pixels together to form a larger object.

  2. Fill small holes: is used to fill small holes in the image to make the object more complete.

  3. Increase the area of ​​an object: Increase the area of ​​an object by extending its boundaries.

2.1.3 Code example of expansion operation

In OpenCV, you can use the cv2.dilate function for dilation operations. Here is a simple Python code example:
random_ellipse1
random_ellipse1.jpg

import cv2
import numpy as np

# 读取图像
image = cv2.imread('random_ellipse1.jpg')
# 定义矩形结构元素
rectangle_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))
# 膨胀操作
dilated_image = cv2.dilate(image, rectangle_kernel, iterations=1)


# 图像连通性
def draw_connected_image(src_img):
    # 将图像转换为灰度
    gray_image = cv2.cvtColor(src_img, cv2.COLOR_BGR2GRAY)
    # 二值化
    threshold_value = 20
    _, binary_image = cv2.threshold(gray_image, threshold_value, 255, cv2.THRESH_BINARY)
    # 进行连通组件标记
    _, labels, stats, centroids = cv2.connectedComponentsWithStats(binary_image)
    # 获取连通组件的数量(排除背景)
    num_labels = len(stats) - 1
    # 遍历每个连通组件并在原图中用矩形标出
    for i in range(1, num_labels + 1):
        x, y, w, h, _ = stats[i]
        cv2.rectangle(src_img, (x, y), (x + w, y + h), (0, 255, 0), 2)
    return src_img


# 共享的参数
shared_params = {
    
    
    "org": (10, 30),
    "fontFace": cv2.FONT_HERSHEY_SIMPLEX,
    "fontScale": 1,
    "thickness": 2,
    "color": (0, 255, 0),
    "lineType": cv2.LINE_AA,
}
# 增加黑框
shared_params2 = {
    
    
    "org": (10, 30),
    "fontFace": cv2.FONT_HERSHEY_SIMPLEX,
    "fontScale": 1,
    "thickness": 10,
    "color": (0, 0, 0),
    "lineType": cv2.LINE_AA,
}
# 添加文字
image_txt = cv2.putText(image.copy(), "Original Image", **shared_params2)
image_txt = cv2.putText(image_txt, "Original Image", **shared_params)
dilated_image_txt = cv2.putText(dilated_image.copy(), "Dilated Image", **shared_params2)
dilated_image_txt = cv2.putText(dilated_image_txt, "Dilated Image", **shared_params)

# 显示原始图像和膨胀结果
cv2.imshow('Dilated Image', cv2.vconcat([
    cv2.hconcat([image_txt, draw_connected_image(image.copy())]),
    np.zeros((10, image.shape[1] * 2, 3), dtype=np.uint8) + 255,
    cv2.hconcat([dilated_image_txt, draw_connected_image(dilated_image.copy())])
]))
cv2.waitKey(0)
cv2.destroyAllWindows()

Dilated Image

We can see that after the expansion operation, two ellipses that were originally unconnected became connected.

2.2 Corrosion operations

In contrast to dilation, erosion is an operation that reduces the area of ​​an object by reducing its pixels. The erosion operation also uses a structural element. When the center of the structural element matches a pixel in the image, the center pixel is set to white (255) only if all pixels of the structural element match the pixels in the image. Otherwise, it is set to white. is black (0).

2.2.1 Principle of corrosion operation

The mathematical expression of the corrosion operation can be expressed in the following form:

( A ⊖ B ) ( x , y ) = ⋂ ( i , j ) ∈ B A ( x + i , y + j ) (A \ominus B)(x, y) = \bigcap_{(i, j) \in B} A(x+i, y+j) (AB)(x,y)=(i,j)BA(x+i,and+j)

Among, A A A This is the imported statue, B B B is a constituent element, ( A ⊖ B ) ( x , y ) (A \ominus B)(x, y) < /span>(AB)(x,y) Displayed image A A A Upper structure element B B B The result of a corrosion operation. In corrosion operations, for structural elements B B Each element in B ( i , j ) (i, j) (i,j), General Statue A A The corresponding pixel in A is compared with the corresponding pixel of the structural element. If all pixels in the area covered by the structural element are white, then the center pixel is set to white, otherwise it is set to black. The final erosion result is the intersection of all areas with white center pixels.

2.2.2 Application of corrosion operations

Corrosion operations have many application scenarios in image processing:

  1. Remove small objects: is used to remove smaller objects or fine details in the image.

  2. Smooth object boundaries: Make the object's boundaries smoother by reducing them.

  3. Separate connected objects: Separate connected objects to make them easier to identify and analyze.

2.2.3 Code example of corrosion operation

In OpenCV, you can use the cv2.erode function to perform corrosion operations. Here is a simple Python code example:
random_ellipse2
random_ellipse2.jpg

import cv2
import numpy as np

# 读取图像
image = cv2.imread('random_ellipse2.jpg')
# 定义矩形结构元素
rectangle_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))
# 腐蚀操作
eroded_image = cv2.erode(image, rectangle_kernel, iterations=1)


# 图像连通性
def draw_connected_image(src_img):
    # 将图像转换为灰度
    gray_image = cv2.cvtColor(src_img, cv2.COLOR_BGR2GRAY)
    # 二值化
    threshold_value = 20
    _, binary_image = cv2.threshold(gray_image, threshold_value, 255, cv2.THRESH_BINARY)
    # 进行连通组件标记
    _, labels, stats, centroids = cv2.connectedComponentsWithStats(binary_image)
    # 获取连通组件的数量(排除背景)
    num_labels = len(stats) - 1
    # 遍历每个连通组件并在原图中用矩形标出
    for i in range(1, num_labels + 1):
        x, y, w, h, _ = stats[i]
        cv2.rectangle(src_img, (x, y), (x + w, y + h), (0, 255, 0), 2)
    return src_img


# 共享的参数
shared_params = {
    
    
    "org": (10, 30),
    "fontFace": cv2.FONT_HERSHEY_SIMPLEX,
    "fontScale": 1,
    "thickness": 2,
    "color": (0, 255, 0),
    "lineType": cv2.LINE_AA,
}
# 增加黑框
shared_params2 = {
    
    
    "org": (10, 30),
    "fontFace": cv2.FONT_HERSHEY_SIMPLEX,
    "fontScale": 1,
    "thickness": 10,
    "color": (0, 0, 0),
    "lineType": cv2.LINE_AA,
}
# 添加文字
image_txt = cv2.putText(image.copy(), "Original Image", **shared_params2)
image_txt = cv2.putText(image_txt, "Original Image", **shared_params)
eroded_image_txt = cv2.putText(eroded_image.copy(), "Eroded Image", **shared_params2)
eroded_image_txt = cv2.putText(eroded_image_txt, "Eroded Image", **shared_params)

# 显示原始图像和腐蚀结果
cv2.imshow('Eroded Image', cv2.vconcat([
    cv2.hconcat([image_txt, draw_connected_image(image.copy())]),
    np.zeros((10, image.shape[1] * 2, 3), dtype=np.uint8) + 255,
    cv2.hconcat([eroded_image_txt, draw_connected_image(eroded_image.copy())])
]))
cv2.waitKey(0)
cv2.destroyAllWindows()

Eroded Image

We can see that one originally connected ellipse turned into two disconnected ellipses after the erosion operation.

3. Opening and closing operations

3.1 Open operation

The principle of the opening operation is to first perform an erosion operation on the image, and then perform an expansion operation on the erosion result. The opening operation is typically used to remove small noise in an image, smooth object boundaries, and preserve the overall shape of the object. In mathematical expression, the opening operation can be expressed as:

Opening ( A ) = ( A ⊖ B ) ⊕ B \text{Opening}(A) = (A \ominus B) \oplus B Opening(A)=(AB)B

Among, A A A This is the imported statue, B B B is a constituent element, ⊖ \ominus Display rot operation, ⊕ \oplus Display expansion operation, Opening ( A ) \text{Opening}(A) Opening(A) Display image A A A is the result of opening operation.

3.1.1 Application of opening operation

Open operation is mainly used in the following scenarios:

  1. Remove small noise: By first eroding, noise that is smaller than structural elements in the image can be removed.

  2. Smooth object boundaries: The erosion operation will shrink the object boundary inward, while the dilation operation will expand it outward, thus smoothing the object boundary.

  3. Preserve the overall shape: The opening operation can preserve the overall shape of the object and help extract the main structure of the object.

3.1.2 Code example of opening operation

In OpenCV, you can use the cv2.morphologyEx function to perform opening operations. Here is a simple Python code example:

import cv2
import numpy as np

# 生成一个黑色背景的图像
image_size = (300, 300)
image = np.zeros(image_size, dtype=np.uint8)

# 在图像中添加一些形状
cv2.circle(image, (50, 50), 20, 255, -1)
cv2.circle(image, (150, 150), 30, 255, -1)
cv2.rectangle(image, (200, 100), (250, 200), 255, -1)
cv2.ellipse(image, (100, 200), (40, 20), 30, 0, 360, 255, -1)
# 绘制白色椭圆弧
cv2.ellipse(image, (210, 210), (100, 50), 0, 180, 270, 255, 2)

# 定义矩形结构元素
rectangle_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))
# 开运算操作
opening_result = cv2.morphologyEx(image, cv2.MORPH_OPEN, rectangle_kernel)


# 图像连通性
def draw_connected_image(src_img):
    # 二值化
    threshold_value = 20
    _, binary_image = cv2.threshold(src_img, threshold_value, 255, cv2.THRESH_BINARY)
    # 进行连通组件标记
    _, labels, stats, centroids = cv2.connectedComponentsWithStats(binary_image)
    # 获取连通组件的数量(排除背景)
    num_labels = len(stats) - 1
    # 遍历每个连通组件并在原图中用矩形标出
    for i in range(1, num_labels + 1):
        x, y, w, h, _ = stats[i]
        cv2.rectangle(src_img, (x, y), (x + w, y + h), 127, 2)
    return src_img


# 共享的参数
shared_params = {
    
    
    "org": (10, 30),
    "fontFace": cv2.FONT_HERSHEY_SIMPLEX,
    "fontScale": 1,
    "thickness": 2,
    "color": 255,
    "lineType": cv2.LINE_AA,
}
# 增加黑框
shared_params2 = {
    
    
    "org": (10, 30),
    "fontFace": cv2.FONT_HERSHEY_SIMPLEX,
    "fontScale": 1,
    "thickness": 10,
    "color": 0,
    "lineType": cv2.LINE_AA,
}
# 添加文字
image_txt = cv2.putText(image.copy(), "Original Image", **shared_params2)
image_txt = cv2.putText(image_txt, "Original Image", **shared_params)
opening_result_txt = cv2.putText(opening_result.copy(), "Opening Result", **shared_params2)
opening_result_txt = cv2.putText(opening_result_txt, "Opening Result", **shared_params)

# 显示原始图像和开运算结果
cv2.imshow('Opening Result', cv2.vconcat([
    cv2.hconcat([image_txt, draw_connected_image(image.copy())]),
    np.zeros((10, image.shape[1] * 2), dtype=np.uint8) + 255,
    cv2.hconcat([opening_result_txt, draw_connected_image(opening_result.copy())])
]))
cv2.waitKey(0)
cv2.destroyAllWindows()

Opening Result
By understanding the principles and applications of opening operations, we can better use this operation to remove noise in images and smooth object boundaries, thereby providing clearer and more accurate data for subsequent image analysis and processing. In practical applications, different structural elements and the number of iterations can be selected according to specific circumstances to achieve the best opening operation effect.

3.2 Closed operation

The principle of closed operation is to first perform a dilation operation on the image, and then perform an erosion operation on the dilation result. Closure operations are often used to connect small holes between objects, fill small gaps, and maintain the overall shape of the object. In mathematical expression, the closed operation can be expressed as:

Closing ( A ) = ( A ⊕ B ) ⊖ B \text{Closing}(A) = (A \oplus B) \ominus B Closing(A)=(AB)B

Among, A A A This is the imported statue, B B B is a constituent element, ⊕ \oplus Display expansion operation, ⊖ \ominus Display operation, Closing ( A ) \text{Closing}(A) Closing(A) Display image A A A is the result of closed operation.

3.2.1 Application of closed operations

Closed operations are mainly used in the following scenarios:

  1. Connect small holes between objects: By expanding first, you can connect small holes between objects to make them more continuous.

  2. Fill small gaps: The dilation operation will expand the object boundary outwards, and the erosion operation will shrink it inward, thus filling small gaps between objects.

  3. Maintain the overall shape: The closing operation can maintain the overall shape of the object and help extract the main structure of the object.

3.2.2 Code example of closing operation

In OpenCV, you can use the cv2.morphologyEx function to perform closed operations. Here is a simple Python code example:

import cv2
import numpy as np

# 生成一个黑色背景的图像
image_size = (300, 300)
image = np.zeros(image_size, dtype=np.uint8)

# 在图像中添加一些形状
cv2.circle(image, (50, 50), 20, 255, -1)
cv2.circle(image, (150, 150), 30, 255, -1)
cv2.rectangle(image, (200, 100), (250, 200), 255, -1)
cv2.ellipse(image, (100, 200), (40, 20), 30, 0, 360, 255, -1)
# 绘制白色椭圆弧
cv2.ellipse(image, (223, 223), (100, 50), 0, 210, 253, 255, 2)

# 定义矩形结构元素
rectangle_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))
# 闭运算操作
closing_result = cv2.morphologyEx(image, cv2.MORPH_CLOSE, rectangle_kernel)


# 图像连通性
def draw_connected_image(src_img):
    # 二值化
    threshold_value = 20
    _, binary_image = cv2.threshold(src_img, threshold_value, 255, cv2.THRESH_BINARY)
    # 进行连通组件标记
    _, labels, stats, centroids = cv2.connectedComponentsWithStats(binary_image)
    # 获取连通组件的数量(排除背景)
    num_labels = len(stats) - 1
    # 遍历每个连通组件并在原图中用矩形标出
    for i in range(1, num_labels + 1):
        x, y, w, h, _ = stats[i]
        cv2.rectangle(src_img, (x, y), (x + w, y + h), 127, 2)
    return src_img


# 共享的参数
shared_params = {
    
    
    "org": (10, 30),
    "fontFace": cv2.FONT_HERSHEY_SIMPLEX,
    "fontScale": 1,
    "thickness": 2,
    "color": 255,
    "lineType": cv2.LINE_AA,
}
# 增加黑框
shared_params2 = {
    
    
    "org": (10, 30),
    "fontFace": cv2.FONT_HERSHEY_SIMPLEX,
    "fontScale": 1,
    "thickness": 10,
    "color": 0,
    "lineType": cv2.LINE_AA,
}
# 添加文字
image_txt = cv2.putText(image.copy(), "Original Image", **shared_params2)
image_txt = cv2.putText(image_txt, "Original Image", **shared_params)
closing_result_txt = cv2.putText(closing_result.copy(), "Closing Result", **shared_params2)
closing_result_txt = cv2.putText(closing_result_txt, "Closing Result", **shared_params)

# 显示原始图像和闭运算结果
cv2.imshow('Closing Result', cv2.vconcat([
    cv2.hconcat([image_txt, draw_connected_image(image.copy())]),
    np.zeros((10, image.shape[1] * 2), dtype=np.uint8) + 255,
    cv2.hconcat([closing_result_txt, draw_connected_image(closing_result.copy())])
]))
cv2.waitKey(0)
cv2.destroyAllWindows()

Closing Result
By understanding the principles and applications of closing operations, we can better use this operation to connect small holes between objects and fill small gaps, thereby making the image more continuous and complete. In practical applications, different structural elements and the number of iterations can be selected according to specific circumstances to achieve the best closed operation effect.

4. Black Hat Operation and Top Hat Operation

4.1 Black hat operation

The principle of black hat operation is to first perform a closed operation on the image, and then subtract the result of the closed operation from the original image. Black hat operation is mainly used to emphasize small details, small objects or local areas in the image. In mathematical expression, black hat operation can be expressed as:

BlackHat ( A ) = A − ( A ⊕ B ) \text{BlackHat}(A) = A - (A \oplus B) BlackHat(A)=A(AB)

Among, A A A This is the imported statue, B B B is a constituent element, ⊕ \oplus represents the expansion operation. The result of the black hat operation is the original image minus the result of the closed operation.

4.1.1 Application of black hat operations

Black hat operations are mainly used in the following scenarios:

  1. Emphasis on small details: Make small details in your image stand out more by highlighting them.

  2. Enhance small objects: is used to enhance small objects in the image to make them easier to detect and identify.

  3. Local area processing: Emphasizes local areas in the image to help analyze local features.

4.1.2 Code examples of black hat operations

Here is a simple Python code example:

import cv2
import numpy as np

# 生成一个黑色背景的图像
image_size = (300, 300)
image = np.zeros(image_size, dtype=np.uint8)

# 在图像中添加一些形状
cv2.circle(image, (50, 50), 20, 255, -1)
cv2.circle(image, (150, 150), 30, 255, -1)
cv2.rectangle(image, (200, 100), (250, 200), 255, -1)
cv2.ellipse(image, (100, 200), (40, 20), 30, 0, 360, 255, -1)
# 绘制白色椭圆弧
cv2.ellipse(image, (223, 223), (100, 50), 0, 210, 253, 255, 2)

# 定义矩形结构元素
rectangle_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))
# 黑帽运算
blackhat_result = cv2.morphologyEx(image, cv2.MORPH_BLACKHAT, rectangle_kernel)

# 共享的参数
shared_params = {
    
    
    "org": (10, 30),
    "fontFace": cv2.FONT_HERSHEY_SIMPLEX,
    "fontScale": 1,
    "thickness": 2,
    "color": 255,
    "lineType": cv2.LINE_AA,
}
# 增加黑框
shared_params2 = {
    
    
    "org": (10, 30),
    "fontFace": cv2.FONT_HERSHEY_SIMPLEX,
    "fontScale": 1,
    "thickness": 10,
    "color": 0,
    "lineType": cv2.LINE_AA,
}
# 添加文字
image_txt = cv2.putText(image.copy(), "Original Image", **shared_params2)
image_txt = cv2.putText(image_txt, "Original Image", **shared_params)
blackhat_result_txt = cv2.putText(blackhat_result.copy(), "BlackHat Result", **shared_params2)
blackhat_result_txt = cv2.putText(blackhat_result_txt, "BlackHat Result", **shared_params)

# 显示原始图像和黑帽运算结果
cv2.imshow('BlackHat Result', cv2.hconcat(
    [image_txt,
     np.zeros((image.shape[0], 10), dtype=np.uint8) + 255,
     blackhat_result_txt]))
cv2.waitKey(0)
cv2.destroyAllWindows()

Insert image description here

4.2 Top hat operation

The principle of the hat operation is to first perform an opening operation on the image, and then subtract the result of the opening operation from the original image. The top hat operation is mainly used to emphasize large details, large objects in the image, or global areas in the image. In mathematical expression, the hat operation can be expressed as:

TopHat ( A ) = ( A ⊕ B ) − A \text{TopHat}(A) = (A \oplus B) - A TopHat(A)=(AB)A

Among, A A A This is the imported statue, B B B is a constituent element, ⊕ \oplus represents the expansion operation. The result of the hat operation is the result of the open operation minus the original image.

4.2.1 Application of hat operation

Top hat operation is mainly used in the following scenarios:

  1. Emphasis on large details: Make large details in an image more noticeable by highlighting them.

  2. Enhance large objects: is used to enhance large objects in images, making them easier to detect and identify.

  3. Global area processing: Emphasizes the global area in the image to help analyze the overall characteristics.

4.2.2 Code example of top hat operation

Here is a simple Python code example:

import cv2
import numpy as np

# 生成一个黑色背景的图像
image_size = (300, 300)
image = np.zeros(image_size, dtype=np.uint8)

# 在图像中添加一些形状
cv2.circle(image, (50, 50), 20, 255, -1)
cv2.circle(image, (150, 150), 30, 255, -1)
cv2.rectangle(image, (200, 100), (250, 200), 255, -1)
cv2.ellipse(image, (100, 200), (40, 20), 30, 0, 360, 255, -1)
# 绘制白色椭圆弧
cv2.ellipse(image, (223, 223), (100, 50), 0, 210, 253, 255, 2)

# 定义矩形结构元素
rectangle_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))
# 礼帽运算
tophat_result = cv2.morphologyEx(image, cv2.MORPH_TOPHAT, rectangle_kernel)

# 共享的参数
shared_params = {
    
    
    "org": (10, 30),
    "fontFace": cv2.FONT_HERSHEY_SIMPLEX,
    "fontScale": 1,
    "thickness": 2,
    "color": 255,
    "lineType": cv2.LINE_AA,
}
# 增加黑框
shared_params2 = {
    
    
    "org": (10, 30),
    "fontFace": cv2.FONT_HERSHEY_SIMPLEX,
    "fontScale": 1,
    "thickness": 10,
    "color": 0,
    "lineType": cv2.LINE_AA,
}
# 添加文字
image_txt = cv2.putText(image.copy(), "Original Image", **shared_params2)
image_txt = cv2.putText(image_txt, "Original Image", **shared_params)
tophat_result_txt = cv2.putText(tophat_result.copy(), "TopHat Result", **shared_params2)
tophat_result_txt = cv2.putText(tophat_result_txt, "TopHat Result", **shared_params)

# 显示原始图像和礼帽运算结果
cv2.imshow('TopHat Result', cv2.hconcat(
    [image_txt,
     np.zeros((image.shape[0], 10), dtype=np.uint8) + 255,
     tophat_result_txt]))
cv2.waitKey(0)
cv2.destroyAllWindows()

TopHat Result

By understanding the principles and applications of black hat operations and top hat operations, we can better use these two operations to emphasize the details in the image and achieve more targeted image processing effects. In practical applications, different structural elements and operation types can be selected according to specific situations to achieve the best results.

5. Hitting and missing

5.1 Hit operation

The hit operation is used to detect regions in the image that match a given template. In the hit operation, a template (structural element) is applied to the input image, and the center pixel is set to white (255) only if all pixels of the template match the pixels at the corresponding position in the image, otherwise it is set to black (0 ). Mathematically, the hit operation can be expressed as:

( A ⊙ B ) ( x , y ) = { 255 if  A ( x + i , y + j ) = B ( i , j )  for all  ( i , j ) ∈ B 0 otherwise (A \odot B)(x, y) = \begin{cases} 255 & \text{if } A(x+i, y+j) = B(i, j) \text{ for all } (i, j) \in B \\ 0 & \text{otherwise} \end{cases} (AB)(x,y)={ 2550if A(x+i,and+j)=B(i,j) for all (i,j)Botherwise

Among, A A A This is the imported statue, B B B is a constituent element, ( A ⊙ B ) ( x , y ) (A \odot B)(x, y) < /span>(AB)(x,y) Displayed image A A A Upper structure element B B B The result of performing a hit operation.

5.2 Miss-hit operation

The miss operation is the complement operation of the hit operation and is used to detect areas in the image that do not match the given template. In the miss operation, a template (structural element) is applied to the input image, and the center pixel is set to white (255) only if all pixels of the template do not match the corresponding pixels in the image, otherwise it is set to black (0). In mathematical expression, the hit-miss operation can be expressed as:

( A ⊘ B ) ( x , y ) = { 255 if  A ( x + i , y + j ) ≠ B ( i , j )  for all  ( i , j ) ∈ B 0 otherwise (A \oslash B)(x, y) = \begin{cases} 255 & \text{if } A(x+i, y+j) \neq B(i, j) \text{ for all } (i, j) \in B \\ 0 & \text{otherwise} \end{cases} (AB)(x,y)={ 2550if A(x+i,and+j)=B(i,j) for all (i,j)Botherwise

Among, A A A This is the imported statue, B B B is a constituent element, ( A ⊘ B ) ( x , y ) (A \oslash B)(x, y) < /span>(AB)(x,y) Displayed image A A A Upper structure element B B B The result of the miss operation.

5.3 Application of hit and miss calculations

  1. Object detection: The hit operation can be used to detect the presence of objects of a specific shape in an image.

  2. Morphological skeleton extraction: Hit operation and miss operation are widely used in morphological skeleton extraction.

  3. Image Segmentation: The operation can be used to segment different regions in an image.

5.4 Code examples of hits and misses

In OpenCV, you can use the cv2.morphologyEx function to perform hit and miss calculations. Here is a simple Python code example:

import cv2
import numpy as np

# 生成一个黑色背景的图像
image_size = (300, 300)
image = np.zeros(image_size, dtype=np.uint8)

# 在图像中添加一些形状
cv2.circle(image, (50, 50), 20, 255, -1)
cv2.circle(image, (150, 150), 30, 255, -1)
cv2.rectangle(image, (200, 100), (250, 200), 255, -1)
cv2.ellipse(image, (100, 200), (40, 20), 30, 0, 360, 255, -1)
# 绘制白色椭圆弧
cv2.ellipse(image, (223, 223), (100, 50), 0, 210, 253, 255, 2)
# 盐噪声
pepper_noise = np.random.rand(*image.shape) < 0.5
image[pepper_noise] = 0

# 定义结构元素(内核)
kernel = np.array([[1, 0, 1],
                   [0, -1, 0],
                   [1, 0, 1]], dtype=np.int8)

# 击中运算
hitmiss_hit_result = cv2.morphologyEx(image.copy(), cv2.MORPH_HITMISS, kernel)

# 击不中运算
hitmiss_miss_result = cv2.morphologyEx(image, cv2.MORPH_HITMISS, cv2.bitwise_not(kernel))

# 显示原始图像、击中运算结果和击不中运算结果
cv2.imshow('Hit and Miss Result', cv2.hconcat([image, hitmiss_hit_result, hitmiss_miss_result]))
cv2.waitKey(0)
cv2.destroyAllWindows()

Hit and Miss
In morphological operations, the structuring element is a small binary matrix (or kernel) that defines the shape of the operation to be applied on the image. The element values ​​in a structural element are usually 0 and 1, where 1 means part of the structural element and 0 means not part of the structural element. Negative numbers are often used to indicate areas that are not considered.

In morphological hit and miss operations, the structural elements are composed of 1, 0 and -1. The specific meaning is as follows:

  • 1: Indicates part of the target (white area).
  • 0: Indicates the area that is not considered.
  • -1: Indicates part of the background (black area).

In the previous code example, the distribution of 1, 0, and -1 in kernel_hit and kernel_miss was designed to conform to the morphological hits and A miss-hit operation. This is to ensure that when the target is hit (kernel_hit), the white part of the structural element coincides with the white part of the target, and when the target is not hit (kernel_miss), the white part of the structural element coincides with the black background part of the target.

This representation can be adapted to specific problems and needs to ensure that morphological operations correctly capture the specific shape of the target.

6. Refinement and coarsening

6.1 Refinement operation

Thinning is a morphological operation used to reduce the width of objects in a binary image so that they are closer to the central axis in the image. The thinning operation gradually reduces the width of the object by iteratively performing hit and miss operations. The refined mathematical expression can be expressed as:

( A ⊗ B ) = A − ( A ⊙ B ) (A \otimes B) = A - (A \odot B) (AB)=A(AB)

Among, A A A This is the imported statue, B B B is a constituent element, ⊗ \otimes Display refinement operation, ⊙ \odot means hit operation. The result of the thinning operation is the original image minus the hit operation.

6.2 Coarse operation

Coarsening is the complementary operation of the thinning operation and is used to increase the width of objects in a binary image to make it closer to the central axis in the image. The coarsening operation gradually increases the width of the object by iteratively performing hit and miss operations. The roughened mathematical expression can be expressed as:

( A ⊙ B ) ∪ A (A \odot B) \cup A (AB)A

Among, A A A This is the imported statue, B B B is a constituent element, ⊙ \odot means hit operation. The result of the coarsening operation is the union of the result of the hit operation and the original image.

6.3 Application of refinement and coarsening operations

  1. Skeleton Extraction: The thinning operation can be used to extract the skeleton of an object in an image.

  2. Character Recognition: In character recognition, thinning helps identify the main shapes of characters.

  3. Image analysis: The thinning and coarsening operations can be used for feature extraction in image analysis.

6.4 Refined and coarsened code examples

In OpenCV, you can use the cv2.morphologyEx function for thinning and coarsening operations. Here is a simple Python code example:

import cv2
import numpy as np

# 生成一个黑色背景的图像
image_size = (300, 300)
image = np.zeros(image_size, dtype=np.uint8)

# 在图像中添加一些形状
cv2.circle(image, (50, 50), 20, 255, -1)
cv2.circle(image, (150, 150), 30, 255, -1)
cv2.rectangle(image, (200, 100), (250, 200), 255, -1)
cv2.ellipse(image, (100, 200), (40, 20), 30, 0, 360, 255, -1)
# 绘制白色椭圆弧
cv2.ellipse(image, (223, 223), (100, 50), 0, 210, 253, 255, 2)

# 执行细化操作
thin = cv2.ximgproc.thinning(image, thinningType=cv2.ximgproc.THINNING_GUOHALL)

# 执行粗化操作
# 创建形态学操作的结构元素
kernel = np.ones((3, 3), np.uint8)

# 使用形态学操作执行粗化
thick = cv2.dilate(image, kernel, iterations=1)

# 显示原始图像、细化操作结果和粗化操作结果
cv2.imshow('Thin and Thick Result', cv2.hconcat([image, thin, thick]))
cv2.waitKey(0)
cv2.destroyAllWindows()

Thin and Thick Result

By understanding the principles and applications of thinning and coarsening operations, we can better use these two operations to achieve image processing tasks such as skeleton extraction and character recognition. In practical applications, different structural elements and the number of iterations can be selected according to specific circumstances to achieve the best refinement or coarsening effect.


Summarize

In this article, we introduce the basics and operations of image morphology. First, through background introduction, we understand the importance of image morphology in image processing. Next, the concept of pixel distance and its measurement method are discussed, and the code implementation for calculating pixel distance is provided. Image connectivity is a key concept, and we detail its definition, different connectivity types, and applications in image processing.

Structural elements serve as the basis for morphological operations. We introduce the different types of structural elements and show how to customize them. Operations such as dilation, erosion, opening, closing, black hat and top hat operations are explained in detail and corresponding code examples are provided. Finally, we learned about hit and miss, thinning and coarsening operations and their application in image processing, and showed corresponding code examples.

Through the study of this article, we have a basic understanding of the principles and practical applications of image morphology, and can flexibly use these operations to solve actual image processing problems. Morphological operations are widely used in computer vision, image recognition and other fields, providing powerful tools and technologies for image processing.

おすすめ

転載: blog.csdn.net/qq_31463571/article/details/134481114