关于C#:cv :: mat CV_8U产品错误和CV_32F产品缓慢

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?