【OpenCV】形态学操作 | 图像平滑 | 边缘检测 | Laplacian算子

news2024/11/18 3:36:46


Ⅰ. 形态学操作

0x00 腐蚀和膨胀

腐蚀和膨胀是最基本的形态学操作,腐蚀和膨胀都是针对白色部分(高亮部分)而言的。

膨胀就是使图像中的高亮部分扩张,效果图拥有比原图更大的高亮区域;腐蚀是原图中的高亮区域被蚕食,效果图拥有比原图更小的高亮区域。膨胀是求局部最大值的操作,腐蚀是求局部最小值的操作。

腐蚀

操作:

用一个结构元素扫描图像中的每一个像素,用结构元素中的每一个像素与覆盖的像素做“与”操作,结果都为 1 ,则该像素为 1 ,否则为 0 。

即用卷积核扫描图像, 只不过腐蚀操作的卷积和一般都是1, 如果卷积核内所有像素点都是白色, 那么锚点即为白色。


作用:

消除物体边界点,使目标缩小,可以消除小于结构元素的噪声点

💬API

cv2.erode(img,kernel,iterations)

参数:

  • img:要处理的图像
  • kernel:核结构
  • iterations:腐蚀的次数,默认为1

膨胀

操作:用一个结构元素扫描图像中的每一个像素,用结构元素中的每一个像素与其覆盖的像素做“与”操作,如果都为 0 ,则该像素为 0 ,否则为 1 。

💬API

cv2.dilate(img,kernel,iterations)

参数:

  • img:要处理的图像
  • kernel:核结构
  • iterations:腐蚀的次数,默认为1

💎示例:

# 1.读取图像
img = cv2.imread('sun.jpg')
# 2.创建核结构
kernel = np.ones((10, 10), np.uint8)
# 3.图像的腐蚀与膨胀
erosion_1 = cv2.erode(img, kernel, iterations=1) # 腐蚀
erosion_2 = cv2.erode(img, kernel, iterations=2)
erosion_3 = cv2.erode(img, kernel, iterations=3)
dilate_1 = cv2.dilate(img, kernel, iterations=1)# 膨胀
dilate_2 = cv2.dilate(img, kernel, iterations=2)
dilate_3 = cv2.dilate(img, kernel, iterations=3)
# 4.图像显示
res_1 =np.hstack((erosion_1, erosion_2, erosion_3))
res_2 =np.hstack((dilate_1, dilate_2, dilate_3))
cv2.imshow('erosion', res_1)
cv2.imshow('dilate', res_2)
cv2.waitKey(0)
cv2.destroyAllWindows()

 上面的腐蚀操作中,我们自行创建了一个5\times 5的全 1 卷积核但是遇到复杂的图像操作时,我们不可能每次都自行指定卷积核,因此\textrm{OpenCV}提供了获取卷积核的 APL.不需要我们手工创建卷积核。

getStructuringElement(shape, ksize[, anchor])

shape是指卷积核的形状, 注意不是指长宽, 是指卷积核中1形成的形状.

  • MORPH_RECT   卷积核中的1是矩形, 常用.
  • MORPH_ELLIPSE  椭圆
  • MORPH_CROSS  十字

0x01 开运算与闭运算

开运算
先腐蚀,再膨胀
作用:分离物体,消除小区域。消除噪点,去除小的干扰块,而不影响原来的图像

闭运算
先膨胀,再腐蚀
作用:消除“闭合”物体里面的孔洞,可以填充闭合区域

💬API

cv2.morphologyEx(img,op,kernel)

参数:

  • img: 要处理的图像
  • op:处理方式,若为开运算,则设为cv2.MORPH_OPEN;若为闭运算,则设为cv2.MORPH_CLOSE
  • kernel:核结构
     

 0x02 礼帽与黑帽

礼帽:用来分离比领近点亮一些的斑块。当一幅图像具有大幅背景的时候,而微小的物品比较有规律的情况下,可以使用礼帽运算作为背景提取。

礼帽 = 原始输入 -  开运算结果
dst = tophat\, (src,element) = src - open\ (src,element)


黑帽:分离比领近点暗一些的斑块。

黑帽 = 闭运算结果  - 原始输入
dst = blackhat\, (src, element) = close\, (src,element) - src

cv2.morphologyEx(img,op,kernel)
cv2.MORPH_TOPHAT#礼帽运算
cv2.MORPH_BLACKHAT#黑帽运算


Ⅱ. 图像平滑

0x00图像噪声

