1. 从一张二值图说起:形态学到底在折腾什么
很多人第一次接触数学形态学,是在图像处理课上听到"腐蚀""膨胀"这两个词,然后被一堆集合论符号劝退。我当初也是这么过来的——课本上写着"用结构元素B对集合A进行腐蚀",配一张文氏图,看完只觉得每个字都认识,连起来不知道在干嘛。直到我自己动手写了几行代码,把一张黑白图跑了一遍腐蚀和膨胀,才突然明白:这玩意儿本质上就是用一个小模板在图像上滑动,根据模板覆盖区域的像素分布,决定中心像素该留还是该扔。
数学形态学的核心思想其实非常朴素。它不关心像素的具体灰度值是多少,而是关心形状——这个区域是圆的还是方的,边界是光滑的还是毛糙的,有没有小孔洞,有没有细连接。它处理问题的角度和卷积、滤波那一套完全不同。卷积是加权求和,形态学是集合运算。你可以把形态学想象成用一块橡皮泥去按压一个图案:橡皮泥的形状决定了图案被"吃掉"多少或者被"撑大"多少。
这套理论最早可以追溯到上世纪六十年代,后来在图像分析领域扎下了根。它的应用场景远比想象中广泛:工业质检里检测PCB板上的焊点缺陷,医学影像里分割细胞核,遥感图像里提取道路和河流,甚至指纹识别里的细化处理,背后都有形态学的影子。关键词里提到的腐蚀、膨胀、开运算、闭运算、结构元素、二值图像、灰度形态学、击中击不中变换、骨架提取、形态学滤波,基本覆盖了这门技术的核心工具箱。
这篇文章适合谁看?如果你正在学数字图像处理,被形态学的数学定义绕晕了,想找个接地气的入口;或者你在做项目时遇到了"怎么把断裂的线条连起来""怎么去掉背景里的小噪点""怎么提取物体的骨架"这类具体问题,翻了一圈资料发现都是公式,那这篇笔记应该能帮到你。我会从最基础的操作讲起,把每个操作的动机、原理、代码实现、参数选择、踩坑经验都过一遍,尽量做到看完就能上手改自己的图。
提示:本文假设你有基本的Python和NumPy使用经验,对图像的基本概念(像素、通道、二值化)有初步了解。如果这些还不熟,建议先补一下再回来,不然有些地方可能会卡住。
2. 腐蚀与膨胀:形态学的两块基石
2.1 腐蚀操作到底在"腐蚀"什么
腐蚀(Erosion)的定义用一句话说就是:用一个结构元素去扫描图像的每一个像素,只有当结构元素完全覆盖的区域都是前景像素时,中心像素才保留为前景。换句话说,它是在"收缩"前景区域,把边界往里啃掉一圈。
为什么叫"腐蚀"?你可以想象一块金属被酸液腐蚀,边缘会一点点被吃掉,面积变小,细小的突起会断掉。腐蚀操作的效果就是这样:前景区域缩小,小于结构元素的孤立噪点直接消失,两个物体之间若有细连接会被切断。
用集合论的语言表达:A被B腐蚀,记作A⊖B,结果是所有满足"B平移x后仍完全包含在A中"的x的集合。这个定义看起来很抽象,但翻译成代码逻辑就很直白:
import numpy as np import cv2 # 读取图像并二值化 img = cv2.imread('test.png', 0) _, binary = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY) # 定义一个3x3的矩形结构元素 kernel = np.ones((3, 3), np.uint8) # 腐蚀操作 eroded = cv2.erode(binary, kernel, iterations=1)这段代码里,kernel就是结构元素,iterations是腐蚀的次数。每多腐蚀一次,前景就多缩一圈。结构元素的大小和形状直接决定了腐蚀的"力度"和"方向性"。
我刚开始用的时候犯过一个错:拿一张白底黑字的图直接做腐蚀,结果发现字变粗了。后来才反应过来,OpenCV默认把非零像素当前景,白底黑字的情况下背景是255,前景是0,腐蚀操作作用在了背景上,效果自然反过来了。所以做形态学之前,一定要确认你的前景是白色(255)还是黑色(0),这个搞反了后面全错。
2.2 膨胀:把断裂的东西接起来
膨胀(Dilation)和腐蚀正好相反:只要结构元素覆盖的区域里有一个前景像素,中心像素就变成前景。效果是前景区域扩张,边界往外长,小孔洞被填满,断裂的线条被连接起来。
膨胀的集合论定义是A⊕B,结果是所有满足"B平移x后与A有交集"的x的集合。代码上就是把cv2.erode换成cv2.dilate:
dilated = cv2.dilate(binary, kernel, iterations=1)膨胀最典型的应用场景是修复断裂的文字或线条。比如你从扫描件里提取出来的文字笔画断了,用膨胀可以把它们重新连上。但膨胀也有副作用:它会让原本分离的物体粘在一起。如果你的图里有两个靠得很近的细胞,膨胀之后可能就分不开了。
这里有个经验之谈:膨胀的次数和结构元素大小要配合着调。我一般先用3x3的核试一次,看效果不够再加次数,而不是一上来就用大核。因为大核的膨胀效果比较"粗暴",容易把不该连的地方也连上。小核多次膨胀的可控性更好,你可以逐步观察变化。
2.3 结构元素:形态学的"模具"
结构元素(Structuring Element)是形态学操作里最关键的参数,没有之一。它决定了操作的形状和方向性。常见的结构元素有:
| 形状 | 生成方式 | 适用场景 |
|---|---|---|
| 矩形 | cv2.getStructuringElement(cv2.MORPH_RECT, (3,3)) | 通用场景,各方向均匀 |
| 椭圆 | cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5,5)) | 处理圆形物体,边缘更平滑 |
| 十字 | cv2.getStructuringElement(cv2.MORPH_CROSS, (3,3)) | 保留水平垂直方向的细节 |
结构元素的大小通常是奇数(3x3、5x5、7x7),因为需要一个中心点。选多大的核,取决于你要处理的物体尺寸和噪点尺寸。原则是:核的大小要大于噪点、小于目标物体。如果噪点比目标还大,那形态学就无能为力了,得换其他方法。
我踩过的一个坑是:用椭圆核对矩形物体做腐蚀,结果四个角被磨圆了。当时没注意,后面做轮廓分析时发现角点检测全乱了。所以结构元素的形状要和目标物体的形状匹配,处理矩形物体就用矩形核,处理圆形物体就用椭圆核,不要随便混用。
3. 开运算与闭运算:腐蚀膨胀的组合拳
3.1 开运算:先腐蚀后膨胀,去噪不留痕
单独用腐蚀会有一个问题:它在去掉噪点的同时,也会把目标物体缩小一圈。如果你只是想去噪,不想改变物体大小,那就需要开运算。
开运算(Opening)的定义是先腐蚀后膨胀,记作A∘B = (A⊖B)⊕B。它的效果很巧妙:腐蚀把小的噪点和细连接去掉了,膨胀再把剩下的物体恢复到原来的大小。因为被腐蚀掉的部分已经没了,膨胀只能把还存在的物体撑回来,所以开运算能在不改变物体主体尺寸的前提下,去掉小噪点和毛刺。
opened = cv2.morphologyEx(binary, cv2.MORPH_OPEN, kernel)开运算的典型应用是去除背景中的小白点噪点。比如你从一张有噪点的扫描件里提取文字,二值化之后背景上有很多孤立的小白点,用开运算一跑,噪点全没了,文字主体基本不变。
但开运算也不是万能的。如果噪点和目标物体大小差不多,开运算会把它们一起干掉。这时候就得考虑其他方案,比如连通域分析后按面积筛选。
3.2 闭运算:先膨胀后腐蚀,填洞补缝
闭运算(Closing)和开运算正好相反:先膨胀后腐蚀,记作A•B = (A⊕B)⊖B。膨胀先把小孔洞和裂缝填上,腐蚀再把物体缩回原来的大小。因为孔洞已经被填了,腐蚀不会重新把它们挖出来,所以闭运算能在不改变物体主体尺寸的前提下,填补小孔洞和裂缝。
closed = cv2.morphologyEx(binary, cv2.MORPH_CLOSE, kernel)闭运算最常用的场景是填充物体内部的小孔洞。比如你做了一个阈值分割,物体内部因为光照不均出现了几个小黑洞,用闭运算就能填上。另一个场景是连接断裂的线条,比如提取出来的血管或者道路断了,闭运算可以把它们接起来。
这里有个选择困难:什么时候用开运算,什么时候用闭运算?我的判断标准很简单——看你要处理的是前景上的噪点还是背景上的噪点。前景上的毛刺和小突起用开运算去掉,背景上的小孔洞和裂缝用闭运算填上。如果两种都有,那就先开后闭,或者先闭后开,看哪个效果好。
注意:开运算和闭运算都是不可逆的。一旦做了,原始信息就丢了。所以如果后续还要做精细分析,建议保留一份原始二值图。
3.3 开闭运算的顺序问题
有时候一张图既有前景噪点又有背景孔洞,那就需要同时用开运算和闭运算。但顺序很重要:先开后闭还是先闭后开,结果可能不一样。
先开后闭:先去前景噪点,再填背景孔洞。适合前景噪点比较多的情况。 先闭后开:先填背景孔洞,再去前景噪点。适合背景孔洞比较多的情况。
我一般会两种都试一下,对比效果再决定。如果差别不大,就选先开后闭,因为开运算去噪更彻底一些,后面闭运算填洞时不会把噪点也一起填进去。
4. 形态学梯度、顶帽与黑帽:边缘与细节的提取
4.1 形态学梯度:用膨胀减腐蚀
形态学梯度(Morphological Gradient)的定义是膨胀图减去腐蚀图。膨胀让物体变大,腐蚀让物体变小,两者相减,剩下的就是物体的边界区域。
gradient = cv2.morphologyEx(binary, cv2.MORPH_GRADIENT, kernel)这个操作提取出来的边缘有一个特点:边缘的粗细取决于结构元素的大小。核越大,边缘越粗。如果你只需要细边缘,就用小核;如果需要粗边缘做后续处理,就用大核。
形态学梯度和Canny、Sobel这些边缘检测算子有什么区别?简单说,Canny和Sobel是基于灰度梯度的,对噪声敏感,需要平滑预处理;形态学梯度是基于集合运算的,对二值图效果稳定,但对灰度图需要先做处理。我一般在对二值图做边缘提取时用形态学梯度,对灰度图用Canny。
4.2 顶帽变换:提取亮细节
顶帽变换(Top Hat)的定义是原图减去开运算结果。开运算去掉了比结构元素小的亮细节,原图减去开运算结果,剩下的就是那些被去掉的亮细节。
tophat = cv2.morphologyEx(gray_img, cv2.MORPH_TOPHAT, kernel)顶帽变换在光照不均的图像上特别有用。比如一张文档照片,中间亮四周暗,直接二值化会导致中间过曝、四周欠曝。用顶帽变换可以把光照不均的背景去掉,只留下文字部分,然后再二值化,效果会好很多。
我做过一个项目,处理的是工业相机拍的金属表面图像,表面反光严重,光照极不均匀。用顶帽变换预处理之后,缺陷检测的准确率提升了不少。当时用的核大小是15x15,比缺陷尺寸略大,这样能把缺陷完整提取出来。
4.3 黑帽变换:提取暗细节
黑帽变换(Black Hat)的定义是闭运算结果减去原图。闭运算填掉了比结构元素小的暗细节,闭运算结果减去原图,剩下的就是那些被填掉的暗细节。
blackhat = cv2.morphologyEx(gray_img, cv2.MORPH_BLACKHAT, kernel)黑帽变换适合提取亮背景上的暗细节,比如白纸上的黑色文字、亮表面上的暗缺陷。和顶帽变换正好互补。
顶帽和黑帽的选择,取决于你要提取的细节是比背景亮还是比背景暗。亮细节用顶帽,暗细节用黑帽。如果不确定,两个都跑一下,看哪个响应强。
5. 击中击不中变换与骨架提取:形态学的进阶玩法
5.1 击中击不中:精确匹配形状
击中击不中变换(Hit-or-Miss Transform)是形态学里用来检测特定形状的操作。它的原理是:用一个结构元素对前景做腐蚀,同时用另一个结构元素对背景做腐蚀,两者都匹配的位置才保留。
这个操作在实际中用得不多,但在某些特定场景下非常有用。比如你要在一张图里找到所有"左上角有像素、右下角没有像素"的位置,就可以设计对应的结构元素来做匹配。
# 定义前景和背景的结构元素 kernel_foreground = np.array([[0, 1, 0], [1, 1, 1], [0, 1, 0]], dtype=np.uint8) kernel_background = np.array([[1, 0, 1], [0, 0, 0], [1, 0, 1]], dtype=np.uint8) # 击中击不中变换 hitmiss = cv2.morphologyEx(binary, cv2.MORPH_HITMISS, kernel_foreground - kernel_background)这个操作的难点在于结构元素的设计。前景和背景的结构元素必须互补,而且要和目标形状精确匹配。我试过用它来检测棋盘格的角点,效果还可以,但调参比较费劲。
5.2 骨架提取:把物体变细成线
骨架提取(Skeletonization)是形态学里一个很实用的操作,它把粗的物体细化成单像素宽的骨架,同时保持物体的拓扑结构不变。骨架提取在文字识别、指纹分析、血管分析里都有应用。
OpenCV本身没有直接的骨架提取函数,但可以用形态学操作组合实现。基本思路是:反复做腐蚀,每次腐蚀后看是否还能再腐蚀,直到只剩下骨架。更常用的方法是使用细化算法(Thinning),OpenCV的扩展模块ximgproc里有thinning函数。
# 需要安装opencv-contrib-python from cv2 import ximgproc # 骨架提取 skeleton = ximgproc.thinning(binary, ximgproc.THINNING_ZHANGSUEN)骨架提取的一个常见问题是毛刺。因为物体边界不规则,提取出来的骨架会有很多小分支。这时候可以用形态学的开运算或者剪枝算法来去掉毛刺。我一般会先对二值图做一次开运算平滑边界,再提取骨架,毛刺会少很多。
5.3 形态学重建:保留特定形状的物体
形态学重建(Morphological Reconstruction)是一种更高级的形态学操作,它用一个图像作为"种子",另一个图像作为"掩膜",通过反复膨胀和取交集,最终恢复出与种子相连的完整物体。
这个操作在去除边界物体、提取特定区域时很有用。比如你有一张细胞图,想提取所有与某个种子点相连的细胞,就可以用形态学重建。OpenCV里没有直接的函数,但可以用迭代膨胀加掩膜的方式实现。
6. 灰度形态学:从二值到灰度的跨越
6.1 灰度腐蚀与膨胀:不是简单的阈值化
前面讲的都是二值形态学,处理的是黑白图。但实际项目中,我们拿到的往往是灰度图。灰度形态学的操作逻辑和二值类似,但定义方式不同。
灰度腐蚀的定义是:在结构元素覆盖的邻域内取最小值。灰度膨胀的定义是:在结构元素覆盖的邻域内取最大值。你可以理解为:腐蚀让图像变暗(取最小),膨胀让图像变亮(取最大)。
# 灰度腐蚀 gray_eroded = cv2.erode(gray_img, kernel, iterations=1) # 灰度膨胀 gray_dilated = cv2.dilate(gray_img, kernel, iterations=1)灰度形态学的一个典型应用是去除椒盐噪声。椒盐噪声表现为随机的极亮或极暗像素,用灰度开运算(先腐蚀后膨胀)可以去掉亮噪点,用灰度闭运算可以去掉暗噪点。
我做过一个实验:对一张加了椒盐噪声的灰度图,分别用中值滤波和灰度开闭运算处理。结果是中值滤波对椒盐噪声更有效,但灰度形态学在保留边缘方面更好。所以如果边缘信息很重要,可以考虑灰度形态学。
6.2 灰度形态学的参数选择
灰度形态学的结构元素选择比二值更讲究。因为灰度图里没有明确的"前景"和"背景",结构元素的大小直接决定了操作的"尺度"。
核太小,效果不明显;核太大,会把有用的细节也抹掉。我的经验是:先分析图像中噪声的尺度,然后选一个比噪声略大的核。比如噪声是1-2个像素的,就用3x3的核;噪声是3-5个像素的,就用5x5或7x7的核。
另外,灰度形态学的结构元素形状也有影响。矩形核在各方向上的效果均匀,椭圆核在边缘处更平滑,十字核保留水平垂直方向的细节。具体选哪个,要看你的应用场景。
6.3 灰度形态学的实际案例
举个我实际做过的例子:从一张X光片里提取骨骼区域。X光片的特点是骨骼亮、软组织暗,但整体对比度不高,而且有噪声。
我的处理流程是:
- 先用灰度闭运算(大核)填充骨骼内部的小暗孔
- 再用灰度开运算(小核)去掉软组织上的亮噪点
- 然后用顶帽变换增强骨骼的亮细节
- 最后做阈值分割提取骨骼
这个流程里,闭运算的核用了15x15,开运算的核用了3x3,顶帽变换的核用了9x9。参数不是拍脑袋定的,是反复试出来的。每调一次参数,就保存一张中间结果图,对比着看,慢慢就找到合适的值了。
7. 形态学在实战中的典型应用与踩坑记录
7.1 工业质检:PCB焊点缺陷检测
PCB焊点检测是形态学的经典应用场景。焊点通常是圆形或椭圆形的亮斑,缺陷表现为焊点缺失、焊点过小、焊点桥接等。
我的处理流程是:
- 对PCB图像做阈值分割,提取焊点区域
- 用开运算去掉小噪点
- 用闭运算填充焊点内部的小孔
- 用形态学梯度提取焊点边缘
- 对每个焊点做圆度分析,判断是否合格
这里踩过的坑是:焊点桥接。两个焊点靠得太近,阈值分割后连在一起了。用腐蚀可以分开,但腐蚀也会让焊点变小。后来我改用距离变换加分水岭算法,效果更好。形态学在这里的作用变成了预处理,而不是最终方案。
7.2 医学影像:细胞核分割
细胞核分割是医学影像分析里的常见任务。细胞核通常是圆形或椭圆形的暗区域(在明场显微镜下),背景是亮的。
处理流程:
- 用顶帽变换增强细胞核的暗细节
- 用闭运算填充细胞核内部的小亮孔
- 用开运算去掉背景上的小暗噪点
- 用阈值分割提取细胞核
- 用形态学梯度提取细胞核边界
踩过的坑是:细胞核粘连。两个细胞核挨在一起,分割后变成一个。用腐蚀可以分开,但会丢失边界信息。后来我用了基于标记的分水岭算法,形态学只用来做预处理和后处理。
7.3 文档处理:文字提取与版面分析
从扫描文档里提取文字,形态学也能帮上忙。特别是当文档有光照不均、背景噪点、文字断裂等问题时。
处理流程:
- 用顶帽变换校正光照不均
- 用闭运算连接断裂的文字笔画
- 用开运算去掉背景噪点
- 用形态学梯度提取文字边缘
- 用连通域分析提取文字区域
踩过的坑是:文字笔画粗细不一。标题字粗,正文字细,用同一个核处理,要么标题过处理,要么正文欠处理。后来我改成了自适应核大小,根据文字区域的笔画宽度动态调整核的大小,效果好了很多。
7.4 遥感图像:道路提取
遥感图像里的道路提取,形态学也有用武之地。道路通常是细长的亮线或暗线,背景是各种地物。
处理流程:
- 用顶帽或黑帽变换增强道路细节
- 用方向性结构元素做形态学滤波,保留特定方向的线条
- 用闭运算连接断裂的道路
- 用开运算去掉非道路的噪点
- 用骨架提取得到道路中心线
踩过的坑是:道路方向多变。用单一方向的结构元素只能保留一个方向的线条,其他方向的会被滤掉。后来我用了多方向的结构元素,分别做形态学滤波,再把结果合并,效果好了很多,但计算量也上去了。
8. 形态学参数调优的几点心得
8.1 结构元素大小:从3x3开始试
我见过很多新手一上来就用15x15的大核,结果把有用的细节全抹掉了。我的建议是:从3x3开始,逐步增大,观察效果变化。每次增大都保存中间结果,对比着看。通常3x3、5x5、7x7这三个尺寸就能覆盖大部分场景。
如果3x3效果不够,先别急着换大核,试试增加迭代次数。iterations=2的3x3核,效果接近5x5核,但可控性更好。
8.2 结构元素形状:匹配目标形状
矩形核适合处理矩形物体,椭圆核适合处理圆形物体,十字核适合保留水平垂直方向的细节。如果目标形状不规则,可以先用矩形核试,效果不好再换。
我做过一个实验:对同一个圆形物体,分别用矩形核和椭圆核做腐蚀。矩形核腐蚀后的物体边缘有棱角,椭圆核腐蚀后的边缘更平滑。所以如果后续要做圆度分析,椭圆核更合适。
8.3 操作顺序:先开后闭还是先闭后开
前面提过,顺序会影响结果。我的经验是:如果前景噪点多,先开后闭;如果背景孔洞多,先闭后开。如果不确定,两种都试,对比效果。
另外,开运算和闭运算的核大小可以不同。比如先用3x3的核做开运算去噪,再用5x5的核做闭运算填洞。这样更灵活。
8.4 灰度形态学的归一化问题
灰度形态学处理前,建议先把图像归一化到0-255范围。如果图像是16位的,直接做形态学可能会溢出。我踩过这个坑:一张16位的医学图像,直接做灰度膨胀,结果像素值超过65535,变成了0,图像全黑了。后来先归一化到8位再处理,就没问题了。
9. 形态学与其他图像处理技术的配合
9.1 形态学+阈值分割:先处理后分割
形态学经常和阈值分割配合使用。顺序通常是:先做形态学预处理,再做阈值分割。因为形态学可以改善图像质量,让阈值分割更容易找到合适的阈值。
比如一张光照不均的图,直接阈值分割效果很差。先用顶帽变换校正光照,再阈值分割,效果就好很多。
9.2 形态学+连通域分析:先分割后筛选
阈值分割后,往往会有很多小噪点。这时候可以用形态学开运算去掉,也可以用连通域分析按面积筛选。我的经验是:如果噪点尺寸比较一致,用形态学开运算更快;如果噪点尺寸差异大,用连通域分析更灵活。
9.3 形态学+边缘检测:互补使用
形态学梯度和Canny、Sobel这些边缘检测算子可以互补。形态学梯度对二值图效果好,Canny对灰度图效果好。我一般会两种都跑一下,看哪种边缘更完整、更连续。
9.4 形态学+深度学习:预处理和后处理
在深度学习时代,形态学依然有用武之地。它可以作为预处理,改善输入图像质量;也可以作为后处理,优化分割结果。比如语义分割出来的mask有毛刺,用开运算平滑一下;有孔洞,用闭运算填一下。
我做过一个实验:在细胞分割任务中,对U-Net的输出做形态学后处理,IoU提升了2个百分点。虽然不多,但说明形态学在深度学习流程里还是有价值的。
10. 一些零散但有用的经验
形态学的代码实现本身不复杂,难的是参数选择和流程设计。我总结了几条经验,都是踩坑踩出来的:
第一,永远保留原始图。形态学操作是不可逆的,一旦做了就回不去了。我习惯把每一步的中间结果都保存下来,方便对比和回退。
第二,先在小图上试。如果图像很大,直接跑形态学会很慢。我一般会先裁剪一块代表性区域,在小图上调好参数,再应用到全图。
第三,可视化很重要。形态学操作的结果往往需要对比着看才能判断好坏。我习惯用matplotlib把原图、中间结果、最终结果拼在一起显示,一目了然。
第四,不要迷信形态学。形态学不是万能的,有些问题用其他方法更合适。比如去噪,中值滤波可能比形态学更好;边缘检测,Canny可能比形态学梯度更好。形态学只是工具箱里的一把锤子,不是所有问题都是钉子。
第五,多试几种组合。形态学的操作组合很多,开运算、闭运算、顶帽、黑帽、梯度,不同顺序、不同参数,结果可能差很多。我一般会设计一个实验矩阵,把几种典型组合都跑一遍,选最好的。
第六,注意图像边界。形态学操作在图像边界处会有特殊处理,OpenCV默认用BORDER_CONSTANT填充,可能导致边界处的效果和内部不一致。如果边界很重要,可以试试BORDER_REPLICATE或BORDER_REFLECT。
第七,性能优化。形态学操作的计算量和核大小、图像尺寸成正比。如果实时性要求高,可以考虑用分离核(比如矩形核可以分解为水平和垂直两个方向分别处理),或者用GPU加速。OpenCV的cv2.UMat可以把计算放到GPU上,速度提升明显。
第八,形态学不是只有二值。很多人一提到形态学就想到二值图,其实灰度形态学同样重要。而且灰度形态学的操作逻辑更通用,二值形态学可以看作是灰度形态学的特例。
第九,结构元素可以自定义。OpenCV提供的矩形、椭圆、十字只是最常用的几种。你可以用NumPy数组自定义任意形状的结构元素。比如要检测特定角度的线条,可以设计一个对应角度的线性结构元素。
第十,形态学重建是个宝藏。很多人不知道形态学重建,但它能解决一些其他方法很难处理的问题,比如去除与边界相连的物体、提取特定区域。值得花时间研究一下。
最后再分享一个小技巧:如果你不确定该用什么形态学操作,可以先对图像做一次开运算和一次闭运算,对比两者的差异。差异大的地方,就是形态学能发挥作用的地方。差异小的地方,说明形态学可能帮不上忙,得换其他方法。这个技巧帮我省了很多试错时间。