I try to use cvCalibrateCamera2, but I get error that rotation matrix is not properly defined:
...calibration.cpp:1495: error: (-5) the output array of rotation vectors must be 3-channel 1xn or nx1 array or 1-channel nx3 or nx9 array, where n is the number of views
I have already tried all possibilities from that info but I still get this error.
My code:
CvMat *object_points = cvCreateMat((int)pp.object_points.size(), 1, CV_32FC3);
CvMat *image_points = cvCreateMat((int)pp.image_points.size(), 1, CV_32FC2);
const CvMat point_counts = cvMat((int)pp.point_counts.size(), 1, CV_32SC1, &pp.point_counts[0]);
for (size_t i=0; i<pp.object_points.size(); i++)
{
object_points->data.fl[i*3+0] = (float)pp.object_points[i].x;
object_points->data.fl[i*3+1] = (float)pp.object_points[i].y;
object_points->data.fl[i*3+2] = (float)pp.object_points[i].z;
image_points->data.fl[i*2+0] = (float)pp.image_points[i].x;
image_points->data.fl[i*2+1] = (float)pp.image_points[i].y;
}
CvMat* tempR = cvCreateMat(1, 3, CV_32F);
cvCalibrateCamera2(object_points, image_points, &point_counts,
cvSize(pp.width, pp.height), camera->m_calib_K,
camera->m_calib_D, tempR, &tempData->m_calib_T,
CV_CALIB_USE_INTRINSIC_GUESS)
// camera->calib_T is defined as:
// double m_calib_T_data[3];
// cvMat(3, 1, CV_64F, camera->m_calib_T_data);
I thought that rotation matrix used in cvCalibrateCamera2 should be 1x3 (then I want to use Rodrigues function to get 3x3 matrix) but it doesn't work as any other combination mentioned in error.
Any ideas?
And I use opencv 2.4.0 (maybe there is bug in that method, but for some reasons I can't use later version of opencv)
I think the statement is clear. I am not confident with C# but I know it requires a strong initialization.
The problem in line
CvMat* tempR = cvCreateMat(1, 3, CV_32F);
is that tempR should have a line 1x3 for every N objects point you use. In this sense, the statement becomes clear
...calibration.cpp:1495: error: (-5) the output array of rotation
vectors must be 3-channel 1xn or nx1 array or 1-channel nx3 or nx9
array, where n is the number of views
You must create a tempR like that (more or less, I do not know how to calculate N in C#)
CvMat* tempR = cvCreateMat(N, 3, CV_32F);
Try to extract N from dimensions of object.point.size. If it does not work, try image.point.size
Related
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;
}
Is there a direct way to compute the column-wise standard deviation for a matrix in opencv? Similar to std in Matlab. I've found one for the mean:
cv::Mat col_mean;
reduce(A, col_mean, 1, CV_REDUCE_AVG);
but I cannot find such a function for the standard deviation.
Here's a quick answer to what you're looking for. I added both the standard deviation and mean for each column. The code can easily be modified for rows.
cv::Mat A = ...; // FILL IN THE DATA FOR YOUR INPUT MATRIX
cv::Mat meanValue, stdValue;
cv::Mat colSTD(1, A.cols, CV_64FC1);
cv::Mat colMEAN(1, A.cols, CV_64FC1);
for (int i = 0; i < A.cols; i++){
cv::meanStdDev(A.col(i), meanValue, stdValue);
colSTD.at<double>(i) = stdValue.at<double>(0);
colMEAN.at<double>(i) = meanValue.at<double>(0);
}
The following is not in a single line,but it is another version without loops:
reduce(A, meanOfEachCol, 0, CV_REDUCE_AVG); // produces single row of columnar means
Mat repColMean;
cv::repeat(meanOfEachCol, rows, 1, repColMean); // repeat mean vector 'rows' times
Mat diffMean = A - repColMean; // get difference
Mat diffMean2 = diffMean.mul(diffMean); // per element square
Mat varMeanF;
cv::reduce(diffMean2, varMeanF, 0, CV_REDUCE_AVG); // sum each column's elements to get single row
Mat stdMeanF;
cv::sqrt(varMeanF, stdMeanF); // get standard deviation
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.
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;
In Matlab, If A is a matrix, sum(A) treats the columns of A as vectors, returning a row vector of the sums of each column.
sum(Image); How could it be done with OpenCV?
Using cvReduce has worked for me. For example, if you need to store the column-wise sum of a matrix as a row matrix you could do this:
CvMat * MyMat = cvCreateMat(height, width, CV_64FC1);
// Fill in MyMat with some data...
CvMat * ColSum = cvCreateMat(1, MyMat->width, CV_64FC1);
cvReduce(MyMat, ColSum, 0, CV_REDUCE_SUM);
More information is available in the OpenCV documentation.
EDIT after 3 years:
The proper function for this is cv::reduce.
Reduces a matrix to a vector.
The function reduce reduces the matrix to a vector by treating the
matrix rows/columns as a set of 1D vectors and performing the
specified operation on the vectors until a single row/column is
obtained. For example, the function can be used to compute horizontal
and vertical projections of a raster image. In case of REDUCE_MAX and
REDUCE_MIN , the output image should have the same type as the source
one. In case of REDUCE_SUM and REDUCE_AVG , the output may have a
larger element bit-depth to preserve accuracy. And multi-channel
arrays are also supported in these two reduction modes.
OLD:
I've used ROI method: move ROI of height of the image and width 1 from left to right and calculate means.
Mat src = imread(filename, 0);
vector<int> graph( src.cols );
for (int c=0; c<src.cols-1; c++)
{
Mat roi = src( Rect( c,0,1,src.rows ) );
graph[c] = int(mean(roi)[0]);
}
Mat mgraph( 260, src.cols+10, CV_8UC3);
for (int c=0; c<src.cols-1; c++)
{
line( mgraph, Point(c+5,0), Point(c+5,graph[c]), Scalar(255,0,0), 1, CV_AA);
}
imshow("mgraph", mgraph);
imshow("source", src);
EDIT:
Just out of curiosity, I've tried resize to height 1 and the result was almost the same:
Mat test;
cv::resize(src,test,Size( src.cols,1 ));
Mat mgraph1( 260, src.cols+10, CV_8UC3);
for(int c=0; c<test.cols; c++)
{
graph[c] = test.at<uchar>(0,c);
}
for (int c=0; c<src.cols-1; c++)
{
line( mgraph1, Point(c+5,0), Point(c+5,graph[c]), Scalar(255,255,0), 1, CV_AA);
}
imshow("mgraph1", mgraph1);
cvSum respects ROI, so if you move a 1 px wide window over the whole image, you can calculate the sum of each column.
My c++ got a little rusty so I won't provide a code example, though the last time I did this I used OpenCVSharp and it worked fine. However, I'm not sure how efficient this method is.
My math skills are getting rusty too, but shouldn't it be possible to sum all elements in columns in a matrix by multiplying it by a vector of 1s?
For an 8 bit greyscale image, the following should work (I think).
It shouldn't be too hard to expand to different image types.
int imgStep = image->widthStep;
uchar* imageData = (uchar*)image->imageData;
uint result[image->width];
memset(result, 0, sizeof(uchar) * image->width);
for (int col = 0; col < image->width; col++) {
for (int row = 0; row < image->height; row++) {
result[col] += imageData[row * imgStep + col];
}
}
// your desired vector is in result