## OpenCV: Equivalent to Matlab’s conv2() function

The numerical computing environment Matlab (or e.g. its free alternative GNU Octave) provides a function called conv2 for the two-dimensional convolution of a given matrix with a convolution kernel. While writing some C++ code based upon the free image processing library OpenCV, I found that OpenCV currently offers no equivalent method.

Although there is a `filter2D()`

method that implements two-dimensional correlation and that can be used to convolute an image with a given kernel (by flipping that kernel and moving the anchor point to the correct position, as explained on the corresponding OpenCV documentation page), it would be nice to have a method offering the same border handling options as Matlab (“full”, “valid” or “same” convolution), e.g. for comparing results of the same algorithm implemented in both Matlab and C++ using OpenCV.

**Here is what I came up with:**

enum ConvolutionType { /* Return the full convolution, including border */ CONVOLUTION_FULL, /* Return only the part that corresponds to the original image */ CONVOLUTION_SAME, /* Return only the submatrix containing elements that were not influenced by the border */ CONVOLUTION_VALID }; void conv2(const Mat &img, const Mat& kernel, ConvolutionType type, Mat& dest) { Mat source = img; if(CONVOLUTION_FULL == type) { source = Mat(); const int additionalRows = kernel.rows-1, additionalCols = kernel.cols-1; copyMakeBorder(img, source, (additionalRows+1)/2, additionalRows/2, (additionalCols+1)/2, additionalCols/2, BORDER_CONSTANT, Scalar(0)); } Point anchor(kernel.cols - kernel.cols/2 - 1, kernel.rows - kernel.rows/2 - 1); int borderMode = BORDER_CONSTANT; filter2D(source, dest, img.depth(), flip(kernel), anchor, 0, borderMode); if(CONVOLUTION_VALID == type) { dest = dest.colRange((kernel.cols-1)/2, dest.cols - kernel.cols/2) .rowRange((kernel.rows-1)/2, dest.rows - kernel.rows/2); } }

In my unit tests, this implementation yielded results that were almost identical with the Matlab implementation. Note that both OpenCV and Matlab do the convolution in Fourier space if the kernel is large enough. The definition of ‘large’ varies in both implementations, but results should still be very similar, even for large kernels.

Also, the performance of this method might be an issue for the ‘full’ convolution case, since the entire source matrix needs to be copied to add a border around it. Finally, If you receive an exception in the `filter2D()`

call and you are using a kernel with only one column, this might be caused by this bug. In that case, set the `borderMode`

variable to e.g. `BORDER_REPLICATE`

instead, or use the latest version of the library from the OpenCV trunk.

0Hey Timm,

Thanks, for the great post, this was just what I needed to know! I just have one small question though. When I implement your code, I get the following error:

error: too few arguments to function ‘void cv::flip(const cv::Mat&, cv::Mat&, int)’

This points to cxcore.hpp:

CV_EXPORTS void flip(const Mat& a, Mat& b, int flipCode);

Do I need to flip the image horizontally and/or vertically?

Thanks!

0Hi Vivek,

you’re right, I forgot to document my flip() method. It just does this:

cv::flip(M, result, -1); // -1 = both axes

This is equivalent to a rotation by 180 degrees. The reason for flipping is the reflection of the kernel (minus sign) in the convolution formula: http://en.wikipedia.org/wiki/Convolution#Definition

0@timm

Hi Timm,

Thanks, that fixes it!

0Hey,

This looks great. I have a question however:

say I have a matrix representing an image

and I crop that matrix to create a kernel (small part of the original image)

if i run convolution using your code, shouldn’t I expect the output to show me the place from which I snapped the small matrix? (i.e. implement some sort of template matching)?

thanks!

–jimmy

0Hi Jimmy! I’m not sure what you mean by “cropping the matrix to create a kernel”. Normally, the image itself and the kernel are two separate functions, that do not have anything in common per se. Convolving an image with a kernel is essentially a linear transform, where the kernel describes the transform that is taking place. If the kernel is a blur kernel, it represents a so-called point-spread function (PSF). The PSF describes the transform which a single pixel of the image undergoes if the image is convolved with that blur kernel.

For example, if your blur kernel consists of a single white pixel at the center, and the rest of the kernel is black (i.e. rgb(0,0,0)), the PSF is the identity, and the convolved image will be the same as the original. If instead, you use a derivative kernel like [1 -1], the convolved image will only contain pixels with a large gradient (=strong edges). See Sobel operator for related information. To smooth an image, you can e.g. use a Gaussian blur kernel. Also note that the kernel should be normalized, i.e. you should divide each element of the blur kernel matrix by the sum of all kernel elements, otherwise the total intensity of the image will change (i.e. image might become very light or dark).

