四、形态学图像处理
4.4 击中、击不中变换
4.4.1击中、击不中变换概念
击中、击不中变换是图像处理中的一种形态学操作,主要用于检测图像中的特定形状。这种变换利用结构元素来实现我们需要在图像中检测的特定形状。
4.4.2击中、不击中变换的基本原理:
击中:结构元素中的“1”对应的图像像素必须满足特定的条件(例如:等于某个特定的值)。
击不中:结构元素中的“0”对应的图像像素必须不满足上述条件。
只有当结构元素覆盖的图像区域完全满足“击中”和“击不中”的条件时,该区域的中心点才会被标记为击中。
4.4.3:击中、不击中变换的作用:
①形状识别:变换后的图像可以用于形状识别任务。例如,通过定义不同的结构元素,可以检测图像中的直线、角点、圆形等形状。
②特定模式的检测:变换后的图像能够突出显示与结构元素匹配的特定模式或形状。这些在原始图像中可能并不明显,但经过变换后会被更加清晰展示出来。
③背景抑制:通过击中、不击中变换,与结构元素不匹配的图像区域(即背景)将被抑制或消除。从而使得特征区域在处理后的图像中更加突出。
import cv2
import numpy as np
# 读取图像
image = cv2.imread('image path', 0)
# 定义结构元素,这里只是一个示例,实际应用中需要根据你的需求来定义
struct_element = np.array([[1, 0],
[1, 0]])
# 通过自定义一个函数来模拟击中、不击中变换
def hit_or_miss(image, struct_element):
hit = cv2.morphologyEx(image, cv2.MORPH_HITMISS, struct_element)
return hit
# 应用击中、不击中变换
result = hit_or_miss(image, struct_element)
# 显示结果
cv2.imshow('Hit-or-Miss Result', result)
cv2.waitKey(0)
cv2.destroyAllWindows()
hit = cv2.morphologyEx(image, cv2.MORPH_HITMISS, struct_element)
morphologyEx
: 是OpenCV中用于执行各种形态学变换的函数,包括击中、不击中变换等。
cv2.MORPH_HITMISS
: 是morphologyEx
函数的一个参数,指定要执行的形态学变换类型是击中、不击中变换。
struct_element
: 定义的击中、不击中变换所使用的结构元素的矩阵。其定义了要在输入图像中查找的特定像素需边界提取求。
4.5 二值图像形态学处理应用
4.5.1边界提取
1.边界提取概念:在二值图像中边界提取可以理解成将前景图像(白色部分)的轮廓提取出来。
2.边界提取的操作:直观的想象一下,想要完成边界提取,就是要扣去前景图像的非边缘部分,这就能联想到减法。要实现这个操作,就离不开腐蚀和膨胀操作
①内边界提取操作:要通过腐蚀操作来使前景图像缩小一下。然后再用原图减去腐蚀操作后的图像,便能内实现边界提取。
②外边界提取操作:要通过膨胀操作来使前景图像扩大。然后再用膨胀操作处理后的图像减去原图,便能实现外边界提取。
import cv2
import numpy as np
# 读取图像
image = cv2.imread('img1.jpg', 0) # 第二个参数0表示以灰度模式读取图像
# 定义腐蚀操作的结构元素
kernel = np.ones((5, 5), np.uint8)
# 腐蚀操作
erosion = cv2.erode(image, kernel, iterations=1)
# 用原图减去腐蚀后的图像得到边缘
edge = image - erosion
# 显示结果
cv2.imshow('Original Image', image)
cv2.imshow('Edge Image', edge)
# 等待任意键按下
cv2.waitKey(0)
cv2.destroyAllWindows()
4.5.2孔洞填充
1.空洞填充的概念:可以简单理解成将前景图像围成区域中的黑色小孔(背景部分)变为白色
2.孔洞填充的操作:找到图像中被前景图像完全包围的部分(黑色孔洞),然后对其进行填充即可。
3.实际应用
import numpy as np
import matplotlib.pyplot as plt
from scipy import ndimage
from skimage import io
from skimage.color import rgba2rgb, rgb2gray
from skimage.filters import threshold_otsu
# 加载图片
image = io.imread('img.jpg')
# 检查图像是否有4个通道(RGBA),如果有,则转换为RGB
if image.shape[2] == 4:
image = rgba2rgb(image)
# 将图片转换为灰度图像
gray_image = rgb2gray(image)
# 使用Otsu阈值将灰度图像转换为二值图像
threshold = threshold_otsu(gray_image)
binary_image = gray_image > threshold
# 使用ndimage.binary_fill_holes函数填充空洞
filled_image = ndimage.binary_fill_holes(binary_image)
# 显示原始二值图像和填充后的图像
plt.figure(figsize=(10, 5))
plt.subplot(1, 2, 1)
plt.imshow(binary_image, cmap='gray')
plt.title('Original Binary Image')
plt.axis('off')
plt.subplot(1, 2, 2)
plt.imshow(filled_image, cmap='gray')
plt.title('Image After Hole Filling')
plt.axis('off')
plt.show()
ndimage.binary_fill_holes:这是空洞填充的函数,它查找图像中所有被前景图像(白色)完全包围的背景(黑色),并将这些区域填充为白色。同时,这保证了只有当孔洞被前景像素完全包围时才会被填充,其余背景部分则不会受到影响。
4.5.3连通域的标记提取
1连通域的标记提取的概念:可以理解成在一副二值图像中,将属于同一个连通区域的像素点赋予相同的标记,而属于不同连通区域的像素点则赋予不同的标记。
import cv2
import numpy as np
# 读取图像文件
image = cv2.imread('img1.jpg', 0) # 第二个参数0表示以灰度模式读取图像
# 对图像进行二值化处理
_, binary_image = cv2.threshold(image, 127, 255, cv2.THRESH_BINARY)
# 查找图像中的连通域并进行标记
num_labels, labels = cv2.connectedComponents(binary_image)
# 创建一个与原图大小相同的彩色图像,用于后续绘制方框
color_image = cv2.cvtColor(image, cv2.COLOR_GRAY2BGR)
# 遍历每个连通域(注意:背景区域的标记为0,因此从1开始遍历)
for label in range(1, num_labels):
# 找到当前连通域的所有像素点的坐标
points = np.where(labels == label)
# 如果当前连通域不为空
if points[0].size > 0:
# 将坐标点转换为OpenCV要求的格式
points_array = np.column_stack((points[1], points[0])) # 注意:OpenCV中点的坐标格式为(x, y)
# 计算当前连通域的最小外接矩形
x, y, w, h = cv2.boundingRect(points_array)
# 在彩色图像上绘制当前连通域的外接矩形方框
cv2.rectangle(color_image, (x, y), (x + w, y + h), (0, 255, 0), 2)
# 显示原图和带有方框的标记图像
cv2.imshow('Original Image', image)
cv2.imshow('Labeled Image with Rectangles', color_image)
# 等待用户按下任意键
cv2.waitKey(0)
cv2.destroyAllWindows() # 关闭所有OpenCV创建的窗口
注:本人为在校学生,博客是边学边写的,主要是为了巩固知识,如有错误请积极指正。
本文的内容主要基于我对张运楚教授编著的《数字图像处理》一书的学习和理解。这本书深入浅出地介绍了数字图像处理的基本理论以及经典算法等,并且提供了丰富的示例代码和实际用例,极大地帮助了我学习图像处理知识。在此,我推荐大家阅读这本书,更加深入的学习有关图像处理的知识。