opencv边缘检测

人眼怎么识别图像边缘?

比如有一幅图,图里面有一条线,左边很亮,右边很暗,那人眼就很容易识别这条线作为边缘.也就是像素的灰度值快速变化的地方.

sobel算子

opencv边缘检测
对于f(t),其导数f'(t)反映了每一处的变化趋势.在变化最快的位置其导数最大. sobel算子的思路就是模拟求一阶导数.

sobel算子是一个离散差分算子.它模拟图像像素点亮度值的梯度.
图像是二维的,即沿着宽度/高度两个方向.
我们使用两个卷积核对原图像进行处理:

  • 水平方向
    opencv边缘检测
    很好理解,原始像素灰度值-->(右边像素值-左边像素值),反映了水平方向的变化情况.

  • 垂直方向
    opencv边缘检测

这样的话,我们就得到了两个新的矩阵,分别反映了每一点像素在水平方向上的亮度变化情况和在垂直方向上的亮度变换情况.

综合考虑这两个方向的变化,我们使用
opencv边缘检测
反映某个像素的梯度变化情况.
有时候为了简单起见,也直接用绝对值相加替代.
opencv边缘检测

opencv里可以使用了如下的卷积核,可以"放大像素的变化情况".
opencv边缘检测
可以参考这个函数Scharr

opencv实现

import cv2 as cv
def test():
    src = cv.imread("/home/sc/disk/keepgoing/opencv_test/sidetest.jpeg")
    src = cv.GaussianBlur(src, (3, 3), 0)
    gray = cv.cvtColor(src, cv.COLOR_BGR2GRAY)
    grad_x = cv.Sobel(gray, -1, 1, 0, ksize=3)
    grad_y = cv.Sobel(gray, -1, 0, 1, ksize=3)
    
    grad  = cv.addWeighted(grad_x, 0.5, grad_y, 0.5, 0)
    
    cv.imshow("origin",src)
    cv.imshow("grad",grad)
    cv.waitKey()
    
test()

首先是高斯模糊去噪.某种意义上说高斯模糊是和sobel相反的过程.高斯模糊平滑了某点像素与周边像素的差异.那为什么还要先高斯去噪呢?

噪声就是像素的强度相对于真值有个突变。从时域上讲,通过高斯滤波能让一个像素的强度与周围的点相关,就减小了突变的影响;从频域上讲,突变引入了高频分量,而高斯滤波器可以滤除高频分量。

高斯去噪是为了防止把噪点也检测为边缘.

然后计算grad_x,grad_y.即对原图做水平方向/垂直方向的sobel卷积核卷积

    grad_x = cv.Sobel(gray, -1, 1, 0, ksize=3)
    grad_y = cv.Sobel(gray, -1, 0, 1, ksize=3)

sobel api
opencv边缘检测
注意区分c++版本和python版本api. 在上述代码中,第二个参数-1代表我们希望输出的图像矩阵和原图有同样的depth,第3/4个参数分别代表在x/y方向做一阶差分.取值0或1.
ksize必须为奇数.

tips:通常我们使用( xorder = 1, yorder = 0, ksize = 3) or ( xorder = 0, yorder = 1, ksize = 3)来计算水平/垂直方向的一阶差分矩阵.ksize=3用的是标准sobel卷积核.如果ksize传入FILTER_SCHARR,则使用的是如下卷积核:
opencv边缘检测

最后将两个矩阵叠加,综合考虑水平和垂直方向的像素灰度值变化强度.得到边缘.

 grad  = cv.addWeighted(grad_x, 0.5, grad_y, 0.5, 0)

完整代码处理效果如下
opencv边缘检测

ksize采用cv.FILTER_SCHARR效果如下:

    grad_x = cv.Sobel(gray, -1, 1, 0, ksize=cv.FILTER_SCHARR)
    grad_y = cv.Sobel(gray, -1, 0, 1, ksize=cv.FILTER_SCHARR)

opencv边缘检测

上一篇:sobel( ) 算子


下一篇:opencv python 如何将float 转换成 CV_8U