I have 3-dimension matrix:
const int n_mat_size = 5;
const int n_mat_sz[] = { n_mat_size , n_mat_size, n_mat_size };
cv::Mat m1(3, n_mat_sz, CV_32FC1);
Now I'd like to iterate its planes and expect that it should be three two-dimensional matrices:
const cv::Mat* arrays[] = { &m1, 0 };
cv::Mat planes[3];
cv::NAryMatIterator it(arrays, planes);
std::cout << it.nplanes << ", " << it.planes[0].rows << ", " << it.planes[0].cols;
I expect to get output "3, 5, 5", but instead I get "1, 1, 125". Where is the slice of matrix?
Because the matrix m1 is continuous, there is only one plane (or slice).
Please refer to the documentation for NAryMatIterator:
It iterates through the slices (or planes), not the elements, where "slice" is a continuous part of the arrays.
For example, the matrix m2 in the following code is not continuous:
const int n_mat_size = 5;
const int n_mat_sz[] = { n_mat_size , n_mat_size, n_mat_size };
cv::Mat m1(3, n_mat_sz, CV_32FC1);
// Get plane 2 and 3 of m1
// and row 2, row 3 and row 4 of every selected plane
// m2 is not continuous
cv::Mat m2 = m1(cv::Range(2,4), cv::Range(2,5));
const cv::Mat* arrays[] = { &m2, 0 };
cv::Mat planes[3];
cv::NAryMatIterator it(arrays, planes);
std::cout << it.nplanes << ", " << it.planes[0].rows << ", " << it.planes[0].cols << std::end;
The output of the above code is: 2, 1, 15.
Note that the number of rows of every plane is always 1 and the number of columns is the number of elements contained in the plane.
There is a statement:
planes[i] = Mat(1, (int)size, A.type(), A.data);
in the function void NAryMatIterator::init, which can be found at https://github.com/opencv/opencv/blob/master/modules/core/src/matrix.cpp#L4596 .
The above statement sets the size of the plane.
To separate the matrix you gave into planes, you could use cv::InputArray::getMatVector.
The following code shows it usage.
int main()
{
const int n_mat_size = 3;
const int n_mat_sz[] = { n_mat_size , n_mat_size, n_mat_size };
cv::Mat m1(3, n_mat_sz, CV_8U);
cv::MatIterator_<uchar> it = m1.begin<uchar>();
cv::MatIterator_<uchar> end = m1.end<uchar>();
for (uchar i = 0; it != end; ++it, ++i)
{
*it = i;
}
cv::InputArray arr(m1);
std::vector<cv::Mat> planes;
arr.getMatVector(planes);
for (size_t i = 0; i < planes.size(); ++i)
{
std::cout << "-------" << std::endl
<< planes[i] << std::endl << "******" << std::endl;
}
}
Its output is as follows:
-------
[ 0, 1, 2;
3, 4, 5;
6, 7, 8]
******
-------
[ 9, 10, 11;
12, 13, 14;
15, 16, 17]
******
-------
[ 18, 19, 20;
21, 22, 23;
24, 25, 26]
******
Maybe the easiest one is to use the method cv::Mat::row(int). The corresponding code is:
int main()
{
const int n_mat_size = 3;
const int n_mat_sz[] = { n_mat_size , n_mat_size, n_mat_size };
cv::Mat m1(3, n_mat_sz, CV_8U);
cv::MatIterator_<uchar> it = m1.begin<uchar>();
cv::MatIterator_<uchar> end = m1.end<uchar>();
for (uchar i = 0; it != end; ++it, ++i)
{
*it = i;
}
int n = m1.size[0];
for (int i = 0; i < n; ++i)
{
cv::Mat three_d_plane = m1.row(i);
// three_d_plane has a size 1x3x3
// std::cout supports only 2-d matrix. Therefore, we change it to 2-d here
cv::Mat two_d_plane(three_d_plane.size[1], three_d_plane.size[2], three_d_plane.type(), three_d_plane.data);
std::cout << two_d_plane << std::endl << "----" << std::endl;
}
}
The output is
[ 0, 1, 2;
3, 4, 5;
6, 7, 8]
----
[ 9, 10, 11;
12, 13, 14;
15, 16, 17]
----
[ 18, 19, 20;
21, 22, 23;
24, 25, 26]
----
Related
I want to copy one OutputArrayOfArrays object to another. Something like this:
void function(OutputArrayOfArrays contour) {
std::vector<std::vector<cv::Point>> contours;
OutputArrayOfArrays _contour(contours);
....Doing something....
contour = _contour;
}
But I'm getting following error:
no viable overloaded '='
contour = _contour;
I do not think you can do it for varying types. You will have to handle types specifically. Take a look at functions that use this type in the opencv sources and you will see they are handled in specific ways: for example, findContours and split functions.
For your specific case of std::vector<std::vector<cv::Point>>, you can follow the findContours way of doing it. Below I have written a simple function to demonstrate this.
void testfunction(OutputArrayOfArrays contour) {
std::vector<std::vector<cv::Point>> contours;
// fill some data
std::vector<cv::Point> v1;
v1.push_back(cv::Point(10, 1));
v1.push_back(cv::Point(11, 2));
v1.push_back(cv::Point(12, 3));
std::vector<cv::Point> v2;
v2.push_back(cv::Point(20, 10));
v2.push_back(cv::Point(21, 20));
contours.push_back(v1);
contours.push_back(v2);
// output
contour.create(contours.size(), 1, 0, -1, true);
for (size_t i = 0; i < contours.size(); i++) {
std::vector<cv::Point>& v = contours[i];
contour.create(v.size(), 1, CV_32SC2, i, true);
Mat m = contour.getMat(i);
for (size_t j = 0; j < v.size(); j++) {
m.at<int>(2*j) = v[j].x;
m.at<int>(2*j+1) = v[j].y;
}
std::cout << m << std::endl;
}
}
And the output looks like
[10, 1, 11, 2, 12, 3]
[20, 10, 21, 20]
2
3
[10, 1]
[11, 2]
[12, 3]
2
[20, 10]
[21, 20]
using the code
std::vector<std::vector<cv::Point>> contours;
testfunction(contours);
std::cout << contours.size() << std::endl;
for (size_t i = 0; i < contours.size(); i++) {
std::vector<cv::Point>& cont = contours[i];
std::cout << cont.size() << std::endl;
for (size_t j = 0; j < cont.size(); j++) {
std::cout << cont[j] << std::endl;
}
}
I checked the file here:
// get the corresponding Eigen tensors for data access
auto input_tensor = input.matrix<double>();
auto weights_tensor = weights.matrix<double>();
auto biases_tensor = biases.matrix<double>();
auto output_tensor = output->matrix<double>();
for (int ix_sample = 0; ix_sample < batch_samples; ix_sample++) {
for (int ix_unit = 0; ix_unit < units; ix_unit++) {
output_tensor(ix_sample, ix_unit) = 0;
for (int ix_input = 0; ix_input < input_feature_width; ix_input++) {
output_tensor(ix_sample, ix_unit) += input_tensor(ix_sample, ix_input) * weights_tensor(ix_input, ix_unit );
}
output_tensor(ix_sample, ix_unit) += biases_tensor(0, ix_unit);
}
}
And the one on Tensorflow tutorial:
// Create an output tensor
Tensor* output_tensor = NULL;
OP_REQUIRES_OK(context, context->allocate_output(0, input_tensor.shape(),
&output_tensor));
auto output_flat = output_tensor->flat<int32>();
// Set all but the first element of the output tensor to 0.
const int N = input.size();
for (int i = 1; i < N; i++) {
output_flat(i) = 0;
}
I am wondering if the output tensor is a 3d tensor, how could I slice it and assign its value vector-wise.
I find the slice method for Eigen:
Eigen::Tensor<int, 2> a(4, 3);
a.setValues({{0, 100, 200}, {300, 400, 500},
{600, 700, 800}, {900, 1000, 1100}});
Eigen::array<int, 2> offsets = {1, 0};
Eigen::array<int, 2> extents = {2, 2};
Eigen::Tensor<int, 1> slice = a.slice(offsets, extents);
cout << "a" << endl << a << endl;
=>
a
0 100 200
300 400 500
600 700 800
900 1000 1100
cout << "slice" << endl << slice << endl;
=>
slice
300 400
600 700
But not clear how could I use it here.
Cross post here
I have build two function with different name to drop the specfiy lines from difference Mat object, this is the code:
Mat drop_rows_int(Mat mat, vector<int> v) {
Mat mat_new = Mat::zeros(mat.rows - v.size(), mat.cols, CV_32SC1);
for (int i = 0, j = 0; i < mat.rows; i++) {
if (find(v.begin(), v.end(), i) != v.end())
{
continue;
}
else
{
int*pmat = mat.ptr<int>(i);
int*pmat_new = mat_new.ptr<int>(j);
for (int w = 0; w < mat.cols; w++) {
pmat_new[w] = pmat[w];
}
j++;
}
}
return mat_new;
}
Mat drop_rows_uchar(Mat mat, vector<int> v) {
Mat mat_new = Mat::zeros(mat.rows - v.size(), mat.cols, CV_8UC1);
for (int i = 0, j = 0; i < mat.rows; i++) {
if (find(v.begin(), v.end(), i) != v.end())
{
continue;
}
else
{
uchar*pmat = mat.ptr<uchar>(i);
uchar*pmat_new = mat_new.ptr<uchar>(j);
for (int w = 0; w < mat.cols; w++) {
pmat_new[w] = pmat[w];
}
j++;
}
}
return mat_new;
}
Then I can use it in my main() function like
int main()
{
Mat mat_uchar = (Mat_<uchar>(5, 4) << 5, 6, 0, 4, 0, 1, 9, 9, 100, 3, 5, 8, 200, 33, 1, 4, 8, 88, 23, 6);
Mat new_mat_uchar = drop_rows_uchar(mat_uchar, {2,4});
Mat mat_int = (Mat_<int>(5, 4) << 5, 6, 0, 4, 0, 1, 9, 9, 100, 3, 5, 8, 200, 33, 1, 4, 8, 88, 23, 6);
Mat new_mat_int = drop_rows_int(mat_int, { 2,4 });
return 0;
}
Yes, I made it. but as I know, the Mat can have 7 kinds of depth, such as CV_8U, CV_8S, CV_16U, CV_16S, CV_32S, CV_32F and CV_64F, So I have to build 7 functions with different name to do such thing?? Can anyone tell me how to use one function to implement it??
You cannot do that with cv::Mat. However, you can use cv::Mat_ and do some templating:
template<typename T>
cv::Mat_<T> drop_rows_int(cv::Mat_ mat, vector<int> v) {
...
}
And here you extract pointers of type T.
Just a piece of advice, for efficiency purposes I suggest sending the vector v as a const reference, if possible.
Here is the full solution:
#include "opencv/cv.h"
#include <vector>
#include <iostream>
template<typename T>
cv::Mat_<T> drop_rows(cv::Mat_<T> mat, const std::vector<int> &v) {
cv::Mat_<T> mat_new = cv::Mat_<T>::zeros(mat.rows - v.size(), mat.cols);
for (int i = 0, j = 0; i < mat.rows; i++) {
if (find(v.begin(), v.end(), i) != v.end())
continue;
else {
for (int w = 0; w < mat.cols; w++) {
mat_new(j, w) = mat(i, w);
}
j++;
}
}
return mat_new;
}
int main() {
cv::Mat_<uchar> mat = (cv::Mat_<uchar>(5, 4) << 5, 6, 0, 4, 0, 1, 9, 9, 100, 3, 5, 8, 200, 1, 2, 3, 4, 5, 6, 7);
auto result = drop_rows(mat, {2, 4});
std::cout << mat << std::endl;;
std::cout << result << std::endl;;
return 0;
}
Note that it works only for Mat_, not for Mat.
I'm trying to Cluster ten two dimensional points with the OpenCV kmeans. The Code looks like this:
vector<Point2f>data( 10 );
data[0] = {118.90323, 1088.7419};
data[1] = {143.5, 1064.5};
data[2] = {110, 1054};
data[3] = {662, 645};
data[4] = {650, 625.5};
data[5] = {94, 363};
data[6] = {60, 360};
data[7] = {103.97369, 315.71054};
data[8] = {70.5, 313};
data[9] = {1466, 278.55554};
cout << "data:" << data << endl;
Mat labels;
int cluster_number = 4;
TermCriteria criteria = TermCriteria( CV_TERMCRIT_EPS + CV_TERMCRIT_ITER, 40, 0.1 );
Mat centers;
kmeans(data, cluster_number, labels, criteria, 1, KMEANS_RANDOM_CENTERS, centers);
cout << "centers:" << centers << endl;
cout << "labels:" << labels << endl;
The output is:
data:
[118.90323, 1088.7419; 143.5, 1064.5; 110, 1054; 662, 645; 650, 625.5; 94, 363; 60, 360; 103.97369, 315.71054; 70.5, 313; 1466, 278.55554]
centers:
[1.9113665e-38, 2.4802983e-43; 0, 0; 118.90323, 1088.7419; 0, 0]
labels:
[2; 0; 0; 0; 0; 0; 0; 0; 3; 1]
This is obviously wrong. I would like to have the labels
[0,0,0,1,1,2,2,2,2,3]
Thank you very much for your help!!
This test works for me:
void main()
{
float data[10][2]=
{{118.90323, 1088.7419},
{143.5, 1064.5},
{110, 1054},
{662, 645},
{650, 625.5},
{94, 363},
{60, 360},
{103.97369, 315.71054},
{70.5, 313},
{1466, 278.55554}};
Mat points(10,2, CV_32FC1,*data);
cout << points << endl;
Mat labels, centers;
int k =4;
cv::kmeans(points,k, labels,TermCriteria( CV_TERMCRIT_EPS+CV_TERMCRIT_ITER, 10, 1.0),3, KMEANS_PP_CENTERS, centers);
std::cout << "labels: " << labels << endl;
std::cout << "centers " << centers << endl;
cv::waitKey(0);
getchar();
}
This code gave me error. The program "stopped working". May I know what's wrong? Am i doing it right?
short* pixelX = grad_x.ptr<short>(0);
short* pixelY = grad_y.ptr<short>(0);
cv::Mat grad = cv::Mat::zeros(original_Mat.size(), CV_64F);
cv::Mat grad_x = cv::Mat::zeros(original_Mat.size(), CV_64F);
cv::Mat grad_y = cv::Mat::zeros(original_Mat.size(), CV_64F);
int a=0,b=0;
for(int i = 0; i < grad_x.rows * grad_x.cols; i++)
{
double directionRAD = atan2(pixelY[i], pixelX[i]);
int directionDEG = (int)(180 + directionRAD / CV_PI * 180);
grad.at<double>(a,b)=directionDEG;// this is the one giving me error
if(a%=319)
{
a=0;
b++;
}
else
a++;
}
This is how you can access/modify matrix elements. You can build further on this base program to fill the matrix with the values you want:
cv::Mat grad = cv::Mat::zeros(4, 5, CV_64F);
int a = 0, b = 0;
for (int i = 0; i < grad.rows * grad.cols; i++)
{
grad.at<double>(b, a) = i; // this is the one giving me error
if (a == grad.cols - 1)
{
a = 0;
b++;
}
else
a++;
}
cout << grad << endl;
this gives:
[0, 1, 2, 3, 4;
5, 6, 7, 8, 9;
10, 11, 12, 13, 14;
15, 16, 17, 18, 19]