Sobel edge detection Implementation
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-
2.My implementation of Sobel Edge Detection -
3.Opencv Sobel edge function output -
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 ...
That' really a weird idea.
I don't understand this line
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
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?
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
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