欢迎来真孝善网,为您提供真孝善正能量书籍故事!

Python边缘检测方法效果对比与分析

时间:11-22 名人轶事 提交错误

其实Python边缘检测方法效果对比与分析的问题并不复杂,但是又很多的朋友都不太了解,因此呢,今天小编就来为大家分享Python边缘检测方法效果对比与分析的一些知识,希望可以帮助到大家,下面我们一起来看看这个问题的分析吧!

图a 图b 图c 在实际的图像分割中,经常只使用一阶和二阶导数。虽然原则上可以使用高阶导数,但由于噪声的影响,在纯二阶导数运算中会出现对噪声的敏感性,三阶以上的导数信息往往失去了应用价值。二阶导数还可以描述灰度突变的类型。在某些情况下,例如灰度变化均匀的图像,仅使用一阶导数可能无法找到边界。在这种情况下,二阶导数可以提供非常有用的信息。二阶导数对噪声也很敏感。解决的办法是对图像进行平滑处理并消除部分噪声,然后进行边缘检测。但利用二阶导数信息的算法基于过零检测,因此获得的边缘点数量相对较少,有利于后续处理和识别工作。

在OpenCV中,边缘检测方法有以下几种:Sobel、Scharr、Laplace和Canny,其中前三种方法是有方向性的。

测试方法是将一张200*200的图片二值化,然后用这些方法进行边缘检测;生成1000*1000的矩阵,绘制网格大小为5*5的网格线。每个网格图上测试图像对应的像素颜色,更容易观察集中边缘检测的效果。

下图是未进行边缘检测的原图生成的对比图。由于该图像是专门生成的图像,没有噪声,因此不需要进行降噪操作。

原图

之所以原始图像映射需要是映射,是因为这里的标准是尽可能得到单像素边缘。如果直接放大图像,得到的图像不够清晰,但贴图后可以清晰显示边缘宽度。

1.Sobel算子

主要用于边缘检测。从技术上讲,它是一个离散差分算子,用于计算图像亮度函数梯度的近似值。 Sobel算子是典型的基于一阶导数的边缘检测算子。由于该算子引入了类似于局部平均的运算,因此对噪声具有平滑作用,可以很好地消除噪声的影响。

Sobel 算子由两组3x3 矩阵组成,即水平模板和垂直模板。通过与图像进行平面卷积,可以分别得到水平和垂直亮度差近似值。实际使用中,常用以下两种模板来检测图像边缘。

图像每个像素的近似水平和垂直梯度可以结合以下公式来计算梯度的大小。

然后可以使用以下公式计算梯度方向。

如果等于0,则表示图像那里有垂直边缘,并且左侧比右侧暗。

在opencv中,Sobel的函数原型为:

dst=cv2.Sobel(src, d深度, dx, dy[, dst[, ksize[, 尺度[, delta[, borderType]]]]])

前四个是必需参数:

src为需要处理的图像;

ddepth是图像的深度,-1表示使用与原始图像相同的深度。目标图像的深度必须大于或等于原始图像的深度;

dx和dy表示求导的顺序,0表示该方向不求导,一般为0、1、2。

接下来是可选参数:

ksize 是Sobel 算子的大小,必须为1、3、5 或7。默认为3。当kSize=1 时,使用的模板为1*3 或3*1,而不是通常的格式;

scale为缩放导数的比例常数,默认没有缩放系数;

delta 是一个可选增量,将添加到最终dst 中。同样,默认情况下不会向dst 添加任何附加值;

borderType是确定图像边界的模式。该参数的默认值为cv2.BORDER_DEFAULT。

部分代码如下:

resImg_x=cv2.Sobel(thresh_1, cv2.CV_64F, 1, 0)

resImg_y=cv2.Sobel(thresh_1, cv2.CV_64F, 0, 1)

resImg_x=cv2.convertScaleAbs(resImg_x)

resImg_y=cv2.convertScaleAbs(resImg_y)

