Canny 边缘检测算法
1986年,JOHN CANNY 提出一个很好的边缘检测算法,被称为Canny编边缘检测器[1]。
Canny边缘检测根据对信噪比与定位乘积进行测度,得到最优化逼近算子,也就是Canny算子。类似与 LoG 边缘检测方法,也属于先平滑后求导数的方法。
使用Canny边缘检测器,图象边缘检测必须满足两个条件:
- 能有效地抑制噪声;
- 必须尽量精确确定边缘的位置。
算法大致流程:
1、求图像与高斯平滑滤波器卷积:
2、使用一阶有限差分计算偏导数的两个阵列P与Q:
3、幅值和方位角:
4、非极大值抑制(NMS ) :细化幅值图像中的屋脊带,即只保留幅值局部变化最大的点。
将梯度角的变化范围减小到圆周的四个扇区之一,方向角和幅值分别为:
非极大值抑制通过抑制梯度线上所有非屋脊峰值的幅值来细化M[i,j],中的梯度幅值屋脊.这一算法首先将梯度角θ[i,j]的变化范围减小到圆周的四个扇区之一,如下图所示:
5、取阈值
-
将低于阈值的所有值赋零,得到图像的边缘阵列
- 阈值τ取得太低->假边缘
- 阈值τ取得太高->部分轮廊丢失
- 选用两个阈值: 更有效的阈值方案.
相关代码
Canny算法实现:
- 用高斯滤波器平滑图像(在调用Canny之前自己用blur平滑)
- 用一阶偏导的有限差分来计算梯度的幅值和方向.
- 对梯度幅值应用非极大值抑制 .
- 用双阈值算法检测和连接边缘.
void cv::Canny( InputArray _src, OutputArray _dst,
double low_thresh, double high_thresh,
int aperture_size, bool L2gradient )
{
Mat src = _src.getMat();
CV_Assert( src.depth() == CV_8U );
_dst.create(src.size(), CV_8U);
Mat dst = _dst.getMat();
if (!L2gradient && (aperture_size & CV_CANNY_L2_GRADIENT) == CV_CANNY_L2_GRADIENT)
{
//backward compatibility
aperture_size &= ~CV_CANNY_L2_GRADIENT;
L2gradient = true;
}
if ((aperture_size & 1) == 0 || (aperture_size != -1 && (aperture_size < 3 || aperture_size > 7)))
CV_Error(CV_StsBadFlag, "");
#ifdef HAVE_TEGRA_OPTIMIZATION
if (tegra::canny(src, dst, low_thresh, high_thresh, aperture_size, L2gradient))
return;
#endif
const int