OpenCV||超详细的形态学应用

Py巡航机 2024-10-08 16:01:05 阅读 84

 概念

形态学图像处理的基本运算有膨胀、腐蚀、开操作和闭操作、击中与击不中变换、TOP-HAT变换、黑帽变换等。

应用

形态学处理在图像处理上的应用有:消除噪声、边界提取、区域填充、连通分量提取、凸壳、细化、粗化,分割出独立的图像元素或者图像中相邻的元素,求取图像中明显的极大值区域和极小值区域,以及求取图像梯度等。

结构元素

设有两幅图像A和B。若A是被处理的对象,而B是用来处理A的,则称B为结构元素,也被形象的称作刷子。结构元素通常是一些比较小的图像。

形态学概述

数学上的形态学,特别是数学形态学(Mathematical morphology),是一门建立在格论和拓扑学基础之上的图像分析学科,它是数学形态学图像处理的基本理论。数学形态学具有坚实的理论基础和广泛的应用价值,其基本思想和方法对图像处理的理论和技术产生了重大影响。以下是对数学形态学的详细阐述:

1、数学形态学概述

数学形态学是法国和德国的科学家在研究岩石结构时建立的一门学科,旨在通过物体和结构元素相互作用的某些运算,获取物体更本质的形态信息。在数学中,格是其非空有限子集都有一个上确界(叫并)和一个下确界(叫交)的偏序集合。拓扑学则研究空间内,在连续变化(如拉伸或弯曲,但不包括撕开或黏合)下维持不变的性质。数学形态学利用这些数学工具,对图像进行定量的描述和分析。

2、基本运算

数学形态学的基本运算包括:

腐蚀(Erosion):是最基本的一种数学形态学运算。腐蚀可以看作是图像中的物体边界点被消除的过程,其结果是图像缩小。【相当于图像与结构元素做”与“运算,只要结果中有0,该点即腐蚀】膨胀可以看作是图像中的物体边界向外扩展的过程,其结果是图像扩大。【相当于图像与结构元素做”或“运算,只要结果中有1,该点即膨胀开运算(Opening):先腐蚀后膨胀的过程称为开运算。开运算能够去除图像中的孤立小点和毛刺,同时保持图像的主要形状不变。闭运算(Closing):先膨胀后腐蚀的过程称为闭运算。闭运算能够填平图像中的小孔和裂缝,同时保持图像的主要形状不变。

此外,数学形态学还包括击中击不中变换、形态学梯度、Top-hat变换、颗粒分析、流域变换等高级运算。

3、应用

膨胀

图像分割

在图像分割中,形态学膨胀常被用于消除噪点、填补图像中的小空洞或缝隙,以及使分割后的对象边界更加平滑。例如,在二值图像中,膨胀操作可以使得连通块的边界扩大,从而有助于将相邻的但由于噪声或分割误差而分离的对象合并为一个整体。

特征提取

通过膨胀操作,可以增强图像中某些重要的特征区域,使其更加突出,便于后续的特征提取和识别。例如,在车牌识别系统中,可以使用膨胀操作来增强车牌字符的轮廓,使其更容易被检测和识别。

图像增强

对于灰度图像,膨胀操作一般采用极大运算,可以提升图像的整体亮度,增强图像的亮细节,消除暗细节。这有助于改善图像的视觉效果,使图像中的信息更加清晰易读。

目标检测与跟踪

在目标检测与跟踪领域,膨胀操作可以用于扩大目标的检测区域,提高检测的准确性。同时,通过连续应用膨胀操作,还可以跟踪目标在图像序列中的运动轨迹。

医学图像处理

在医学图像处理中,形态学膨胀被广泛应用于病灶区域的分割和识别。通过对医学图像进行膨胀操作,可以消除病灶区域周围的噪声和干扰因素,提高病灶区域检测的敏感性和特异性。

腐蚀

噪声去除

腐蚀操作可以通过去除图像中的小且无意义的物体或区域来减少噪声。当噪声表现为小的、离散的点或区域时,腐蚀操作可以通过侵蚀这些区域的边界来有效地消除它们。

