ARTICLE DETAIL

资讯详情

深耕商务建站与企业官网运营的一线实战洞察。

图像形态学操作全解析:腐蚀膨胀、开闭运算与顶帽黑帽工业实战

图像形态学操作全解析:腐蚀膨胀、开闭运算与顶帽黑帽工业实战 形态学操作这个东西刚接触图像处理的时候我也觉得它没啥技术含量——无非就是拿个小方块在图上滑来滑去做点加减法。直到后来接手一个工业质检的项目一个金属件表面缺陷检测二值化之后图像上密密麻麻全是噪点阈值怎么调都不干净。试了高斯滤波、中值滤波边缘糊了试了频域方法又太慢。最后用了一次开运算噪点没了缺陷轮廓还完整保留着。从那以后我才真正把形态学当成一套有逻辑的工具来看而不是几个孤立的函数。这篇内容会把腐蚀、膨胀、开运算、闭运算、形态学梯度、顶帽、黑帽这一整套讲透。不是简单罗列API参数而是按我自己的理解路径来走先说清楚形态学到底在解决什么类型的问题再逐个拆解每个操作底层的像素逻辑然后给出一套可以直接复现的完整实操流程最后把我在项目里踩过的坑整理成排查表。无论你是在做工业检测、医学影像分割、文档版面分析还是单纯想搞明白OpenCV里这几个函数到底发生了什么下面的内容应该都能给你一些直接可用的东西。1. 形态学操作的整体设计思路与选型考量1.1 形态学操作到底解决的是哪一类问题先明确一件事形态学操作处理的对象绝大多数时候是二值图像或者灰度图像它不关心像素值的连续变化趋势它关心的是像素的空间结构关系。这跟卷积滤波有本质区别。高斯滤波是把一个像素和它邻域像素的值做加权平均输出的是新的灰度形态学操作则是用一个叫结构元素的小模板去和图像做局部的逻辑运算输出的结果取决于模板覆盖区域内像素的极值或者集合关系。用一个生活化的类比假设你在一张白纸上撒了一把黑芝麻形态学操作就像拿一个固定形状的印章往纸上按。腐蚀是拿印章去吃掉黑芝麻边缘的部分膨胀是拿印章把黑芝麻往外撑大一圈。印章的形状和大小是你自己定的可以是方的、圆的、十字的这就是结构元素。那这类操作适合解决什么问题我归纳下来主要是四类去除小噪点二值化之后图像上孤立的小白点或小黑点用腐蚀或者开运算可以干净地清掉。连接断裂区域文字笔画断了、细胞轮廓有缺口、道路线条中间有断开用膨胀或者闭运算可以把它们接上。分离粘连目标两个本该分开的物体粘在一起了用腐蚀可以把它们分开。提取特定结构比如提取比背景亮的细小纹理顶帽、提取比背景暗的细节黑帽、提取目标边界形态学梯度。注意形态学操作不是万能的。如果图像本身对比度就很差、目标边界模糊不清先做增强和分割形态学放在后面做后处理顺序不能反。1.2 结构元素形态学操作的模具结构元素Structuring Element简称SE是整个形态学操作里最关键的变量。它由两部分决定形状和尺寸。OpenCV里通过cv2.getStructuringElement()来生成结构元素常用的形状有三种cv2.MORPH_RECT矩形全部元素为1。cv2.MORPH_ELLIPSE椭圆形近似圆形适合处理有弧度的目标。cv2.MORPH_CROSS十字形只在水平和垂直方向有值。我见过很多人直接用一个np.ones((3,3), np.uint8)当核这在矩形核的情况下完全等价但如果你想要椭圆或十字核就必须用官方函数生成。形状的选择直接影响结果的各向同性程度。矩形核在水平和垂直方向的处理力度是一样的但在对角线方向的处理就弱一些椭圆核在各个方向上的响应更均匀处理圆形目标时边缘更自然。尺寸的选择更讲究。核越大腐蚀和膨胀的效果越强烈但同时也会带来两个副作用一是计算量上升二是小目标的细节会被吃掉。我在实际项目里的经验是先用3×3的核试效果不够再逐步加到5×5、7×7每次加2。不要一上来就用大核否则很容易把小目标整个腐蚀没掉你连问题出在哪都找不到。还有一个很多人忽略的点核的锚点位置。默认锚点在核的中心对于奇数尺寸的核来说就是正中间。如果你用的是偶数尺寸的核锚点会偏向一侧导致处理结果出现半个像素的偏移。所以核的尺寸尽量用奇数这是最省心的做法。1.3 各操作之间的组合逻辑与依赖关系刚才说的七种操作不是并列关系它们之间有明确的依赖和组合关系。理清这个关系你在选操作的时候就不会乱。腐蚀和膨胀是最底层的两个操作其他五种都可以由它们推导出来。开运算 先腐蚀后膨胀。闭运算 先膨胀后腐蚀。形态学梯度 膨胀结果减腐蚀结果。顶帽 原图减开运算结果。黑帽 闭运算结果减原图。理解了这个组合关系你就能明白为什么开运算能去噪但保留大目标腐蚀阶段把小的亮区域整个吃掉了膨胀阶段再把剩下的主体恢复回原来的大小被吃掉的小噪点就回不来了。闭运算反过来膨胀阶段把小的暗孔洞填上腐蚀阶段再把主体缩回去填上的孔洞保留了下来。实操建议是先确定你的目标是去亮噪点还是填暗孔洞再决定用开还是闭。如果你的噪点是亮的、背景是暗的用开运算如果目标是亮的主体主体内部有暗的小孔洞需要填用闭运算。方向搞反了效果会完全相反。我在一个项目里就犯过这个错一个红外图像里人体是亮区域身上有一些暗的噪点我一开始用了开运算想清理结果人体整体被削弱了暗噪点还在。后来改成闭运算暗点填掉了人体主体几乎没损失。方向选对问题就解决了一大半。2. 腐蚀与膨胀两个基础操作的核心细节2.1 腐蚀让目标瘦身的像素级逻辑腐蚀的数学定义是集合运算但我更愿意用一句话概括对于图像中每一个前景像素如果以它为中心的结构元素覆盖范围内存在任何一个背景像素那么这个前景像素就被改为背景。换句话说只有当结构元素完全落在前景区域内时中心像素才被保留为前景。这就导致了前景区域的边界向内收缩收缩的幅度取决于结构元素的尺寸。用cv2.erode()调用核心参数就三个import cv2 import numpy as np img cv2.imread(mask.png, 0) ret, binary cv2.threshold(img, 127, 255, cv2.THRESH_BINARY) kernel cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3)) eroded cv2.erode(binary, kernel, iterations1)这里iterations是迭代次数相当于把腐蚀操作重复执行N遍。iterations2和用5×5的核做一次腐蚀在很多情况下效果接近但不完全等价。用迭代次数控制强度更平滑也更容易微调。腐蚀的典型应用场景去除孤立小噪点尺寸小于核的亮斑会被整个腐蚀掉。分离粘连物体两个物体连接处的桥比较窄腐蚀可以把它切断。细化目标轮廓让后续的轮廓检测更规整减少毛刺。有一个参数叫borderType控制图像边界的处理方式。默认是cv2.BORDER_CONSTANT边界外的像素当成某个固定值处理。如果你的目标紧贴图像边缘腐蚀会让它从边缘缩进来这是正常的不用特别处理。2.2 膨胀扩张边界的实现与参数影响膨胀和腐蚀是镜像关系对于图像中每一个背景像素如果以它为中心的结构元素覆盖范围内存在任何一个前景像素那么这个背景像素就变为前景。效果就是前景区域向外扩张扩张幅度同样由结构元素决定。dilated cv2.dilate(binary, kernel, iterations1)膨胀最直接的应用是填补前景内部的小孔洞和连接断裂区域。比如文字识别里有些字的笔画因为扫描质量差断开了用3×3的核膨胀一次笔画就接上了而且整体字的结构基本不变。这里有一个实操上的细节值得说膨胀会让目标面积增大如果你后续需要面积测量或者周长计算记得在做形态学之前或者之后做相应的补偿。我在做颗粒计数的时候先用膨胀连接了同一颗粒被光照切成两半的区域但膨胀后的面积比真实颗粒大了大约15%后来我用等效直径反推的时候专门做了修正。膨胀还有一个容易被忽视的用途边缘扩展。当你的目标区域已经从图像边缘切出去了一部分膨胀可以让它重新和边界连接上避免后续轮廓检测时把贴边的目标漏掉。2.3 核大小与迭代次数的实测对比很多人纠结到底该用多大的核、迭代几次我给一个我自己常用的对照方法。准备一张典型的测试图用不同参数跑一遍把结果拼在一起看。核大小迭代次数腐蚀效果膨胀效果适用场景3×31轻微收缩去小噪点轻微扩张接小断裂精细处理目标较小3×32中等收缩中等扩张一般去噪和连接5×51明显收缩明显扩张噪点较大或断裂较宽5×53强收缩强扩张目标较大、噪点严重7×71很强收缩很强扩张粗处理慎用这张表不是固定公式是我自己的经验区间。拿我最近一个电路板焊点检测的项目来说焊点直径大约在20个像素左右噪点大多是3到5个像素的亮点。我先用3×3的核迭代2次腐蚀再把结果用同样大小的核膨胀2次也就是开运算噪点清得干干净净焊点面积损失不到5%。如果你拿不准可以用一个很笨但很有效的办法写个小循环把核从3×3试到11×11迭代从1试到3把九张结果图并排显示用眼睛选。形态学处理的可视化反馈非常直接大多数情况下你一眼就能看出哪个参数组合是对的。3. 开运算与闭运算组合操作的实战价值3.1 开运算先去噪再还原的去毛刺方案开运算的定义是先腐蚀后膨胀OpenCV里可以直接用cv2.morphologyEx()配合cv2.MORPH_OPEN来调用opened cv2.morphologyEx(binary, cv2.MORPH_OPEN, kernel)它等价于eroded cv2.erode(binary, kernel) opened cv2.dilate(eroded, kernel)开运算的核心价值在于它能在去除小噪点的同时基本保持大目标的尺寸不变。腐蚀阶段把小于核尺寸的亮区域全部吃掉膨胀阶段把保留下来的大目标恢复到接近原来的大小。因为小噪点在腐蚀阶段已经被完全消除了膨胀阶段没有东西可以恢复它们就永久消失了。这正是开运算和单纯腐蚀的区别。单纯腐蚀会把主目标也缩小一圈而开运算不会。开运算的典型应用二值化后去毛刺目标边缘有很多细小的突起开运算可以把这些突起削掉让边缘更光滑。去除背景中的孤立亮点文档扫描后的背景杂点用开运算清掉。分离轻度粘连的目标比单纯腐蚀更温和分离的同时尽量保持主体完整。实操心得开运算的核形状对结果影响很大。处理圆形目标时椭圆核比矩形核的边缘更自然不会出现明显的方形棱角。如果你做完开运算发现目标边缘变成了方块状多半是你用了矩形核。3.2 闭运算填补孔洞与连接断裂区域闭运算是先膨胀后腐蚀用cv2.MORPH_CLOSE调用closed cv2.morphologyEx(binary, cv2.MORPH_CLOSE, kernel)它的作用是填补前景内部的暗色小孔洞和连接断裂。膨胀阶段先把前景向外扩张暗孔洞被覆盖掉断裂处被接上腐蚀阶段再把前景缩回原来的大小但孔洞和断裂处因为已经被填上了就保留了下来。闭运算的应用场景非常具体文字笔画修复OCR预处理的标准操作把断开的笔画连上。医学图像填孔细胞核内部因为染色不均出现的暗点用闭运算填掉。道路提取后处理遥感图像中提取的道路因为树荫遮挡出现断开闭运算可以连接。目标内部小孔填充连通域分析前把目标内部的小孔填上避免一个目标被算成多个。这里有一个重要的经验闭运算的核大小要略大于你要填补的孔洞尺寸。如果你的孔洞最大是5个像素宽用3×3的核可能填不干净用7×7的核比较保险。但核也不能太大否则会把本来应该分开的两个目标连在一起。3.3 开闭运算顺序不可颠倒的原因有人会问开运算和闭运算都做一遍是不是能把噪点和孔洞一起解决答案是能但顺序很重要。正确的组合是先开运算去噪再闭运算填孔。理由是这样的如果先做闭运算膨胀会把噪点也一起放大本来3个像素的小噪点膨胀后变成7个像素再做开运算的时候如果核不够大就清不掉了。而先做开运算噪点在一开始就被清除后面的闭运算只需要处理真正的孔洞问题任务更单纯。# 正确的顺序先开后闭 kernel_open cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3)) kernel_close cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (7, 7)) temp cv2.morphologyEx(binary, cv2.MORPH_OPEN, kernel_open) result cv2.morphologyEx(temp, cv2.MORPH_CLOSE, kernel_close)注意这两个阶段用的核大小可以不一样。去噪用的核通常小一些因为噪点小填孔用的核要大一些因为孔洞尺寸由目标本身决定。我一般会分别调这两个核不要图省事用同一个。4. 梯度、顶帽与黑帽进阶形态学工具4.1 形态学梯度提取边缘的另一种思路形态学梯度定义为膨胀结果减去腐蚀结果用cv2.MORPH_GRADIENT调用gradient cv2.morphologyEx(binary, cv2.MORPH_GRADIENT, kernel)得到的图像上只有目标的边界处是亮的其余区域都是暗的。原理很直观膨胀把边界向外扩了一个核的距离腐蚀把边界向内缩了一个核的距离两者相减留下来的恰好是边界附近那一圈像素。和Canny、Sobel这些传统边缘检测算子相比形态学梯度的特点是对噪声更鲁棒因为本质上还是基于形态学的极值运算不太受灰度渐变的影响。边缘更粗边缘宽度等于核的尺寸不太适合需要精细边缘的场合。只能用于二值图或灰度图对彩色图需要先转灰度。我在做零件尺寸测量的时候用过这个方法。因为零件是标准二值化后的轮廓形态学梯度出来的边界厚度均匀配合轮廓拟合得到的尺寸重复性比Canny好。Canny在参数没调好时会出现断边或假边形态学梯度因为依赖的是确定性运算结果更稳定。4.2 顶帽运算提取亮背景上的暗细节顶帽Top Hat定义为原图减去开运算结果用cv2.MORPH_TOPHAT调用tophat cv2.morphologyEx(gray, cv2.MORPH_TOPHAT, kernel)要理解顶帽得先理解开运算去掉了什么。开运算去掉了比核小的亮区域所以原图减去开运算剩下的恰好就是那些被去掉的小亮区域。换句话说顶帽提取的是图像中比背景亮、且尺寸小于结构元素的细节。典型的应用场景是光照不均匀的背景下提取亮细节。比如一张金属表面图像中间有个光斑整体亮度不均。直接阈值化光斑区域全部被当成目标。用顶帽运算结构元素要选得比你要提取的缺陷尺寸大但比光斑尺寸小运算后光斑区域被当作背景抑制掉了缺陷细节被保留。这个思路在表面缺陷检测里非常常用。一个容易犯的错误顶帽运算用在二值图上没有意义。它的价值在于处理灰度图利用形态学操作对灰度值的局部极值特性来提取细节。对二值图做顶帽结果往往全是黑的或者全是白的。4.3 黑帽运算光照不均场景下的补救手段黑帽Black Hat定义为闭运算结果减去原图用cv2.MORPH_BLACKHAT调用blackhat cv2.morphologyEx(gray, cv2.MORPH_BLACKHAT, kernel)黑帽提取的是图像中比背景暗、且尺寸小于结构元素的细节。闭运算填掉了这些小暗区域所以闭运算结果减去原图剩下的就是被填掉的小暗区域。黑帽的实用场景暗背景上的亮背景光照不均当背景过亮导致目标变暗时黑帽能提取出这些暗目标。检测亮表面上的暗缺陷比如白色陶瓷表面的裂纹、金属表面的暗斑。车牌字符分割预处理车牌是亮底黑字的情况下黑帽能把字符区域提取出来。顶帽和黑帽是一对互补工具。目标比背景亮就用顶帽目标比背景暗就用黑帽。实际项目中我经常把两个都跑一遍看哪个能更干净地分离出目标然后再决定用哪个。5. 完整实操流程与代码落地5.1 环境准备与基础代码框架先说环境。OpenCV的Python包安装最省事的方式是pip install opencv-python numpy matplotlib如果你需要用到SIFT、SURF这类专利算法装opencv-contrib-pythonpip install opencv-contrib-python这两个包不要同时装会冲突。我一般只用opencv-contrib-python功能全不会少东西。基础代码框架我通常这么搭把形态学参数集中管理方便调import cv2 import numpy as np import matplotlib.pyplot as plt def morphology_pipeline(image_path, thresh_val127, open_kernel_size(3, 3), close_kernel_size(7, 7), kernel_shapecv2.MORPH_ELLIPSE): img cv2.imread(image_path, 0) if img is None: raise FileNotFoundError(f无法读取图像: {image_path}) # 自适应直方图均衡改善对比度 clahe cv2.createCLAHE(clipLimit2.0, tileGridSize(8, 8)) img_eq clahe.apply(img) # 二值化 _, binary cv2.threshold(img_eq, thresh_val, 255, cv2.THRESH_BINARY) # 开运算去噪 k_open cv2.getStructuringElement(kernel_shape, open_kernel_size) opened cv2.morphologyEx(binary, cv2.MORPH_OPEN, k_open) # 闭运算填孔 k_close cv2.getStructuringElement(kernel_shape, close_kernel_size) closed cv2.morphologyEx(opened, cv2.MORPH_CLOSE, k_close) return img, binary, opened, closed def show_results(imgs, titles): plt.figure(figsize(16, 10)) for i, (im, title) in enumerate(zip(imgs, titles)): plt.subplot(2, 3, i 1) plt.imshow(im, cmapgray) plt.title(title, fontsize12) plt.axis(off) plt.tight_layout() plt.show()这个框架里我加了CLAHE做对比度增强因为在工业图像里光照不均太常见了。如果你的图像质量本来就很好这一步可以去掉。关键是要把每一步的结果都保存下来方便对比排查问题出在哪。5.2 一个工业检测场景的完整案例我用一个金属垫片表面缺陷检测的例子来走完整流程这个逻辑和实际项目里几乎一样。需求是检测垫片表面的凹坑缺陷。垫片是亮色的金属凹坑是暗色的小区域。图像尺寸1280×960缺陷尺寸大约在10到30个像素之间背景有轻微的光照梯度。第一步预处理。先转灰度用CLAHE增强局部对比度。第二步顶帽/黑帽选择。因为缺陷是暗的、背景是亮的用黑帽运算。结构元素的尺寸要大于缺陷的最大尺寸30像素但小于光照梯度的尺度几百像素。我选51×51的矩形核。gray cv2.imread(washer.png, 0) clahe cv2.createCLAHE(clipLimit3.0, tileGridSize(8, 8)) gray_eq clahe.apply(gray) # 黑帽提取暗缺陷核尺寸要大于缺陷最大尺寸 k_blackhat cv2.getStructuringElement(cv2.MORPH_RECT, (51, 51)) blackhat cv2.morphologyEx(gray_eq, cv2.MORPH_BLACKHAT, k_blackhat)这一步跑完光照梯度被压平了缺陷区域的灰度响应被凸显出来。第三步阈值分割。黑帽结果做Otsu阈值。_, defect_mask cv2.threshold(blackhat, 0, 255, cv2.THRESH_BINARY cv2.THRESH_OTSU)第四步形态学后处理。这时候mask上会有些小噪点同时缺陷区域内部可能有些小孔。先用3×3椭圆核开运算去噪再用5×5椭圆核闭运算填孔。k_open cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3)) k_close cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5)) mask_clean cv2.morphologyEx(defect_mask, cv2.MORPH_OPEN, k_open) mask_final cv2.morphologyEx(mask_clean, cv2.MORPH_CLOSE, k_close)第五步轮廓提取与筛选。连通域分析按面积过滤掉太小的区域。contours, _ cv2.findContours(mask_final, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) valid_defects [] for cnt in contours: area cv2.contourArea(cnt) if 80 area 2000: valid_defects.append(cnt) x, y, w, h cv2.boundingRect(cnt) cv2.rectangle(gray, (x, y), (x w, y h), (0, 255, 0), 2)整套流程跑下来单张图处理时间在30到40毫秒之间可以满足产线的节拍要求。关键参数就两个黑帽核的尺寸和面积筛选的下限。黑帽核的尺寸一定要大于缺陷最大尺寸否则缺陷会被自己抵消掉一部分面积下限要根据实际缺陷的最小尺寸来定我一般设成理论最小面积的0.7倍留一些余量。5.3 参数调优记录表与效果对比调参这个环节我的做法是每次都把参数和结果记录在一张表里跑几轮下来规律就出来了。下面是我在这个垫片项目里的实际记录你可以直接参考这个格式来建自己的调参表。轮次黑帽核尺寸开运算核闭运算核缺陷召回数误检数备注131×313×33×3128核偏小光照残留251×513×33×3143改善明显351×513×35×5142孔洞填补好471×715×55×5131略大小缺陷丢失551×515×57×7142和轮次3接近从这五轮的结果看黑帽核尺寸是最敏感的参数。从31加到51效果提升明显再从51加到71反而开始丢缺陷。这说明核尺寸已经接近缺陷尺度部分小缺陷被当成背景处理了。最后我选定的是轮次3的参数组合。调参心得不要盲目追求误检数为零。误检数为零的时候往往召回率已经在下降了。工业场景下我一般优先保证召回率把误检通过后端的轮廓筛选或者分类模型来处理。形态学这一层的目标是把候选区域找全不是一步到位做到完美。6. 常见问题与排查技巧实录6.1 边界处理与边缘伪影问题贴边目标被处理掉这是最常见的一类问题。原因在borderType。默认的cv2.BORDER_CONSTANT把图像外的像素当成0对二值图就是背景所以腐蚀的时候贴边目标的边界像素会接触到虚拟背景被当成边界腐蚀掉。解决办法有两个如果目标贴边是你的正常场景把borderType改成cv2.BORDER_REPLICATE让边界外的像素复制边界像素的值。更常见的做法是在预处理阶段给图像加一圈padding处理完再裁回去。# 加10像素padding处理完裁回 pad 10 img_pad cv2.copyMakeBorder(binary, pad, pad, pad, pad, cv2.BORDER_CONSTANT, value0) processed cv2.morphologyEx(img_pad, cv2.MORPH_OPEN, kernel) result processed[pad:-pad, pad:-pad]另一个常见的边缘问题是形态学梯度在图像边界处产生的伪边缘。膨胀在边界处会向外扩张腐蚀在边界处会向内收缩两者在边界处相减会产生一条虚假的边缘线。如果后续要做轮廓检测这条伪边缘会被当成真实轮廓。处理方法是在取梯度之前先加padding或者在处理后把边缘一定宽度的区域直接屏蔽掉。6.2 核形状选择的踩坑记录核形状这个事我是真踩过坑。早期做圆形药片计数用了矩形核做开运算结果每个药片边缘都被削出了方角轮廓拟合出来的直径误差有7%到8%。换成椭圆核之后误差降到2%以内。这个差异在做尺寸测量的时候是致命的。后来我总结了一个简单的选择原则目标接近圆形或形状不规则用椭圆核。目标有明显水平垂直结构文字、矩形零件用矩形核。需要保持水平和垂直方向独立处理比如提取水平线、垂直线用十字核。还有一个容易被忽略的细节椭圆核在小尺寸下比如3×3其实不是真正的圆OpenCV生成的是一个近似。如果你对形状精度要求很高可以自己用np.zeros手动构造核把想要的像素位置设为1。我做过一个项目需要提取特定角度的斜线就是自己构造了一个斜向的核。注意事项核的尺寸和形状一旦确定整个处理流程中尽量保持一致。如果开运算用3×3椭圆核闭运算用7×7矩形核两个阶段的处理特性不一样结果可能不是你想要的效果。如果确实需要用不同的核每次改变后都要重新可视化验证。6.3 常见报错与排查速查表下面这张表是我在带新人和自己排查时整理的高频问题基本覆盖了形态学操作会遇到的大部分情况。问题现象可能原因排查与解决方法处理完图像全黑腐蚀迭代次数过多目标被吃光降低迭代次数或减小核尺寸先验证单次腐蚀效果处理完图像全白膨胀过度或二值图反了检查二值化方向减少膨胀迭代噪点没去掉核尺寸小于噪点尺寸增大核尺寸或改用开运算目标整体变小只做了腐蚀没做膨胀改用开运算代替纯腐蚀孔洞没填上核尺寸小于孔洞尺寸增大闭运算的核尺寸边缘出现方块感用了矩形核处理圆形目标换成椭圆核贴边目标消失边界处理默认当背景加padding或改borderType顶帽/黑帽结果全黑用于二值图而非灰度图确保输入是灰度图核尺寸要大于目标处理速度慢核太大、图像分辨率高先下采样处理再上采样或用分离核结果不稳定每次不同参数随机或图像输入不一致固定所有参数确保图像预处理一致关于处理速度补充一点形态学操作的计算复杂度和核面积成正比。一个51×51的核计算量是3×3核的289倍。如果图像分辨率很高用大核做黑帽运算可能会成为性能瓶颈。我的处理方式是先把图像缩小到原来的1/2或者1/4用小一点的核处理再把结果放大回去。因为形态学操作对分辨率不敏感只要核的相对尺寸不变效果基本一致。这个技巧在处理4K图像的时候特别管用速度能提升5到10倍。最后说一个我自己用了很久的习惯每做一步形态学操作都把中间结果存成图片。不要在一个脚本里一口气跑完只输出最终结果。出了问题的时候你能一眼看出来是哪一步开始不对的。形态学操作的每一步都是确定性的只要中间结果对了最终结果就不会错如果中间结果不对那问题一定出在参数或者输入上排查方向非常明确。这个习惯帮我省下来的时间比调参本身多得多。
返回列表
PREV
查看更多资讯
NEXT
返回资讯列表