一 ,Canny算法介绍:

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边缘检测满足三个主要标准:

  1. 低错误率:意味着只检测存在的边缘。
  2. 良好的定位:必须最小化检测到的边缘像素和真实边缘像素之间的距离。
  3. 最小响应:每个边缘只有一个检测器响应。

Canny算法步骤:

  1. 高斯模糊 — GaussianBlur
  2. 灰度转化 —CvtColor
  3. 计算梯度 — Sobel / Scharr
  4. 非最大信号抑制:边缘信号很强,但只能有一个信号(边缘只能是一条线,而不能是一个有宽度的面),对其他的非边缘信号进行压制(在梯度方向上,如果不是最大信号就剔除)。
  5. 高低阈值输出二值图像。

非最大信号抑制公式原理:

梯度计算 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=+121000+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+120+210+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 之间,表示梯度方向 ,值表现了梯度在那个方向的变化率最大。

在这里插入图片描述
45 ,90 ,135 对应区域

  1. 黄色区域其值范围: 0 ~ 22.5 与 157.5 ~ 180
  2. 绿色区域取值范围:22.5 ~ 67.5
  3. 蓝色区域取值范围:67.5 ~ 112.5
  4. 棕色区域取值范围:112.5 ~ 157.5

有梯度得到变化趋势方向,在与变化趋势垂直的方向寻找相邻的左右两个像素,与中间值作比较。

计算出来的像素值 G G G 在其阈值范围中,寻找值对应的梯度 上下两个像素值,如果上下像素值小于当前像素值,则上下像素值将被舍弃(不是最大信号),当前的像素值将被保留。如果当前的像素值比上下像素值小,则舍弃当前像素值(不是最大信号),保留上下像素值。

高低阈值链接

  1. 假设 T1 是低阈值 T2是高阈值。凡是高于T2的都保留,凡是低于T1的都舍弃。如果在两个阈值之间 则仅当它连接到T2才接受,反之则舍弃。
  2. Canny建议 高阈值 T2 与 低阈值 T1 之比 为 : ( 2 :1 ) 或 ( 3 : 1 )

二 API:

Cv2.Canny():

参数描述
InputArray src8 bit 输入图像
OutputArray edges输出边缘图像,一般是二值图像,背景是黑色
double threshold1低阈值
double threshold2高阈值 在程序中设置为下限阈值的三倍或二倍(遵循Canny的建议)
int apertureSize = 3Soble算子的 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);
        }




        /// <summary>
        /// 委托函数
        /// </summary>
        /// <param name="pos">变化的量</param>
        /// <param name="userdata">传入的数据对象</param>
        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

拖动滑块,实现不同效果:
在这里插入图片描述
在这里插入图片描述

Logo

助力广东及东莞地区开发者,代码托管、在线学习与竞赛、技术交流与分享、资源共享、职业发展,成为松山湖开发者首选的工作与学习平台

更多推荐