2

我正在尝试在 2772x128 矩阵和 4000x128 矩阵之间制作产品。两者都是 SIFT 描述符的矩阵,使用下面的代码:

Mat a = Mat(nframes, descrSize, CV_8U, DATAdescr);
Mat b = Mat(vocabulary_size, descrSize, CV_8U, vocabulary);
Mat ab =a * b.t();

问题是在计算产品时,它会抛出一个错误说

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

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 秒。我想尝试相同的产品,但使用整数,看看我是否可以加快速度。难道我做错了什么?我看不出我不能在 CV_8U 矩阵之间做矩阵乘积的任何原因。

编辑:答案与使用其他库或解决其他方式有关。我正在考虑打开一个新线程来提供解决我的问题的建议,但是有人可以回答我最初的问题吗?我不能乘以 CV_8U 或 CV32S 矩阵吗?真的吗?

4

4 回答 4

2

在您的另一条消息中,您说以下代码需要 0.9 秒。

MatrixXd A = MatrixXd::Random(1000, 1000);
MatrixXd B = MatrixXd::Random(1000, 500);
MatrixXd X;

我在我的机器上尝试了一个小基准测试,intel core i7 在 linux 上运行。我的完整基准代码如下:

#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 编译器):

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.048 秒,而在你的 0.9 秒示例中使用更大的矩阵。为什么 ??

使用 Eigen 进行编译器优化非常重要。2/ 进行一些优化编译:

g++ -I/usr/include/eigen3 matcal.cpp -O2 -o matcal
time ./matcal
real    0m0.324s
user        0m0.298s
sys     0m0.024s

现在是 0.324 秒,这样更好!

3/切换所有优化标志(至少我所知道的,我不是这个领域的专家)

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 并行化每个块的乘法

于 2012-09-29T21:36:47.310 回答
1

由 remi 建议,我使用 Eige 实现了相同的矩阵乘法。这里是:

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();

关键线是说

ab = a*b.transpose();

DATAdescr 词汇表是无符号字符数组。DATAdescr 为 2782x128,词汇表为 4000x128。我在实现时看到我可以使用 Map,但一开始我没有使用它。分配的初始循环是 0.001 成本,因此这不是瓶颈。整个过程约1.23 s

matlab(0.05s)中的相同实现是:

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));

提前感谢雷米的帮助。

于 2012-09-21T09:03:09.653 回答
0

如果您将矩阵相乘,则将元素值相乘并将它们相加 - 如果您只有 0-255 的范围,则乘积很可能会超过 255。因此 CV_8U 矩阵的乘积不是很有用。

如果您知道您的结果将适合一个字节,您可以通过循环元素自己进行乘法运算。

编辑:浮动版本慢得多,我有点惊讶,通常opencv在性能方面非常好 - 具有多核和优化的SSE2指令。你是从源代码构建的吗?你有 TBB(即多线程)和 SSE2 cpu 吗?

于 2012-09-18T15:07:18.423 回答
0

尝试使用 EIGEN 作为后端编译 OpenCV。在 CMakeList 中有一个选项。我在您的命令中读到您使用 OpenCV 只是为了加速矩阵乘法,所以您甚至可能想直接尝试 EIGEN

最后一个解决方案,使用 OpenCV 的 GPU 模块。

于 2012-09-18T20:56:54.697 回答