特征提取

在图像处理中,腐蚀操作可以帮助提取图像中的关键特征。通过缩小前景对象的边界,腐蚀操作可以简化图像结构,使得后续的特征提取过程更加容易和准确。

形态学梯度计算

形态学梯度是膨胀图像与腐蚀图像之差,它提供了对象边界的轮廓信息。腐蚀操作是计算形态学梯度的重要步骤之一,通过腐蚀操作得到的图像与膨胀操作得到的图像相减,可以得到更加清晰的边界信息。

细化处理

在某些应用场景中,需要将图像中的对象细化到单像素宽度,以便进行进一步的分析或处理。腐蚀操作可以通过逐步侵蚀对象的边界来实现这一目的,使得对象逐渐细化到所需的宽度。

分割与连接

腐蚀操作还可以用于图像分割和连接处理。通过调整腐蚀操作的结构元素大小和形状,可以控制腐蚀的程度和方向,从而实现将图像中的不同对象分割开来的目的。同时,在某些情况下,腐蚀操作还可以用于连接邻近的对象或区域,使它们形成一个整体。

去除毛刺和小凸起

在机械加工或图像处理中,产品或图像边缘可能会存在毛刺或小凸起。腐蚀操作可以通过侵蚀这些区域的边界来去除这些不希望的特征,使得产品或图像的边缘更加平滑和规则。

预处理步骤

在许多图像处理流程中,腐蚀操作被用作预处理步骤之一。通过对原始图像进行腐蚀处理,可以去除图像中的无关细节和噪声干扰,为后续的处理步骤(如边缘检测、图像分割等)提供更好的输入图像质量。

开运算

消除细小物体和噪声

开运算能够有效地消除图像中比结构元素小的孤立点、噪声和小物体。这是因为腐蚀操作首先去除了这些细小物体,而随后的膨胀操作则在一定程度上恢复了较大物体的原始形状,但保留了去除小物体的效果。这一特性使得开运算在图像预处理阶段非常有用,可以显著提高图像的质量。

 在纤细点处分离物体

当两个物体之间仅通过纤细的连接点相连时,开运算可以在这些连接点处有效地将它们分离。这是因为腐蚀操作会缩小物体的边界,包括纤细的连接点,而膨胀操作虽然会恢复物体的部分边界,但通常不足以重新连接那些已经被腐蚀操作断开的部分。

平滑较大物体的边界

开运算还能够在不明显改变物体面积的情况下,平滑较大物体的边界。这是因为腐蚀操作去除了边界上的不规则部分,而膨胀操作则在一定程度上恢复了边界的平滑性。这种平滑效果有助于改善图像的视觉效果,并有助于后续的图像分析和处理任务。

保留重要的结构特征

尽管开运算会改变图像中的某些细节,但它通常能够保留图像中重要的结构特征。这是因为腐蚀和膨胀操作都是基于结构元素的局部操作,它们对图像的整体结构影响有限。因此,在适当的结构元素大小下,开运算可以在去除噪声和小物体的同时,保留图像中的主要结构和特征。

结合其他形态学操作

开运算经常与其他形态学操作(如闭运算、形态学梯度等)结合使用,以实现更复杂的图像处理任务。例如,连续的开闭运算可以去除二值化图像的噪声并平滑其边界;形态学梯度则可以利用开运算的结果来提取图像的边缘信息。

闭运算

填充物体内的小空洞

闭运算能够填充图像中物体内部的小空洞或细小缺失部分,使物体的形状更加完整和规则。这对于图像预处理、物体识别等领域具有重要意义。

连接邻近的物体

在图像处理中,如果物体之间由于某些原因(如拍摄角度、光照条件等)而产生了微小的间隙或断裂,闭运算可以通过膨胀操作将这些间隙或断裂部分连接起来,从而形成一个整体。这对于图像分割、图像重建等领域具有重要作用。

平滑物体的边界

闭运算在平滑物体边界方面也具有显著效果。通过先膨胀后腐蚀的过程,闭运算可以平滑物体的边缘,减少边缘的锯齿状或其他不规则形状,使物体的轮廓更加平滑和自然。这对于图像美化、图像增强等领域具有重要意义。

