marcman marcman - 2 months ago 10
C++ Question

Computing outer (tensor) product of cv::Mat in OpenCv

Is there a way to computer the outer product (z * transpose(z) for some column vector z) using the

cv::Mat
data structure in OpenCV?

I've checked the documentation and there's no built in function. But I'm getting exceptions trying to use the standard matrix multiplication expression (*) with a vector of type
cv::Mat
.

Here's the (pseudo)code:

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


The outer product calculation gives exceptions. (Yes I have actual values in the tmp matrix in my actual code, but this description gives more info about the data type being used)

Ideally, the
cv::Mat outerProduct
should end up as a 9x9 matrix.

I could do it using the scaling multiplication property of
cv::Mat
(i.e. repeat the column vector
tmp
by its dimensions, and for each column, scale the elements by the value in the index--as in how you may solve this kind of multiplication by hand):

cv::Mat outerProduct = cv::repeat(tmp, 1, 9);
for (int i = 0; i < 9; i++)
{
outerProduct.col(i) *= tmp.at<int>(i, 0);
}


...but it would be nice to have a better way if there is one.

Answer

Note that while my answer is correct, @kaanoner's answer gives better performance.


They sneak these methods in where you least expect. This one is in Operations on Arrays and it's called mulTransposed.

cv::Mat tmp = (Mat_<double>(9,1) << 1, 2, 3, 4, 5, 6, 7, 8, 9);
cv::Mat outerProduct;
mulTransposed(tmp, outerProduct, false);

The third parameter is aTa. If it's true, the method calculates aTa. If it's false, it calculates aaT.

Output is:

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]

Looking through the source code, it appears that CV_32S is not supported by mulTransposed. Here are the source and destination types they specify:

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

As this implies, the destination type is always a float type. Even when I specify a dtype of CV_16S, I get a matrix that's CV_32F.

Comments