Add vector to Matrix in Opencv - opencv

I have a question like this, ex:
I have a vector =
[1, 2, 3]
I have a matrix =
[1, 2, 3]
[4, 5, 6]
[7, 8, 9]
expected result is:
[1+1, 2+2, 3+3]
[4+1, 5+2, 6+3]
[7+1, 8+2, 9+3]
it is to "add vector (or row) to matrix".
Is there an API or convenient way?
ps: I am coding in C++.

You can't add a vector to a matrix.
You can add matrix and scalar or matrix and matrix.
To do what you want, you can add your row matrix to each row of your 3x3 matrix :
unsigned char values[9] = { 1,2,3,4,5,6,7,8,9 };
cv::Mat matrix(3,3,CV_8UC1, values);
unsigned char rowval[3] = { 1,2,3 };
cv::Mat rowmat(1,3,CV_8UC1, rowval);
std::cout << "input:\n";
std::cout << matrix << "\n";
std::cout << "rowmat:\n";
std::cout << rowmat << "\n";
for(int i=0;i<3;i++)
matrix.row(i) += rowmat;
std::cout << "result:\n";
std::cout << matrix << "\n";
Which outputs:
input:
[ 1, 2, 3;
4, 5, 6;
7, 8, 9]
rowmat:
[ 1, 2, 3]
result:
[ 2, 4, 6;
5, 7, 9;
8, 10, 12]

There is now straightfoward way of doing this. The two of many approaches I can think of are: mat addition and subsequent col addition.
Mat addition:
cv::Mat a = (cv::Mat_<uchar>(3, 3) << 1, 2, 3, 4, 5, 6, 7, 8, 9);
cv::Mat b = (cv::Mat_<uchar>(3, 3) << 1, 2, 3, 1, 2, 3, 1, 2, 3);
cv::Mat r = a + b;
Per col addition:
std::vector s{1,2,3};
for (int col = 0; col < a.cols; ++col)
{
a.col(col) += s[col];
}

Related

Unable to access 6 channel Mat of type 16UC(6)