resImg=cv2.addWeighted(src1=resImg_x, alpha=0.5, src2=resImg_y, beta=0.5, gamma=0)

cv2.CV_64F 用于Sobel 函数的第二个参数。因为OpenCV文档中对Sobel算子的介绍中有这样一句话:“在8位输入图像的情况下,它将导致截断导数”。即Sobel函数计算导数后会有负值,并且会有大于255的值。原始图像是uint8,是8位无符号数,所以Sobel创建的图像确实没有足够的数字,将被截断。因此,使用较大的数据类型,例如cv2.CV_16S、cv2.CV_32F或cv2.CV_64F,然后在计算完成后使用convertScaleAbs()函数将其转换回原始的uint8形式。

由于Sobel算子是在两个方向上计算的,所以最终需要使用cv2.addWeighted函数将其组合起来。

边缘检测结果如下:

当ksize设置为1时:

没有获得单像素边缘。当ksize为1时,边缘较窄,但可能会出现孔洞。

2.Scharr算子

Scharr 运算符与Sobel 基本相似。它的出现是由于当ksize为3时,Sobel可能会出现明显的误差,是提出的一个解决方案。与索贝尔具有相同的速度,但结果更准确。他的模板有两个方向:

在opencv中,Sobel的函数原型为

dst=cv2.Scharr(src, d深度, dx, dy[, dst[, 尺度[, 增量[, borderType]]]])

其参数含义与Sobel基本相同,只是ksize固定为3。

部分代码如下:

resImg_x=cv2.Scharr(thresh_1, cv2.CV_16S, 1, 0)

resImg_y=cv2.Scharr(thresh_1, cv2.CV_16S, 0, 1)

resImg_x=cv2.convertScaleAbs(resImg_x)

resImg_y=cv2.convertScaleAbs(resImg_y)

resImg=cv2.addWeighted(src1=resImg_x, alpha=0.5, src2=resImg_y, beta=0.5, gamma=0)

边缘检测结果如下:

就场景而言,沙尔和索贝尔并没有太大区别。

3.Laplacian算子

拉普拉斯算子是n维欧几里德空间中的二阶微分算子,常用于图像增强和边缘提取领域。它通过灰度差计算邻域内的像素。基本过程是:确定图像中心像素的灰度值及其周围其他像素的灰度值。如果中心像素点的灰度值较高,则增大中心像素点的灰度值;否则降低中心像素的灰度,实现图像锐化。在算法实现过程中,拉普拉斯算子计算邻域内中心像素的四个或八个方向的梯度,然后将梯度相加,以确定中心像素的灰度与邻域内其他像素的灰度之间的关系。邻居。最后,通过梯度运算的结果是对像素灰度的调整。

该算子是各向同性算子,二阶微分算子,具有旋转不变性。当只关心边缘位置而不考虑周围像素的灰度差异时比较适合。同时,它只适用于无噪声图像,因为它对孤立像素的响应比对边缘或线条的响应更强。当存在噪声时,需要先进行相关的降噪操作。

二维图像函数的拉普拉斯变换是各向同性二阶导数,定义为:

其离散形式为:

拉普拉斯算子也可以用模板的形式来表达,如下图所示。从模板形式不难看出,如果图像较暗的区域出现了亮点,那么拉普拉斯运算就会让该亮点变得更亮。由于图像中的边缘是灰度级跳跃的区域,因此拉普拉斯锐化模板在边缘检测中非常有用。对于陡峭边缘和缓慢变化的边缘,一般的增强技术很难确定边缘线的位置。然而,该算子可以通过二次微分的正峰值和负峰值之间的零交叉点来确定。它对孤立点或端点比较敏感,因此特别适合突出图像中的孤立点、孤立线或线端点的目的。场合。当然,这种增强也作用于图像中的噪声。

拉普拉斯算子分为四邻域和八邻域。四邻域是求邻域中心像素四个方向的梯度,八邻域是求八个方向的梯度。四邻域模板如下式所示:

八邻域模板如下:

