cv::mat CV_8U product error and slow CV_32F product
我正在尝试在2772x128矩阵和4000x128矩阵之间进行乘积运算。两者都是SIFT描述符的矩阵,使用下一个代码:
1 2 3 | Mat a = Mat(nframes, descrSize, CV_8U, DATAdescr); Mat b = Mat(vocabulary_size, descrSize, CV_8U, vocabulary); Mat ab =a * b.t(); |
问题在于,计算乘积时会抛出错误,提示
1 | err_msg = 0x00cdd5e0"..\\..\\..\\src\\opencv\\modules\\core\\src\\matmul.cpp:711: error: (-215) type == B.type() && (type == CV_32FC1 || type == CV_64FC1 || type == CV_32FC2 || type == CV_64FC2)" |
此问题的解决方案是将数据类型转换为CV_32FC1
1 2 3 4 5 | Mat a = Mat(nframes, descrSize, CV_8U, DATAdescr); Mat b = Mat(vocabulary_size, descrSize, CV_8U, vocabulary); a.convertTo(a, CV_32FC1); b.convertTo(b, CV_32FC1); Mat ab = a * b.t(); |
它运作良好,但耗时过多,约1.2 s。我想尝试相同的产品,但使用整数,以查看是否可以加快速度。难道我做错了什么?我看不到任何无法在CV_8U矩阵之间进行矩阵乘积的原因。
编辑:答案与使用其他库或其他解决方法有关。我当时正在考虑用新的思路提出建议来解决我的问题,但是有人可以回答我原来的停诉请求吗?我不能将CV_8U或CV32S矩阵相乘吗?真的吗?
在另一条消息中,您说以下代码将花费0.9秒。
1 2 3 | MatrixXd A = MatrixXd::Random(1000, 1000); MatrixXd B = MatrixXd::Random(1000, 500); MatrixXd X; |
我在机器上尝试了一个基准测试,即在Linux上运行的intel核心i7。我完整的基准代码如下:
1 2 3 4 5 6 7 8 9 10 | #include <Eigen/Dense> using namespace Eigen; int main(int argc, char *argv[]) { MatrixXd A = MatrixXd::Random(2772, 128); MatrixXd B = MatrixXd::Random(4000, 128); MatrixXd X = A*B.transpose(); } |
我只是从Linux使用time命令,因此运行时间包括可执行文件的启动和停止。
1 /没有优化的编译(gcc编译器):
1 2 3 4 5 | g++ -I/usr/include/eigen3 matcal.cpp -O0 -o matcal time ./matcal real 0m13.177s -> this is the time you should be looking at user 0m13.133s sys 0m0.022s |
13秒,这非常慢。顺便说一句,没有矩阵乘法需要花费0.048s,而在您的0.9s示例中矩阵更大。为什么??
使用Eigen进行编译器优化非常重要。
2 /进行一些优化编译:
1 2 3 4 5 | g++ -I/usr/include/eigen3 matcal.cpp -O2 -o matcal time ./matcal real 0m0.324s user 0m0.298s sys 0m0.024s |
现在0.324秒,那就更好了!
3 /切换所有优化标志(至少我所知道的所有,我不是该领域的专家)
1 2 3 4 5 | g++ -I/usr/include/eigen3 matcal.cpp -O3 -march=corei7 -mtune=corei7 -o matcal time ./matcal real 0m0.317s user 0m0.291s sys 0m0.024s |
0.317,关闭,但是获得了几毫秒(对于一些测试而言,一致)。因此,我认为您对Eigen的使用确实存在问题,或者您不切换编译器优化,或者您的编译器本身没有这样做。
我不是Eigen的专家,我只使用过几次,但是我认为文档非常好,您可能应该阅读它以充分利用它。
关于与MatLab的性能比较,上一次我了解到Eigen时,它不是多线程的,而MatLab可能使用的是多线程库。对于矩阵乘法,您可以将矩阵分成几个块,并使用TBB
并行化每个块的乘法
由remi建议,我使用Eige实现了相同的矩阵乘法。它是:
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 26 27 28 29 30 | const int descrSize = 128; MatrixXi a(nframes, descrSize); MatrixXi b(vocabulary_size, descrSize); MatrixXi ab(nframes, vocabulary_size); unsigned char* dataPtr = DATAdescr; for (int i=0; i<nframes; ++i) { for (int j=0; j<descrSize; ++j) { a(i,j)=(int)*dataPtr++; } } unsigned char* vocPtr = vocabulary; for (int i=0; i<vocabulary_size; ++i) { for (int j=0; j<descrSize; ++j) { b(i,j)=(int)*vocPtr ++; } } ab = a*b.transpose(); a.cwiseProduct(a); b.cwiseProduct(b); MatrixXi aa = a.rowwise().sum(); MatrixXi bb = b.rowwise().sum(); MatrixXi d = (aa.replicate(1,vocabulary_size) + bb.transpose().replicate(nframes,1) - 2*ab).cwiseAbs2(); |
关键行是表示
的行
1 | ab = a*b.transpose(); |
词汇表DATAdescr是无符号字符数组。 DATAdescr是2782x128,词汇量是4000x128。在实现过程中,我看到可以使用Map,但是最初却无法使用它。分配的初始循环成本为0.001,因此这不是瓶颈。整个过程约为1.23 s
在matlab中(0.05s。)的相同实现是:
1 2 | aa=sum(a.*a,2); bb=sum(b.*b,2); ab=a*b'; d = sqrt(abs(repmat(aa,[1 size(bb,1)]) + repmat(bb',[size(aa,1) 1]) - 2*ab)); |
预先感谢remi对您的帮助。
尝试使用EIGEN作为后端来编译OpenCV。 CMakeList中有一个选项。我在您的命令中读到您使用OpenCV只是为了加快矩阵乘法,所以您甚至可能想直接尝试EIGEN。
最后一个解决方案,使用OpenCV的GPU模块。
如果将矩阵相乘,则将元素值相乘并相加-如果范围只有0-255,则乘积很可能会超过255。因此,CV_8U矩阵的乘积是\\不是很有用。
如果您知道结果将适合一个字节,则可以通过循环遍历元素来自己进行乘法。
edit:令我感到惊讶的是float版本的运行速度如此之慢,通常opencv在性能方面是相当不错的-使用多核和优化的SSE2指令。您是从源头构建的吗?您是否有TBB(即多线程)和SSE2 cpu?