当前位置: 首页 > news >正文

图像形态学:膨胀、腐蚀和边缘检测与绘制

图形膨胀和腐蚀

腐蚀

腐蚀操作会使前景物体的边界向内收缩

算法简述

        用一个结构元素(通常是一个小的矩形或圆形核)在图像上滑动。只有当结构元素完全覆盖前景像素时,中心像素才保留,否则就被腐蚀(变为背景)。腐蚀操作的过程和膨胀类似,其原理可以看成最小值滤波,也就是用结构元素覆盖原图像范围内的最小值替换锚点位置的像素值。对于腐蚀来说,边界补充的时候补充的是较大值,在下面的例子中也就是1:

api接口

api接口有三个参数

        第一个是要处理的图像

        第二个是腐蚀核的大小

        第三个是腐蚀操作次数

代码展示

腐蚀和膨胀
tu=cv2.imread('taiyang.png')
kernel=np.ones((5,5),np.uint8)
erosion = cv2.erode(tu,kernel,iterations=1)cv2.imshow('tu',tu)
cv2.imshow('erosion',erosion)
cv2.waitKey(0)

效果展示


膨胀

膨胀操作会使前景物体的边界向外扩张。

算法简述

用一个结构元素在图像上滑动。只要结构元素覆盖的区域中有一个像素是前景,中心像素就变成前景(即膨胀)。用结构元素覆盖原图像范围内的最大值替换锚点位置的像素值。对于膨胀来说,边界补充的时候补充的是较小值,在下面的例子中也就是0:

api接口

和腐蚀一样有三个接口

        图片        膨胀核大小        膨胀次数

代码展示

tu=cv2.imread('taiyang.png')
kernel=np.ones((5,5),np.uint8)
dilation = cv2.dilate(tu,kernel,iterations=1)cv2.imshow('tu',tu)
cv2.imshow('dilation',dilation)
cv2.waitKey(0)

结果展示

总结

实战例子

膨胀

如果我们有一段断断续续的文字看不清楚,我们可以膨胀一下。

tu=cv2.imread('wenzi.png')kernel=np.ones((3,3),np.uint8)
dilation = cv2.dilate(tu,kernel,iterations=1)cv2.imshow('tu',tu)
cv2.imshow('dilation',dilation)
cv2.waitKey(0)

腐蚀

我们有一个指纹信息,但是有许多噪声点,这个时候我们可以腐蚀一下,然后就可以去噪了。

tu1=cv2.imread('zhiwen.png')
kernel=np.ones((2,2),np.uint8)
erosion = cv2.erode(tu1,kernel,iterations=1)cv2.imshow('tu',tu1)
cv2.imshow('erosion',erosion)
cv2.waitKey(0)

综合

开运算(先腐蚀后膨胀)

对于上面的指纹,我们腐蚀完有些部分断开了,如果我我们想连上,我们可以先腐蚀再膨胀,这样就可以达到了。

tu=cv2.imread('wenzi.png')
tu1=cv2.imread('zhiwen.png')
kernel=np.ones((2,2),np.uint8)
erosion = cv2.erode(tu1,kernel,iterations=1)
dilation = cv2.dilate(erosion,kernel,iterations=1)
cv2.imshow('tu',tu1)
cv2.imshow('erosion',erosion)
cv2.imshow('erosopn+dilation',dilation)
cv2.waitKey(0)

这个有一个集成的函数zhiwen_new = cv2.morphologyEx(zhiwen,cv2.MORPH_OPEN,kernel)

zhiwen=cv2.imread('zhiwen.png')kernel=np.ones((2,2),np.uint8)
zhiwen_new = cv2.morphologyEx(zhiwen,cv2.MORPH_OPEN,kernel)cv2.imshow('tu',zhiwen)
cv2.imshow('new',zhiwen_new)
cv2.waitKey(0)

闭运算(先膨胀后腐蚀)

有些时候我们的指纹刚开始就断断续续,这样我们就要先膨胀后腐蚀了。但效果不是很好。