0it is great code.

0Dear Timm,

Thanks a lot for the informative post and for the code, it sure helped me a lot.

However I didn’t understand this part of the code:

13. Mat source = img;

14. if(CONVOLUTION_FULL == type) {

15. source = Mat();

The ‘=’ operator in 13 makes a header copy of ‘img’ to ‘source’.

Then, in line 15, a new Mat() is being assigned to ‘source’, but this doesn’t

invalidate the const attribuition of ‘img’?

Thanks in beforehand and sorry if I’m not very clear, English is not my motherlanguage.

0Hi Tadashi! Your English is fine . You are right in observing that the assignment in line 15 will override the previous assignment. This is because the copyMakeBorder(…) call in the following line will write data into this new matrix (by copying from ‘img’, and adding padding).

Without the ‘source = Mat()’ statement, the copyMakeBorder() call would override the content of ‘img’ (because ‘source = img’ only creates a shallow copy), and this is not desired.

So the code I have posted is correct, but I could have put the assignment in line 13 into an ‘else’ part of the ‘if’, of course.

0@timm

Dear Timm,

Thanks for the quick answer, now the code is very clear for me

0Dear Timm,

I’m so glad that I saw your code that solve my problem, as I need to find a code equal to conv2 in matlab.

But it seems that your code is not compeltely!So, if it is convinience for you to paste the whole code for me ?

As English is not my motherlanguage,I hope you can understand what I said.Thank you!

0Dear Timm,

As I used opencv 2.0, so it doesn’t support Mat, can you give code equivalent to matlab’s conv2 that use CvMat?

It is very important for me, if it conveniente for you ,please contact with me.

Thank you very much.

My email is:805577846@qq.com

0@Vivek

Hey

I need this code to convolution two martix, so the kenel may big. And I come accross with the same problem with you ,as I was a freshman in opencv,so can you told me that how did you modify the code so that it can run without any error?

0@anne

See my reply to Vivek’s question to see how my flip() method was defined: It just delegates to cv::flip(), supplies an input and output matrix, and uses -1 for the flip mode.

0@anne

I do not have access to my development environment right now, and I have not written any code for the OpenCV C API. Nonetheless, you should easily be able to replace the C++ API calls by the corresponding ones from the C API — e.g. copyMakeBorder should be cvCopyMakeBorder, filter2D is cvFilter2D (just check that the parameter order is the same). I’m not sure about the .colRange() and .rowRange(), but you should be able to come up with something on your own here — this just extracts the image sub-region in the given rectangle.

0@timm

Thank you very much! As there is so little information about cv::Mat in China now that I don’t know how to use it effective. I will try my best to come up with the problems.

0@timm

Hey Timm, me again! I studied your code and try to understand it .But I can’t understand that why you write this code:

Point anchor(Kernel.cols-Kernel.cols/2-1,Kernel.rows-Kernel.rows/2-1);

As I search on the Internet and find that filter2D takes anchor of Point(-1,-1) for default, so I don not know that why you change anchor to that one?

0You might be right here — probably using (-1,-1) would also work since this special coordinate pair also represents the center of the kernel.

0I’ve tried to run this code hopefully and I’ve just realised when I compare conv2 from matlab and this code with flag “same” , it has a different result. Have you had different results?

i say that because the the result matrix from matlab that is in double has different values from result matrix from opencv(this code) which values are double

I’m using this code because i want to calculate the predominant orientation from a region(from a image)

thanks for code!

0I forgot it.

When a show the matrix from conv2 in matlab is black and white with different portions and in opencv is similar just the top and down, in the middle in white.

I use this variables:

String imageName = “./img/test.jpg”;

IplImage *img = cvLoadImage(imageName.c_str()); // Image in grayscale

Mat dest;

double a[] = {0.0266, 0.2160, 0.4839, 0, -0.4839, -0.2160, -0.0266};

Mat kernel = Mat(1, 7,CV_8SC1 , a);

Mat imgMat(img);

conv2(imgMat, kernel, CONVOLUTION_SAME,dest);

The key I think is in the kernel where I can use CV_8SC1,CV_64FC1 etc. In many web pages when I wanna use create a Mat say to use CV_64FC1. The problem with CV_64FC1 is when I call conv2 the result is almost black and with CV_8SC1 is a bit similiar(as I describe above).

CV_8SC1 is the only constant one is applied the result is a bit similar.

I mean, in matlab the result is portion black and white, typicall.

And i opencv is imagine the image as square, there are two horitzontals rectangle where first is a bit similar and the second is almost like a white square and is not similar to matlab and the third is as small as first rectangle and is similar to matlab.

0What is the version of opencv you have used?

0I used OpenCV 2.2 if I recall correctly.