一,什么是Sobel算子

  • Sobel算子是像素图像边缘检测中最重要的算子之一
  • Sobel算子是一个离散微分算子。(discrete differentiation operator)用来计算图像灰度的近似值
  • Sobel算子功能集合了高斯平滑和微分求导
  • 又被称为一阶微分算子,求导算子,在水平和垂直两个方向上求导,得到图像X方向和Y方向的梯度图像。

官方图例:

在这里插入图片描述
在图片中 红圈处,是头发和脸交汇处(边缘),是不同的颜色,他们之间的像素差别最大。是像素值发生跃迁的地方,变化曲线可以用f(t)f(t)f(t)表示,f(t)f(t)f(t)函数图中红圈表示变化最大的点,而对f(t)f(t)f(t) 求导 得到 f‘(t)f`(t)f(t)。差值变化曲线,像一个钟摆行。最高处代表边缘点(差值最大处)。
如果f(t)f(t)f(t) 看作函数的话,f‘(t)f`(t)f(t)就是他的导函数,我们通过前一个像素和当前像素的差值,边缘处得到最大体现,而非边缘处的差值很小或几乎是0,得到最小体现,然后整个图像进行积分求导,提取最大体现的边缘。

卷积应用-- 边缘提取:

  1. 边缘是什么,是像素值发生跃迁的地方,是图像的显著特征之一,在图像特征提取,对象检测,模式识别等方面都有很重要的作用。
  2. 如何获取边缘 ,对图像求他的一阶导数。
    delta=f(x)−f(x−1) delta=f(x) - f(x -1) delta=f(x)f(x1)
    deltadeltadelta 越大,说明像素在 X 的方向变换越大,边缘信号越强。

二,Sobel算子:

Sobel算子是基于权重比,来扩大像素之间的差异,从而更好的寻找边缘。下面两个矩阵都是对称的。

水平梯度:
Gx=[−10+1−20+2−10+1]∗I G_x=\left[ \begin{matrix} -1 & 0 & +1 \\ -2& 0 & +2 \\ -1 & 0 & +1 \end{matrix} \right] *I Gx=121000+1+2+1I

垂直梯度:
Gy=[−1−2−1000+1+2+1]∗I G_y=\left[ \begin{matrix} -1 & -2 & -1 \\ 0& 0 & 0 \\ +1 & +2 & +1 \end{matrix} \right] *I Gy=10+120+210+1I

最终图像梯度:

  1. G=Gx2+Gy2 G= \sqrt{G_x^2+G_y^2} G=Gx2+Gy2
  2. 默认的计算方式: G=∣Gx∣+∣Gy∣ G= |G_x|+|G_y| G=Gx+Gy

OpenCv中边缘加强算子 (Cv2.Scharr函数):
在求取导数的近似值,kernel=3kernel=3kernel=3 时不是很准确,OpenCv 使用了改进版本 Cv2.Scharr函数,其算子如下:
Gx=[−30+3−100+10−30+3]∗I G_x=\left[ \begin{matrix} -3 & 0 & +3 \\ -10& 0 & +10\\ -3 & 0 & +3 \end{matrix} \right] *I Gx=3103000+3+10+3I
Gy=[−3−10−3000+3+10+3]∗I G_y=\left[ \begin{matrix} -3 & -10 & -3 \\ 0& 0 & 0 \\ +3 & +10 & +3 \end{matrix} \right] *I Gy=30+3100+1030+3I

三,API

Cv2.Sobel():使用扩展的Sobel算子计算第一、第二、第三或混合图像导数

参数 说明
InputArray src 源图像
OutputArray dst 输出图像
MatType ddepth 输出图像的深度
int xorder X方向 ,几阶导数
int yorder Y方向 ,几阶导数
int ksize = 3 Sobel算子 kernel大小,必须是奇数,1,3,5,7,… …
double scale = 1 计算的导数值的可选缩放因子(默认情况下,不应用缩放),就是放大或缩小计算结果
double delta = 0 可选的增量值,在将结果存储到dst之前添加到结果中,相当于一个增益值
BorderTypes borderType = BorderTypes.Reflect101 边缘处理类型,默认即可

MatType ddepth参数值:计算后的值可能大于 255 所以要选择对应的数据类型,否则计算的结果可能有错误。

Input depth(src.depth()) Output depth (ddepth)
CV_8U -1/CV_16S/CV_32F/CV_64F
CV_16U/CV_16S -1/CV_32F/CV_64F
CV_32F -1/CV_32F/CV_64F
CV_64F -1/CV_64F

改进版 Cv2.Scharr() 参数与上面一样。

边缘提取步骤:

  1. 高斯平滑滤波,Sobel算子对噪声比较敏感,为了不影响计算结果,要进行降噪处理。
  2. 转为灰度色彩空间
  3. 求X方向和Y 方向的梯度图像
  4. 根据公式得到最终图像(振幅图像,X梯度+Y梯度)

四,代码:

	    /// <summary>
        /// Sobel算子图像边缘处理
        /// </summary>
        private static void Sobel(string path)
        {
            using (Mat src = new Mat(path, ImreadModes.AnyColor | ImreadModes.AnyDepth))
            {
                //1:高斯模糊平滑
                Mat dst = new Mat();
                Cv2.GaussianBlur(src, dst, new Size(3, 3), 0, 0, BorderTypes.Default);
                //转为灰度
                Mat gray = new Mat();
                Cv2.CvtColor(dst, gray, ColorConversionCodes.BGR2GRAY);

                MatType m = src.Type();

                //求 X 和 Y 方向的梯度  Sobel  and scharr
                Mat xgrad = new Mat();
                Mat ygrad = new Mat();
                Cv2.Sobel(gray, xgrad, MatType.CV_16S, 1, 0, 3);
                Cv2.Sobel(gray, ygrad, MatType.CV_16S, 0, 1, 3);

                Cv2.ConvertScaleAbs(xgrad, xgrad);//缩放、计算绝对值并将结果转换为8位。不做转换的化显示不了,显示图相只能是8U类型 
                Cv2.ConvertScaleAbs(ygrad, ygrad);

                //加强边缘检测
                //Cv2.Scharr(gray, xgrad, -1, 1, 0, 3);
                //Cv2.Scharr(gray, ygrad, -1, 0, 1, 3);

                Mat output = new Mat(xgrad.Size(), xgrad.Type());
                //图像混合相加(基于权重 0.5)不精确
                //Cv2.AddWeighted(xgrad, 0.5, ygrad, 0.5, 0, output);

                //基于 算法 G=|Gx|+|Gy|    
                int width = xgrad.Cols;
                int hight = xgrad.Rows;
                //for (int x = 0; x <hight ; x++)
                //{
                //    for (int y = 0; y < width; y++)
                //    {
                //        int xg = xgrad.At<byte>(x, y);
                //        int yg = ygrad.At<byte>(x, y);
                //        //byte xy =(byte) (xg + yg);
                //        int v = xg + yg;
                //        if(v>255)
                //        {
                //            v = 255;
                //        }
                //        if(v<0)
                //        {
                //            v = 0;
                //        }
                //        byte xy = (byte)v;
                //        output.Set<byte>(x, y, xy);
                //    }
                //}

                //基于 G= (Gx*Gx +Gy*Gy)的开方根
                for (int x = 0; x < hight; x++)
                {
                    for (int y = 0; y < width; y++)
                    {
                        int xg = xgrad.At<byte>(x, y);
                        int yg = ygrad.At<byte>(x, y);
                        //byte xy =(byte) (xg + yg);
                        double v1 = Math.Pow(xg, 2);
                        double v2 = Math.Pow(yg, 2);
                        int val = (int)Math.Sqrt(v1 + v2);
                        if (val > 255) //确保像素值在 0 -- 255 之间
                        {
                            val = 255;
                        }
                        if (val < 0)
                        {
                            val = 0;
                        }
                        byte xy = (byte)val;
                        output.Set<byte>(x, y, xy);
                    }
                }
                using (new Window("X Image", WindowMode.Normal, xgrad))
                using (new Window("Y Image", WindowMode.Normal, ygrad))
                using (new Window("OUTPUT Image", WindowMode.Normal, output))
                using (new Window("SRC", WindowMode.Normal, src))
                {
                    Cv2.WaitKey(0);
                }
            }
        }

X 向量 和 Y向量
在这里插入图片描述
源图与最终合成输出图像,边缘非常清晰。
在这里插入图片描述

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