去除图像中的小型暗点

闭运算能够去除图像中值低于邻近点的孤立点(即小型暗点),达到去除图像中噪声的作用。这对于提高图像的清晰度和质量具有重要作用。

结合开运算进行噪声去除和边界平滑

在实际应用中,闭运算经常与开运算结合使用。开运算能够消除图像中值高于邻近点的孤立点(即小型亮点),并平滑较大物体的边界。通过连续使用开运算和闭运算,可以去除图像中的噪声,同时平滑物体的边界,使图像更加清晰和易于分析。

OpenCV中的形态学操作

1、膨胀

API

<code>cv2.dilate(src, kernel, iterations[, dst[, anchor[, borderType[, borderValue]]]])

src:输入图像,即源图像,通常是一个numpy.ndarray类型,可以是任意通道数的图像,但图像的数据类型需要是OpenCV支持的类型之一,如CV_8UCV_16UCV_16SCV_32FCV_64F

kernel:膨胀操作所使用的结构元素(或称为核)。这个参数定义了膨胀操作的邻域形状和大小。可以使用cv2.getStructuringElement()函数来生成结构元素,或者自己定义一个numpy.ndarray。如果不指定,默认为一个3x3的矩形结构元素。

iterations:膨胀操作的迭代次数,默认为1。这个参数决定了膨胀的程度,迭代次数越多,膨胀效果越明显。

dst:输出图像,即膨胀后的图像。该图像将与输入图像src具有相同的尺寸和数据类型。如果提供该参数,则函数将结果存储在其中;否则,将创建一个新的图像。

anchor:结构元素的锚点位置,默认为(-1, -1),表示锚点位于结构元素的中心。该参数决定了结构元素相对于输入图像中当前像素的位置。

borderType:边界像素的外推方法,默认为cv2.BORDER_DEFAULT。这个参数指定了如何处理图像边界之外的像素。不同的外推方法可能导致不同的膨胀结果。

borderValue:当borderTypecv2.BORDER_CONSTANT时,需要指定边界值。这个参数定义了当膨胀操作扩展到图像边界之外时,边界像素的像素值。

示例

import cv2

import numpy as np

# 读取图像

img = cv2.imread('your_image.jpg', cv2.IMREAD_GRAYSCALE)

# 转换为二值图像(这里仅作为示例,具体阈值需根据实际情况调整)

_, binary = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)

# 创建结构元素

kernel = np.ones((5, 5), np.uint8)

# 进行膨胀操作

dilated = cv2.dilate(binary, kernel, iterations=1)

# 显示结果

cv2.imshow('Original', binary)

cv2.imshow('Dilated', dilated)

cv2.waitKey(0)

cv2.destroyAllWindows()

2、腐蚀

API

dst = cv2.erode(src, kernel[, dst[, anchor[, iterations[, borderType[, borderValue]]]]])

srcInputArray类型,输入图像,即源图像,填cv2.Mat类的对象即可。图像通道的数量可以是任意的,但图像深度应为CV_8UCV_16UCV_16SCV_32FCV_64F其中之一。kernelInputArray类型,腐蚀操作的内核。它定义了邻域的形状和大小,用于腐蚀操作。如果为NULL,则使用默认值为3x3的矩形核。通常,我们使用cv2.getStructuringElement()函数来生成特定形状和大小的结构元素。

结构元素可以是矩形(MORPH_RECT)、椭圆(MORPH_ELLIPSE)或十字形(MORPH_CROSS)等。dstOutputArray类型,即目标图像,需要和源图片有一样的尺寸和类型。这个参数是可选的,如果不用,函数会自动创建一个。anchorPoint类型,锚点的位置,其有默认值(-1,-1),表示锚位于单位(element)的中心。此参数通常不需要修改。iterationsint类型,腐蚀操作的迭代次数。默认值为1。通过增加迭代次数,可以增强腐蚀效果。borderTypeint类型,用于推断图像外部像素的某种边界模式。有默认值BORDER_DEFAULT。通常不需要修改,除非对边界处理有特殊要求。borderValueconst Scalar&类型,当边界为常数时的边界值。有默认值morphologyDefaultBorderValue(),一般也不需要修改。

 示例

