Transforming a Mat matrix - c++

I am using C++ and opencv. I have to obtain a transformation matrix when I multiply a matrix,A, with another matrix,B. But matrix B needs to change before multiplying it to A. If B is a 2x3 matrix, it needs to be changed to a 3x3 with the first 2 rows containing the same elements as the original B matrix,but with the last row having all 1's. More simple put,I need to add a last row of 1's to the original B matrix. I want to know whether I can achieve this with any specific Mat matrix operation. Thankyou

You need to use Mat::push_back which will adds elements to the bottom of the matrix.
For example
Mat A = (Mat_<uchar>(3,4) << 1, 2, 3, 4,\
5, 6, 7, 8,\
9, 10, 11, 12); // 3X4 matrix.
Mat B = (Mat_<uchar>(1,4) << 13, 14, 15, 16); // 1X4 matrix
A.push_back(B); // Now A become 4X4 matrix

A straight forward way, but probably not the fastest or prettiest
Mat B_new(3,3,CV_32F);
B_new.row(0) = B.row(0);
B_new.row(1) = B.row(1);
B_new.row(2) = Mat::ones(1,3,CV_32F);
You should take a look at the Mat type documentation

Related

Best way to broadcast Armadillo matrix operations similar to Numpy

Consider the matrices A and B where A is a 5x5 matrix and B is a 1x5 matrix (or a row vector). If I try to do A + B in Numpy, its broadcasting capabilities will implicitly create a 5x5 matrix where each row has the values of B and then do normal matrix addition between those two matrices. This can be written in Armadillo like this;
mat A = randu<mat>(4,5);
mat B = randu<mat>(1,5);
A + B;
But this fails. And I have looked at the documentation and couldn't find a built-in way to do broadcasting. So I want to know the best (fastest) way to do an operation similar to the above.
Of course, I could manually resize the smaller matrix into the size of the larger, and copy the first-row value to each other row using a for loop and use the overloaded + operator in Armadillo. But, I'm hoping that there is a more efficient method to achieve this. Any help would be appreciated!
Expanding on the note from Claes Rolen. Broadcasting for matrices in Armadillo is done using .each_col() and .each_row(). Broadcasting for cubes is done with .each_slice().
mat A(4, 5, fill::randu);
colvec V(4, fill::randu);
rowvec R(5, fill::randu);
mat X = A.each_col() + V; // or A.each_col() += V for in-place operation
mat Y = A.each_row() + R; // or A.each_row() += R for in-place operation
cube C(4, 5, 2, fill::randu);
cube D = C.each_slice() + A; // or C.each_slice() += A for in-place operation

Regular Multiplication of different shaped Eigen Matrices

I have an Nx3 Eigen matrix.
I have an Nx1 Egein marix.
I'm trying to get the coefficient multiplication of each row in the Nx3 by the corresponding scal in the Nx1 so I can scale a bunch of 3d vectors.
I'm sure I'm overlooking something obvious but I can't get it to work.
#include <Eigen/Dense>
MatrixXf m(4, 3);
m << 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12;
MatrixXf dots(4, 1)
dots << 2,2,2,2;
I want to resulting matrix to be Nx3 like so:
2,4,6
8,10,12,
14,16,18,
20,22,24
You can use broadcasting:
m = m.colwise().cwiseProduct(dots);
or observe that all you want to do is to apply a non uniform scaling:
m = dots.asDiagonal() * m;
Both expressions will generate similar code.
Okay, so I got something working. I'm probably doing something wrong but this worked for me so I thought I would share. I wrote my first line of c++ a week ago so I figure I deserve some grace. Anyone with a better solution is encouraged to post.
// scalar/coefficient multiplication (not matrix) on Nx3 x N. For multiplying dot products by vectors
void N3xNcoefIP(MatrixXf &A, MatrixXf &B) {
A.array() *= B.replicate(1, A.size()).array();
}

SparseMatrix and Vector addition broadcasting in Eigen

