Compare OpenCV Mat with scalar elementwise - c++

I have a cv::Mat A, which has CV_32F. However it holds integer values like 1,2....100. I want to form a mask of same size as A.
But the mask must contain zeros if A(x,y) not equal to 5 (say).
The mask must contain ones if A(x,y) equal to 5 (say).
I want to later use it as ROIs.

// you will have a much simpler construct,
// this is just for demonstration
Mat_<float> A(3,3); mf << 1,5,5,2,5,5,1,2,3;
// now use a simple MatExpr to get a mask:
Mat mask = (A == 5);
// show results:
cerr << A << endl;
cerr << mask << endl;
------------------------------
[1, 5, 5;
2, 5, 5;
1, 2, 3]
[0, 255, 255;
0, 255, 255;
0, 0, 0]

Related

OpenCV access current and previous frame

I need the current and previous frame for some calculations in OpenCV C++.
Here is my code so far:
vector <cv::Mat> frames;
// Current frame
Mat M = current_frame;
Mat F; // previous frame
// set Previous Frame
if (frames.empty()) {
F = M;
frames.push_back(M);
cout << "empty" << endl;
}
else {
F = frames.back();
frames.push_back(M);
cout << " NOTempty" << endl;
}
// print frame Mat means to see if things look okay
cout << mean(M) << endl;
cout << mean(F) << endl;
The idea is that M is the current frame and F is the previous frame. Frames are stored in frames, a vector of matrices. If frames is empty, let F = M because there is no previous frame. Then, add M to frames. If frames is not empty, than M is the current frame and F is the last Mat from the vector frames. After F is extracted, the current frame M is added to the end of frames.
I am printing out the mean of M and F so that I have some easy to read reference in the terminal if it worked properly. Unfortunately, it looks not right.
This is what is printed out:
empty
[5.09352, 6.60551, 8.54364, 0]
[5.09352, 6.60551, 8.54364, 0]
NOTempty
[5.02325, 6.46646, 8.39534, 0]
[92.0037, 97.9186, 106.677, 0]
NOTempty
[4.94272, 6.38162, 8.32141, 0]
[91.7741, 97.7845, 106.555, 0]
It should look like this:
empty
[5.09352, 6.60551, 8.54364, 0]
[5.09352, 6.60551, 8.54364, 0]
NOTempty
[5.02325, 6.46646, 8.39534, 0]
[5.09352, 6.60551, 8.54364, 0]
NOTempty
[4.94272, 6.38162, 8.32141, 0]
[5.02325, 6.46646, 8.39534, 0]
This should be simple, what am I doing wrong?
Are you sure you are not missing a loop somewhere in that snippet? If I try something like this:
//create a dummy vector of mats:
std::vector<cv::Mat> sourceMats;
sourceMats.push_back( cv::Mat(1, 4, CV_32F, {1, 1, 1, 1}) );
sourceMats.push_back( cv::Mat(1, 4, CV_32F, {2, 2, 2, 2}) );
sourceMats.push_back( cv::Mat(1, 4, CV_32F, {3, 3, 3, 3}) );
//frames buffer:
std::vector <cv::Mat> frames;
//loop thru all frames:
for( int i = 0; i < (int)sourceMats.size(); i++ ){
// Current frame
cv::Mat M = sourceMats[i];
cv::Mat F; // previous frame
// set Previous Frame
if (frames.empty()) {
F = M;
frames.push_back(M);
std::cout << "empty" << std::endl;
}
else {
F = frames.back();
frames.push_back(M);
std::cout << " NOTempty" << std::endl;
}
// do not compute the mean, I just want to check out the FIFO contents:
std::cout << M << std::endl;
std::cout << F << std::endl;
}
I get this output (note that I removed the mean function - just to check out the raw data):
empty
[1, 1, 1, 1]
[1, 1, 1, 1]
NOTempty
[2, 2, 2, 2]
[1, 1, 1, 1]
NOTempty
[3, 3, 3, 3]
[2, 2, 2, 2]
Seems like your FIFO is working as expected... What did you expect from the mean function?

Eigen rowwise cross product between arrays

