(18)sobel算子

/*
*Sobel算子
*卷积的应用-图像边缘提取
        边缘是什么 -是像素值发生跃迁的地方,是图形显著特征之一,在图像特征提取、对象检测、模式识别等
                              方面都要重要的作用
        如何捕捉/提取边缘 -对图像求它的一阶导数
        delta = f(x) - f(x-1), delta越大,说明像素在x方向变化越大,边缘信号越强
        用Sobel算子卷子操作解决
*Sobel算子
        是离散微分算子(discrete differentiation operaror), 用来计算图像灰度的近似梯度,梯度越大的地方越可能是图像的边缘
        Sobel算子功能合集了高斯平滑和微分求导两个方面,所以又被称为一阶微分算子,求导算子,
        在水平和垂直两个方向上求导,得到图像x方向与y方向梯度图像

        水平梯度:左边右边不一样,那么水平方向上有差异的,通过这个水平方向求导算子
                        因为Sobel算子它是有一个不同的权重,通过权重值扩大这种差异 30 - 20 = 10 加上权重因子2的话差异变成20
                        通过这个扩大水平方向的差异
         同样垂直方向通过不同的权重扩大垂直方向的差异

        对图像用水平方向的卷积核得到的是x方向的梯度,用垂直方向的卷积核得到的是y方向的梯度,最后图像上每一个点最终的边缘
        用水平和垂直方向的平方和开方的公式求出来,在计算机为了求速度就用两个绝对值相加,cpu对加和减要比乘除要快,理论和实际有差别
        实战中要学会推导数学公式找近似来提高运行速度

        因为Kernel = 3时不时很准确,所以opencv用改进版的Scharr函数 就是权重变得更大了
*API说明cv::Sobel
        cv::Sobel(
        InputArray src, //输入图像
        OutputArray dst, //输出图像
        int depth, //输出图像深度 只能大于等于输入图像的深度,如果不这样做它会报错的
                        //为了避免溢出和截断所以位数要比原图像要大,不能等
                        //CV_8U:unchar, CV_16S:有符号的char, CV_32F:float, CV_64F:可以看出double
        int dx, //x方向,几阶导数
        int dy, //Y方向,几阶导数
        int ksize, //SOBEL算子kernel大小必须时奇数
        double scale = 1, //要不要做出来的值放大几倍或者缩小几倍
        double delta = 0, //常量值
        int borderType = BORDER_DEFAULT)

        opencv的改进版本:cv::Scarr 参数几乎和上面相同

        步骤:
        1.高斯平滑-Sobel算子有一个缺点,对噪声比较敏感,容易受到影响,所以通过高斯模糊降噪(平滑就可以降噪),所以先模糊
        2.转灰度-cvtColor变成一张灰度图像
        3.通过Sobel算子求梯度,求x和y 的两个梯度
        4.混合x和y得到振幅图像,信号越强的像素振幅越大,边缘越强
 */

#include <iostream>
#include <opencv2/opencv.hpp>

using namespace std;
using namespace cv;

int main()
{
    Mat src, res;
    src = imread("D:/A_Graduation/Pictures/Segmentatio/1.jpg");
    imshow("src", src);

    //对图像进行高斯模糊
    GaussianBlur(src, res, Size(3, 3), 0, 0); //3*3是一个很小的模糊

    //转化成一张灰度图像
    Mat gray_res;
    cvtColor(res, gray_res, CV_BGR2GRAY);

    //对图像做Sobel计算 x方向的梯度,y方向的梯度
    Mat xgrad, ygrad;
    Scharr(gray_res, xgrad, CV_16S, 1, 0, 3);
    Scharr(gray_res, ygrad, CV_16S, 1, 0, 3);
    //它的边缘得到了更大的加强,几乎把所有的边缘更大的加强,这样如果有一点干扰根本不怕,像Sobel稍微有点干扰还是会怕的

//    Sobel(gray_res, xgrad, CV_16S, 1, 0, 3);
//    Sobel(gray_res, ygrad, CV_16S, 0, 1, 3);
//如果不知道数据会被截断,后面又没进行convertScaleAbs导致负的即绝对值很大的量它也是差值也代表图像边缘很强的信号,所以会导致你的图像出来的时候漏掉了很多信息
    //这一步漏掉了你下面的提取特征什么东西就全错了,就有很多东西都不一样了,最后做出来的算法的精准度就下降了很多,可以说失之毫厘谬以千里,整个精度对我们算法的影响
    //至关重要,如果做特征提取,最后提取的特征点或者描述子够不够好就是我们每一步计算处理数据是不是正确
//    Sobel(gray_res, xgrad, -1, 1, 0, 3);
//    Sobel(gray_res, ygrad, -1, 0, 1, 3);
//图像的效果都是对数据的处理造成的,一定要学会处理数据,用正确的方式让他显示正确的结果,然后处理正确的数据

    //这样做出来的结果不一定是正数还有的时候是负数,负数也会在显示的时候因为要在0-255之间也会调整成0
    convertScaleAbs(xgrad, xgrad); //全部变成正的
    convertScaleAbs(ygrad, ygrad);

    imshow("xgrad", xgrad);
    imshow("ygrad", ygrad);

    //融合
    Mat xygrad;
    addWeighted(xgrad, 0.5, ygrad, 0.5, 0, xygrad);
    imshow("xygrad", xygrad);

    //上面的融合不是绝对值相加,为了得到绝对值相加:
    Mat re_xygrad = Mat(xgrad.size(), xgrad.type());
    int width = xgrad.cols;
    int height = ygrad.rows;

    printf("type:  ", xgrad.type());
    //黑色的点的地方恰恰是最亮的地方,这就说明有被截断,就是给出的类型不一致造成的,type是0是cv_8uchar

    for(int row = 0; row<height; row++)
    {
        for(int col = 0; col<width; col++)
        {
            int xg = xgrad.at<uchar>(row, col);   //char 16位的
            int yg = ygrad.at<uchar>(row, col);
            int xy = xg + yg;
            re_xygrad.at<uchar>(row, col) = saturate_cast<uchar>(xy);
        }
    }
    imshow("re_xygrad", re_xygrad);
    /*
     * x、y方向梯度求的很好,最终的结果也出来的很好,往下深入得到的就好了
     */

    waitKey(0);
    return 0;
}


猜你喜欢

转载自blog.csdn.net/nuc_sheryl/article/details/81068935