概念:由于图像采集、处理、传输等过程不可避免的会受到噪声的污染,妨碍人们对图像理解及分析处理。常见的图像噪声有高斯噪声、椒盐噪声等。

1. 椒盐噪声
椒盐噪声也称为脉冲噪声,是图像中经常见到的一种噪声,它是一种随机出现的白点或者黑点,可能是亮的区域有黑色像素或是在暗的区域有白色像素(或是两者皆有)。

椒盐噪声的成因可能是影像讯号受到突如其来的强烈干扰而产生、类比数位转换器或位元传输错误等。

例如失效的感应器导致像素值为最小值,饱和的感应器导致像素值为最大值。

 2. 高斯噪声

高斯噪声是指噪声密度函数服从高斯分布的一类噪声。

由于高斯噪声在空间和频域中数学上的易处理性,这种噪声(也称为正态噪声)模型经常被用于实践中。高斯随机变量z的概率密度函数由下式给出:

其中 Z 表示灰度值, \mu 表示 Z 的平均值或期望值, \sigma 表示 Z 的标准差。标准差的平方 \sigma ^2​  称为Z 的方差。

高斯函数的曲线如图所示

 

0x01图像平滑
概念:图像平滑从信号处理的角度看就是去除其中的高频信息,保留低频信息。因此我们可以对图像实施低通滤波。低通滤波可以去除图像中的噪声,对图像进行平滑。

根据滤波器的不同可分为均值滤波,高斯滤波,中值滤波, 双边滤波。

均值滤波
采用均值滤波模板对图像噪声进行滤除。令 S \left \{ x \, y\right \} 表示中心在 \left ( x,y \right ) 点,

尺寸为 m\times n 的矩形子图像窗口的坐标组。 均值滤波器可表示为:

由一个归一化卷积框完成的。它只是用卷积框覆盖区域所有像素的平均值来代替中心元素。

例如,3\times 3 标准化的平均过滤器如下所示:

均值滤波的优点是算法简单,计算速度较快;

缺点是在去噪的同时去除了很多细节部分,将图像变得模糊。

💬API

cv.blur(src, ksize, anchor, borderType)

参数:

  •  src:输入图像
  •  ksize:卷积核的大小
  •  anchor:默认值 (-1,-1) ,表示核中心
  •  borderType:边界类型

高斯滤波

二维高斯是构建高斯滤波器的基础,其概率分布函数如下所示:

G(x,y) 的分布是一个突起的帽子的形状。这里的 \sigma 可以看作两个值,一个是 x 方向的标准差 \sigma x ,另一个是 y 方向的标准差 \sigma y 

​当 \sigma x 和 \sigma y 取值越大,整个形状趋近于扁平;

\sigma x 和\sigma y取值越小,整个形状越突起。

正态分布是一种钟形曲线,越接近中心,取值越大,越远离中心,取值越小。

计算平滑结果时,只需要将"中心点"作为原点,其他点按照其在正态曲线上的位置,分配权重,就可以得到一个加权平均值。

高斯平滑在从图像中去除高斯噪声方面非常有效。

高斯平滑的流程:

  1.  首先确定权重矩阵


假定中心点的坐标是\left ( 0,0 \right ),那么距离它最近的8个点的坐标如下:

 更远的点以此类推。


为了计算权重矩阵,需要设定 \sigma 的值。假定 \sigma=1.5,则模糊半径为1的权重矩阵如下:

这9个点的权重总和等于0.4787147,如果只计算这9个点的加权平均,还必须让它们的权重之和等于1,因此上面9个值还要分别除以0.4787147,得到最终的权重矩阵。

        2.  计算高斯模糊

有了权重矩阵,就可以计算高斯模糊的值了。
假设现有9个像素点,灰度值(0-255)如下:

 每个点乘以对应的权重值:

 得到

将这9个值加起来,就是中心点的高斯模糊的值。

对所有点重复这个过程,就得到了高斯模糊后的图像。如果原图是彩色图片,可以对RGB三个通道分别做高斯平滑。

💬API

cv2.GaussianBlur(src,ksize,sigmaX,sigmay,borderType)

参数:

  • src: 输入图像
  • ksize:高斯卷积核的大小,注意 : 卷积核的宽度和高度都应为奇数,且可以不同
  • sigmaX: 水平方向的标准差
  • sigmaY: 垂直方向的标准差,默认值为0,表示与\sigma x相同
  • borderType:填充边界类型

中值滤波
中值滤波是一种典型的非线性滤波技术,基本思想是用像素点邻域灰度值的中值来代替该像素点的灰度值。

