how do I do boolean operation on mat, such as mat3 = mat1 & mat2? - opencv

I would like to do boolean operation on mat structure.
such as
"and/or/xor" ing two binary images
is it possible to do that ?

just do it !
Mat a,b;
Mat c = a & b;

For the AND and OR operation, here is the syntax :
A | B /* OR operator */
A & B /* AND operator */
The XOR operator doesn't exists, but still, you can do it like this :
(A | B) & (A != B) /* Pseudo-XOR operator */
You may want to check the Compare function to do what you want

All logical operations you have to base on the following functions.
First group of them, consists of three, four parameters functions: bitwise_and(), bitwise_or(), bitwise_xor().
For example:
C++: void bitwise_and(InputArray src1, InputArray src2, OutputArray dst, InputArray mask=noArray())
Python: cv2.bitwise_and(src1, src2[, dst[, mask]]) → dst
There is also bitwise_not(), which takes three parameters.
C++: void bitwise_not(InputArray src, OutputArray dst, InputArray mask=noArray())
Python: cv2.bitwise_not(src[, dst[, mask]]) → dst
Documentation: https://docs.opencv.org/2.4/modules/core/doc/operations_on_arrays.html

Related

Copy Vector of Contour Points into Mat

I am using OpenCV 3.1 with VS2012 C++/CLI.
I have stored the result of a finContours call into:
std::vector<std::vector<Point>> Contours;
Thus, Contours[0] is a vector of the contour points of the first contour.
Contours[1] is a vector of the contour points of the second vector, etc.
Now, I want to load one of the contours into a Mat Based on Convert Mat to vector <float> and Vector<float> to mat in opencv I thought something like this would work.
Mat testMat=Mat(Images->Contours[0].size(),2,CV_32FC1);
memcpy(testMat.data,Images->Contours[0].data(),Images->Contours[0].size()*CV_32FC1);
I specified two columns because I each underlying pint must be composed of both an X point and a Y point and each of those should be a float. However, when I access the Mat elements, I can see that the first element is not the underlying data but the total number of contour points.
Any help on the right way to accomplish this appreaciated.
You can do that with:
Mat testMat = Mat(Images->Contours[0]).reshape(1);
Now testMat is of type CV_32SC1, aka of int. If you need float you can:
testMat.convertTo(testMat, CV_32F);
Some more details and variants...
You can simply use the Mat constructor that accepts a std::vector:
vector<Point> v = { {0,1}, {2,3}, {4,5} };
Mat m(v);
With this, you get a 2 channel matrix with the underlying data in v. This means that if you change the value in v, also the values in m change.
v[0].x = 7; // also 'm' changes
If you want a deep copy of the values, so that changes in v are not reflected in m, you can use clone:
Mat m2 = Mat(v).clone();
Your matrices are of type CV_32SC2, i.e. 2 channels matrices of int (because Point uses int. Use Point2f for float). If you want a 2 columns single channel matrix you can use reshape:
Mat m3 = m2.reshape(1);
If you want to convert to float type, you need to use convertTo:
Mat m4;
m2.convertTo(m4, CV_32F);
Here some working code as a proof of concept:
#include <opencv2\opencv.hpp>
#include <vector>
using namespace std;
using namespace cv;
int main()
{
vector<Point> v = { {0,1}, {2,3}, {4,5} };
// changes in v affects m
Mat m(v);
// changes in v doesn't affect m2
Mat m2 = Mat(v).clone();
// m is changed
v[0].x = 7;
// m3 is a 2 columns single channel matrix
Mat m3 = m2.reshape(1);
// m4 is a matrix of floats
Mat m4;
m2.convertTo(m4, CV_32F);
return 0;
}

incorrect size of vector when converting Mat to Vector

