简体   繁体   中英

Applying a kernel with OpenCV in C++

If I apply a Sobel filter to an image in Python using scipy.ndimage.filters.convole I get meaningful results, for example, for this simple input image img

0 255 0
0 255 0
0 255 0

the convolution

dimage.filters.convolve(img, Kx)

with Kx

-1 0 1
-2 0 2
-1 0 1

returns a meaningful gradient in x-direction:

-1020 0 1020
-1020 0 1020
-1020 0 1020

I don't know how to get a equivalent result using openCV2 in C++ though. When I define the input image by

int image_data[9] = {0, 255, 0, 0, 255, 0, 0, 255, 0};
cv::Mat image = cv::Mat(3, 3, CV_32F, image_data);

and apply the kernel by

cv::Mat gradientx; 
double sobelx_data[9] = {-1, 0, 1, -2, 0, 2, -1, 0, 1};
cv::Mat sobelx = cv::Mat(3, 3, CV_32F, sobelx_data);
cv::filter2D(image, gradientx, -1, sobelx);

I get the following result from

for(int row=0; row<gradientx.rows; row++) {
  for(int col=0; col<gradientx.cols; col++) {
  std::cout << gradientx.at<int>(row,col) << std::endl;
  }
}

it returns the following image

478 -2147482660 478
478 -2147482660 478
478 -2147482660 478

There seems to be an overflow problem, but I don't know why. Trying to get values from gradientx.at<double>(row,col) produces

-1.68911e-311 8.10602e-312 8.11663e-312
-1.68911e-311 8.10602e-312 8.11663e-312
-1.68911e-311 2.122e-314 8.54412e-72

Can someone tell me why this is? Isn't filter2D supposed to do a 2D convolution on the image, and why do I get weird values when addressing the output pixels with <double> ? Thank you.

Okay, here's your code with the types corrected (I've also added more parameters to filter2D ):

float image_data[9] = {0, 255, 0, 0, 255, 0, 0, 255, 0};
cv::Mat image = cv::Mat(3, 3, CV_32F, image_data);
std::cout << "image = " << std::endl << image << std::endl;

cv::Mat gradientx;
float sobelx_data[9] = {-1, 0, 1, -2, 0, 2, -1, 0, 1};
cv::Mat sobelx = cv::Mat(3, 3, CV_32F, sobelx_data);
std::cout << "sobelx = " << std::endl << sobelx << std::endl;

cv::filter2D(image, gradientx, -1, sobelx, cv::Point(-1, -1), 0,
             cv::BORDER_DEFAULT);
std::cout << "gradientx = " << std::endl << gradientx << std::endl;

The result is:

image = 
[0, 255, 0;
 0, 255, 0;
 0, 255, 0]
sobelx = 
[-1, 0, 1;
 -2, 0, 2;
 -1, 0, 1]
gradientx = 
[0, 0, 0;
 0, 0, 0;
 0, 0, 0]

If you look at the top of the documentation page on filtering , you'll see all of the border types that OpenCV uses. By default, filter2D uses BORDER_REFLECT_101 . This is probably not what we want, so let's change it to BORDER_REPLICATE .

cv::filter2D(image, gradientx, -1, sobelx, cv::Point(-1, -1), 0,
             cv::BORDER_REPLICATE);

Result:

image = 
[0, 255, 0;
 0, 255, 0;
 0, 255, 0]
sobelx = 
[-1, 0, 1;
 -2, 0, 2;
 -1, 0, 1]
gradientx = 
[1020, 0, -1020;
 1020, 0, -1020;
 1020, 0, -1020]

That's better, but the values are flipped. If you look at the bottom of the function description for filter2D you'll see that it actually computes the cross correlation rather than the convolution. So we need to flip the kernel to get the correct results.

cv::Mat sobelxflip;
cv::flip(sobelx, sobelxflip, -1);

cv::filter2D(image, gradientx, -1, sobelxflip, cv::Point(-1, -1), 0,
             cv::BORDER_REPLICATE);
std::cout << "gradientx = " << std::endl << gradientx << std::endl;

Result:

gradientx = 
[-1020, 0, 1020;
 -1020, 0, 1020;
 -1020, 0, 1020]

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM