Canny : 边缘检测是由 J . C a n n y J.Canny J.Canny 在1986年 对简单的 L a p l a c e Laplace Laplace 滤波器改进而成的边缘检测算法。在 C a n n y Canny Canny 算法中,先在 X 和 Y 方向求得一阶导数,然后将它们组合成4个方向 的导数。其中方向导数是局部最大值的点是组成边缘的候选项。 C a n n y Canny Canny算法最明显的创新,就是将单个的边缘候选像素加入轮廓。
Canny边缘检测满足三个主要标准:
Canny算法步骤:
非最大信号抑制公式原理:
梯度计算 Sobel 卷积核:
X方向:
G x = [ + 1 0 + 1 − 2 0 + 2 − 1 0 + 1 ] G_x=\left[ \begin{matrix} +1 & 0 & +1 \\ -2 & 0 & +2 \\ -1 & 0 & +1 \end{matrix} \right] Gx=⎣⎡+1−2−1000+1+2+1⎦⎤
Y方向:
G y = [ − 1 − 2 − 1 0 0 0 + 1 + 2 + 1 ] G_y=\left[ \begin{matrix} -1 & -2 & -1 \\ 0 & 0 & 0 \\ +1 & +2 & +1 \end{matrix} \right] Gy=⎣⎡−10+1−20+2−10+1⎦⎤
找到梯度强度和方向:
像 素 值 : G = G x 2 + G y 2 像素值: G=\sqrt{G_x^2+G_y^2} 像素值:G=Gx2+Gy2
梯 度 方 向 : θ = a r c t a n ( G y G x ) ( a r c t a n 为 反 正 切 函 数 ) 梯度方向:\theta=arctan( \dfrac{G_y}{G_x}) (\tt arctan为反正切函数) 梯度方向:θ=arctan(GxGy)(arctan为反正切函数)
θ \theta θ:在 0 ~ 180 之间,表示梯度方向 ,值表现了梯度在那个方向的变化率最大。
有梯度得到变化趋势方向,在与变化趋势垂直的方向寻找相邻的左右两个像素,与中间值作比较。
计算出来的像素值 G G G 在其阈值范围中,寻找值对应的梯度 上下两个像素值,如果上下像素值小于当前像素值,则上下像素值将被舍弃(不是最大信号),当前的像素值将被保留。如果当前的像素值比上下像素值小,则舍弃当前像素值(不是最大信号),保留上下像素值。
高低阈值链接
Cv2.Canny():
参数 | 描述 |
---|---|
InputArray src | 8 bit 输入图像 |
OutputArray edges | 输出边缘图像,一般是二值图像,背景是黑色 |
double threshold1 | 低阈值 |
double threshold2 | 高阈值 在程序中设置为下限阈值的三倍或二倍(遵循Canny的建议) |
int apertureSize = 3 | Soble算子的 size 一般 3X3 大小 取3 |
bool L2gradient = false | 选择 true 用 L2 归一化 ,否则 L1归一化 默认 fase |
L1 与 L2 计算公式:
L 2 : n o r m = ( d I / d x ) 2 + ( d I / d y ) 2 L2 : norm=\sqrt{(dI/dx)^2+(dI/dy)^2} L2:norm=(dI/dx)2+(dI/dy)2
L 1 : n o r m = ∣ d I / d x ∣ + ∣ d I / d y ∣ L1 : norm=|dI/dx|+|dI/dy| L1:norm=∣dI/dx∣+∣dI/dy∣
#region Canny算子 边缘提取
static Mat src = new Mat();
static Mat dst = new Mat();
static Mat gray = new Mat();
static Mat output = new Mat();
static int minVal = 50;
static int maxVal = 255;
const string outputName = "Canny Result";
private static void Canny(string path)
{
src = new Mat(path, ImreadModes.AnyColor | ImreadModes.AnyDepth);
dst = new Mat(src.Size(), src.Type());
new Window("SRC", WindowMode.AutoSize, src);
src.CopyTo(dst);
Cv2.CvtColor(dst, gray, ColorConversionCodes.RGB2GRAY); //转为灰度图(必须转)
new Window(outputName, WindowMode.AutoSize);
Cv2.Blur(gray, gray, new Size(3, 3), new Point(-1, -1), BorderTypes.Default);//模糊处理(降低噪点)
CvTrackbarCallback2 CannyDome = new CvTrackbarCallback2(CallBarck_CannyDome);
CvTrackbar cvt = new CvTrackbar("Bar :", outputName, minVal, maxVal, CallBarck_CannyDome, gray);
//Cv2.Canny(gray, dst, 100, 255, 3,true);
//using (new Window("DST", WindowMode.AutoSize, dst))
//using (new Window("SRC", WindowMode.AutoSize, src))
//{
Cv2.WaitKey(0);
}
///
/// 委托函数
///
/// 变化的量
/// 传入的数据对象
private static void CallBarck_CannyDome(int pos, object userdata)
{
Mat m = (Mat)userdata;
Cv2.Canny(m, output, pos, pos * 2, 3, true);
dst = new Mat(src.Size(), src.Type());
/*
* 拷贝数据
* 参数 Mat m :拷贝到
* Mat mask : 要拷贝的对象 (mask 的像素是非0的才拷贝,是0不拷贝)
*
*
*/
src.CopyTo(dst, output); //拷贝:
Cv2.ImShow(outputName, dst);
}
#endregion