上一篇我们学习了轮廓检测与形状分析,能识别目标的形状、计算目标参数,但实际场景中,我们得到的轮廓往往存在缺陷——比如轮廓有缺口、有噪声杂点,或者多个目标粘连在一起,导致轮廓检测不准确、形状识别错误。
今天我们就来学习OpenCV中的形态学操作,作用是修饰轮廓、分离粘连,通过简单的数学运算,让轮廓更完整、更清晰。

形态学操作的核心是“基于结构元素(一个小的矩阵),对二值化图像的轮廓进行操作”,最基础的两个操作是腐蚀和膨胀。
概念:结构元素(通常是3×3、5×5的正方形矩阵),相当于一个“模板”,用这个模板遍历图像的每个像素,根据模板与像素的对应关系,修改像素值,从而实现轮廓的修饰。
效果:收缩轮廓,去除小的噪声杂点,让轮廓变细。
原理:用结构元素遍历图像,若结构元素覆盖的区域内,所有像素都是白色(255),则该像素保留为白色;否则,将该像素设为黑色(0)。简单来说,就是收缩轮廓的边缘,吃掉杂点。
适用场景:去除图像中的小噪声、细化轮廓、分离轻微粘连的目标。
效果:扩张轮廓,填补轮廓的小缺口,让轮廓变粗。
原理:用结构元素遍历图像,若结构元素覆盖的区域内,有一个像素是白色(255),则该像素设为白色;否则,保留为黑色。就是扩张轮廓的边缘,填补小缺口。
适用场景:修复轮廓的小缺口、连接断裂的轮廓、加粗轮廓。
开运算和闭运算是腐蚀和膨胀的组合,针对性解决更复杂的轮廓问题:
开运算(Opening):先腐蚀,后膨胀。核心效果:去除小噪声,同时基本保持轮廓的原始大小和形状(避免腐蚀后轮廓变细),适合噪声多、轮廓完整的场景。
闭运算(Closing):先膨胀,后腐蚀。核心效果:填补轮廓的小缺口,同时基本保持轮廓的原始大小和形状(避免膨胀后轮廓变粗),适合轮廓有缺口、无明显噪声的场景。
OpenCV中,形态学操作的核心函数是cv2.erode()(腐蚀)、cv2.dilate()(膨胀)、cv2.morphologyEx()(开运算、闭运算等组合操作)。
import cv2import numpy as np# 读取图像并二值化,目标为白色,背景为黑色img = cv2.imread("test.png")img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)ret, img_binary = cv2.threshold(img_gray, 127, 255, cv2.THRESH_BINARY_INV)# 定义结构元素(3×3正方形矩阵)kernel = np.ones((3, 3), np.uint8)# 腐蚀操作(迭代越多,腐蚀越明显)erosion = cv2.erode(img_binary, kernel, iterations=3)# 膨胀操作(迭代越多,膨胀越明显)dilation = cv2.dilate(img_binary, kernel, iterations=3)cv2.imshow("Original Binary Image", img_binary)cv2.imshow("Erosion Operation", erosion)cv2.imshow("Dilation Operation", dilation)cv2.waitKey(0)cv2.destroyAllWindows()
(膨胀)

(腐蚀)
import cv2import numpy as np# 预处理img = cv2.imread("test.png")img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)ret, img_binary = cv2.threshold(img_gray, 127, 255, cv2.THRESH_BINARY_INV)kernel = np.ones((3, 3), np.uint8)# 开运算opening = cv2.morphologyEx(img_binary, cv2.MORPH_OPEN, kernel)# 闭运算closing = cv2.morphologyEx(img_binary, cv2.MORPH_CLOSE, kernel)cv2.imshow("Original Binary Image", img_binary)cv2.imshow("Opening Operation", opening)cv2.imshow("Closing Operation", closing)cv2.waitKey(0)cv2.destroyAllWindows()现实中,经常遇到“多个目标粘连在一起”的情况,此时可以通过“开运算+腐蚀”的组合操作,分离粘连目标。
import cv2import numpy as np# 预处理img = cv2.imread("test.png")img_copy = img.copy()img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)ret, img_binary = cv2.threshold(img_gray, 127, 255, cv2.THRESH_BINARY_INV)# 定义结构元素(5×5正方形矩阵)kernel = np.ones((5, 5), np.uint8)# 开运算去除噪声,再腐蚀分离粘连目标opening = cv2.morphologyEx(img_binary, cv2.MORPH_OPEN, kernel)erosion = cv2.erode(opening, kernel, iterations=2) # 迭代2次,增强分离效果# 4. 轮廓检测,验证效果contours, _ = cv2.findContours(erosion, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)cv2.drawContours(img_copy, contours, -1, (0, 255, 0), 2)cv2.imshow("Original Image", img)cv2.imshow("Separated Targets", img_copy)cv2.waitKey(0)cv2.destroyAllWindows()print("分离后的目标数量:", len(contours))

腐蚀:收缩轮廓、去噪声,适合噪声多、轮廓粗的场景;
膨胀:扩张轮廓、补缺口,适合轮廓有缺口、断裂的场景;
开运算:先腐蚀后膨胀,去噪声且不改变轮廓大小,适合噪声多、轮廓完整的场景;
闭运算:先膨胀后腐蚀,补缺口且不改变轮廓大小,适合轮廓有缺口、无噪声的场景。
结构元素大小设计 常用3×3、5×5的正方形矩阵;目标越大、粘连越严重,结构元素越大;迭代次数越多,操作效果越明显(但不宜过多)。
分离粘连目标效果不好 增大结构元素尺寸,增加腐蚀的迭代次数;或先进行开运算去除噪声,再腐蚀分离。
轮廓失真 避免过度腐蚀/膨胀,迭代次数控制在1-3次;若轮廓失真严重,减小结构元素尺寸。
本篇我们学习了四种常用的形态学操作——腐蚀、膨胀、开运算、闭运算,了解了每种操作的原理和适用场景,。
下篇我们讲OpenCV的一个案例——人脸检测。
往期回顾: