OpenCV中是否有与Matlab conv2函数等效的功能?

6

是否有opencv的直接函数可以替代matlab中的conv2函数?我尝试使用cvFilter2D(),但是它似乎给出的结果与conv2()不同。

例如:

CvMat * Aa = cvCreateMat(2, 2, CV_32FC1);
CvMat * Bb = cvCreateMat(2, 2, CV_32FC1);
CvMat * Cc = cvCreateMat(2, 2, CV_32FC1);
cvSetReal2D(Aa, 0, 0, 1);
cvSetReal2D(Aa, 0, 1, 2);
cvSetReal2D(Aa, 1, 0, 3);
cvSetReal2D(Aa, 1, 1, 4);
cvSetReal2D(Bb, 0, 0, 5);
cvSetReal2D(Bb, 0, 1, 5);
cvSetReal2D(Bb, 1, 0, 5);
cvSetReal2D(Bb, 1, 1, 5);
cvFilter2D(Aa, Cc, Bb);

This produces the matrix [20 30; 40 50]


In MATLAB:
>> A=[1 2; 3 4]
A =
1 2
3 4

>> B=[5 5; 5 5]
B =
5 5
5 5

>> conv2(A,B,'shape')
ans =
50 30
35 20

请帮助我。这对我非常有用。谢谢。

祝好

Arangarajan.


小修改:正确的调用是 conv2(A,B,'same') - Amro
2个回答

9
Matlab(或其免费替代品GNU Octave)提供了一个名为conv2的函数,用于将给定矩阵与卷积核进行二维卷积。在编写基于免费图像处理库OpenCV的C++代码时,我发现OpenCV当前没有等效的方法。虽然有一个filter2D()方法实现了二维相关性,并可以用于将一个图像与给定的内核进行卷积(通过翻转内核并将锚点移动到正确位置,如对应的OpenCV文档页面所述),但类似Matlab的相同边界处理选项的方法将会很好(“full”,“valid”或“same”卷积),例如用于比较使用OpenCV在Matlab和C ++中实现的相同算法的结果。下面是我设计的方法:
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);
  }
}

在我的单元测试中,这种实现的结果几乎与Matlab的实现相同。请注意,如果内核足够大,OpenCV和Matlab都会在傅里叶空间中执行卷积运算。在两个实现中,“大”的定义不同,但即使对于大内核,结果也应该非常相似。
此外,对于“full”卷积情况下,此方法的性能可能会成为一个问题,因为整个源矩阵需要被复制以添加周围的边框。最后,如果您在filter2D()调用中收到异常,并且使用的内核只有一列,则可能是由于此错误引起的。在这种情况下,请将borderMode变量设置为例如BORDER_REPLICATE,或者使用来自OpenCV trunk的最新版本库。

目前为止最佳答案。代码片段很好,执行的操作与MATLAB的conv2非常相似。另外,提醒一下OpenCV的filter2D执行的是相关而不是卷积,这也是一个好事。 - Tristan Jahier
flip()函数不应该至少需要两个参数吗?根据C++文档:void flip(InputArray src, OutputArray dst, int flipCode)。因此,它应该是cv::flip(kernel, kernel, 0)。 - iqbalnaved

6
如果您正在使用卷积,矩阵的边缘会出现问题。卷积掩模需要在矩阵之外具有值。OpenCV和Matlab的算法使用不同的策略来应对这个问题。OpenCV只复制边框像素,而Matlab则假定所有这些像素都为零。
因此,如果您想在OpenCV中模拟Matlab的行为,可以手动添加这个零填充。甚至有一个专门的函数用于此。让我给您举个例子,说明如何修改您的代码:
CvMat * Ccb = cvCreateMat(3, 3, CV_32FC1);
CvMat * Aab = cvCreateMat(3, 3, CV_32FC1);
cvCopyMakeBorder(Aa,Aab, cvPoint(0,0),IPL_BORDER_CONSTANT, cvScalarAll(0));
cvFilter2D(Aab, Ccb, Bb);

这样做得到的结果是:
20.000   30.000   20.000 
40.000   50.000   30.000 
30.000   35.000   20.000 

为了得到您想要的结果,您只需要删除第一列和第一行,以去除我们添加的边框引入的额外数据。

4
请记住,函数cvFilter2D执行相关操作,而conv2执行卷积操作。对于上面的示例,这并不重要,但如果核不对称,则需要将其旋转180度才能匹配结果。 - Amro

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接