import cv2

import numpy as np

# 读取图像

img = cv2.imread('path_to_image.jpg', 0) # 0 表示以灰度模式读取

# 定义腐蚀操作的结构元素

kernel = np.ones((3, 3), np.uint8)

# 应用腐蚀操作

eroded_img = cv2.erode(img, kernel, iterations=1)

# 显示结果

cv2.imshow('Original Image', img)

cv2.imshow('Eroded Image', eroded_img)

cv2.waitKey(0)

cv2.destroyAllWindows()

3、主要形态学操作

API

retval, dst = cv2.morphologyEx(src, op, kernel[, dst[, anchor[, iterations[, borderType[, borderValue]]]]])

src:源图像,必须是单通道的灰度图像。op:形态学操作的类型。该参数决定了要执行的具体形态学操作,如腐蚀、膨胀、开运算、闭运算等。常用的操作类型包括:

cv2.MORPH_ERODE:腐蚀操作 --> 0。cv2.MORPH_DILATE:膨胀操作--> 1。cv2.MORPH_OPEN:开运算(先腐蚀后膨胀)--> 2。cv2.MORPH_CLOSE:闭运算(先膨胀后腐蚀)--> 3。cv2.MORPH_GRADIENT:形态学梯度(膨胀图与腐蚀图之差)--> 4。cv2.MORPH_TOPHAT:顶帽运算(原图像与开运算图之差)--> 5。cv2.MORPH_BLACKHAT:黑帽运算(闭运算的结果图与原图像之差)--> 6。CV2.MORPH_HITMISS:击中与非击中 --> 7。kernel:形态学操作的核,通常是一个矩形、椭圆或十字形的小矩阵。核的大小可以是正奇数,决定了形态学操作的范围和强度。当为NULL时,表示使用默认大小的核(通常是3x3)。dst(可选):输出图像,即形态学操作后的结果图像。如果未指定,则函数会创建一个新的输出图像。anchor(可选):核的锚点位置,即核的参考点。默认值是核的中心点。通过改变锚点位置,可以控制形态学操作的方向和效果。iterations(可选):迭代次数,即形态学操作执行的次数。默认值是1,表示只执行一次操作。通过增加迭代次数,可以增强形态学操作的效果。borderType(可选):边界拓展的方法。默认值是cv2.BORDER_CONSTANT,表示边界外的像素值被设置为一个常数值(由borderValue指定)。其他常用的边界拓展方法还包括cv2.BORDER_REPLICATE(复制边界像素值)、cv2.BORDER_REFLECT(反射边界像素值)等。borderValue(可选):当borderTypecv2.BORDER_CONSTANT时,该参数指定了边界外的像素值。默认值是一个特殊的值,通常不需要手动设置。

返回值

retval:在OpenCV的某些版本中,该参数可能不会被使用,或者总是返回None。但在某些情况下,它可能包含操作的状态码或结果。dst:形态学操作后的结果图像。

示例

通过修改op参数,可以执行不同的形态学操作,如膨胀、开运算、闭运算等。同时,通过调整kernel的大小和形状,以及iterations的值,可以控制形态学操作的强度和效果。

import cv2

import numpy as np

# 读取图像

img = cv2.imread('example.jpg', 0) # 以灰度模式读取图像

# 定义核

kernel = np.ones((5, 5), np.uint8) # 创建一个5x5的核

# 执行腐蚀操作

eroded_img = cv2.morphologyEx(img, cv2.MORPH_ERODE, kernel)

# 显示原图和腐蚀后的图像

cv2.imshow('Original Image', img)

cv2.imshow('Eroded Image', eroded_img)

cv2.waitKey(0)

cv2.destroyAllWindows()

形态学运算检测边缘

原理很简单,膨胀时”扩张“,腐蚀时”收缩“,膨胀图像减去收缩图像即可得到边缘。

示例

import cv2

import numpy as np

# 读取图像,转换为灰度图

