Ask Your Question
1

Sobel edge detection Implementation

asked 2016-11-05 13:33:54 -0600

kevgeo gravatar image

updated 2016-11-05 23:12:01 -0600

I am trying to implement sobel edge detection from scratch but my output can't seem to match with OpenCV's sobel function. I performed correlation on the image with the sobel operator in both x and y directions and then computed gradient magnitude as square root of sum of squares of magnitudes in both x & y direction. I believe the problem is how I assign the threshold for edge detection.

Images-

1.Original Image-

image description

2.My implementation of Sobel Edge Detection - image description

3.Opencv Sobel edge function output - image description

Code-

#include <iostream>
#include <bits/stdc++.h>
#include "opencv2/core/core.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/opencv.hpp"
#include "opencv2/imgproc/imgproc.hpp" 
#include <opencv2/objdetect/objdetect.hpp>
#include <math.h>

using namespace cv; 
using namespace std; 

int main()
{
// Reading image
Mat img = imread("1.jpg");


// Blurring and Converting to grayscale
Mat img_gray,image_blur;
GaussianBlur( img, image_blur, Size(5,5), 3, 3);
    cvtColor(image_blur,img_gray,CV_RGB2GRAY);

int cols = img_gray.cols;
int rows = img_gray.rows;

// Creating sobel operator in x direction
int sobel_x[3][3] = {-1,0,1,-2,0,2,-1,0,1};
// Creating sobel operator in y direction
int sobel_y[3][3] = {-1,-2,-1,0,0,0,1,2,1};


int radius = 1;

// Handle border issues
Mat _src;
copyMakeBorder(img_gray, _src, radius, radius, radius, radius, BORDER_REFLECT101);

// Create output matrix
Mat gradient_x = img_gray.clone();
Mat gradient_y = img_gray.clone();
Mat gradient_f = img_gray.clone();

int max=0;

// Correlation loop in x direction 

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

        // Iterate on kernel
        for (int i = -radius; i <= radius; ++i)
        {
            for (int j = -radius; j <= radius; ++j)
            {
                s += _src.at<uchar>(r + i, c + j) * sobel_x[i + radius][j + radius];
            }
        }
        gradient_x.at<uchar>(r - radius, c - radius) = s/30;

        /*if(s>200)
            gradient.at<uchar>(r - radius, c - radius) = 255;
        else
            gradient.at<uchar>(r - radius, c - radius) = 0;
        */    
    }
}


// Conrrelation loop in y direction 

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

        // Iterate on kernel
        for (int i = -radius; i <= radius; ++i)
        {
            for (int j = -radius; j <= radius; ++j)
            {
                s += _src.at<uchar>(r + i, c + j) * sobel_y[i + radius][j + radius];
            }
        }

        gradient_y.at<uchar>(r - radius, c - radius) = s/30;

        /*if(s>200)
            gradient.at<uchar>(r - radius, c - radius) = 255;
        else
            gradient.at<uchar>(r - radius, c - radius) = 0;
        */    
    }
}




//Calculating gradient magnitude
for(int i=0; i<gradient_f.rows; i++)
{
    for(int j=0; j<gradient_f.cols; j++)
    {
        gradient_f.at<uchar>(i,j) = sqrt( pow(gradient_x.at<uchar>(i,j),2) + pow(gradient_y.at<uchar>(i,j),2) );  

         if(gradient_f.at<uchar>(i,j) >240)
            gradient_f.at<uchar>(i,j) = 100;
        else
            gradient_f.at<uchar>(i,j) = 0;
    }
}




imshow("grad magnitude",gradient_f);
waitKey(0);




cv::Mat Gx, Gy; int ksize=3;
Mat abs_grad_x, abs_grad_y;
cv::Sobel(img_gray, Gx, CV_8U, 1, 0, ksize);
convertScaleAbs( Gx, abs_grad_x );
cv ...
(more)
edit retag flag offensive close merge delete

Comments

That' really a weird idea.

I don't understand this line

gradient_x.at<uchar>(r - radius, c - radius) = s/30;

It should be gradient_x.at<uchar>(r , c ) = s/30; and why s/30? What about negative value?

Gradient could be a negative or positive value. There is no threshold...

Remark :

Sobel is a separable fiter

LBerger gravatar imageLBerger ( 2016-11-06 05:29:39 -0600 )edit

Hey LBerger, actually that indexing is required to access all the pixels in the image. Initially, I pad the image with one pixel across the whole border so that I could implement correlaton properly.

I'm calculating gradient magnitude which is sum of square of gradients in x and y direction, so the negative values are getting squared. Or am I suppose to ignore the negative gradients?

Oh so sobel doens't require a threshold? Do you have any idea how the OpenCV implementation works as I'm almost following allthe steps in Sobel edge detection but still my output is different from Opencv?

kevgeo gravatar imagekevgeo ( 2016-11-06 23:39:05 -0600 )edit

About correlation and padding I don't understand.

