在从matlab转opencv的代码移植中,主要关注(x,y)的索引、矩阵相乘、函数计算的差异、是否需要flip等。
(1)针对(x,y)的索引中
在matlab中,M(x,y)表示的行为x,列为y,即x对应左边索引的值,y对应上边索引的值,即如下:
a =
1 2
3 4
>> a(2,1)
ans =
3
在opencv中,Mat型矩阵,M(x,y)表示的rows索引(行)为x,cols索引(列)为y,即x对应左边索引的值,y对应上边索引的值,即如下:
matlab与opencv的坐标索引不同之处在前者是从1开始索引,而后者是从0开始索引的。
(2)矩阵相乘
在matlab中,矩阵相乘(不是点乘)是直接进行的,c = a * b;
在opencv中,两个Mat型矩阵相乘,其方式与matlab的方式是一样的,c = a * b;
也就是二者对应的矩阵的值是保存一致的,不存在需要什么倒置等行为(除非涉及到内置函数中可能存在的不同)。
(3)卷积操作
在matlab中,卷积操作可通过函数convn来完成(或conv2来完成);
在opencv中,与matlab对应的函数是filter2D,但此时要注意,直接调用filter2D会产生和matlab中convn不一样的结果。当 将matlab中convn函数转成opencv实现时,应加处理,例子如下:
Point anchor(m_vecKernel[i].cols - m_vecKernel[i].cols / 2 - 1, m_vecKernel[i].rows - m_vecKernel[i].rows / 2 - 1);
int borderMode = BORDER_REPLICATE;
flip(m_vecKernel[i], m_vecKernel[i], -1);
filter2D(m_im1, c, m_im1.depth(), m_vecKernel[i], anchor, 0, borderMode);
c = c.colRange((m_vecKernel[i].cols - 1) / 2, c.cols - m_vecKernel[i].cols / 2)
.rowRange((m_vecKernel[i].rows - 1) / 2, c.rows - m_vecKernel[i].rows / 2);
m_vecConvMap[i] = c;
参考如下网站:http://blog.timmlinder.com/2011/07/opencv-equivalent-to-matlabs-conv2-function/
内容如下:
OpenCV: Equivalent to Matlab’s conv2() function
The numerical computing environment Matlab (or e.g. its free alternative GNU Octave) provides a function called conv2for 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:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
enum
ConvolutionType {
CONVOLUTION_FULL,
CONVOLUTION_SAME,
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.
(4)函数计算差异
如(3)所知,两个都是卷积操作,但结果会有所不同,需要做处理才行,得引起注意。
(5)数值类型注意
针对matlab中进行处理的数值类型,如float、double、int型等,在opencv中处理时,要引起注意,最好保持一致,同时opencv中数值类型之间转换时可能出错,要引起注意,比如一个错误如下:
int a[] = { 1, 2, 3, 4 };
Mat a1 = Mat(2, 2, CV_32FC1, a);
cout << a1 << endl;
cout << a1.at<float>(1, 0);
上面想将一个int型数据a赋给浮点型数据a1,结果是未知数:
[1.4012985e-045 2.8...e-045
4.2..........e-45 5.6......e-45]
需要这样表示才行:
float a[] = { 1, 2, 3, 4 };
Mat a1 = Mat(2, 2, CV_32FC1, a);
cout << a1 << endl;
cout << a1.at<float>(1, 0);
即将a表示为浮点型float。