I am unable to access a Mat of type 16UC(6). Below is the code used to iterate over the Mat.
//6 channel Mat
int cols=1280, rows=720;
Mat mat1=Mat(cols, rows, CV_16UC(6), Scalar::all(0));
Mat grid(Size(cols, rows), CV_16UC2, Scalar::all(0));
//create a grid of numbers - the value of each pixel in the grid
contains the coordinate of the pixel
for (int i = 0; i < grid.rows; ++i) {
for (int j = 0; j < grid.cols; ++j) {
grid.at<Vec2s>(i, j)[0] = (ushort)j;
grid.at<Vec2s>(i, j)[1] = (ushort)i;
}
}
vector<Mat> imgs(2); //create copies of the grid for each image
for(int i=0;i<2;i++){
imgs[i] = grid.clone();
}
//Values in Mat1 are filled with values of imgs[0] and imgs[1] using
// some logic.
int rows=mat1.rows;
int channels=mat1.channels();
int cols=mat1.cols * channels;
uchar* p;
for(int i=0;i<rows;i++){
p=mat1.ptr<uchar>(i);
for(int j=0;j<cols;j+=6){
cout<<"Value 0 :"<<p[j]<<endl;
cout<<"Value 1 :"<<p[j+1]<<endl;
cout<<"Value 2 :"<<p[j+2]<<endl;
cout<<"Value 3 :"<<p[j+3]<<endl;
cout<<"Value 4 :"<<p[j+4]<<endl;
cout<<"Value 5 :"<<p[j+5]<<endl;
}
}
But im getting ^E and ^# as values. When tried casting to (int) I am getting all zeros.
I am able to access the Mat properly using MatIterator. I am not sure where I went wrong, there must be some issue with the Mat ype and the way I am trying to access the value.Can anyone help me in solving the issue.
You have:
grid.at<Vec2s>(i, j)[0] = (ushort)j;
Vec2s is for shorts, but you have Unsigned Short matrix. You should use Vec2w (not sure who came with the w... or why) that is for unsigned short.
This can be rewritten as:
grid.at<cv::Vec2w>(i, j)[0] = static_cast<ushort>(j);
Then, you show uchar values from the 16U matrix... each uchar is 8bit and each pixel is 16bit...
Here is an example of how you can access each pixel with iterators in a CV_16UC(6) matrix.
// create dummy 3x3 matrix
cv::Mat b(3,3,CV_16UC(6));
// use the templated functions begin and end (you may templated with <ushort>
// directly and it will represent the value of each channel of each pixel)
for (auto it = b.begin<cv::Vec<ushort, 6>>(); it != b.end<cv::Vec<ushort, 6>>(); ++it)
{
// assign some values (this part can be skipped if it is already filled)
(*it)[0] = 5;
(*it)[1] = 7;
(*it)[2] = 8;
(*it)[3] = 9;
(*it)[4] = 1;
(*it)[5] = 2;
// Print the Vec<ushort, 6>, OpenCV already has a way to print it
std::cout << *it << std::endl;
}
And the result of this small code is:
[5, 7, 8, 9, 1, 2]
[5, 7, 8, 9, 1, 2]
[5, 7, 8, 9, 1, 2]
[5, 7, 8, 9, 1, 2]
[5, 7, 8, 9, 1, 2]
[5, 7, 8, 9, 1, 2]
[5, 7, 8, 9, 1, 2]
[5, 7, 8, 9, 1, 2]
[5, 7, 8, 9, 1, 2]
Which is what we expect. You may have notice that I used cv::Vec<ushort, 6>, cv::Vec can be templated with any number of channels (probably there is a limit) and any type (I have only tested it with native numeric types). Actually cv::Vec2w or cv::Vec2s are just typedef of cv::Vec and cv::Vec respectively, and you can also create your typedef if you use it all over the code.
using Vec6w = cv::Vec<ushort, 6>;
and then you can replace it in the for loop:
...
for (auto it = b.begin<Vec6w>(); it != b.end<Vec6w>(); ++it)
...
and achieve the same result

Element-wise power using OpenCV

I am currently reading this book. The author wrote a code snippet on page 83 in order to (if i understand it correctly) calculate the element-wise power of two matrices. But i think the code doesn't fulfill its purpose because the matrix dst does not contain the element-wise power after the execution.
Here is the original code:
const Mat* arrays[] = { src1, src2, dst, 0 };
float* ptrs[3];
NAryMatIterator it(arrays, (uchar**)ptrs);
for( size_t i = 0; i < it.nplanes; i++, ++it )
{
for( size_t j = 0; j < it.size; j++ )
{
ptrs[2][j] = std::pow(ptrs[0][j], ptrs[1][j]);
}
}
Since the parameter of the constructor or cv::NAryMatIterator is const cv::Mat **, i think change of values in the matrix dst is not allowed.
I tried to assign ptrs[2][j] back in dst but failed to get the correct indices of dst. My questions are as follows:
Is there a convenient method for the matrix element-wise power, like A .^ B in Matlab?
Is there a way to use cv::NAryMatIterator to achieve this goal? If no, then what is the most efficient way to implement it?
You can get this working by converting the src1, src2 and dst to float (CV_32F) type matrices. This is because the code treats them that way in float* ptrs[3];.
An alternative implementation using opencv functions log, multiply and exp is given at the end.
As an example for your 2nd question,
Mat src1 = (Mat_<int>(3, 3) <<
1, 2, 3,
4, 5, 6,
7, 8, 9);
Mat src2 = (Mat_<uchar>(3, 3) <<
1, 2, 3,
1, 2, 3,
1, 2, 3);
Mat dst = (Mat_<float>(3, 3) <<
1, 2, 3,
4, 5, 6,
7, 8, 9);
src1.convertTo(src1, CV_32F);
src2.convertTo(src2, CV_32F);
cout << "before\n";
cout << dst << endl;
const Mat* arrays[] = { &src1, &src2, &dst, 0 };
float* ptrs[3];
NAryMatIterator it(arrays, (uchar**)ptrs);
for( size_t i = 0; i < it.nplanes; i++, ++it )
{
for( size_t j = 0; j < it.size; j++ )
{
ptrs[2][j] = std::pow(ptrs[0][j], ptrs[1][j]);
}
}
cout << "after\n";
cout << dst << endl;
outputs
before
[1, 2, 3;
4, 5, 6;
7, 8, 9]
after
[1, 4, 27;
4, 25, 216;
7, 64, 729]
If you remove the src1.convertTo(src1, CV_32F); or src2.convertTo(src2, CV_32F);, you won't get the desired result. Try it.
If this is a separate function, don't place the convertTo within the function, as it modifies the image representation, that could affect later operations. Instead, use convertTo on temporary Mats, like
Mat src132f, src232f, dst32f;
src1.convertTo(src132f, CV_32F);
src2.convertTo(src132f, CV_32F);
dst.convertTo(dst32f, CV_32F);
pow_mat(&src132f, &src232f, &dst32f); /* or whatever the name */
As for your first question, I'm not aware of such function. But you can try something like
Mat tmp;
cv::log(src1, tmp);
cv::multiply(src2, tmp, dst);
cv::exp(dst, dst);
using the relation that c = a^b is equivalent to c = e^(b.ln(a)). Here, the matrices should have type 32F or 64F. This produces
[1, 4, 27.000002;
4, 25.000002, 216.00002;
6.9999995, 64, 729.00006]
for the example above.

