Sobel derivative in OpenCV

馋奶兔 提交于 2019-11-27 05:39:58

This code snippet is to demonstrate how to compute Sobel 3x3 derivatives convolving the image with Sobel kernels. You can easily extend to different kernel sizes giving the kernel radius as input to my_sobel, and creating the appropriate kernel.

#include <opencv2\opencv.hpp>
#include <iostream>
using namespace std;
using namespace cv;


void my_sobel(const Mat1b& src, Mat1s& dst, int direction)
{
    Mat1s kernel;
    int radius = 0;

    // Create the kernel
    if (direction == 0)
    {
        // Sobel 3x3 X kernel
        kernel = (Mat1s(3,3) << -1, 0, +1, -2, 0, +2, -1, 0, +1);
        radius = 1;
    }
    else
    {
        // Sobel 3x3 Y kernel
        kernel = (Mat1s(3, 3) << -1, -2, -1, 0, 0, 0, +1, +2, +1);
        radius = 1;
    }

    // Handle border issues
    Mat1b _src;
    copyMakeBorder(src, _src, radius, radius, radius, radius, BORDER_REFLECT101);

    // Create output matrix
    dst.create(src.rows, src.cols);

    // Convolution loop

    // Iterate on image 
    for (int r = radius; r < _src.rows - radius; ++r)
    {
        for (int c = radius; c < _src.cols - radius; ++c)
        {
            short s = 0;

            // Iterate on kernel
            for (int i = -radius; i <= radius; ++i)
            {
                for (int j = -radius; j <= radius; ++j)
                {
                    s += _src(r + i, c + j) * kernel(i + radius, j + radius);
                }
            }
            dst(r - radius, c - radius) = s;
        }
    }
}

int main(void)
{
    Mat1b img = imread("path_to_image", IMREAD_GRAYSCALE);

    // Compute custom Sobel 3x3 derivatives
    Mat1s sx, sy;
    my_sobel(img, sx, 0);
    my_sobel(img, sy, 1);

    // Edges L1 norm
    Mat1b edges_L1;
    absdiff(sx, sy, edges_L1);


    // Check results against OpenCV
    Mat1s cvsx,cvsy;
    Sobel(img, cvsx, CV_16S, 1, 0);
    Sobel(img, cvsy, CV_16S, 0, 1);
    Mat1b cvedges_L1;
    absdiff(cvsx, cvsy, cvedges_L1);

    Mat diff_L1;
    absdiff(edges_L1, cvedges_L1, diff_L1);

    cout << "Number of different pixels: " << countNonZero(diff_L1) << endl;

    return 0;
}

If i were you, i would almost always avoid using for loops(if possible). Unnecessary for loops tend to slow down the execution. Instead, reuse wherever possible. For example, the code below uses filter2D give 2d Correlation result:

Mat kern = (Mat_<float>(3,3)<<-1,0,1,-2,0,2,-1,0,1);
Mat dest;
cv::filter2D(src,dest,src.type(),kern);

If you would like to get convolution results, you would need to flip the kernel 'kern' before filtering.

cv::flip(kern,kern, -1);

If you would like to squeeze more performance, you can use separable filters 'sepFilter2D'.

Haval Sadeq

thanks for the post, I was able to generate gradiant map using the above kernel, and using openCV code filter2D getting from Using custom kernel in opencv 2DFilter - causing crash ... convolution how?

to convolve the image with the kernel. the code that I used is

    #include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <stdlib.h>
#include <stdio.h>
#include <iostream>

using namespace cv;
using namespace std;

int main(int argc, char** argv) {

    //Loading the source image
    Mat src;
    //src = imread("1.png");
    src = cv::imread("E:\\Gray_Image.bmp", 0);
    //Output image of the same size and the same number of channels as src.
    Mat dst1,dst2,grad;
    //Mat dst = src.clone();   //didn't help...

    //desired depth of the destination image
    //negative so dst will be the same as src.depth()
    int ddepth = -1;

    //the convolution kernel, a single-channel floating point matrix:


    //Mat kernel = imread("kernel.png");

    Mat kernel_x = (Mat_<float>(3, 3) << -1, 0, 1, -2, 0, 2, -1, 0, 1);

    Mat kernel_y = (Mat_<float>(3, 3) << -1, -2, -1, 0, 0, 0, 1, 2, 1);
    kernel_x.convertTo(kernel_x, CV_32F);  kernel_y.convertTo(kernel_y, CV_32F);   //<<not working
                                          //normalize(kernel, kernel, 1.0, 0.0, 4, -1, noArray());  //doesn't help

                                          //cout << kernel.size() << endl;  // ... gives 11, 11

                                          //however, the example from tutorial that does work:
                                          //kernel = Mat::ones( 11, 11, CV_32F )/ (float)(11*11);

                                          //default value (-1,-1) here means that the anchor is at the kernel center.
    Point anchor = Point(-1, -1);

    //value added to the filtered pixels before storing them in dst.
    double delta = 0;

    //alright, let's do this...
    filter2D(src, dst1, ddepth, kernel_x, anchor, delta, BORDER_DEFAULT);
    filter2D(src, dst2, ddepth, kernel_y, anchor, delta, BORDER_DEFAULT);

    imshow("Source", src);     //<< unhandled exception here
    //imshow("Kernel1", kernel_x);  imshow("Kernel2", kernel_y);
    imshow("Destination1", dst1);
    imshow("Destination2", dst2);
    addWeighted(dst1, 0.5, dst2, 0.5, 0, grad);
    imshow("Destination3", grad);
    waitKey(1000000);

    return 0;
}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!