通过模板可以发现,当邻域像素灰度相同时,模板的卷积运算结果为0;当中心像素的灰度高于邻域其他像素的平均灰度时,模板的卷积运算结果为正数。当中心像素的灰度低于邻域其他像素的平均灰度时,模板的卷积为负。通过对卷积运算的结果进行适当的衰减因子处理,并将其与原始中心像素相加,可以对图像进行锐化。

还有两个扩展模板:

opencv中的拉普拉斯函数原型为

dst=cv2.Laplacian(src, d深度[, dst[, ksize[, 尺度[, delta[, borderType]]]]])

在:

src表示输入图像;

d深度表示目标图像所需的深度。

接下来是可选参数:

ksize表示滤波器的孔径大小,其值必须为正数且为奇数,默认值为1;

scale 表示用于计算拉普拉斯算子值的可选缩放因子。默认值为1;

delta 表示添加到结果中的可选增量值,默认值为0;

borderType代表边框模式。

拉普拉斯算子实际上是利用Sobel算子的运算来获得图像在x和y方向上的导数,最终得到结果。

上面的算子本质上都是卷积。因此,如果使用filter2D函数直接对图像和算子进行2D卷积,其结果与直接使用算子没有什么区别。这里有几个可以尝试的模板。看一下,部分代码如下:

resImg=cv2.拉普拉斯(thresh_1, -1)

# 内核=np.matrix("0 1 0; 1 -4 1; 0 1 0")

# 内核=np.matrix("1 1 1; 1 -8 1; 1 1 1")

# kernel=np.matrix("0 -1 0; -1 4 -1; 0 -1 0")

# kernel=np.matrix("-1 -1 -1; -1 8 -1; -1 -1 -1")

# resImg=cv2.filter2D(thresh_1, -1,内核)

第一行直接调用拉普拉斯函数,所以结果应该和直接使用模板卷积一致。边缘检测结果如下:

拉普拉斯函数执行结果

使用4邻域模板卷积的结果与直接使用拉普拉斯函数的结果相同。

使用8 邻域模板卷积的结果

使用4 邻域扩展模板卷积的结果

使用8邻域扩展模板卷积的结果非常接近单像素边缘的目标。

4.Canny函算子

Canny边缘检测是一种比较新的边缘检测算子,边缘检测性能很好,但实现起来比较麻烦。 Canny算子是一个具有过滤、增强和检测功能的多阶段优化算子。在处理之前,Canny算子首先使用高斯平滑滤波器对图像进行平滑以去除噪声。 Canny分割算法利用一阶偏导数的有限差分来计算梯度幅值和方向。在处理的过程中,Canny算子也会经历一个非极大值抑制的过程,最后Canny算子也是使用两个阈值来连接边。

Canny边缘检测算法包含以下四个步骤:

Step1.用高斯滤波器平滑图象:

高斯平滑函数:

设g为平滑后的图像,使用h对图像f进行平滑可表示为:

其中*代表卷积

Step2.使用一阶有限差分计算偏导数阵列P和Q:

平滑图像g的梯度可以使用22一阶有限差分近似来计算x和y偏导数的两个数组

M反映图像的边缘强度,反映边缘的方向。使M获得局部最大值的方向角反映了边缘的方向。

Step3.非极大值抑制:

将梯度角离散为圆的四个扇区之一,以便使用33 窗口进行抑制操作。这四个扇区编号为0 到3,对应于33 邻域的四种可能组合。

在每个点,邻域的中心像素M 与沿梯度线的两个像素进行比较。如果M的梯度值不大于沿梯度线相邻两个像素的梯度值,则令M=0

Step4.用双阈值算法检测和连接边缘

对非极大值抑制图像应用两个阈值th1和th2,两者的关系为th1=0.4th2。我们将梯度值小于th1的像素点的灰度值设置为0,得到图像1。然后将梯度值小于th2的像素点的灰度值设置为0,得到图像2。由于图像2的阈值越高,大部分噪声被去除,但有用的边缘信息也丢失了。图1的阈值较低,保留了更多的信息。我们可以以图像2为基础,以图像1为补充,连接图像的边缘。

