OpenCV逐元素矩阵乘法

Posted

技术标签:

【中文标题】OpenCV逐元素矩阵乘法【英文标题】:OpenCV element-wise matrix multiplication 【发布时间】:2014-04-25 21:23:10 【问题描述】:

OpenCV docs 说 A.mul(B) 是每个元素的乘法。然而下面的代码产生了以下输出,然后给出了这个错误:

OpenCV Error: Sizes of input arguments do not match

.

cout << laplacian_pyramids[i][numLevels - 1 - l].rows << endl;
cout << gaussian_weight_pyramids[i][l].rows << endl;
cout << laplacian_pyramids[i][numLevels - 1 - l].cols << endl;
cout << gaussian_weight_pyramids[i][l].cols << endl;

给予:

339
339
571
571

然后:

Mat prod = gaussian_weight_pyramids[i][l].mul(laplacian_pyramids[i][numLevels - 1 - l]);

给出错误。我尝试Mat::multiply 达到类似的效果。

【问题讨论】:

laplacian_pyramidsgaussian_weight_pyramids 有可能有不同的频道吗?你能发布一个 SSCCE 来证明你的问题吗? 是的,我的拉普拉斯算子有三个通道 (BGR),而我的体重金字塔只有一个(它们只是用来衡量拉普拉斯算子)。以相同数量缩放所有三通道像素的好方法是什么?将单通道矩阵转换为三通道?拆分 3 通道图像,然后在逐元素乘法后合并它们? 【参考方案1】:

我建议将单通道转换为三个通道:

    Mat A = Mat::zeros(100, 200, CV_32FC1);
    Mat B = Mat::zeros(100, 200, CV_32FC3);

    // Mat C = A.mul(B); // Sizes of input arguments do not match

    Mat Afc3;
    Mat t[] = A, A, A;
    merge(t, 3, Afc3);

    Mat C = Afc3.mul(B); // now Afc3 has 3 channels ans it is type of 32_FC3 
                         // we can multiply each elem in B by the same coef from A

但是如果 B 是 CV_8UC3 类型,它就不起作用,因为 opencv 不允许乘以具有不同类型像素的 Mats。在这种情况下,请记住将 8UC3 转换为 32FC3 以将每个像素缩放 1/255.0,因为 32FC3 中的每个像素的值介于 0.0 和 1.0 之间(当然 8UC3 中的每个像素的值介于 0 和 255 之间)。

    Mat A = Mat::zeros(100, 200, CV_32FC1);
    Mat B = Mat::zeros(100, 200, CV_8UC3);

    // Mat C = A.mul(B);

    Mat Afc3, Bfc3;
    Mat t[] = A, A, A;
    merge(t, 3, Afc3);

    B.convertTo(Bfc3, CV_32FC3, 1/255.0);

    Mat C = Afc3.mul(Bfc3);

【讨论】:

【参考方案2】:

这种错误可能有两个原因:不同数量的通道或不同类型的数据(例如,如果第一个矩阵包含 unsigned char 而第二个矩阵包含 unsigned short)。当然,可能有这两个原因。对于您遇到的问题,一般有 3 种类型的解决方案:

1) 编写您自己的“for”循环来执行您需要的操作。您不会从 OpenCV 函数中可能存在的优化中受益,但其他解决方案将有自己的开销。您可以查看this tutorial 了解如何以有效的方式访问像素。

2) 使用“merge”或“convertTo”等函数来创建相同类型和通道数的输入。有关代码示例,请参阅@marol 发布的答案。在此解决方案中,主要开销是数据副本。这意味着额外的时间和空间。如果您要对两个图像执行多个操作,这是一个合理的解决方案。但如果你只需要简单的乘法,它就不会很有效。

3) 使用变通方法。例如,如果您的矩阵类型相同但通道数不同,您可以使用reshape 函数:

// two matrices of same size but different number of channels
Mat laplac(100, 200, CV_32FC3);
Mat gauss(100, 200, CV_32FC1);

// turn them into single channel matrices. they have NxM rows and 1 or 3 columns.
// note that there no copy of data. any change in them will affect original matrices
Mat laplac2 = laplac.reshape( 1, laplac.rows*laplac.cols );
Mat gauss2 = gauss.reshape( 1, gauss.rows*gauss.cols ;

// perform multiplication
laplac2.col(0) = laplac2.col(0).mul(gauss2);
laplac2.col(1) = laplac2.col(1).mul(gauss2);
laplac2.col(2) = laplac2.col(2).mul(gauss2);

这样您只使用 OpenCV 内置函数而没有复制开销。但我怀疑这会比解决方案 1 更快,因为解决方案 1 在内存访问方面更有效。

在任何情况下,您都不会有只需要一行的漂亮而干净的操作:(

【讨论】:

以上是关于OpenCV逐元素矩阵乘法的主要内容,如果未能解决你的问题,请参考以下文章

如何在 numpy 中获得逐元素矩阵乘法(Hadamard 乘积)?

逐元素矩阵乘法:R 与 Rcpp(如何加快此代码的速度?)

Apple Metal元素矩阵乘法(Hadamard产品)

大矩阵的Opencv乘法

opencv六通道矩阵乘法

Scipy CSR 矩阵逐元素加法