广州正规网站建设企业,网站设计西安网站建设,骨科医生咨询在线咨询免费,网站维护项目文章目录Canny算子非极大值抑制非极大值抑制中的插值滞后阈值实际应用直接使用Canny算子使用膨胀先阈值分割Canny算子
上一篇说到#xff0c;我在一个小项目里需要在一幅图像中提取一根试管里的两种液体的截面。为了达到这个目的使用传统图像里的区域分割技术#xff0c;实际…
文章目录Canny算子非极大值抑制非极大值抑制中的插值滞后阈值实际应用直接使用Canny算子使用膨胀先阈值分割Canny算子
上一篇说到我在一个小项目里需要在一幅图像中提取一根试管里的两种液体的截面。为了达到这个目的使用传统图像里的区域分割技术实际上就是想把这个图像分成两类然后再找到这个两个类的边界。 上一张最后提到我是使用一种拟合的方法来做的边界的判断后来突然想到opencv里面提供了现成的方法边缘检测的Canny算子直接就可以提取图像的边界。
Canny算子在官网上有介绍
调用方式edges cv2.Canny(image, threshold1, threshold2, apertureSize, L2gradient)参数一输入图像为二值图像。参数二阈值1和阈值2一样是用于控制边缘检测的度的后面在详细过程说明中来描述。参数三阈值2参数四apertureSizesobel算子的卷积大小。sobel算子用于计算图像的梯度参考sobel算子的文章(https://blog.csdn.net/pcgamer/article/details/127942102?spm1001.2014.3001.5502)参数五L2gradient是否使用二级梯度计算。实际上就是使用Laplacian算子进行二阶梯度计算同样可以参考上面的那片文章。返回值就是一个只有边缘信息的二值图像。
Canny算子的一般介绍中主要提到了一下几个步骤
噪声去除计算梯度非极大值抑制滞后阈值
非极大值抑制
噪声去除这个步骤一般是采用高斯模糊在做的这个在滤波的那一篇中已经提到过这里就不多说了可以参考 https://blog.csdn.net/pcgamer/article/details/124989015?spm1001.2014.3001.5502
计算梯度之前也提过了这里也不多说。 所以从非极大值抑制这里继续。
非极大值抑制用人话说就是只留下最大值。那么这里有几个问题
为什么留下最大值留下什么最大值就是这个最大值怎么定义怎么留下最大值
首先回答为什么的问题在图像处理中一般来说边缘就是像素值变化最大的地方这个很容易理解。 所以第二个问题也很简单这里的最大值就是上文提到的梯度最大。而且是在梯度方向上梯度最大。 在写清晰度的那片文章中提到了梯度方向的计算方式其实可以理解成当前边缘方向的法线放下那个有点绕吧用几个图来说明一下 首先梯度方向定义为arctan(gxgy)arctan(\frac{g_x}{g_y})arctan(gygx)也就是
放大到一整张图中
黑线指向的方向就是梯度的方向。
那么上面说到非极大值抑制就是要确定某一个点在这条梯度方向是是附近(局部)的最大值。一般来说这个附近就是旁边的一个像素值。
非极大值抑制中的插值
虽然就是比较这个梯度方向上的三个像素点但是这不是能直接比较的。 首先上面的梯度方向不是固定的只有当这个梯度方向恰好是45度的整数倍时才恰好对应到一个像素点 可以从图中看到如果是梯度方向是45225的时候中心像素点就是和对角线上的两个像素点比较(右上和左下)如果是梯度方向是135315的时候中心像素点就是和对角线上的两个像素点比较(左上和右下)如果是0180就是和左右两个像素点做比较如果是90和270度就是和上下两个像素点做比较。上面的几种情况都是比较巧合的情况但是如果不是这些角度呢 如果不是这些角度就需要进行插值了见下图 比如上面的dTemp1这个点很明显不是一个实际存在的像素点这个像素点可以被称作一个亚像素(sub pixel)这个点的像素值可以根据旁边的两个实际像素点来插值计算在canny算子中插值方法就是普通的线性插值 这个亚像素的dTemp1的像素值就是 dTemp1p1LdTemp1−p1p2−p1dTemp1 p1 \frac{L_{dTemp1} - p1}{p2-p1}dTemp1p1p2−p1LdTemp1−p1 上面的LdTemp1L_{dTemp1}LdTemp1可以根据梯度方向的gxgy\frac{g_x}{g_y}gygx来计算出来 同样下面的dTemp2也可以通过插值方法计算出来。 插值计算完成后就可以完成非极大值抑制的比较计算了。如果中心像素是最大的那么就保留不是极大值则赋值为0。 以上面的逻辑完成整张图像的计算就完成了非极大值抑制这个过程的计算。
滞后阈值
完成非极大值抑制后边缘已经精细了很多了但是还不保证所有留下的像素点都是边缘所以最后一步是通过阈值来控制这些留下的梯度值(从非极大值抑制出来的数据已经不是像素值而是梯度值矩阵)单独通过一个阈值来控制过于简单粗暴所以Canny算子用了两个。。。。
如果高于T1也就是两个阈值中较高的阈值则保留。如果低于T2也就是两个阈值中较低的阈值则丢弃。中间的怎么办呢从所有的高于T1的梯度值出发如果能连接的上的中间值则保留否则就丢弃。一般来说T1 2T2
完成上面的滞后阈值计算后就完成了Canny算子的边缘检测当然最后是输出像素值的二值图像
实际应用
直接使用Canny算子
不管三七二十一我把之前的图转换成灰度图后直接调用Canny算子 img cv2.imread(./images/tubeImg.jpeg)grey cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)edge cv2.Canny(grey, 120, 54)cv2.imshow(origin, grey)cv2.imshow(result, edge)效果非常的糟糕
使用膨胀
因为Canny算子中的高斯模糊针对的是微小的高斯噪声这个图像中的噪声都是大块的噪声所以我就想着用膨胀函数试一下
img cv2.imread(./images/tubeImg.jpeg)grey cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)# 做一把膨胀kernel_e cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))bin_clo cv2.dilate(grey, kernel_e, iterations10)edge cv2.Canny(bin_clo, 120, 54)cv2.imshow(origin, grey)cv2.imshow(result, edge)效果好了一点
先阈值分割
但是还是去的不干净而且最上面的分界线没有弄完成突然想到上一次用大津法的阈值分割基本已经完成了前景和背景的分割再加上一次阈值分割用膨胀再清除一把最后再做边缘检测
img cv2.imread(./images/tubeImg.jpeg)grey cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)# 如果大于阈值赋值为255小于阈值赋值为0# ret, binary cv2.threshold(grey, 60, 255, cv2.THRESH_BINARY cv2.THRESH_OTSU)## 做一把腐蚀kernel_e cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))bin_clo cv2.dilate(grey, kernel_e, iterations10)edge cv2.Canny(bin_clo, 120, 54)cv2.imshow(origin, grey)cv2.imshow(result, edge)效果妥妥的
大功告成