OpenCV Python Canny 边缘检测
【目标】
- Canny 边缘检测的概念
- cv2.Canny
【原理】
1. 去噪
由于边缘检测非常容易收到图像的噪声影响,第一步使用 5x5 高斯滤波去除图像中的噪声。
2. 寻找图像的亮度梯度
在平滑后(去噪后)的图像利用 Sobel 算子计算图像的 X-, Y- 的一阶导数 G ( x ) G(x) G(x)和 G ( y ) G(y) G(y),从这两幅图像中我们可以获得边缘的梯度值和方向。
E d g e G r a d i e n t ( G ) = G x 2 + G y 2 Edge \space Gradient(G) = \sqrt{G_x^2+ G_y^2} Edge Gradient(G)=Gx2+Gy2
A n g l e ( θ ) = t a n − 1 ( G x G y ) Angle(\theta) = tan^{-1}\left( \frac{G_x}{G_y} \right) Angle(θ)=tan−1(GyGx)
渐变方向始终垂直于边,被四舍五入到垂直、水平和两个对角四个角度里。
3. 非最大值抑制
获得梯度大小和方向后,对图像进行全扫描,去除可能不构成边缘的任何不需要的像素。在每个像素处,检查像素在梯度方向是否是其领域中的局部最大值。
点A位于垂直边缘上,梯度方向为 A->B, B 和 C 都是梯度方向上的点,如果 A 是邻域内最大的,则保留,否则设置为0。简而言之,会得到一个细的边缘。
4. 滞后阈值
这个阶段决定哪些是真正的边缘,哪些不是。为此,我们需要两个阈值,minVal和maxVal,梯度强度大于maxVal确定是边缘,低于minVal值的边缘点被抛弃,位于这两个值中间的值,根据其邻域点的属性来决定,如果连接到强边缘,则被判定为强边缘,否则丢弃。
上图中,可以看出,尽管C点在maxVal以下,但是与A连接,则C和A都是强边缘点。而B没有强边缘连接,则被丢弃。
【代码】
import numpy as np
import cv2
from matplotlib import pyplot as plt
img = cv2.imread('messi5.jpg',0)
edges = cv2.Canny(img,100,200)
cv2.imshow("src", img)
cv2.imshow("edge", edges)
cv2.waitKey(0)
cv2.destroyAllWindows()
- trackerbar 控制 Canny 边缘检测阈值
import numpy as np
import cv2
# 空函数
def nothing(x):
pass
img = cv2.imread('messi5.jpg', 0)
cv2.namedWindow('image')
cv2.createTrackbar('min', 'image', 10, 200, nothing)
cv2.createTrackbar('max', 'image', 0, 255, nothing)
cv2.setTrackbarPos('min', 'image', 50)
cv2.setTrackbarPos('max', 'image', 150)
while(1):
cv2.imshow('image', img)
if cv2.waitKey(2) & 0xFF == 27:
break
minVal = cv2.getTrackbarPos('min', 'image')
maxVal = cv2.getTrackbarPos('max', 'image')
if maxVal < minVal:
maxVal = minVal + 10
cv2.setTrackbarPos('max', 'image', maxVal)
edges = cv2.Canny(img, minVal, maxVal)
cv2.imshow('canny', edges)
cv2.destroyAllWindows()
【接口】
void cv::Canny ( InputArray image,
OutputArray edges,
double threshold1,
double threshold2,
int apertureSize = 3,
bool L2gradient = false
);
void cv::Canny ( InputArray dx,
InputArray dy,
OutputArray edges,
double threshold1,
double threshold2,
bool L2gradient = false
);
cv.Canny( image, threshold1, threshold2[, edges[, apertureSize[, L2gradient]]] ) -> edges
cv.Canny( dx, dy, threshold1, threshold2[, edges[, L2gradient]] ) -> edges
用Canny方法计算图像的边缘
- image: 8位输入图像
- edges: 输出的边缘图像, 单通道8位图像,尺寸与原图一致
- threshold1: 滞后过程的第一阈值
- threshold2: 滞后过程的第二阈值
- L2gradient: 一个决定是否需要更好精度的标志,L2gradient=true
- dx: 输入图像的16位x导数
- dy: 输入图像的16位y导数
【参考】
- John Canny. A computational approach to edge detection. Pattern Analysis and Machine Intelligence, IEEE Transactions on, (6):679–698, 1986.
- OpenCV官网文档