I am working with OpenCV and C++. I have a matrix X like this
Mat X = Mat::zeros(13,6,CV_32FC1);
and I want to update just a submatrix 4x3 of it, but I have doubts on how to access that matrix in an efficient way.
Mat mat43= Mat::eye(4,3,CV_32FC1); //this is a submatrix at position (4,4)
Do I need to change element by element?
One of the quickest ways is setting a header matrix pointing to the range of columns/rows you want to update, like this:
Mat aux = X.colRange(4,7).rowRange(4,8); // you are pointing to submatrix 4x3 at X(4,4)
Now, you can copy your matrix to aux (but actually you will be copying it to X, because aux is just a pointer):
mat43.copyTo(aux);
Thats it.
First, you have to create a matrix that points to the original one:
Mat orig(13,6,CV_32FC1, Scalar::all(0));
Mat roi(orig(cv::Rect(1,1,4,3))); // now, it points to the original matrix;
Mat otherMatrix = Mat::eye(4,3,CV_32FC1);
roi.setTo(5); // OK
roi = 4.7f; // OK
otherMatrix.copyTo(roi); // OK
Keep in mind that any operations that involves direct attribution, with the "=" sign from another matrix will change the roi matrix source from orig to that other matrix.
// Wrong. Roi will point to otherMatrix, and orig remains unchanged
roi = otherMatrix;
Related
Why does the following code segment work
int sizearray[3] = {3,4,2};
Mat OneM = Mat::ones(3,4,CV_8UC1);
Mat TwoM = Mat::ones(3,4,CV_8UC1)+1;
Mat OneTwo3D = Mat::zeros(3,sizearray,CV_8UC1);
Mat OneTwo3DPlaneM;
Range OneTwo3DRanges[] = {Range(1,3),Range(1,4),Range(1,2)};
OneTwo3DPlaneM = OneTwo3D(OneTwo3DRanges);
but if I change the ranges to
Range OneTwo3DRanges[] = {Range(1,4),Range(1,4),Range(1,2)};
it blows up.
In the working one, OneTwo3DPlaneM is apparently a 2x3x1 matrix as expected given that the "end" of a Range is exclusive (confirmed by looking at the working code/range result OneTwo3DPlaneM.size.p which is [2,3,1]). However, just increasing the first range from Range(1,3) to Range(1,4) causes an error on
OneTwo3DPlaneM = OneTwo3D(OneTwo3DRanges);
Note, what I'm trying to do, eventually, is assign an image into a slice of a 3d arrray (via OneM.copyTo(OneTwo3DPlaneM) I think). The code above is just a test for that. So, I just want to create a matrix that references a plane OneTwo3D so that, for this test, I can assign the matrices OneM to the first plane and, eventually, TwoM to the second plane (after I change the third range to point to OneTwo3D's second plane)
Given that I don't have much experience with opencv I assume I'm doing something retarded. Apologies up front.
The code below seems to work Apparently, copyTo doesn't like assigning from a 2d into a slice of a 3d array. So, as shown below, I changed the OneM and TwoM from 3x4 matrices to 3x4x1 and then it worked. I guess then the question is given an image (2d) can I just reshape it to a 3d entity with a singleton 3rd dimension to copyTo happy. I'm not sure how to do that considering that reshape only works on 2d arrays.
int sizearray1[3] = {3,4,1};
int sizearray[3] = {3,4,2};
// Mat OneM = Mat::ones(3,4,CV_8UC1);
Mat OneM = Mat::ones(3,sizearray1,CV_8UC1);
// Mat TwoM = Mat::ones(3,4,CV_8UC1)+1;
Mat TwoM = Mat::ones(3,sizearray1,CV_8UC1)+1;
Mat OneTwo3D = Mat::zeros(3,sizearray,CV_8UC1);
Mat OneTwo3DPlaneM;
Range OneTwo3DRanges[] = {Range::all(),Range::all(),Range(0,1)};
OneTwo3DPlaneM = OneTwo3D(OneTwo3DRanges);
OneM.copyTo(OneTwo3DPlaneM);
OneTwo3DRanges[2] = Range(1,2);
OneTwo3DPlaneM = OneTwo3D(OneTwo3DRanges);
TwoM.copyTo(OneTwo3DPlaneM);
I have 2 vectors (p1 and p2) of point3f variables which represent 2 3D pointclouds. In order to match these two point clouds I want to use SVD to find a transformation for this. The problem is that SVD requires a matrix (p1*p2 transpose). My question is how do I convert a vector of size Y to a Yx3 matrix?
I tried cv::Mat p1Matrix(p1) but this gives me a row vector with two dimensions.I also found fitLine but I think this only works for 2D.
Thank you in advance.
How about something like:
cv::Mat p1copy(3, p1.size(), CV_32FC1);
for (size_t i = 0, end = p1.size(); i < end; ++i) {
p1copy.at<float>(0, i) = p1[i].x;
p1copy.at<float>(1, i) = p1[i].y;
p1copy.at<float>(2, i) = p1[i].z;
}
If this gives you the desired result, you can make the code faster by using a pointer instead of the rather slow at<>() function.
I use reshape function for convert vector of points to Mat.
vector<Point3f> P1,P2;
Point3f c1,c2;//center of two set
... //data association for two set
Mat A=Mat(P1).reshape(1).t();
Mat B=Mat(P2).reshape(1).t();
Mat AA,BB,CA,CB;
repeat(Mat(c1),1,P1.size(),CA);
repeat(Mat(c2),1,P2.size(),CB);
AA=A-CA;
BB=B-CB;
Mat H=AA*BB.t();
SVD svd(H);
Mat R_;
transpose(svd.u*svd.vt,R_);
if(determinant(R_)<0)
R_.at<float>(0,2)*=-1,R_.at<float>(1,2)*=-1,R_.at<float>(2,2)*=-1;
Mat t=Mat(c2)-R_*Mat(c1);
This answer states how to copy a row of a matrix to another row: How to copy a row of a Mat to another Mat's column in OpenCv?
However if I try to copy a row of a matrix to a column vector the program is ends abruptly.
Example:
Mat A(640,480,CV_64F);
Mat B(480,1,CV_64F);
A.row(0).copyTo(B.col(0));
A possible workaround is:
Mat A(640,480,CV_64F);
Mat B;
A.row(0).copyTo(B);
B = B.t();
But how can I get type CV_64F in B if A is a different type? Is the data type copied, too? And - most important – why do I need this workaround?
copyTo() function can only copy data between the matrices of same size (width and height) and same type. For this reason you can't use it directly to copy row of matrix to column of matrix. Normally OpenCV will reallocate target matrix so that its size and type will match original image, but call to col() function returns constant temporary object that can't be reallocated. As a result your program crushed.
Actually this problem can be solved. You can copy row to column (if they have same amount of elements) and you can copy data between matrices of different types. Mat provides an iterator to its data, and that means you can use all C++ functions that works with iterators. copy() function for example.
Mat A(640, 480, CV_64F);
Mat B(480, 1, CV_32S); // note that the type don't have to be the same
Mat tmp = A.row(0); // no data is copied here. it is needed only because taking iterator to temporary object is really bad idea
copy(tmp.begin<double>(), tmp.end<double>(), B.begin<int>());
Since code like the following (transposing the row only) is not supported by the API:
A.row(0).t().copyTo( B.col(0) );
The workaround is either create a temporary matrix from the row, transpose it, then copy to your target matrix
Mat temp = A.row(0);
temp = temp.t();
temp.copyTo( B.col(0) );
I would rather do something like this though:
Mat A( 640, 480,CV_64F );
Mat B( 1, 480, CV_64F ); /* rather than 480 x 1, make a 1 x 480 first */
A.row(0).copyTo( B.row(0) );
B = B.t(); /* transpose it afterwards */
I presume this is all just because the API is not supporting it yet.
I have some problems with opencv flann::Index -
I'm creating index
Mat samples = Mat::zeros(vfv_net_quie.size(),24,CV_32F);
for (int i =0; i < vfv_net_quie.size();i++)
{
for (int j = 0;j<24;j++)
{
samples.at<float>(i,j)=(float)vfv_net_quie[i].vfv[j];
}
}
cv::flann::Index flann_index(
samples,
cv::flann::KDTreeIndexParams(4),
cvflann::FLANN_DIST_EUCLIDEAN
);
flann_index.save("c:\\index.fln");
A fter that I'm tryin to load it and find nearest neiborhoods
cv::flann::Index flann_index(Mat(),
cv::flann::SavedIndexParams("c:\\index.fln"),
cvflann::FLANN_DIST_EUCLIDEAN
);
cv::Mat resps(vfv_reg_quie.size(), K, CV_32F);
cv::Mat nresps(vfv_reg_quie.size(), K, CV_32S);
cv::Mat dists(vfv_reg_quie.size(), K, CV_32F);
flann_index.knnSearch(sample,nresps,dists,K,cv::flann::SearchParams(64));
And have access violation in miniflann.cpp in line
((IndexType*)index)->knnSearch(_query, _indices, _dists, knn,
(const ::cvflann::SearchParams&)get_params(params));
Please help
You should not load the flann-file into a Mat(), as it is the place where the index is stored. It is a temporary object destroyed after the constructor was called. That's why the index isn't pointing anywhere useful when you call knnSearch().
I tried following:
cv::Mat indexMat;
cv::flann::Index flann_index(
indexMat,
cv::flann::SavedIndexParams("c:\\index.fln"),
cvflann::FLANN_DIST_EUCLIDEAN
);
resulting in:
Reading FLANN index error: the saved data size (100, 64) or type (5) is different from the passed one (0, 0), 0
which means, that the matrix has to be initialized with the correct dimensions (seems very stupid to me, as I don't necessarily know, how many elements are stored in my index).
cv::Mat indexMat(samples.size(), CV_32FC1);
cv::flann::Index flann_index(
indexMat,
cv::flann::SavedIndexParams("c:\\index.fln"),
cvflann::FLANN_DIST_EUCLIDEAN
);
does the trick.
In the accepted answer is somehow not clear and misleading why the input matrix in the cv::flann::Index constructor must have the same dimension as the matrix used for generating the saved Index. I'll elaborate on #Sau's comment with an example.
KDTreeIndex was generated using as input a cv::Mat sample, and then saved. When you load it, you must provide the same sample matrix to generate it, something like (using the templated GenericIndex interface):
cv::Mat sample(sample_num, sample_size, ... /* other params */);
cv::flann::SavedIndexParams index_params("c:\\index.fln");
cv::flann::GenericIndex<cvflann::L2<float>> flann_index(sample, index_params);
L2 is the usual Euclidean distance (other types can be found in opencv2/flann/dist.h).
Now the index can be used as shown the find the K nearest neighbours of a query point:
std::vector<float> query(sample_size);
std::vector<int> indices(K);
std::vector<float> distances(K);
flann_index.knnSearch(query, indices, distances, K, cv::flann::SearchParams(64));
The matrix indices will contain the locations of the nearest neighbours in the matrix sample, which was used at first to generate the index. That's why you need to load the saved index with the very matrix used to generate the index, otherwise the returned vector will contain indices pointing to meaningless "nearest neighbours".
In addition you get a distances matrix containing how far are the found neighbours from your query point, which you can later use to perform some inverse distance weighting, for example.
Please also note that sample_size has to match across sample matrix and query point.
I am trying to store a IPL_DEPTH_8U, 3 channel image into an array so that I can store 100 images in memory.
To initialise my 4D array I used the following code (rows,cols,channel,stored):
int size[] = { 324, 576, 3, 100 };
CvMatND* cvImageBucket; = cvCreateMatND(3, size, CV_8U);
I then created a matrix and converted the image into the matrix
CvMat *matImage = cvCreateMat(Image->height,Image->width,CV_8UC3 );
cvConvert(Image, matImage );
How would I / access the CvMatND to copy the CvMat into it at the position of stored?
e.g. cvImageBucket(:,:,:,0) = matImage; // copied first image into array
You've tagged this as both C and C++. If you want to work in C++, you could use the (in my opinion) simpler cv::Mat structure to store each of the images, and then use these to populate a vector with all the images.
For example:
std::vector<cv::Mat> imageVector;
cv::Mat newImage;
newImage = getImage(); // where getImage() returns the next image,
// or an empty cv::Mat() if there are no more images
while (!newImage.empty())
{
// Add image to vector
imageVector.push_back(image);
// get next image
newImage = getImage();
}
I'm guessing something similar to:
for ith matImage
memcpy((char*)cvImageBucket->data+i*size[0]*size[1]*size[2],(char*)matImage->data,size[0]*size[1]*size[2]);
Although I agree with #Chris that it is best to use vector<Mat> rather than a 4D matrix, this answer is just to be a reference for those who really need to use 4D matrices in OpenCV (even though it is a very unsupported, undocumented and unexplored thing with so little available online and claimed to be working just fine!).
So, suppose you filled a vector<Mat> vec with 2D or 3D data which can be CV_8U, CV_32F etc.
One way to create a 4D matrix is
vector<int> dims = {(int)vec.size(), vec[0].rows, vec[0].cols};
Mat m(dims, vec[0].type(), &vec[0]);
However, this method fails when the vector is not continuous which is typically the case for big matrices. If you do this for a discontinuous matrix, you will get a segmentation fault or bad access error when you would like to use the matrix (i.e. copying, cloning, etc). To overcome this issue, you can copy matrices of the vector one by one into the 4D matrix as follows:
Mat m2(dims, vec[0].type());
for (auto i = 0; i < vec.size(); i++){
vec[i].copyTo(temp.at<Mat>(i));
}
Notice that both methods require the matrices to be the same resolution. Otherwise, you may get undesired results or errors.
Also, notice that you can always use for loops but it is generally not a good idea to use them when you can vectorize.