Opencv - how does the filter2D() method actually work?

c++, computer-vision, opencv

Solution

I think the issue is probably one of scale: if your input image is an 8-bit image, most of the time the convolution will produce a value that overflows the maximum value 255.

In your implementation it looks like you are getting the wrapped-around value, but most OpenCV functions handle overflow by capping to the maximum (or minimum) value. That explains why most of the output of OpenCV's function is white, and also why you are getting concentric shapes in your output too.

To account for this, normalize your `megapixelarray` filter by dividing every value by the entire sum of the filter (i.e. make sure that the sum of the filter values is 1):

For example, instead of this filter (sum = 10):

1 1 1
1 2 1
1 1 1

Try this filter (sum = 1):

0.1 0.1 0.1
0.1 0.2 0.1
0.1 0.1 0.1

Problem

I did look for the source code to Filter2D but could not find it. Neither could Visual c++. Are there any experts on the filter2D algorithm here? I know how it's supposed to work but not how it actually works. I made my own filter2d() function to test things, and the results are substantially different from opencvs filter2D(). Here's my code: ``` Mat myfilter2d(Mat input, Mat filter){ Mat dst = input.clone(); cout << " filter data successfully found. Rows:" << filter.rows << " cols:" << filter.cols << " channels:" << filter.channels() << "\n"; cout << " input data successfully found. Rows:" << input.rows << " cols:" << input.cols << " channels:" << input.channels() << "\n"; for (int i = 0-(filter.rows/2);i<input.rows-(filter.rows/2);i++){ for (int j = 0-(filter.cols/2);j<input.cols-(filter.cols/2);j++){ //adding k and l to i and j will make up the difference and allow us to process the whole image float filtertotal = 0; for (int k = 0; k < filter.rows;k++){ for (int l = 0; l < filter.rows;l++){ if(i+k >= 0 && i+k < input.rows && j+l >= 0 && j+l < input.cols){ //don't try to process pixels off the endge of the map float a = input.at<uchar>(i+k,j+l); float b = filter.at<float>(k,l); float product = a * b; filtertotal += product; } } } //filter all proccessed for this pixel, write it to dst st.at<uchar>(i+(filter.rows/2),j+(filter.cols/2)) = filtertotal; } } return dst; } ``` Anybody see anything wrong with my implementation? (besides being slow) Here is my execution: ``` cvtColor(src,src_grey,CV_BGR2GRAY); Mat dst = myfilter2d(src_grey,filter); imshow("myfilter2d",dst); filter2D(src_grey,dst2,-1,filter); imshow("filter2d",dst2); ``` Here is my kernel: ``` float megapixelarray[basesize][basesize] = { {1,1,-1,1,1}, {1,1,-1,1,1}, {1,1,1,1,1}, {1,1,-1,1,1}, {1,1,-1,1,1} }; ``` And here are the (substantially different) results: Thoughts, anyone? EDIT: Thanks to Brians answer I added this code: ``` //normalize the kernel so its sum = 1 Scalar mysum = sum(dst); dst = dst / mysum[0]; //make sure its not 0 dst = dst * -1; //show negetive ``` and filter2d worked better. Certain filters give an exact match, and other filters, like the Sobel, fail miserably. I'm getting close to the actual algorithm, but not there yet. Anyone else with any ideas?

Original source