OpenCV之图像处理(十八) Sobel算子

卷积应用-图像边缘提取
    边缘是什么 – 是像素值发生跃迁的地方,是图像的显著特征之一,在图像特征提取、对象检测、模式识别等方面都有重要的作用。
    如何捕捉/提取边缘 – 对图像求它的一阶导数
          delta =  f(x) – f(x-1), delta越大,说明像素在X方向变化越大,边缘信号越强,
    怎么求一阶导数? 我已经忘记啦,不要担心,用Sobel算子就好!卷积操作!

Sobel算子
    是离散微分算子(discrete differentiation operator),用来计算图像灰度的近似梯度
    Soble算子功能集合高斯平滑和微分求导
    又被称为一阶微分算子,求导算子,在水平和垂直两个方向上求导,得到图像X方法与Y方向梯度图像。  拉普拉斯算子是二阶求导算子
最终图像梯度:理论上 G = sqrt(Gx^2 + Gy^2) ,  实际为了减少计算速度 G = |Gx| + |Gy|

求取导数的近似值,kernel=3时不是很准确,OpenCV使用改进版本 Scharr 函数,算子为: ┌ -3  0  3 ┐   ┌ -3 -10 -3 ┐  相比Sobel算子,得到的差异更大
                                                                                 │-10  0 10 │   │  0   0  0 │
                                                                                 └ -3  0  3 ┘   └  3  10  3 ┘
    Scharr 将梯度更加的放大,相比于 sobel ,非常的不怕干扰
cv::Sobel (  // Scharr 函数与 Sobel 函数参数几乎一致,没有 ksize 参数,固定是3 ?   Laplacian 函数也是如此
    InputArray Src // 输入图像
    OutputArray dst// 输出图像,大小与输入图像一致
    int depth // 输出图像深度.         input depth        output depth                输入图像深度与输出图像深度关系(注意颜色数据的数据类型与深度的数据类型)
                                     CV_8U              -1/CV_16S/CV_32F/CV_64F     -1表示跟输入深度一样,输出深度最好大于输入深度。 S 表示有符号
                                     CV_16U/CV_16S      -1/CV_32F/CV_64F
                                     CV_32F             -1/CV_32F/CV_64F
                                     CV_64F             -1/CV_64F
    Int dx.  // X方向,几阶导数
    int dy // Y方向,几阶导数. 
    int ksize, SOBEL算子kernel大小,必须是1、3、5、7、
    double scale  = 1
    double delta = 0
    int borderType = BORDER_DEFAULT
)

代码

    #include "../common/common.hpp"

    static void run(Mat , Mat , Mat , Mat , char * , char * , char * , char * ); //图像梯度处理

    void main(int argc, char** argv)
    {
        Mat src, dst1, gray, sobelX, sobelY, add_sobel, sum_sobel, scharrX, scharrY, add_scharr, sum_scharr;
        src = imread(getCVImagesPath("images/test1_3.png"), IMREAD_COLOR);

        GaussianBlur(src, dst1, Size(3, 3), 0, 0);//先模糊,为了去噪声
        cvtColor(dst1, gray, CV_BGR2GRAY);//再转灰度图
        int ddepth = CV_16S;
        // 输出图像使用 CV_16S 相比输出深度为原图的 CV_8U ,输出的图像梯度更明显
        Sobel(gray, sobelX, ddepth, 1, 0, 3);//Sobel X 方向求导,求梯度,输出的图像是二值图
        Sobel(gray, sobelY, ddepth, 0, 1, 3);//Sobel Y 方向求导,求梯度
        run(sobelX, sobelY, add_sobel, sum_sobel, "sobelX", "sobelY", "add_sobel", "sum_sobel");//图像梯度处理

        Scharr(gray, scharrX, ddepth, 1, 0);//Scharr 将梯度更加的放大,相比于 sobel ,非常的不怕干扰
        Scharr(gray, scharrY, ddepth, 0, 1);
        run(scharrX, scharrY, add_scharr, sum_scharr, "scharrX", "scharrY", "add_scharr", "sum_scharr");

        waitKey(0);
    }

    void run(Mat gx, Mat gy, Mat addWeight, Mat sum, char * gxName, char * gyName, char * addName, char * sumName)
    {
        convertScaleAbs(gx, gx);//convertScaleAbs函数将颜色数据取绝对值,否则图像可能无法显示
        convertScaleAbs(gy, gy);
        imshow(gxName, gx);
        imshow(gyName, gy);

        addWeighted(gx, 0.5, gy, 0.5, 0.0, addWeight);//振幅图像,图像合成
        imshow(addName, addWeight);

        sum.create(gx.size(), gx.type());
        int width = gx.cols;
        int height = gy.rows;
        for (int row = 0; row < height; row++)
        {
            for (int col = 0; col < width; col++)
            {
                int xg = gx.at<uchar>(row, col);
                int yg = gy.at<uchar>(row, col);
                sum.at<uchar>(row, col) = saturate_cast<uchar>(xg + yg);//此方式比addWeighted,合成之后的梯度更明显
            }
        }
        imshow(sumName, sum);
    }

效果图

这里写图片描述

猜你喜欢

转载自blog.csdn.net/huanghuangjin/article/details/81138129