连接边的具体步骤如下:

1.扫描图像2.当遇到非零灰度像素p(x,y)时,从p(x,y)开始跟踪轮廓线,直到轮廓线终点q(x,y)。

2. 检查图像1 中点s(x,y) 对应于图像2 中点q(x,y) 位置的8 个相邻区域。如果存在非零像素s(x,y), s(x,y)点的8个邻近区域,它作为r(x,y)点包含在图像2中。从r(x,y) 开始,重复第一步,直到我们无法在图像1 和图像2 中继续。

3. 完成包含p(x,y)的等高线的连接后,将该等高线标记为已访问。返回第一步并寻找下一条轮廓线。重复第一步、第二步和第三步,直到在图像2 中找不到新的轮廓线。

至此,Canny算子的边缘检测就完成了。

在opencv中,Canny的函数原型为:

def cv2.Canny(图像, 阈值1, 阈值2[, 边缘[, 孔径大小[, L2梯度]]])

在:

image 表示输入图像;

Threshold1和threshold2代表两个阈值;

接下来是可选参数:

apertureSize表示算子核大小,只能取3到7之间的值,使用Sobel算子;

L2gradient表示是否使用更精确的L2范数进行计算,bool类型。

部分调用代码如下:

resImg=cv2.Canny(thresh_1、150、300、apertureSize=3、L2gradient=True)

边缘检测结果如下:

可以看出,本次测试的结果并不是完全封闭的。

对比以上四种算子的结果,从单个像素边缘来看,Canny算子虽然是单个像素,但其边缘并不封闭,而Laplacian算子效果更好,但对噪声非常敏感,因此它与Noise方法是相对依赖的。

如果只需要获取单个像素边缘的角度,可以通过opencv提供的findContours函数获取轮廓,然后绘制边缘。 findContours的原理比较复杂,就不赘述了,贴个结果图。

完整代码如下:

导入操作系统

导入CV2

将numpy 导入为np

img=cv2.imdecode(np.fromfile("./test.jpg", dtype=np.uint8), cv2.IMREAD_COLOR)

img_grey=cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

_, thresh_0=cv2.threshold(img_grey, 0, 255, cv2.THRESH_OTSU)#二值化自动取阈值

thresh_1=cv2.resize(thresh_0, (200, 200))

# resImg=cv2.bitwise_not(thresh_1)

resImg=cv2.Canny(thresh_1、150、300、apertureSize=3、L2gradient=True)

# resImg_x=cv2.Sobel(thresh_1, cv2.CV_64F, 1, 0, ksize=1)

# resImg_y=cv2.Sobel(thresh_1, cv2.CV_64F, 0, 1, ksize=1)

# resImg_x=cv2.convertScaleAbs(resImg_x)

# resImg_y=cv2.convertScaleAbs(resImg_y)

# resImg=cv2.addWeighted(src1=resImg_x, alpha=0.5, src2=resImg_y, beta=0.5, gamma=0)

# resImg_x=cv2.Scharr(thresh_1, cv2.CV_16S, 1, 0)

# resImg_y=cv2.Scharr(thresh_1, cv2.CV_16S, 0, 1)

# resImg_x=cv2.convertScaleAbs(resImg_x)

# resImg_y=cv2.convertScaleAbs(resImg_y)

# resImg=cv2.addWeighted(src1=resImg_x, alpha=0.5, src2=resImg_y, beta=0.5, gamma=0)

# resImg=cv2.Laplacian(thresh_1, -1)

# 内核=np.matrix("0 1 0; 1 -4 1; 0 1 0")

# 内核=np.matrix("1 1 1; 1 -8 1; 1 1 1")

# kernel=np.matrix("0 -1 0; -1 4 -1; 0 -1 0")

# kernel=np.matrix("-1 -1 -1; -1 8 -1; -1 -1 -1")

# resImg=cv2.filter2D(thresh_1, -1,内核)

