在OpenCv中计算cv::Mat的外积(张量积)

3

有没有一种方法可以使用OpenCV中的cv::Mat数据结构计算外积(z * 转置(z),其中z是某个列向量)?

我已经检查过文档,没有内置函数。但是我尝试使用类型为cv::Mat的向量与标准矩阵乘法表达式(*)时,出现异常。

以下是(伪)代码:

cv::Mat tmp = cv::Mat::zeros(9, 1, CV_32SC1)
cv::Mat outerProduct = tmp * tmp.t();

外积计算出现异常。(是的,我的实际代码中tmp矩阵中有实际值,但此描述提供了更多关于所使用数据类型的信息)
理想情况下,cv::Mat outerProduct应该得到一个9x9的矩阵。
我可以使用cv::Mat的缩放乘法属性来完成它(即重复列向量tmp的维度,并为每个列,按索引值缩放元素--就像你如何手动解决这种乘法一样):
cv::Mat outerProduct = cv::repeat(tmp, 1, 9);
for (int i = 0; i < 9; i++)
{
    outerProduct.col(i) *= tmp.at<int>(i, 0);
}

...但如果有更好的方法,那就太好了。


不支持int类型,您需要使用CV_32F或类似类型。 - berak
1
文档中可以了解到,mulTransposed函数“不仅可以乘以浮点矩阵。”请参见下面@beaker的答案。他列出了支持的类型。整数是支持的,但仅限于8位和16位类型。 - marcman
2个回答

8
请注意,尽管我的答案是正确的,@kaanoner的答案 提供了更好的性能。
他们会在你最不经意的地方悄悄使用这些方法。这个方法在 数组操作 中,被称为 mulTransposed
cv::Mat tmp = (Mat_<double>(9,1) << 1, 2, 3, 4, 5, 6, 7, 8, 9);
cv::Mat outerProduct;
mulTransposed(tmp, outerProduct, false);

第三个参数是 aTa。如果为 true,该方法计算 aTa。如果为 false,则计算 aaT
输出为:
tmp = 
[1; 2; 3; 4; 5; 6; 7; 8; 9]
outerProduct = 
[1, 2, 3, 4, 5, 6, 7, 8, 9;
 2, 4, 6, 8, 10, 12, 14, 16, 18;
 3, 6, 9, 12, 15, 18, 21, 24, 27;
 4, 8, 12, 16, 20, 24, 28, 32, 36;
 5, 10, 15, 20, 25, 30, 35, 40, 45;
 6, 12, 18, 24, 30, 36, 42, 48, 54;
 7, 14, 21, 28, 35, 42, 49, 56, 63;
 8, 16, 24, 32, 40, 48, 56, 64, 72;
 9, 18, 27, 36, 45, 54, 63, 72, 81]

浏览源代码,发现mulTransposed不支持CV_32S。以下是它们指定的源和目标类型:

(stype == CV_8U && dtype == CV_32F)
(stype == CV_8U && dtype == CV_64F)
(stype == CV_16U && dtype == CV_32F)
(stype == CV_16U && dtype == CV_64F)
(stype == CV_16S && dtype == CV_32F)
(stype == CV_16S && dtype == CV_64F)
(stype == CV_32F && dtype == CV_32F)
(stype == CV_32F && dtype == CV_64F)
(stype == CV_64F && dtype == CV_64F)

正如其意,目标类型始终为浮点类型。即使我指定了CV_16S的dtype,我也会得到一个CV_32F的矩阵。

也许我做错了什么,但是当我在一个类型为CV_32SC1Mat上调用该函数时,OpenCV会报告“不支持的格式或格式组合”错误。我检查了文档,它们特别提到mulTransposed“不仅可以乘以浮点矩阵。”对于我来说,tmpCV_32SC1类型,但是当我指定outerProduct Mat的类型时,它仍然失败。有什么想法吗? - marcman
检查了一下,但是当我使用CV_32SC1而不是double时,即使我将dtype参数指定为CV_32SC1-1,我仍然看到相同的结果。 - beaker
奇怪...我将不得不四处查找。再次感谢! - marcman
哇,只是为了好玩,试试 CV_16SC1。我正在查看代码,没有提到 CV_32S,只有 16 位整数。 - beaker

3
在我的运行中,转置和乘法方法比单个mulTransposed函数调用快两倍。
Mat descriptor; //(1 rows x 192 cols, CV_32F)
Mat outerProduct;

// calculates outer products for 10000 different descriptors in a loop
mulTransposed(descriptor, outerProduct, true); // => takes 33 secs
outerProduct = descriptor.t()*descriptor; // => takes 14 secs 

1
这真的很有趣。我注意到的一件事是输入数组必须是浮点数,但对于这样的性能提升来说,这似乎不是一个非常大的限制。干得好! - beaker

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