中值滤波对椒盐噪声(salt-and-pepper noise)来说尤其有用,因为它不依赖于邻域内那些与典型值差别很大的值。


💬API

cv.medianBlur(src, ksize )

参数:

  • src:输入图像
  • ksize:卷积核的大小

总结

图像噪声

  • 椒盐噪声:图像中随机出现的白点或者黑点
  • 高斯噪声:噪声的概率密度分布是正态分布


图像平滑

  • 均值滤波:算法简单,计算速度快,在去噪的同时去除了很多细节部分,将图像变得模糊cv2.blur()
  • 高斯滤波: 去除高斯噪声 cv2.GaussianBlur()
  • 中值滤波: 去除椒盐噪 cv2.medianBlur()

Ⅲ. 边缘检测

 边缘检测是图像处理和计算机视觉中的基本问题,其目的是为了识别数字图像中亮度变化明显的点。

图片边缘检测大幅度地减少了数据量,并且剔除了可以认为不相关的信息,保留了图像重要的结构属性。

有许多方法用于边缘检测,它们的绝大部分可以划分为两类:基于搜索和基于零穿越

基于搜索:通过寻找图像一阶导数中的最大值来检测边界,然后利用计算结果估计边缘的局部方向,通常采用梯度的方向,并利用此方向找到局部梯度模的最大值。

代表的算法是Sobel算子和Scharr算子。

  基于零穿越:通过寻找图像的二阶导数零穿越来寻找边界。

代表的算法是Laplacian算子。

 0x00 Sober检测算子

  • 水平变化:将图像I与奇数大小的模版进行卷积,结果为Gx.比如,当模板大小为3时, Gx为:

  • 垂直变化:将图像I与奇数大小的模板进行卷积,结果为Gy。比如,当模板大小为3时, Gy为:

 在图像的每一- 点,结合以上两个结果求出:

统计极大值所在的位置,就是图像的边缘。

Tip:当内核大小为3时,以上Sobel内核可能产生比较明显的误差,为解决这一问题,我们使用Scharr函数,但该函数仅作用于大小为3的内核。该函数的运算与Sobel函数一样快,但结果却更加精确,其计算方法为: 


即:右减左,下减上 

参数:

  • src:传入的图像
  • ddepth:图像的深度
  • dx和dy:指求导的阶数,dx=1dy=0沿着水平方向求导;dx=0dy=1沿着垂直方向求导
  • ksize:是Sobel算子的大小,即卷积核的大小,必须为奇数,默认为3(若ksize= -1 ,就演变成3\times 3的Scharr算子)
  • scale:缩放导数的比例常数,默认情况为没有伸缩系数
  • borderType:图像边界的模式,默认值为cv2.BORDER_DEFAULT

Sobel函数求完导数后会有负值,还有会大于255的值。而原图像是uint8,即8位无符号数,所以Sobel建立的图像位数不够,会有截断。

因此要使用16位有符号的数据类型,即cv2.CV_16S。

处理完图像后,再使用cv2.convertScaleAbs( )函数将其转回原来的uint8格式,否则图像无法显示。

Sobel算子是在两个方向计算的,最后还需要用cv2.addWeighted( )函数将其组合起来

💬API

Scale_abs = cv2.convertScaleAbs(src)#格式转换函数
result = cv2.addWeighted(src1, alpha, src2, beta)#图像混合

💎实例:

1.若只处理x轴:

img = cv2.imread('sun.jpg')
sobel = cv2.Sobel(img, cv2.CV_64F, 1, 0, ksize=3)
cv2.imshow('sobel', sobel)
cv2.waitKey(0)  # 等待时间,毫秒级,0表示按任意键终止
cv2.destroyAllWindows()

 2.处理x轴与y轴

img = cv2.imread('sun.jpg')
gray_image = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
sobelx = cv2.Sobel(gray_image,cv2.CV_64F, 1, 0, ksize=3)
sobely = cv2.Sobel(gray_image, cv2.CV_64F, 0, 1, ksize=3)
sobelx = cv2.convertScaleAbs(sobelx)#convertScaleAbs 图像增强函数
sobely = cv2.convertScaleAbs(sobely)
sobelxy = cv2.addWeighted(sobelx, 0.5, sobely, 0.5, 0)

0x01 Laplacian算子

Laplacian是利用二阶导数来检测边缘。因为图像是 "2维",我们需要在两个方向求导,如下式所示:

那不连续的函数的二阶导数是:

 那使用的卷积核是:

 即中间点和周围比较