zhiwen=cv2.imread('zhiwen.png')kernel=np.ones((3,3),np.uint8)
zhiwen_new = cv2.morphologyEx(zhiwen,cv2.MORPH_CLOSE,kernel)cv2.imshow('tu',zhiwen)
cv2.imshow('new',zhiwen_new)
cv2.waitKey(0)

梯度运算(膨胀-腐蚀)

这个就是把原图变细,然后再把原图变粗,减去这个细的就得出一个框了。

tu=cv2.imread('wenzi.png')kernel=np.ones((2,2),np.uint8)
erosion = cv2.erode(tu,kernel,iterations=1)
dilation = cv2.dilate(tu,kernel,iterations=1)
a=dilation-erosion
td=cv2.morphologyEx(tu, cv2.MORPH_GRADIENT, kernel)
cv2.imshow('tu',tu)
cv2.imshow('erosion',erosion)
cv2.imshow('dilation',dilation)
cv2.imshow('GRADIENT',td)
cv2.imshow('a',a)
cv2.waitKey(0)

顶帽和黑帽

顶帽 = 原始图像 - 开运算图像

黑帽 = 闭运算图像 - 原始图像

tu=cv2.imread('taiyang.png')
cv2.imshow('tu',tu)
kernel=np.ones((2,2),np.uint8)open_sun=cv2.morphologyEx(tu,cv2.MORPH_OPEN,kernel)
cv2.imshow('open_sun',open_sun)
toplat=cv2.morphologyEx(tu,cv2.MORPH_TOPHAT,kernel)
cv2.imshow('toplat',toplat)
close_sun=cv2.morphologyEx(tu,cv2.MORPH_CLOSE,kernel)
cv2.imshow('close_sun',close_sun)
blackhat=cv2.morphologyEx(tu,cv2.MORPH_BLACKHAT,kernel)
cv2.imshow('blackhat',blackhat)
erosion = cv2.erode(tu,kernel,iterations=1)
dilation = cv2.dilate(tu,kernel,iterations=1)
cv2.imshow('dilation',dilation)
cv2.imshow('erosion',erosion)
cv2.waitKey(0)

顶帽图像展示

黑帽图像展示

边缘检测

这个有许多算法,先讲Sobel算法。

api接口参数解释

        cv2.Sobel(src, ddepth, dx, dy, ksize)

        src: 输入图像
ddepth: 输出图像的深度,深度是指输出图像的数据类型,决定了像素值的范围。参数为负数(如-1)表示输出图像与输入图像具有相同的深度。由于Sobel算子计算梯度时会产生负数,所以通常使用浮点数类型(如cv2.CV_64F)来保留负梯度,然后再取绝对值转换回8位无符号整数(如cv2.CV_8U)。这里直接使用-1(与输入相同)可能会截断负值,所以后续常配合                                        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​      cv2.convertScaleAbs处理。
dx: x方向导数阶数
dy: y方向导数阶数
ksize: Sobel核大小
这里只列出几个常用的。

y方向

yuan=cv2.imread('yuan.png',0)               #这里本身就是黑白的,所以转化灰度图对结果没啥影响,但对彩色图片是有影响的
y1=cv2.Sobel(yuan,-1,dx=0,dy=1,ksize=5)
cv2.imshow('yuan',yuan)
cv2.imshow('yuan1',y1)
cv2.waitKey(0)

这里可以看到为-1的时候只能检测一般,x方向也是只能检测一半,因为上面只能保存正值,负数舍弃了。所以我们后面引入了一个浮点数类型(如cv2.CV_64F)来保留负梯度,然后再配合绝对值来显示。如下

yuan=cv2.imread('yuan.png',0)               #这里本身就是黑白的,所以转化灰度图对结果没啥影响,但对彩色图片是有影响的
y1=cv2.Sobel(yuan,-1,dx=1,dy=0,ksize=5)
cv2.imshow('yuan',yuan)
cv2.imshow('yuan1',y1)
cv2.waitKey(0)
y2=cv2.Sobel(yuan,cv2.CV_64F,dx=1,dy=0,ksize=5)#这里只能转化一边,因为这个只能保存大于0的值,这里要保存为CV_64F格式的,然后下面再取绝对值就可以了。
yuan_x_full = cv2.convertScaleAbs(y2)#转换为绝对值,负数转换为正数
cv2.imshow('yuan_x_full',yuan_x_full)
cv2.waitKey(0)