I want to add a dense VectorXf to a SparseMatrix, and I know that I could do matrix + vector easily for dense matrix, like this
Eigen::MatrixXf mat(2,4);
Eigen::VectorXf v(2);
mat << 1, 2, 6, 9,
3, 1, 7, 2;
v << 0,
1;
//add v to each column of m
mat.colwise() += v;
but how to do this for sparse matrix?
The simplest way to do this is to iterate over all columns and add a sparse vector to your sparse matrix. And you should keep in mind, that if you need colwise operation, your matrix should be stored as Column-Major.
// allocate dense object
Eigen::MatrixXd M(2,4);
Eigen::VectorXd v(2);
// allocate sparse vector
Eigen::SparseVector<double> spV(2);
// allocate Column-major sparse matrix
Eigen::SparseMatrix<double, Eigen::ColMajor> spM(2, 4);
// initialize dense objects values
M << 1, 0, 0, 2,
4, 5, 0, 0;
v << 1,
3;
// convert dense objects to sparse representation
spM = M2.sparseView();
spV = v2.sparseView();
// iterate over sparse Matrix columns
for(int i = 0; i< spM.cols(); ++i) {
spM.col(i) += spV;
}
It can't be done in such way for dense vector and one of the reason is that this operation does not make any sense, since the resulting matrix will be dense. In this case you will need to create some dense matrix and perform colwise operation with your dense vector like this:
Eigen::MatrixXd(spM).colwise() + v

fill a mulit-dimentional matrix with uniformly distributed random numbers with diffierent ranges

I want to fill a 10000x2 matrix in OpenCV (v3.2) with random numbers in uniform distribution but with different ranges for each column and here is the problem with the following code:
Mat centers(10000, 2, CV_32F);
RNG rng(time(NULL));
rng.fill(centers, RNG::UNIFORM, Scalar(0, 0), Scalar(10, 1000));
I expect the first column to be randomly filled with values between zero and 10 and the second column to be filled with values between zero and 1000. But both columns are filled with values between zero and 10,
So I decided to implemented it in the following form.
Mat centers(10000, 2, CV_32F);
RNG rng(time(NULL));
rng.fill(centers.colRange(0, 1), RNG::UNIFORM, 0, 10);
rng.fill(centers.colRange(1, 2), RNG::UNIFORM, 0, 1000);
But it does not work either. I think because RNG::fill does not support noncontinuous matrices (which is not mentioned in the documentation)
So the only remaining way is to use for loop which is waste of time and performance. Am I doing sth wrong above or should I give up and use a for loop
You have misinterpreted the API documentation of RNG::fill(), which clearly defines the parameters a and b as:
a - first distribution parameter; in case of the uniform distribution,
this is an inclusive lower boundary.
b - second distribution parameter;
in case of the uniform distribution, this is a non-inclusive upper
boundary.
So there is no mention in the documentation that you can pass multiple ranges in a and b. So the solution is to create two Mat of 1000 x 1 dimensions, use different values of a and b for both of them and later join both of them to create a unified mat with 1000 x 2 dimension.
cv::RNG rng = cv::RNG(0xFFFFFFFF);
cv::Mat centers(10000, 1, CV_32F);
cv::Mat centers2(10000, 1, CV_32F);
rng.fill(centers, cv::RNG::UNIFORM, cv::Scalar(0), cv::Scalar(10));
rng.fill(centers2, cv::RNG::UNIFORM, cv::Scalar(0), cv::Scalar(1000));
cv::Mat final;
cv::hconcat(centers, centers2, final);
Simply I made a mistake useing the RNG::fill api with multi-range distribution parameters. one should use multi-channels and not multi-columns matrices, even though they share the same underlying data structure. So, Mat centers(10000, 2, CV_32F); should be Mat centers(10000, 1, CV_32FC2);

Largest eigenvalues (and corresponding eigenvectors) in ArrayFire

Given a square matrix A, I need to obtain a diagonal matrix D that contains A's 5 largest magnitude eigenvalues and a matrix V whose columns are the corresponding eigenvectors. In Matlab the code is [V,D] = eigs(A,5). Is there a similar function in ArrayFire C++?
In ArrayFire I use af::eigen(Values,Vectors,A). What is the order of elements in Values? In one test I had the elements of Values sorted in the order of increasing magnitude, however in the other case Values was sorted in the order of decreasing magnitude. Essentially, I need to extract 5 eigenvectors that correspond to the largest magnitude eigenvalues. Do I have to use the sort function to achieve this?
UPDATE
Here's a simple example:
// first example
float a[]={1, 2, 5, -2, 1, -5, 3, -2, 1};
array b(3,3,a);
array evalues, evectors;
af::eigen(evalues, evectors, b);
print(evalues); //`evalues` are not in order
// second example
float a2[]={1, -3, 3, 3, -5, 3, 6, -6, 4};
array b2(3,3,a2);
array evalues2, evectors2;
af::eigen(evalues2, evectors2, b2);
print(evalues2); //`evalues2` are in the decreasing order