I have two Eigen::ArrayX3d objects, that's N rows and 3 columns. To make this concrete, the first array consists of 3d velocities of N particles. The other one consists of magnetic field vectors at the position of each of the particles. I'm trying to compute the Lorentz force, v x B - this means I have to take each pair of rows and compute the cross product. In Python, this would mean simply doing numpy.cross(v, B).
I'm trying to figure out how to do this in Eigen and failing hard. It seems as though cross is defined for Matrix and Vectors only, but it doesn't really make sense to me to keep my data as a Matrix (though I'm of course open to suggestions).
Is there any reasonable way to perform this operation? I'd be very grateful for any pointers.
This setup is a good example::
ArrayX3d a(4,3);
ArrayX3d b(4,3);
a <<1,0,0,
0,1,0,
0,0,1,
1,0,0;
b <<0,1,0,
0,0,1,
1,0,0,
0,1,0;
A successful application of the a x b operation should just shift the 1's by 1 place to the right in each row.
I can get the result using a matrix or array:
MatrixX3d a(4, 3);
MatrixX3d b(4, 3);
a << 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0;
b << 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0;
for(int i = 0; i < a.rows(); i++){
cout << a.row(i).cross(b.row(i)) << endl;
}
With an array:
ArrayX3d a(4, 3);
ArrayX3d b(4, 3);
a << 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0;
b << 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0;
for(int i = 0; i < a.rows(); i++){
cout << a.row(i).matrix().cross(b.matrix().row(i)) << endl;
}
The output:
0 0 1
1 0 0
0 1 0
0 0 1
This result could be saved into a matrix or array for each row.

Multiplication of each matrix column by each vector element using Eigen C++ Library

I need to multiply each matrix column by each vector element using Eigen C++ library. I tried colwise without success.
Sample data:
Eigen::Matrix3Xf A(3,2); //3x2
A << 1 2,
2 2,
3 5;
Eigen::Vector3f V = Eigen::Vector3f(2, 3);
//Expected result
C = A.colwise()*V;
//C
//2 6,
//4 6,
//6 15
//this means C 1st col by V first element and C 2nd col by V 2nd element.
Matrix A can have 3xN and V Nx1. Meaning (cols x rowls).
This is what I would do:
Code
Eigen::Matrix3Xf A(3, 2); // 3x2
A << 1, 2, 2, 2, 3, 5;
Eigen::Vector3f V = Eigen::Vector3f(1, 2, 3);
const Eigen::Matrix3Xf C = A.array().colwise() * V.array();
std::cout << C << std::endl;
Example output:
1 2
4 4
9 15
Explanation
You were close, the trick is to use .array() to do broadcasting multiplications.
colwiseReturnType doesn't have a .array() method, so we have to do our colwise shenanigans on the array view of A.
If you want to compute the element-wise product of two vectors (The coolest of cool cats call this the Hadamard Product), you can do
Eigen::Vector3f a = ...;
Eigen::Vector3f b = ...;
Eigen::Vector3f elementwise_product = a.array() * b.array();
Which is what the above code is doing, in a columnwise fashion.
Edit:
To address the row case, you can use .rowwise(), and you'll need an extra transpose() to make things fit
Eigen::Matrix<float, 3, 2> A; // 3x2
A << 1, 2, 2, 2, 3, 5;
Eigen::Vector2f V = Eigen::Vector2f(2, 3);
// Expected result
Eigen::Matrix<float, 3, 2> C = A.array().rowwise() * V.transpose().array();
std::cout << C << std::endl;
Example output:
2 6
4 6
6 15
In other words, you want to scale each column by a different factor, that is, apply a non uniform scaling. Scaling are best represented as a diagonal matrix, thus:
C = A * V.asDiagonal();
Since Eigen is based on expression template, this does not create any temporary and amount to a code similar to Jacob's answer:
C = A.array().rowwise() * V.transpose().array();

Equivalent function of Matlab's filter2 in OpenCV

I need to convert the following matlab code into OpenCV and obtain exactly the same result.
In matlab:
A = [1 2 3];
f = [4 5 6];
result = filter2(f, A);
This gives out as:
result = [17 32 23]
In OpenCV, I tried these lines:
cv::Mat A = (cv::Mat_<float>(1, 3) << 1, 2, 3);
cv::Mat f = (cv::Mat_<float>(1, 3) << 4, 5, 6);
cv::Mat result;
cv::filter2D(A, result, -1, f, cv::Point(-1, -1), 0, cv::BORDER_REPLICATE);
This gives me:
result = [21 32 41]
How can I obtain the same result as of Matlab?? I doubt the anchor point in OpenCV causes this difference, but I cannot figure out how to change it. Thanks in advance.
Use cv::BORDER_CONSTANT, which pads the array with zero rather than duplicating the neighboring element:
cv::filter2D(A, result, -1, f, cv::Point(-1, -1), 0, cv::BORDER_CONSTANT);
Result is:
result = [17, 32, 23]

OpenCV Mat::ones function

According to the docs, this function should return a Mat with all elements as ones.
Mat m = Mat::ones(2, 2, CV_8UC3);
I was expecting to get a 2x2 matrix of [1,1,1]. Instead, I got this:
[1, 0, 0] [1, 0, 0]
[1, 0, 0] [1, 0, 0]
Is this the expected behaviour?
It looks like Mat::ones() works as expected only for single channel arrays. For matrices with multiple channels ones() sets only the first channel to ones while the remaining channels are set to zeros.
Use the following constructor instead:
Mat m = Mat(2, 2, CV_8UC3, Scalar(1,1,1));
std::cout << m;
Edit. Calling
Mat m = Mat::ones(2, 2, CV_8UC3);
is the same as calling
Mat m = Mat(2, 2, CV_8UC3, 1); // OpenCV replaces `1` with `Scalar(1,0,0)`