重要代码

y2=cv2.Sobel(yuan,cv2.CV_64F,dx=1,dy=0,ksize=5)#这里只能转化一边,因为这个只能保存大于0的值,这里要保存为CV_64F格式的,然后下面再取绝对值就可以了。
yuan_x_full = cv2.convertScaleAbs(y2)#转换为绝对值,负数转换为正数

优化

我们上面可以看出,如果我们x方向,那么在圆的正上方和正下方那块的左右没变化,然后就断开了,在y方向的时候,最左和最右的上下也是对应的没变化,那么也是有一个缺口,我们该怎么补全呢?

这个时候我们只要把两个有缺的加一起就可以凑出一个完整的了。

yuan=cv2.imread('yuan.png',0)              
cv2.imshow('yuan',yuan)
y2=cv2.Sobel(yuan,cv2.CV_64F,dx=1,dy=0,ksize=5)。
yuan_x_full = cv2.convertScaleAbs(y2)#转换为绝对值,负数转换为正数
cv2.imshow('yuan_x_full',yuan_x_full)
yuan_y_full=cv2.Sobel(yuan,cv2.CV_64F,dx=0,dy=1,ksize=5)
yuan_y_full = cv2.convertScaleAbs(yuan_y_full)#转换为绝对值,负数转换为正数
cv2.imshow('yuan_y_full',yuan_y_full)
full=yuan_x_full+yuan_y_full
cv2.imshow('full',full)
cv2.waitKey(0)

那么有人回想,如果我x=1,y=1呢?会不会就好了,其实使得其反的,这个时候缺点全集齐了。

yuan=cv2.imread('yuan.png',0)
xy=cv2.Sobel(yuan,cv2.CV_64F,dx=1,dy=1,ksize=5)
xy=cv2.convertScaleAbs(xy)
cv2.imshow('xy',xy)
cv2.waitKey(0)

综合四种方法

先给出这四种的方法代码

# 读取图像 (灰度模式)
img = cv2.imread('zl.png', 0)  # flags=0 表示灰度读取# ========== Sobel边缘检测 ==========
# X方向梯度 (检测垂直边缘)
grad_x = cv2.Sobel(img, cv2.CV_64F, 1, 0)  # dx=1, dy=0
abs_grad_x = cv2.convertScaleAbs(grad_x)    # 转换负数为绝对值# Y方向梯度 (检测水平边缘)
grad_y = cv2.Sobel(img, cv2.CV_64F, 0, 1)  # dx=0, dy=1
abs_grad_y = cv2.convertScaleAbs(grad_y)    # 转换负数为绝对值# 合并X/Y方向梯度
sobel_combined = cv2.addWeighted(abs_grad_x, 1, abs_grad_y, 1, 0)# ========== Scharr边缘检测 (更敏感) ==========
# X方向梯度
scharr_x = cv2.Scharr(img, cv2.CV_64F, 1, 0)
scharr_x_abs = cv2.convertScaleAbs(scharr_x)# Y方向梯度
scharr_y = cv2.Scharr(img, cv2.CV_64F, 0, 1)
scharr_y_abs = cv2.convertScaleAbs(scharr_y)# 合并结果
scharr_combined = cv2.addWeighted(scharr_x_abs, 1, scharr_y_abs, 1, 0)# ========== Laplacian边缘检测 ==========
laplacian = cv2.Laplacian(img, cv2.CV_64F)
laplacian_abs = cv2.convertScaleAbs(laplacian)  # 二值化前需绝对值转换# ========== Canny边缘检测 (最优效果) ==========
canny_edges = cv2.Canny(img, 100, 200)  # 阈值可调整# ========== 显示所有结果 ==========
cv2.imshow('1. Original', img)
cv2.imshow('2. Sobel X', abs_grad_x)
cv2.imshow('3. Sobel Y', abs_grad_y)
cv2.imshow('4. Sobel Combined', sobel_combined)
cv2.imshow('5. Scharr Combined', scharr_combined)
cv2.imshow('6. Laplacian', laplacian_abs)
cv2.imshow('7. Canny', canny_edges)cv2.waitKey(0)  # 按任意键关闭窗口
cv2.destroyAllWindows()