What are the different ways of constructing cv::Mat?

OpenCV Version 3.2.0
I am reading Bradski and trying to make Different cv::Mat constructors - single channel.
Can someone please tell, why the constructors do not work?
float data1[6] = {1,2,3,4,5,6};
float data2[6] = {10,20,30,40,50,60};
float data3[6] = {100,200,300,400,500,600};
cv::Mat mat1(3,4,CV_32FC1); //OK
cv::Mat mat2(3,4,CV_32FC1,cv::Scalar(33.3)); //OK
cv::Mat mat3(3,4,CV_32FC1,data1,sizeof(float)); //OK
cv::Mat mat4(cv::Size(3,4),CV_32FC1); //OK
cv::Mat mat5(cv::Size(3,4),CV_32FC1,cv::Scalar(66.6)); //OK
cv::Mat mat6(cv::Size(3,4),CV_32FC1,data2,sizeof(float)); //OK
int sz[] = {8, 8, 8};
cv::Mat bigCube1(3, sz, CV_32FC1); // OK
cv::Mat bigCube2(3, sz, CV_32FC1, cv::Scalar::all(99)); // OK
cv::Mat bigCube3(3, sz, CV_32FC1, data3, 4); // Not OK, How to initialise a 3D from data?
std::cout << mat1 << std::endl << mat2 << std::endl << mat3 << std::endl << mat4 << std::endl << mat5 << std::endl << mat6 << std::endl; // OK
std::cout << bigCube1.at<float>(10,10,10) << std::endl << bigCube2.at<float>(10,10,10) << std::endl; // OK
cv::Mat img_rgb = cv::imread("lena.jpg", CV_LOAD_IMAGE_COLOR);
std::vector<cv::Range> ranges(3, cv::Range(2,3));
cv::Mat roiRange( img_rgb, cv::Range(100, 300), cv::Range(0, 512)); //OK
cv::Mat roiRect( img_rgb, cv::Rect(0,100,512,200)); // OK
cv::Mat roiRangeMultiple( bigCube1, ranges); // OK
cv::namedWindow("range", CV_WINDOW_AUTOSIZE);
imshow("range", roiRange); // OK
cv::namedWindow("rect", CV_WINDOW_AUTOSIZE);
imshow("rect", roiRect); // OK
std::cout << roiRangeMultiple.at<float>(0,1,1); // Not OK. Expecting a float value as answer
cv::waitKey(0);
The corresponding answers are:
[4.6634629e-10, 0, 0, 0;
0, 0, 0, 0;
127.62516, 2.8025969e-45, 0, 0]
[33.299999, 33.299999, 33.299999, 33.299999;
33.299999, 33.299999, 33.299999, 33.299999;
33.299999, 33.299999, 33.299999, 33.299999]
[1, 2, 3, 4;
2, 3, 4, 5;
3, 4, 5, 6]
[0, 0, 0;
0, 0, 0;
0, 0, 0;
0, 0, 0]
[66.599998, 66.599998, 66.599998;
66.599998, 66.599998, 66.599998;
66.599998, 66.599998, 66.599998;
66.599998, 66.599998, 66.599998]
[10, 20, 30;
20, 30, 40;
30, 40, 50;
40, 50, 60]
0 // bigCube1
99 // bigCube2
And then the corresponding answers for lena.jpg is the cropped version from Range and Rect. I dont know how to use the ranges though.
Multiple issues.
cv::Mat mat3(3,4,CV_32FC1,data1,sizeof(float));
This will crash in debug mode, failing an assertion. Even though this is not in the documentation, the step size must be at least the length of a row (i.e. no overlap is allowed).
The correct code for this scenario would be something like.
float data1[12] = { 1, 2, 3, 4, 2, 3, 4, 5, 3, 4, 5, 6 };
cv::Mat mat3(3, 4, CV_32FC1, data1, 4 * sizeof(float));
cv::Mat mat6(cv::Size(3,4),CV_32FC1,data2,sizeof(float));
Similar situation as in previous case. Also note that this produces a differently shaped array -- previous was 3 rows, 4 columns, this one has 4 rows and 3 columns (see docs of cv::Size).
float data2[12] = { 10, 20, 30, 40, 20, 30, 40, 50, 30, 40, 50, 60 };
cv::Mat mat6(cv::Size(3, 4), CV_32FC1, data2, 3 * sizeof(float));
cv::Mat bigCube1(3, sz, CV_8UC1);
std::cout << bigCube1 << std::endl;
Formatting of arrays with more than 2 dimensions is not supported.
You can test that the array was correctly created by manually printing all the values:
for (auto const& v : cv::Mat1b(bigCube2)) {
std::cout << uint(v) << " ";
}
std::cout << "\n";
cv::Mat bigCube3(3, sz, CV_32FC1, data3, 4);
The problem here is the last parameter. From the docs
cv::Mat::Mat(int ndims,
const int * sizes,
int type,
void * data,
const size_t * steps = 0
)
steps - Array of ndims-1 steps in case of a multi-dimensional array (the last step is always set to the element size). If not specified, the matrix is assumed to be continuous.
You're not passing an array of steps as the last parameter (only a single integer)
you don't pass enough data
and the rows would again overlap.
One way to do this would be something like
float data3[8 * 8 * 8];
// Populate the data with sequence 0..511
std::iota(std::begin(data3), std::end(data3), 0.0f);
int sz[] = { 8, 8, 8 };
size_t steps[] = { 8 * 8 * sizeof(float), 8 * sizeof(float) };
cv::Mat bigCube3(3, sz, CV_32FC1, data3, steps);
cv::Mat bigCube1(3, sz, CV_8UC1);
// ...
std::cout << roiRangeMultiple.at<float>(0,1,1); // Not OK. Expecting a float value as answer
The data type is CV_8UC1, so each element is an unsigned char. That means you shouldn't be extracting float values from it. Your expectation is incorrect. (Now I see you changed the code in your question).
Also, note that with cv::Range "start is an inclusive left boundary of the range and end is an exclusive right boundary of the range". Since you extract cv::Range(2,3) in each axis, the resulting Mat is 1 x 1 x 1. Hence, you're accessing elements out of range (again, this would trigger a debug mode assertion).
std::cout << roiRangeMultiple.at<unsigned char>(0,0,0);
After the change you have the correct type. However notice that you never initialize bigCube1. You most likely get a 0.0f as a result, which will print as 0. You can try this yourself, just execute std::cout << 0.0f; and see.

