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_pyramids
和gaussian_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逐元素矩阵乘法的主要内容,如果未能解决你的问题,请参考以下文章