I'm trying to convert the result Mat of templateMatch with the following code (which was found at: this question):
void convertmatVec(const cv::Mat& m, std::vector<uchar>& v) {
if (m.isContinuous()) {
v.assign(m.datastart, m.dataend);
}
else {
printf("failed to convert / not continuous");
return;
}
}
and when I check the size of the output it's not the same as the product of result's columns and rows (which is the same when I try to convert another Mat that I created):
result:
another Mat created with:
cv::Mat test(result.size(),false);
test.setTo(cv::Scalar(255));
which is then converted shows that the size is the same as the product:
So my question is how can I get the result's data so I can process it futher because I'm assuming the size of the vector should be the same as the product which it clearly isn't.
EDIT1: Added templateMatching code
void matchTemplatenoRotation(cv::Mat src, cv::Mat templ) {
cv::Mat img_display, result;
src.copyTo(img_display);
int result_cols = src.cols - templ.cols + 1;
int result_rows = src.rows - templ.rows + 1;
result.create(result_rows, result_cols, CV_32FC1);
cv::matchTemplate(src, templ, result, CV_TM_SQDIFF_NORMED);
cv::normalize(result, result, 0, 1, cv::NORM_MINMAX, -1, cv::Mat());
cv::Point minLoc, maxLoc;
double minVal, maxVal;
cv::minMaxLoc(result, &minVal, &maxVal, &minLoc, &maxLoc, cv::Mat());
cv::Point matchLoc = minLoc;
//end of templatematching
EDIT2 follow up question:
How come when I create another Mat with following code:
cv::Mat test(cv::Size(result.cols, result.rows),true);
test.setTo(cv::Scalar(255));
//cv::imshow("test3", test);
std::vector<float> testVector;
convertmatVec(test, testVector);
the vector size is as following:
You have 4 times the expected number of elements in your vector because your matrix is of type CV_32FC1. If you look at the type of m.datastart and m.dataend you will see that they are uchar* and not float* as you expect.
To correct this, change v.assign(m.datastart, m.dataend); to v.assign((float*)m.datastart, (float*)m.dataend);. And you will need to pass a vector of float instead of a vector<uchar>.
Of course, your conversion function will only work for float type matrices. you could add some tests to detect the type of the matrix inside the function.
To answer your follow up question, it appears that you have the inverse problem. You are passing a CV_8U type matrix to a function that expects a CV_32F type one. Use your old conversion function for 8-bit matrices and use the fix I suggested for 32-bit floating-values matrices. You can also add a test inside the conversion function to automatically choose the right conversion. I also advise you to read a bit on OpenCV Mat class to understand better what type of data is in your matrices.
It looks like your const cv::Mat& m has four channels.
Another thing to consider: if this Mat is the result of a matchTemplate(), then you should be using a vector<float>, instead of vector<uchar>.

Adding a scalar to a Mat object

So I'm trying to add a scalar value to all elements of a Mat object in openCV, however for raw_t_ubit8 and raw_t_ubit16 types I get wrong results. Here's the code.
Mat A;
//Initialize Mat A;
A = A + 0.1;
The Matrix is initially
The result of the addition is exactly the same matrix. This problem does not occur when I try to add scalars to raw_t_real types of matrices. By raw_t_ubit8 I mean the depth is CV_8UC1
If, as you mentioned in the comments, the contained values are scaled in the matrix to fit the integer domain 0..255, then you should also scale the scalar value you sum. Namely:
A = A + cv::Scalar(round(0.1 * 255) );
Or even better:
A += cv::Scalar(round(0.1 * 255) );
Note that cv::Scalar, as pointed out in the comments by Miki, is in any case made from a double (it's a cv::Scalar_<double>).
The rounding could be omitted, but then you leave the choice on how to convert your double into integer to the function implementation.
You should also check what happens when the values saturate.
Documentation for Opencv matrix expressions.
As stated in the comments and in #Antonio's answer, you can't add 0.1 to an integer.
If you are using CV_8UC1 matrices, but you want to work with floating points values, you should multiply by 255.
Mat1b A; // <-- type CV_8UC1
...
A += 0.1 * 255;
If the result of the operation need to be casted, as in this case, then ultimately saturated_cast is called.
This is equivalent to #Antonio's answer, but it results in cleaner code (at least for me).
The same code will be used, either if you sum a double or a Scalar. A Scalar object will be created in both ways using:
template<typename _Tp> inline
Scalar_<_Tp>::Scalar_(_Tp v0)
{
this->val[0] = v0;
this->val[1] = this->val[2] = this->val[3] = 0;
}
However if you need to sum exactly 0.1 to your matrix (and not to scale it by 255), you need to convert your matrix to CV_32FC1:
#include <opencv2/opencv.hpp>
using namespace cv;
int main(int, char** argv)
{
Mat1b A = (Mat1b(3,3) << 1,2,3,4,5,6,7,8,9);
Mat1f F;
A.convertTo(F, CV_32FC1);
F += 0.1;
return 0;
}

Mat and Vec_ types multiplication

Is there any easy way to multiplicate Mat and Vec_? (Provided, that they have proper sizes, e.g.:
Mat_<double> M = Mat(3,3,CV_32F);
Vec3f V=(1,2,3);
result = M*V //?
Maybe there is some easy method of creating row (or col) Mat based on Vec3?
You can't just multiply Mat and Vec (or, more generally, Matx_) elements. Cast the Vec object to Mat:
Mat_<float> M = Mat::eye(3,3,CV_32F);
Vec3f V=(1,2,3);
Mat result = M*Mat(V);
Also, I noticed an error in your code: when constructing M, the type CV_32F corresponds to float elements, not double. This is also corrected in my code example.
Hope that it helps.

OpenCV vector to Mat but not element->row

There is a very simple way to construct a Mat from a vector...just by doing:
vector<int> myVector;
Mat myMatFromVector(myVector,true); //the boolean is to define if you want to copy the data
The problem with this contructor is that each vector's element will be placed in each row of the Matrix. What I want is each element of my vector to be placed in each column of the matrix.
As is:
vector<int> = [1,2,3,4]
Matrix = [1;2;3;4]
I want:
vector<int> = [1,2,3,4]
Matrix = [1,2,3,4]
Either specify the shape and type of the Matrix and pass the vector data
// constructor for matrix headers pointing to user-allocated data
Mat(int _rows, int _cols, int _type, void* _data, size_t _step=AUTO_STEP);
Mat(Size _size, int _type, void* _data, size_t _step=AUTO_STEP);
Or call reshape on the Mat to swap the number of row sand columns ( doesn't change any data)
// creates alternative matrix header for the same data, with different
// number of channels and/or different number of rows. see cvReshape.
Mat reshape(int _cn, int _rows=0) const;
The matrix formed by reflecting a matrix through its main diagonal (ie interchanging the rows and columns) is called the transpose. Using OpenCV, you can easily obtain the transpose of a matrix A as:
Mat A;
Mat A_transpose = A.t();
If A is [1; 2; 3; 4], A_transpose will be [1, 2, 3, 4] as required.
So, you could either create a transposed copy of your matrix after converting it from the vector, or you could create it easily when subsequently required in your calculations.
Mat A, B;
Mat answer = A.t() * B;

Resources