What's the difference between Mat::clone and Mat::copyTo?

I know 'copyTo' can handle mask. But when mask is not needed, can I use both equally?
http://docs.opencv.org/modules/core/doc/basic_structures.html#mat-clone
Actually, they are NOT the same even without mask.
The major difference is that when the destination matrix and the source matrix have the same type and size, copyTo will not change the address of the destination matrix, while clone will always allocate a new address for the destination matrix.
This is important when the destination matrix is copied using copy assignment operator before copyTo or clone. For example,
Using copyTo:
Mat mat1 = Mat::ones(1, 5, CV_32F);
Mat mat2 = mat1;
Mat mat3 = Mat::zeros(1, 5, CV_32F);
mat3.copyTo(mat1);
cout << mat1 << endl;
cout << mat2 << endl;
Output:
[0, 0, 0, 0, 0]
[0, 0, 0, 0, 0]
Using clone:
Mat mat1 = Mat::ones(1, 5, CV_32F);
Mat mat2 = mat1;
Mat mat3 = Mat::zeros(1, 5, CV_32F);
mat1 = mat3.clone();
cout << mat1 << endl;
cout << mat2 << endl;
Output:
[0, 0, 0, 0, 0]
[1, 1, 1, 1, 1]
This is the implementation of Mat::clone() function:
inline Mat Mat::clone() const
{
Mat m;
copyTo(m);
return m;
}
So, as #rotating_image had mentioned, if you don't provide mask for copyTo() function, it's same as clone().
Mat::copyTo is for when you already have a destination cv::Mat that (may be or) is already allocated with the right data size. Mat::clone is a convenience for when you know you have to allocate a new cv::Mat.
copyTo doesn't allocate new memory in the heap which is faster.