💬API

laplacian = cv2.Laplacian(src, ddepth[, dst[, ksize[, scale[, delta[, borderType]]]]])

💎示例:

三种算子的处理对比:

img = cv2.imread('sun.jpg')
gray_image = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
sobelx = cv2.Sobel(gray_image,cv2.CV_64F, 1, 0, ksize=3)
sobely = cv2.Sobel(gray_image, cv2.CV_64F, 0, 1, ksize=3)
sobelx = cv2.convertScaleAbs(sobelx)#convertScaleAbs 图像增强函数
sobely = cv2.convertScaleAbs(sobely)
sobelxy = cv2.addWeighted(sobelx, 0.5, sobely, 0.5, 0)


scharrx = cv2.Scharr(gray_image, cv2.CV_64F, 1, 0)
scharry = cv2.Scharr(gray_image, cv2.CV_64F, 0, 1)
scharrx = cv2.convertScaleAbs(scharrx)
scharry = cv2.convertScaleAbs(scharry)
scharrxy = cv2.addWeighted(scharrx, 0.5, scharry, 0.5, 0)

laplacian = cv2.Laplacian(gray_image, cv2.CV_64F)
laplacian = cv2.convertScaleAbs(laplacian)

res = np.hstack((sobelxy, scharrxy,laplacian))
cv2.imshow('res', res)
cv2.waitKey(0)  # 等待时间,毫秒级,0表示按任意键终止
cv2.destroyAllWindows()

 从左到右分别为sobel算子处理,scharr算子处理,laplacian算子处理

总结:

边缘检测的原理

  • 基于搜索:利用一阶导数的最大值获取边界
  • 基于零穿越:利用二阶导数为0获取边界

Sobel算子

  • 基于搜索的方法获取边界

cv.sobel()

cv.convertScaleAbs()

cv.addweights()

Laplacian算子

  • 基于零穿越获取边界

cv.Laplacian()

算子比较: 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/163945.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C语言文件补充笔记2:VS查看定义、文件章节涉及到的函数

1 VS查看函数的定义与库的原码 (1)查看库函数的定义 右击要查看的函数,然后“转到定义” 这里就跳转到了定义的所在文件 在右上角关闭相关文件 (2) 查看库原码 将鼠标放到导入的库中,然后右击&#…

给数组创建复制(深拷贝)给数组创建复制(深拷贝)