第一种是Sobel,上面提到了,这里展示下结果

第二种是Scharr边缘检测

第三种 Laplacian边缘检测

第四种 Canny边缘检测

这个要重要说下,这个就是Sobel的3*3核时算法升级,有以下四种升级。

我们对比下两个的图像,首先噪声消除了许多,边缘正常了一些没那么粗了,还要些。。。

主要源于以下四个升级。

        高斯滤波降噪 - 解决噪声敏感性

        非极大值抑制(NMS) - 解决边缘粗大

        双阈值检测 - 解决阈值单一问题

        边缘连接 - 解决边缘断裂

边缘绘制

# 边缘检测and边缘绘制
pen1=cv2.imread('pen.png',0)
ret,pen=cv2.threshold(pen1,120,255,cv2.THRESH_BINARY)
cv2.imshow('pen',pen)
contours, hierarchy = cv2.findContours(pen,cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)print(contours)
print(len(contours))
pen1=pen.copy()
aa = cv2.cvtColor(pen1, cv2.COLOR_GRAY2BGR)
cv2.drawContours(aa,contours,-1,(0,255,0),2)# cv2.imshow('contours',contours)
cv2.imshow('pen1',aa)
cv2.waitKey(0)

通过contours, hierarchy = cv2.findContours(pen,cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)来获取轮廓contours,这个contours保存的是一个一个的坐标点。

hierarchy参数表示轮廓之间的层级关系,它描述了图像中轮廓的嵌套结构(没啥用)

然后再通过drawContours可以进行绘制。

http://www.dtcms.com/a/342987.html

相关文章:

  • Java后端面试场景题大全:2025年高频考点深度解析
  • 大模型部署
  • 造成云手机闪退的原因有哪些?
  • 使用VBA宏批量修改Word中表格题注格式
  • HYPE分布式水文模型建模方法与案例分析实践技术应用
  • AI 时代的数字伦理选择题
  • Mac 电脑 IDEA 执行 Maven 出现 No route to host 问题
  • Django REST框架核心:GenericAPIView详解
  • 【力扣 买股票的最佳时机II Java/Python】
  • rt-thread使用sfud挂载spi flash, 并使用spi驱动st7789 lcd的trace分析(使用spi dma)
  • 生信分析自学攻略 | R语言函数与参数介绍
  • kylin v10只允许kylin用户登录解决办法
  • 雅菲奥朗SRE知识墙分享(四):『AI已开始重塑劳动力市场,美国年轻科技从业者首当其冲』
  • 数据结构-HashSet
  • 黑马程序员jbdc笔记
  • 迅为R3568开发板OpeHarmony学习开发手册-配置远程访问环境
  • mac m1上使用Kerberos访问远程linux hadoop集群的正确姿势
  • Android 定位技术全解析:从基础实现到精准优化
  • redis在Spring中应用相关
  • LeetCode算法日记 - Day 17: 算法中的位运算技巧总结
  • 【黑客技术零基础入门】硬核科普什么是HTMLHTML基本结构以及HTML基本使用(非常详细)零基础入门到精通,收藏这一篇就够了!
  • 轻量级加密的下一站:后量子、AI 与自动化验证
  • 【iOS】SDWebImage第三方库源码学习笔记
  • JupyterLab 安装(python3.10)
  • 大模型之原理篇——Transformer基础、分词器
  • 深度剖析:PCB 厚铜板铜厚检测,铜厚不足的连锁反应及检测手段
  • 性能测试中性能分析与调优学习大纲整理
  • C++中纯虚函数与普通虚函数的深度解析
  • 面试紧张情绪管理:如何保持冷静自信应对挑战
  • CLAUDE.md文件介绍(Claude Code核心配置文件,开始对话或执行任务时自动加载的上下文文件)