About negative value you kill gradient gradient_y.at<uchar>(r - radius, c - radius) = s; here if s=-30 means gradient become 0 with uchar

you should use gradient_y.at<char>(r - radius, c - radius) = s;

In your image there in no gradient between black to white because you use cv::Sobel(img_gray, Gx, CV_8U, 1, 0, ksize); and it should be cv::Sobel(img_gray, Gx, CV_8S, 1, 0, ksize);

I think now I understand your threshold gradient negative become 0... If you want to implement "sobel edge detection from scratch " don't kill negative gradient

LBerger gravatar imageLBerger ( 2016-11-07 01:26:08 -0600 )edit

I do padding to take care of edge pixels.

If I use gradient_y.at<char>(r - radius, c - radius) = s; then my edges become much worse. I don't understand the reason why but when I'm dividing by 30, the edges becomes much more clearer.

In the link below, final1.png is s/30, final2.png is s.

link

kevgeo gravatar imagekevgeo ( 2016-11-07 23:33:36 -0600 )edit

1 answer

Sort by ยป oldest newest most voted
0

answered 2016-11-08 03:51:00 -0600

LBerger gravatar image

updated 2016-11-08 04:22:46 -0600

That's not an answer. But I must say that's I don't understand results given by my program. One thing I still don't understand this line gradient_x.at<uchar>(r - radius, c - radius) I have changed to gradient_x.at<uchar>(r, c )

Now your program with my change :

Mat img_gray = (Mat_<uchar>(7, 7) <<
    0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0,
    0,0,1,2,1,0,0,
    0,0,28,3,12,0,0,
    0,0,1,32,11,0,0,
    0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0);


Mat gradient_x(img_gray.rows, img_gray.cols,CV_16S,Scalar(0));

int sobel_x[3][3] = { { -1,0,1 },{ -2,0,2 },{ -1,0,1 } };
int scharr_x[3][3] = { { -3,0,3 },{ -10,0,10 },{ -3,0,3 } };

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

        // Iterate on kernel
        for (int i = -1; i <= 1; ++i)
        {
            for (int j = -1; j <= 1; ++j)
            {
                s += img_gray.at<uchar>(r + i, c + j) * scharr_x[i+1][j+1];
            }
        }
        gradient_x.at<short>(r , c ) = saturate_cast<short>(s) ;

    }
}
Mat Gx;
cout << img_gray << endl;
Sobel(img_gray, Gx, CV_32F, 1, 0, CV_SCHARR, 1, 0, BORDER_ISOLATED);
cout << "Scharr OPENCV=\n" << Gx << endl;
cout << "gradientx manual=\n" << gradient_x << endl;

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

        // Iterate on kernel
        for (int i = -1; i <= 1; ++i)
        {
            for (int j = -1; j <= 1; ++j)
            {
                s += img_gray.at<uchar>(r + i, c + j) * sobel_x[i + 1][j + 1];
            }
        }
        gradient_x.at<short>(r, c) = saturate_cast<short>(s);

    }
}

/// BUG Sobel(img_gray, Gx, CV_32F, 1, 0, 1,1,0,BORDER_CONSTANT);
Sobel(img_gray, Gx, CV_32F, 1, 0, 3,1,0,BORDER_CONSTANT); // GOOD with ksize 3 (I should read the doc!)
cout << "Sobel OPENCV =\n" << Gx << endl;
cout << "gradientx manual=\n" << gradient_x << endl;

results are :

[  0,   0,   0,   0,   0,   0,   0;
   0,   0,   0,   0,   0,   0,   0;
   0,   0,   1,   2,   1,   0,   0;
   0,   0,  28,   3,  12,   0,   0;
   0,   0,   1,  32,  11,   0,   0;
   0,   0,   0,   0,   0,   0,   0;
   0,   0,   0,   0,   0,   0,   0]
Scharr =
[0, 0, 0, 0, 0, 0, 0;
 0, 3, 6, 0, -6, -3, 0;
 0, 94, 29, -48, -29, -46, 0;
 0, 286, 132, -130, -132, -156, 0;
 0, 94, 329, 52, -329, -146, 0;
 0, 3, 96, 30, -96, -33, 0;
 0, 0, 0, 0, 0, 0, 0]
gradientx=
[0, 0, 0, 0, 0, 0, 0;
 0, 3, 6, 0, -6, -3, 0;
 0, 94, 29, -48, -29, -46, 0;
 0, 286, 132, -130, -132, -156, 0;
 0, 94, 329, 52, -329, -146, 0;
 0, 3, 96, 30, -96, -33, 0;
 0, 0, 0, 0, 0, 0, 0]
Sobel =
[0, 0, 0, 0, 0, 0, 0;
 0, 1, 2, 0, -2, -1, 0 ...
(more)
edit flag offensive delete link more

Question Tools

1 follower

Stats

Asked: 2016-11-05 13:33:54 -0600

Seen: 2,329 times

Last updated: Nov 08 '16