【小白从小学Python、C、Java】 【计算机等级考试500强双证书】 【Python-数据分析】 数组的深拷贝、浅拷贝、引用拷贝 修改原数组不会影响复制后的数组 numpy.copy() [太阳]选择题 对于以下python代码最后输出的结果是? import numpy as np print("【执行】a np.arang…

awesome平铺窗口使用笔记

这个故事要从vim开始。从入了vim的坑以后,就爱上了平铺窗口模式。在macOS中就开始使用yabai。使用了yabai以后,就很大程序可以用键盘完成大部分操作了。然后我开始用linux,使用的是i3wm。感觉非常不错,几乎就与vim中的窗口管理差不…

[Android Studio] 如何查看Android Studio的版本信息

🟧🟨🟩🟦🟪 Android Debug🟧🟨🟩🟦🟪 Topic 发布安卓学习过程中遇到问题解决过程,希望我的解决方案可以对小伙伴们有帮助。 📋笔记目…

命令行批量 PDF 转换器:2PDF 2.0.8x Crack

命令行 PDF 转换器 2PDF 是一个专业的命令行实用程序,用于以批处理模式将办公文档和图像转换为 PDF。2PDF 基于命令行界面和语法,通过简单的标准命令提供对批量转换为 PDF 的完全控制。 从 Windows 命令提示符将文档和图像转换为 PDF 2PDF 可以将 PDF 和…

面试官问我微服务注册中心如何保证数据强一致性?头秃了。。。

目录 1、再回顾:什么是服务注册中心?2、Consul服务注册中心的整体架构3、Consul如何通过Raft协议实现强一致性?4、Consul如何通过Agent实现分布式健康检查? 1、再回顾:什么是服务注册中心? 先回顾一下什么…

Promise 详解

Promise 详解示例一个抽奖小游戏原生 JavaScript 实现Promise 实现读取文件原生 JavaScript 实现Promise 实现Promise 对象Promise 对象的状态Promise 对象的创建Promise 对象的状态的改变pending 转换为 fulfilledpending 转换为 rejected与 Promise 对象相关的 APIPromise.th…

Linux---gdb调试方法

1. 背景 程序的发布方式有两种,debug模式和release模式 Linux gcc/g出来的二进制程序,默认是release模式 要使用gdb调试,必须在源代码生成二进制程序的时候, 加上 -g 选项2. gdb调试 进入/退出调试: list/l 行号:显…

佳能2420报错代码E000007-0000

应该是定影的问题,先试一下清零,如果还是出现这个问题,不是电压就是应该换定影了。(包含:轴套、定影膜、缺硅油了【定影膜硅油、润滑脂】) 维修模式菜单: CLEAR > ENGIN > ERRCLR &…

交大博士学长:研究生计算机专业的方向选择!

Datawhale干货 作者:一辈闲,上海交大博士,Datawhale邀约作者作者知乎:https://www.zhihu.com/people/yi-bei-xian-16目前计算机专业的研究方向主要分为四个大方向分别是:AI(人工智能)、Systems&…

常微分方程组解稳定性的分析

文章未完相空间的绘制我们随机选一个方程,随机选的,不是有数学手册吗,一般来说考题不可能出数学手册上的例子import scipy.integrate as si import matplotlib.pyplot as plt import numpy as np## dx/dt x**2-y**2xy ## dy/dt x*y**2 - x**2*yf lambda x,y:x**2-y**2xy g…

HashMap、HashTable和ConcurrentHashMap的区别

HashMap是线程不安全的,HashTable和ConcurrentHashMap是线程安全的。HashTable的实现线程安全的方式是:将所有的方法都加上锁,也就相当于对this加锁,此时,无论访问HashTable的任何一个元素都会加锁操作,在多…

ESP32设备驱动-MMA8451加速度计驱动

MMA8451加速度计驱动 1、MMA8451介绍 MMA8451 是一款具有 14 位分辨率的低功耗加速度计,具有灵活用户可编程选项的嵌入式功能,可配置为两个中断引脚。嵌入式中断功能可实现整体节能,从而使主机处理器免于连续轮询数据访问低通滤波数据和高通滤波数据,最大限度地减少颠簸检…

DockerCompose安装卸载、文件语法格式

DockerCompose安装卸载、文件语法格式 一、DockerCompose的概念和作用 1.1 相关概念 DockerCompose可以基于Compose文件帮我们快速的部署分布式应用,不需要我们手动一个个创建和运行容器。 Compose文件是一个文本文件,通过指令定义集群中的每个容器如…

【蓝桥杯】时间显示(省赛)Java

【问题描述】 小蓝要和朋友合作开发一个时间显示的网站。在服务器上,朋友已经获取了当前的时间,用一个整数表示,值为从1970年1月1日O0:00:00到当前时刻经过的毫秒数。 现在,小蓝要在客户端显示出这个时间。小蓝不用显示出年月日&a…

IIC通信协议

数据有效性 IC由两条线组成,一条双向串行数据线SDA,一条串行时钟线SCL。 SDA线上的数据必须在时钟的高电平周期保持稳定,数据线的高或低电平状态只有在 SCL 线的时钟信号是低电平时才能改变。 换言之, SCL为高电平时表示有效数据…

Crack:结构分析和设计软件:Cross Section Analysis-Design

Cross Section Analysis & Design (美国、欧洲、亚洲和澳大利亚最受好评的结构软件)是一款功能强大的应用程序,可以执行各种横截面计算,包括钢筋混凝土截面的设计(钢筋计算器)。所提供的横截面可以是简…

Python之argparse模块的使用

我们在写一个成熟的Python项目时候,需要传入若干指定的参数。而不是写死在程序里,这个时候就要用到argparse模块。argparse 是 Python 内置的一个用于命令项选项与参数解析的模块,通过在程序中定义好我们需要的参数,argparse 将会…

【FPGA笔记系列3】assign语句和if-esle语句

结构化建模 前面几节中采用的方法称为结构化建模。 assign语法(数据流建模方式) assign语句仅能描述组合逻辑电路,没有涉及时钟、触发器等! 五人投票电路(由于CGD100板子原因,需修改逻辑使按下点亮,弹起熄灭) 因为板子当key按下时为低电平,弹起时为高电平;led高电平点…

MyBatis查询接收数据 批量删除

MyBatis查询接收数据 批量删除查询出的数据只有一条通过实体类对象接收通过List集合接收通过map集合接收查询出的数据有多条通过list集合接收通过map类型的list集合接收MapKey注解模糊查询批量删除${}和#{}的区别查询出的数据只有一条 通过实体类对象接收 mapper接口代码: 映射…