image = cv2.imread('your_image.jpg', cv2.IMREAD_GRAYSCALE)

if image is None:

print("Error: Image cannot be loaded.")

exit()

# 定义形态学运算的结构元素

# 这里使用3x3的矩形结构元素

kernel = np.ones((3, 3), np.uint8)

# 膨胀操作

dilated_img = cv2.dilate(image, kernel, iterations=1)

# 腐蚀操作

eroded_img = cv2.erode(image, kernel, iterations=1)

# 计算形态学梯度:膨胀图像减去腐蚀图像

# 注意:这里的结果可能需要取绝对值,因为腐蚀后的图像在某些区域可能比膨胀后的图像更亮(理论上不应该,但取决于图像内容)

# 但对于灰度图像,膨胀总是会使图像变亮,腐蚀会使图像变暗,所以直接相减即可

# 可以使用cv2.absdiff()

morphological_gradient = cv2.subtract(dilated_img, eroded_img)

# 由于形态学梯度可能包含负值(尽管在这个特定情况下不太可能,但为了通用性),我们可以使用cv2.convertScaleAbs将其转换为正值

# 但在这个例子中,我们直接显示结果,因为对于灰度图像,相减的结果已经是非负的

# 显示结果

cv2.imshow('Original Image', image)

cv2.imshow('Dilated Image', dilated_img)

cv2.imshow('Eroded Image', eroded_img)

cv2.imshow('Morphological Gradient', morphological_gradient)

# 等待按键后关闭窗口

cv2.waitKey(0)

cv2.destroyAllWindows()

击中与击不中

1、 结构元素定义

击中击不中变换需要定义两个结构元素:B1和B2,它们组合成一个结构元素对B=(B1, B2)。

B1(击中部分):用于探测图像内部的特定形状,即希望匹配的形状。B2(击不中部分):用于探测图像外部,即希望该形状周围不存在某些像素的部分。B1和B2在空间中不应该有交集,即B1∩B2=Φ。

2、腐蚀操作

腐蚀是形态学中的一种基本操作,它通过结构元素对图像进行局部区域的缩小或细化。在击中击不中变换中,腐蚀操作被用于两个目的:

对原图像进行腐蚀:使用B1结构元素对原图像f(x, y)进行腐蚀,得到Process1。这一步的目的是找到与B1形状相匹配的区域。对原图像的补集进行腐蚀:首先计算原图像的补集fc(x, y)(即原图像中值为0的像素变为1,值为1的像素变为0),然后使用B2结构元素对补集进行腐蚀,得到Process2。这一步的目的是确保B2形状所代表的外部条件在原图像中不成立。

3、交集运算

最后,将Process1和Process2进行交集运算。交集的结果即为同时满足B1和B2条件的像素点集合。换句话说,这些点既在图像内部与B1形状相匹配,又在图像外部与B2所定义的“不存在某些像素”的条件相匹配。

4、数学表达式

击中击不中变换的数学表达式为:

g(x,y)=hitmiss[f(x,y),B]=erode[f(x,y),B1] AND erode[fc​(x,y),B2]

其中,f(x,y)是原图像,fc​(x,y)是原图像的补集,erode[⋅,⋅]表示腐蚀操作,hitmiss[⋅,⋅]表示击中击不中变换。

5、OpenCV实现击中击不中变换示例

import cv2

import numpy as np

def create_hit_miss_kernels(shape_size, cross_thickness=1):

# 假设我们检测一个中心交叉

# 击中部分

hit = np.zeros((shape_size, shape_size), dtype=np.uint8)

cv2.drawContours(hit, [np.array([[int(shape_size/2), int(shape_size/2)-cross_thickness],

[int(shape_size/2), int(shape_size/2)+cross_thickness],

[int(shape_size/2)-cross_thickness, int(shape_size/2)],

[int(shape_size/2)+cross_thickness, int(shape_size/2)]]).astype(np.int32)], -1, 1, thickness=cv2.FILLED)

# 击不中部分

miss = np.ones((shape_size, shape_size), dtype=np.uint8)