can't use copyTo to copy a rectangular matrix into a 3D matrix

I have 5rows x 4cols cv::Mat:
int output_size[] = {5,4};
cv::Mat im1(2, output_size, CV_32FC1);
float* ptr = (float*)im1.data;
for (unsigned int r = 0; r < output_size[0]; r++) {
for (unsigned int c = 0; c < output_size[1]; c++) {
*ptr = (r*im1.size.p[1])+c;
std::cout << *ptr++ << ",";
}
std::cout << std::endl;
}
So that matrix looks like this:
[ 0, 1, 2, 3,
4, 5, 6, 7,
8, 9, 10, 11,
12, 13, 14, 15,
16, 17, 18, 19]
Furthermore, I have another 3depth x 5rows x 4cols cv::Mat:
int output_size2[] = {3,5,4};
cv::Mat im2(3, output_size2, CV_32FC1);
im2 = 0;
Now I want to copy im1 into, let's say, the second layer of im2. I do the following:
cv::Range rngs[] = {cv::Range(1,2), cv::Range::all(), cv::Range::all()};
cv::Mat dst = im2(rngs);
im1.copyTo(dst);
This doesn't seem to work. im1.copyTo(dst) has no effect on im2 - all the second layer values remain zero after the operation. After some introspection, it seems like opencv is finding that since the size of dst is 1x5x4 and not 5x4, it reassigns dst.
What would be the right way to copy a rectangular matrix into one layer of a 3D matrix?
Ok this works:
void* ptr = im2.data + im2.step[0]*1;
memcpy(ptr, (void*)im1.data, im1.total()*sizeof(float));
but is there an "opencv" way to solve it.
If you have n 2-dim images and you want to use them as layers in a 3D matrix that has a third dimension of size n you can use cv::merge. see documentation.
See also:
cv:split documentation

Resources