copying elements from an opencv matrix to a eigen matrix - c++

I have a std::vector filled with 3x3 opencv matrices. I want to unfold the matrices and write them in a 9xn eigen::matrix.
std::vector<cv::Mat1d> cvMatrix;
// some code that generates a std::vector with 3880 cv matrices, the cv matrices have the size 3x3
Eigen::Matrix<double, Eigen::Dynamic, 9> eigenMatrix;
for (int i = 0; i < curvatures.size(); i++) {
eigenMatrix.resize(i + 1, 9);
for (int j = 0; j < 9; j++) {
eigenMatrix(i, j) = cvMatrix[i](j / 3, j % 3);
}
}
If I check the elements right after they are written (e.g. printing the values of eigenMatrix if i==10) everything seems to be find, but after the for loop is finished that does not hold anymore. Most of the elements in eigenMatrix seem to contain zeros. Does anyone can explain what happens here?

eigenMatrix.resize(i + 1, 9); destroys the content of eigenMatrix.
Since you already know the final dimension at the beginning, just write
Eigen::Matrix<double, Eigen::Dynamic, 9> eigenMatrix;
eigenMatrix.resize(curvatures.size(), 9);
or even just
Eigen::Matrix<double, Eigen::Dynamic, 9> eigenMatrix(curvatures.size(), 9);
before starting the for-loop.
If you need to resize a matrix, but keep the content, you can use conservativeResize() -- but that should be avoided since it requires a full copy for each resizing.

Related

How to convert an std::vector to a matrix in Eigen?