cv2.drawContours(miss, [np.array([[int(shape_size/2)-cross_thickness-1, int(shape_size/2)-cross_thickness-1],

[int(shape_size/2)-cross_thickness-1, int(shape_size/2)+cross_thickness+1],

[int(shape_size/2)+cross_thickness+1, int(shape_size/2)-cross_thickness-1],

[int(shape_size/2)+cross_thickness+1, int(shape_size/2)+cross_thickness+1]]).astype(np.int32)], -1, 0, thickness=cv2.FILLED)

return hit, miss

# 示例:创建一个5x5的结构元素

hit, miss = create_hit_miss_kernels(5)

def hit_or_miss_transform(image, hit, miss):

# 腐蚀图像以匹配结构元素大小

eroded = cv2.erode(image, np.ones((hit.shape[0], hit.shape[1])))

# 击中部分

hit_part = cv2.bitwise_and(eroded, eroded, mask=hit)

# 击不中部分,先对原图像取反

inverted = cv2.bitwise_not(eroded)

miss_part = cv2.bitwise_and(inverted, inverted, mask=miss)

# 逻辑与操作:只有当两者都满足时,结果才为1

result = cv2.bitwise_and(hit_part, miss_part)

# 注意:由于miss_part实际上是在寻找不应该有的部分,我们可能需要对结果进行取反

# 但在这个例子中,因为我们直接在eroded上操作,所以不需要额外取反

return result

# 假设image是你的二值图像

# result = hit_or_miss_transform(image, hit, miss)

 形态学运算提取水平线和垂直线

1、步骤

图像预处理

灰度化:如果输入图像是彩色的,首先将其转换为灰度图像。这一步是为了简化后续处理,因为灰度图像只包含亮度信息,而不包含颜色信息。二值化:将灰度图像转换为二值图像,即图像中的像素点只有两种可能的取值(通常是0和255),分别代表黑色和白色。这一步是为了更清晰地表示图像中的边缘和线条。定义结构元素

水平结构元素:为了提取水平线,需要定义一个水平方向的结构元素。这个结构元素通常是一个矩形,其宽度远大于高度(例如,宽度为图像宽度的1/16,高度为1)。垂直结构元素:类似地,为了提取垂直线,需要定义一个垂直方向的结构元素。这个结构元素是一个矩形,其高度远大于宽度(例如,高度为图像高度的1/16,宽度为1)。形态学操作

腐蚀:使用定义好的结构元素对二值图像进行腐蚀操作。腐蚀操作会缩小图像中的高亮区域(即白色区域),并去除小于结构元素的对象。膨胀:接着对腐蚀后的图像进行膨胀操作。膨胀操作会扩大图像中的高亮区域,并可能恢复一些在腐蚀过程中被去除的细节。开运算:腐蚀后紧接着进行膨胀的操作称为开运算。开运算能够去除小的对象和噪声,同时保持较大的结构和形状不变。通过开运算,可以更有效地提取出图像中的水平线和垂直线。提取结果

经过开运算后,图像中的水平线和垂直线将被清晰地提取出来。此时,可以根据需要保存或显示提取结果。

2、示例

import cv2

import numpy as np

# 读取图像并转换为灰度图像

img = cv2.imread('input.jpg', cv2.IMREAD_GRAYSCALE)

# 二值化处理

_, binary = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)

# 定义结构元素

horizontal_size = binary.shape[1] // 16

vertical_size = binary.shape[0] // 16

horizontal_struct = cv2.getStructuringElement(cv2.MORPH_RECT, (horizontal_size, 1))

vertical_struct = cv2.getStructuringElement(cv2.MORPH_RECT, (1, vertical_size))

# 开运算提取水平线和垂直线

horizontal = cv2.morphologyEx(binary, cv2.MORPH_OPEN, horizontal_struct)

vertical = cv2.morphologyEx(binary, cv2.MORPH_OPEN, vertical_struct)

# 显示结果

cv2.imshow('Horizontal Lines', horizontal)

cv2.imshow('Vertical Lines', vertical)

cv2.waitKey(0)

cv2.destroyAllWindows()



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。