#thresh_1=cv2.bitwise_not(thresh_1)

# _, 轮廓, _=cv2.findContours(thresh_1, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

# resImg=np.full((200, 200), 0, dtype=np.uint8)

# cv2.drawContours(resImg, 轮廓, -1, (255, 255, 255), 1)

newImg=np.full((1000, 1000, 3), 0, dtype=np.uint8)

对于我在范围内(len(newImg)):

对于范围内的j(len(newImg[i])):

如果resImg[i//5, j//5] 0:

newImg[i,j]=(255, 255, 255)

对于我在范围内(len(newImg)):

对于范围内的j(len(newImg[i])):

如果i % 5==0 或j % 5==0:

newImg[i,j]=(0, 255, 0)

cv2.imencode(".jpg", newImg)[1].tofile("./Laplacian_d.jpg")

# cv2.imshow("res", newImg)

#cv2.waitKey()

参考:

OK,关于Python边缘检测方法效果对比与分析和的内容到此结束了,希望对大家有所帮助。

用户评论

今非昔比'

看了题目感觉很有意思,想学习一下Python如何实现边缘检测。

    有7位网友表示赞同!

尘埃落定

不同方法的效果确实有所差别,想知道最优选用的方法是哪种

    有8位网友表示赞同!

岁岁年年

我以前用OpenCV做过边缘检测,可以用Python的哪些其他库?

    有9位网友表示赞同!

毒舌妖后

希望能看到具体的代码实例,方便对比和理解。

    有18位网友表示赞同!

面瘫脸

边缘检测应用场景很多,比如图像处理、机器视觉等等。

    有7位网友表示赞同!

素衣青丝

这篇对比分析应该能够帮助我选择适合我的边缘检测算法。

    有7位网友表示赞同!

┲﹊怅惘。

Python 确实很适合科学计算和图像处理,越来越常用啦!

    有13位网友表示赞同!

鹿先森,教魔方

期待看到分析报告中对不同方法的优缺点比较清晰的描述。

    有16位网友表示赞同!

有阳光还感觉冷

学习一下边缘检测的原理有助于更好地理解算法的工作机制。

    有6位网友表示赞同!

搞搞嗎妹妹

这篇文章正好可以帮我在机器视觉项目里选合适的算法。

    有10位网友表示赞同!

嗯咯

希望比较中能给出实验结果图像对比,直观感受不同方法的效果差异。

    有6位网友表示赞同!

如你所愿

现在很多CV库都有现成的边缘检测函数,这个分析比较有深度的哦!

    有17位网友表示赞同!

熏染

平时学习的时候可以参考一下这篇对比分析,对算法的选择更有帮助了。

    有15位网友表示赞同!

青山暮雪

如果能讨论一下不同方法的运算速度和资源占用,就更好了!

    有7位网友表示赞同!

殃樾晨

感觉Python的边缘检测方法越来越先进,希望未来能实现更加高效的效果!

    有7位网友表示赞同!

念旧情i

期待看到作者对未来边缘检测研究趋势的展望。

    有9位网友表示赞同!

怀念·最初

这个题目很有价值,希望能帮助更多人学习和应用Python中的边缘检测算法

    有8位网友表示赞同!

挽手余生ら

图像处理是机器学习一个重要的分支,了解这些算法很必要!

    有5位网友表示赞同!

烟雨萌萌

分享这篇文章可以方便大家互相学习和交流经验。

    有10位网友表示赞同!

【Python边缘检测方法效果对比与分析】相关文章:

1.蛤蟆讨媳妇【哈尼族民间故事】

2.米颠拜石

3.王羲之临池学书

4.清代敢于创新的“浓墨宰相”——刘墉

5.“巧取豪夺”的由来--米芾逸事

6.荒唐洁癖 惜砚如身(米芾逸事)

7.拜石为兄--米芾逸事

8.郑板桥轶事十则

9.王献之被公主抢亲后的悲惨人生

10.史上真实张三丰:在棺材中竟神奇复活