I am somewhat new to Stack Overflow and C++ so feel free to correct any errors in my code and the formatting of this question.
I am trying to make a linear regression calculator using the normal equation which involved the transposing of matrices and multiplication of vectors (and their inverses). The program is supposed to read from a csv file and pass the information from that file into a matrix and calculate the regression line. To make the job easier, I decided to use a library called Eigen for matrix-matrix multiplication.
The problem that I have run into is that the Map function can only take in an array as opposed to a std::vector.
This is what I have so far:
float feature_data[] = { 1, 1, 1, 1, 1, 1,
2, 4.5, 3, 1,4, 5};
float labels[] = { 1, 4, 3, 2, 5, 7 };
//maps the array to a matrix called "feature_data"
MatrixXf mFeatures = Map< Matrix<float, 6, 2> >(feature_data);
MatrixXf mLabels = Map< Matrix<float, 6, 1> >(labels);
//use the toArray function
std::vector<float> test_vector = { 2,1,3 };
float* test_array = toArray(test_vector);
calcLinReg(mFeatures, mLabels);
const int n = 2;
int arr[n];
system("pause");
For context, the toArray function is my unsuccessful attempt to make an array from a vector (in all honesty, it works but it returns a pointer which you can't pass into the Map function in Eigen.) calcLinReg does exactly what it sounds like: calculates the linear regression line parameters.
Is there anyway I can convert a vector to an array or convert a vector to a matrix in Eigen?
How about trying to use the vectors data() method, which gives you access to the memory array used internally by the vector, like this:
std::vector<float> test_vector = { 2,1,3 };
float* test_array = test_vector.data();
Eigen::MatrixXf test = Eigen::Map<Eigen::Matrix<float, 3, 1> >(test_array);
Or shorter:
std::vector<float> test_vector = { 2,1,3 };
Eigen::MatrixXf test = Eigen::Map<Eigen::Matrix<float, 3, 1> >(test_vector.data());
Beware The asignment actually copies the data, therefore this is safe. However, you can also directly use the data of the vector like this
std::vector<float> test_vector(3,2);
Eigen::Map<Eigen::Matrix<float, 3, 1> > dangerousVec (test_vector.data());
If vector goes out of scope the memory is deallocated and dangerousVec's data is dangeling.
Someone in a comment is asking for the case of dynamic numbers of rows and columns. This is possible, as follows:
typedef Eigen::Matrix<double,Eigen::Dynamic,Eigen::Dynamic> MyMatrix;
size_t nrow = ...;
size_t ncol = ...;
MyMatrix M = Eigen::Map<MyMatrix>(test_vector.data(), nrow, ncol);

C++ : Create 3D array out of stacking 2D arrays

In Python I normally use functions like vstack, stack, etc to easily create a 3D array by stacking 2D arrays one onto another.
Is there any way to do this in C++?
In particular, I have loaded a image into a Mat variable with OpenCV like:
cv::Mat im = cv::imread("image.png", 0);
I would like to make a 3D array/Mat of N layers by stacking copies of that Mat variable.
EDIT: This new 3D matrix has to be "travellable" by adding an integer to any of its components, such that if I am in the position (x1,y1,1) and I add +1 to the last component, I arrive to (x1,y1,2). Similarly for any of the coordinates/components of the 3D matrix.
SOLVED: Both answers from #Aram and #Nejc do exactly what expected. I set #Nejc 's answer as the correct one for his shorter code.
The Numpy function vstack returns a contiguous array. Any C++ solution that produces vectors or arrays of cv::Mat objects does not reflect the behaviour of vstack in this regard, becase separate "layers" belonging to individual cv::Mat objects will not be stored in contiguous buffer (unless a careful allocation of underlying buffers is done in advance of course).
I present the solution that copies all arrays into a three-dimensional cv::Mat object with a contiguous buffer. As far as the idea goes, this answer is similar to Aram's answer. But instead of assigning pixel values one by one, I take advantage of OpenCV functions. At the beginning I allocate the matrix which has a size N X ROWS X COLS, where N is the number of 2D images I want to "stack" and ROWS x COLS are dimensions of each of these images.
Then I make N steps. On every step, I obtain the pointer to the location of the first element along the "outer" dimension. I pass that pointer to the constructor of temporary Mat object that acts as a kind of wrapper around the memory chunk of size ROWS x COLS (but no copies are made) that begins at the address that is pointed-at by pointer. I then use copyTo method to copy i-th image into that memory chunk. Code for N = 2:
cv::Mat img0 = cv::imread("image0.png", CV_IMREAD_GRAYSCALE);
cv::Mat img1 = cv::imread("image1.png", CV_IMREAD_GRAYSCALE);
cv::Mat images[2] = {img0, img1}; // you can also use vector or some other container
int dims[3] = { 2, img0.rows, img0.cols }; // dimensions of new image
cv::Mat joined(3, dims, CV_8U); // same element type (CV_8U) as input images
for(int i = 0; i < 2; ++i)
{
uint8_t* ptr = &joined.at<uint8_t>(i, 0, 0); // pointer to first element of slice i
cv::Mat destination(img0.rows, img0.cols, CV_8U, (void*)ptr); // no data copy, see documentation
images[i].copyTo(destination);
}
This answer is in response to the question above of:
In Python I normally use functions like vstack, stack, etc to easily create a 3D array by stacking 2D arrays one onto another.
This is certainly possible, you can add matrices into a vector which would be your "stack"
For instance you could use a
std::vector<cv::Mat>>
This would give you a vector of mats, which would be one slice, and then you could "layer" those by adding more slices vector
If you then want to have multiple stacks you can add that vector into another vector:
std::vector<std::vector<cv::Mat>>
To add matrix to an array you do:
myVector.push_back(matrix);
Edit for question below
In such case, could I travel from one position (x1, y1, z1) to an immediately upper position doing (x1,y1,z1+1), such that my new position in the matrix would be (x1,y1,z2)?
You'll end up with something that looks a lot like this. If you have a matrix at element 1 in your vector, it doesn't really have any relationship to the element[2] except for the fact that you have added it into that point. If you want to build relationships then you will need to code that in yourself.
You can actually create a 3D or ND mat with opencv, you need to use the constructor that takes the dimensions as input. Then copy each matrix into (this case) the 3D array
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;
int main() {
// Dimensions for the constructor... set dims[0..2] to what you want
int dims[] = {5, 5, 5}; // 5x5x5 3d mat
Mat m = Mat::zeros(5, 5, CV_8UC1);
for (size_t i = 0; i < 5; i++) {
for (size_t k = 0; k < 5; k++) {
m.at<uchar>(i, k) = i + k;
}
}
// Mat with constructor specifying 3 dimensions with dimensions sizes in dims.
Mat 3DMat = Mat(3, dims, CV_8UC1);
// We fill our 3d mat.
for (size_t i = 0; i < m2.size[0]; i++) {
for (size_t k = 0; k < m2.size[1]; k++) {
for (size_t j = 0; j < m2.size[2]; j++) {
3DMat.at<uchar>(i, k, j) = m.at<uchar>(k, j);
}
}
}
// We print it to show the 5x5x5 array.
for (size_t i = 0; i < m2.size[0]; i++) {
for (size_t k = 0; k < m2.size[1]; k++) {
for (size_t j = 0; j < m2.size[2]; j++) {
std::cout << (int) 3DMat.at<uchar>(i, k, j) << " ";
}
std::cout << endl;
}
std::cout << endl;
}
return 0;
}
Based on the question and comments, I think you are looking for something like this:
std::vector<cv::Mat> vec_im;
//In side for loop:
vec_im.push_back(im);
Then, you can access it by:
Scalar intensity_1 = vec_im[z1].at<uchar>(y, x);
Scalar intensity_2 = vec_im[z2].at<uchar>(y, x);
This assumes that the image is single channel.

How to map/built the c++ vector from the eigen matrix?

MatrixXf A = MatrixXf::Random(3, 3);
MatrixXf B = A.row(1);
std::vector<float> vec;
I want to built the vector "vec" with elements from the row Eigen matrix "B". Something like this "vec=B.data()"
In addition to the obvious answer (manual push_backs or pre-allocation + index-by-index assignment), you can initialize it directly using the base pointer returned by ::data():
Eigen::MatrixXf A = Eigen::MatrixXf::Random(3, 3);
Eigen::MatrixXf B = A.row(1);
std::vector<float> vec(B.data(), B.data() + B.size());
Just be careful that Eigen may use memory alignment to take advantage of SSE-family instructions, so it may not work correctly with higher dimensions.
Just use a loop:
MatrixXf A = MatrixXf::Random(3, 3);
MatrixXf B = A.row(1);
std::vector<float> vec(B.size());
for(size_t row=0; row < vec.size(); row++) {
vec[row] = B[row];
}

Filter points inside of a sphere with Eigen

I have a set of 3D points, and I need to compute which ones are the nearest to a given point p. I am wondering which could be the correct way to do it in Eigen. So far, I have:
Matrix<double, Dynamic, 3> points; // The set of 3D points
Matrix<double, 1, 3> p;
// Populate the "points" matrix
...
// Fill a matrix with several copies of "p" in order to match the size
of "points"
Matrix<double, Dynamic, 3> pp(points.rows(), 3);
pp = Matrix<double, Dynamic, 1>::Ones(points.rows, 1) * p;
Matrix<double, Dynamic, 1> sq_distances = (points - pp).rowwise.squaredNorm();
Matrix<bool, Dynamic, 1> nearest_points = sq_distances < (dist_threshold * dist_threshold);
Can I then have some way of extracting the points in "points" that fullfill the "nearest_points" condition like in
Matrix<double, Dynamic, 3> nearest = points(nearest_points);
?
For the nearest I'd suggest:
int i;
double sqdist = (points.rowwise()-p).rowwise().squaredNorm().minCoeff(&i);
nearest = points.row(i);
For the ones in a given ball, you currently have to write one loop yourself:
ArrayXd sqdists = (points.rowwise()-p).rowwise().squaredNorm();
Matrix<double,Dynamic,3> nearests( (sqdists<sqradius).count(), 3 );
int count = 0;
for(int i=0; i<points.rows(); ++i)
if(sqdists(i)<sqradius)
nearests.row(count++) = points.row(i);

Convert 1D vector to 2D vector

I got a compiling error when using std::copy to convert a 1D vector to a 2D vector.
int main()
{
std::vector< std::vector<float> > v2D(10, std::vector<float>(10, 0.0));
std::vector<float> v1D(100, 0.0);
for (int i = 0; i < 100; ++i)
v1D.push_back(i);
for (unsigned int j = 0; j < 10; j++)
{
std::copy(v1D.begin() + j * 10, v1D.begin() + (j + 1) * 10, std::back_inserter(v2D[j].begin()));
}
}
Would you please help solve this proplem? Thank you.
std::back_inserter takes a container, not an iterator. Change std::back_inserter(v2D[j].begin()) to std::back_inserter(v2D[j]). Note that this will be calling .push_back() on the std::vector<float> at v2D[j], so you probably also want to change std::vector< std::vector<float> > v2D(10, std::vector<float>(10, 0.0)); to std::vector< std::vector<float> > v2D(10);.
Alternatively, you can change std::back_inserter(v2D[j].begin()) to v2D[j].begin(). This works because std::copy wants an output iterator, and std::vector<>::iterator behaves as one when there are a sufficient number of elements in the vector<> to overwrite. And this way, your current initialization of v2D is already ideal.
EDIT: Someone else said this in a separate answer then deleted it, so I'll say it on their behalf because it's definitely noteworthy: because you initialize v1D with 100 elements, the [1..100] digits you then push_back are appended to the initial 100 elements (which all have your specified value of 0) rather than overwriting them. You should change std::vector<float> v1D(100, 0.0); to std::vector<float> v1D; to get the behavior you apparently want (or std::vector<float> v1D; v1D.reserve(100); if you're really being pedantic about efficiency).