Opencv-Python学习笔记(七):边缘检测

本片文章记录学习边缘检测的运用。

  • 了解坎尼边缘检测的概念
  • 学习函数:cv2.Canny()。

Canny 边缘检测

  • 使用高斯滤波器,以平滑图像,滤除噪声。

       由于边缘检测很容易受到噪声影响,所以第一步是使用 5x5 的高斯滤波器去除噪声。

  • 计算图像中每个像素点的梯度强度和方向

       对平滑后的图像使用 Sobel 算子计算水平方向和竖直方向的一阶导数(图像梯度)( Gx 和 Gy)。根据得到的这两幅梯度图( Gx 和 Gy)找到边界的梯度和方向,公式如下:

                                                                 Edge Gradient (G)=\sqrt{G_{X}^{2}+G_{Y}^{2}}    

                                                                  Angle(\theta )=tan^{-1}(\frac{G_{X}}{G_{Y}})

      梯度的方向一般总是与边界垂直。梯度方向被归为四类:垂直,水平,和两个对角线。

  • 应用非极大值抑制,以消除边缘检测带来的杂散影响。

       在获得梯度的方向和大小之后,应该对整幅图像做一个扫描,去除那些非边界上的点。对每一个像素进行检查,看这个点的梯度是不是周围具有相同梯度方向的点中最大的。如下图所示:

    点A在边缘(垂直方向)上。渐变方向垂直于边缘。点B和C在梯度方向上。因此,将A点与B点和C点进行检查,看是否形成局部最大值。如果是这样,则考虑将其用于下一阶段,否则将其抑制(置为零)。即得到的是一个包含“窄边界”的二值图像。

  • 应用双阈值检测来确定真实和潜在的边缘。

      现在要确定那些边界才是真正的边界。这时我们需要设置两个阈值:minVal 和 maxVal。当图像的灰度梯度高于 maxVal 时被认为是真的边界,那些低于 minVal 的边界会被抛弃。如果介于两者之间的话,就要看这个点是否与某个被确定为真正的边界点相连,如果是就认为它也是边界点,如果不是就抛弃。如下图:

 

       A 高于阈值 maxVal 所以是真正的边界点, C 虽然低于 maxVal 但高于minVal 并且与 A 相连,所以也被认为是真正的边界点。而 B 就会被抛弃,因为他不仅低于 maxVal 而且不与真正的边界点相连。所以选择合适的 maxVal和 minVal 对于能否得到好的结果非常重要。在这一步一些小的噪声点也会被除去,因为我们假设边界都是一些长的线段。

  • 通过抑制孤立的弱边缘最终完成边缘检测。

​​​​​​​接下来我们看一下效果:

# -*- coding: utf-8 -*-
# @Time    : 2019/10/19 17:11
# @Author  : MMagicLoren
# @Email   : [email protected]
# @File    : 边缘检测.py
# @Software: PyCharm
import cv2 as cv
import numpy as np


def canny_demo(image):
    edges1 = cv.Canny(image, 80, 150)
    edges2 = cv.Canny(image, 30, 100)
    res = np.hstack((edges1, edges2))
    cv.imshow('res', res)


if __name__ == '__main__':
    src = cv.imread("F:/Pycharm/opencv_exercises-master/images/CrystalLiu1.jpg")  # 读入图片放进src中
    # cv.namedWindow("input image", cv.WINDOW_AUTOSIZE)  # 创建窗口, 窗口尺寸自动调整
    # cv.imshow("input image", src)
    canny_demo(src)
    cv.waitKey(0)  # 等有键输入或者1000ms后自动将窗口消除,0表示只用键输入结束窗口
    cv.destroyAllWindows()

可见,设置的阈值越小,检测出来的边缘越多。

我们知道了Canny边缘检测的原理之后,我们可以用之前学过的知识,自己写个demo看看效果。

# -*- coding: utf-8 -*-
# @Time    : 2019/10/19 17:11
# @Author  : MMagicLoren
# @Email   : [email protected]
# @File    : 边缘检测.py
# @Software: PyCharm
import cv2 as cv
import numpy as np


def edges_demo(image):
    blurred = cv.GaussianBlur(image, (3, 3), 0)
    gray = cv.cvtColor(blurred, cv.COLOR_BGR2GRAY)
    xgray = cv.Sobel(gray, cv.CV_16SC1, 1, 0)
    ygray = cv.Sobel(gray, cv.CV_16SC1, 0, 1)

    edges_output = cv.Canny(xgray, ygray, 50, 150)  # edges_output = cv.Canny(gray, 50, 150)
    cv.imshow("Canny Edges", edges_output)
    dst = cv.bitwise_and(image, image, mask=edges_output)  # 彩色边缘输出
    cv.imshow("Color Edges", dst)


if __name__ == '__main__':
    src = cv.imread("F:/Pycharm/opencv_exercises-master/images/CrystalLiu1.jpg")  # 读入图片放进src中
    # cv.namedWindow("input image", cv.WINDOW_AUTOSIZE)  # 创建窗口, 窗口尺寸自动调整
    # cv.imshow("input image", src)
    canny_demo(src)
    # edges_demo(src)
    cv.waitKey(0)  # 等有键输入或者1000ms后自动将窗口消除,0表示只用键输入结束窗口
    cv.destroyAllWindows()

完整工程代码:

# -*- coding: utf-8 -*-
# @Time    : 2019/10/19 17:11
# @Author  : MMagicLoren
# @Email   : [email protected]
# @File    : 边缘检测.py
# @Software: PyCharm
import cv2 as cv
import numpy as np


def canny_demo(image):
    edges1 = cv.Canny(image, 80, 150)
    edges2 = cv.Canny(image, 30, 100)
    res = np.hstack((edges1, edges2))
    cv.imshow('res', res)


def edges_demo(image):
    blurred = cv.GaussianBlur(image, (3, 3), 0)
    gray = cv.cvtColor(blurred, cv.COLOR_BGR2GRAY)
    xgray = cv.Sobel(gray, cv.CV_16SC1, 1, 0)
    ygray = cv.Sobel(gray, cv.CV_16SC1, 0, 1)

    edges_output = cv.Canny(xgray, ygray, 50, 150)  # edges_output = cv.Canny(gray, 50, 150)
    cv.imshow("Canny Edges", edges_output)
    dst = cv.bitwise_and(image, image, mask=edges_output)  # 彩色边缘输出
    cv.imshow("Color Edges", dst)


if __name__ == '__main__':
    src = cv.imread("F:/Pycharm/opencv_exercises-master/images/CrystalLiu1.jpg")  # 读入图片放进src中
    # cv.namedWindow("input image", cv.WINDOW_AUTOSIZE)  # 创建窗口, 窗口尺寸自动调整
    # cv.imshow("input image", src)
    # canny_demo(src)
    edges_demo(src)
    cv.waitKey(0)  # 等有键输入或者1000ms后自动将窗口消除,0表示只用键输入结束窗口
    cv.destroyAllWindows()
发布了29 篇原创文章 · 获赞 83 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/l59565455/article/details/102639105
今日推荐