OpenCV 2.3.1. cv::Mat to std::vector cast - c++

I do have a trouble converting cv::Mat to std::vector:
cv::Mat m = cv::Mat_<int>::eye(3, 3);
std::vector<int> vec = m;
gives me the following:
OpenCV Error: Assertion failed (dims == 2 && (size[0] == 1 || size[1] == 1 || size[0]*size[1] == 0)) in create, file /build/buildd-opencv_2.3.1-11-i386-tZNeKk/opencv-2.3.1/modules/core/src/matrix.cpp, line 1225
terminate called after throwing an instance of 'cv::Exception'
what(): /build/buildd-opencv_2.3.1-11-i386-tZNeKk/opencv-2.3.1/modules/core/src/matrix.cpp:1225: error: (-215) dims == 2 && (size[0] == 1 || size[1] == 1 || size[0]*size[1] == 0) in function create
from mat.hpp:
template<typename _Tp> inline Mat::operator vector<_Tp>() const
{
vector<_Tp> v;
copyTo(v);
return v;
}
and later on the following code in copyTo is executed:
//mat.hpp
template<typename _Tp> inline _OutputArray::_OutputArray(vector<_Tp>& vec) : _InputArray(vec) {}
template<typename _Tp> inline _InputArray::_InputArray(const vector<_Tp>& vec)
: flags(STD_VECTOR + DataType<_Tp>::type), obj((void*)&vec) {}
// operations.hpp
template<typename _Tp> inline Size_<_Tp>::Size_()
: width(0), height(0) {}
and then I get an exceptions.
Any idea? Is it a bug? Probably, I do not understand something...
Thank You in advance!

It seems like you are trying to convert a two-dimensional 3x3 matrix into a one-dimensional vector. Not sure what result you're expecting from that, but you probably want to convert a row of the matrix into a vector. You can use this by giving the vector constructor a pointer to the row data:
int *p = eye.ptr<int>(0); // pointer to row 0
std::vector<int> vec(p, p+eye.cols); // construct a vector using pointer

Very Well Then!
cv::Mat is stored as an array of bytes!
So, if You want to represent your matrix as vector, You may do something like this:
cv::Mat m = cv::Mat_<int>::eye(3, 3);
int* data = reinterpret_cast<int*>(m.data);
int len = m.rows * m.cols;
std::vector<int> vec(len);
std::copy(data + 0, data + len, vec.begin());

From the error message, it looks like you can only convert matrices where one dimension is 1 to std::vector, i.e. only row or column vectors (mathematically speaking):
dims == 2 && (size[0] == 1 || size[1] == 1)
Which kind of makes sense...

Related

Copy only row indices of Mat from findNonZero function in C++ OpenCV

I am trying to convert MATLAB code to C++.
In MATLAB, I use the find function to get the indices of a vector and then copy these to other variables. For example:
idx = find(A>s);
B = idx;
% A, idx, and B are vectors; s is a scalar
In C++ OpenCV (C++14 / OpenCV 3.4.10) I know I can use the findNonZero function, but it returns both row and column indices:
double s;
Mat1d A;
Mat1i B;
Mat idx;
.
.
.
findNonZero(A>s, idx);
I do not know how I can copy only row-indices directly (without using a for loop). I thought it could be done by defining Mat2i idx and using mixChannels like this:
Mat2i idx;
findNonZero(A>s, idx);
B = Mat1i::zeros(idx.size());
int from_to[] = {1, 0};
mixChannels(&idx, 1, &B, 1, from_to, 1);
However, I get the following error while running the findNonZero function:
OpenCV(3.4.10) Error: Assertion failed (!fixedType() || ((Mat*)obj)->type() == mtype) in cv::debug_build_guard::_OutputArray::create,
But if I set Mat idx, I get another error while running the mixChannel function:
OpenCV(3.4.10) Error: Assertion failed (j < nsrcs && src[j].depth() == depth) in cv::mixChannels,
I'm not sure what I should do. Any help is appreciated.
MATLAB's find determines the column-major indices of where values are nonzero in the input matrix. This is true if you specify the single output version of it. If you provide two output variables, that generates both the row and column locations of the nonzero values in the input. In your example you have provided the single output version of find so I will be working with this.
OpenCV's cv::Mat lays out the image in row-major. I'm assuming you would like the row-major indices. If so, since cv::findNonZero outputs both the row and column coordinates, you must loop through the output coordinates yourself and create the row-major indices. You shouldn't be afraid of using loops here. In fact, for loops over cv::Mats are optimised for quick access. Therefore:
Mat2i idx;
Mat1d A; // Define somewhere else
double s; // Define somewhere else
findNonZero(A > s, idx);
B = Mat1i::zeros(idx.total());
for (int i = 0; i < idx.total(); ++i) {
B.at<int>(i) = idx.at<Point>(i).y * A.cols + idx.at<Point>(i).x;
}
B will contain the row-major indices in a cv::Mat1i. If I have misunderstood your inquiry and simply want the row locations of the nonzero values, then it's just:
Mat2i idx;
Mat1d A; // Define somewhere else
double s; // Define somewhere else
findNonZero(A > s, idx);
B = Mat1i::zeros(idx.total());
for (int i = 0; i < idx.total(); ++i) {
B.at<int>(i) = idx.at<Point>(i).y;
}
Remember you are only iterating over the nonzero values, so the worst case complexity is to iterate over the locations that are nonzero.

Write arbitrary Eigen object to row-major plain storage

I am writing a module to write data to a file which uses by convention only row-major storage. I would like my function to be able to allow both column-major and row-major Eigen objects as input.
Currently I first use Eigen to copy a column-major object to a row-major object, before I write. My code works well for most cases, but for Eigen::VectorXi compiling fails with an assertion that I don't understand. How do I solve this? Can I avoid creating many cases?
The code (writing is mimicked by outputting a std::vector):
#include <vector>
#include <iostream>
#include <Eigen/Eigen>
template <class T, int Rows, int Cols, int Options, int MaxRows, int MaxCols>
std::vector<T> write(const Eigen::Matrix<T,Rows,Cols,Options,MaxRows,MaxCols>& matrix)
{
std::vector<T> data(static_cast<size_t>(matrix.size()));
if (matrix.IsRowMajor) {
std::copy(matrix.data(), matrix.data()+matrix.size(), data.begin());
return data;
} else {
Eigen::Matrix<T, Rows, Cols, Eigen::RowMajor, MaxRows, MaxCols> tmp = matrix;
return write(tmp);
}
}
int main()
{
Eigen::VectorXi matrix = Eigen::VectorXi::LinSpaced(10, 0, 9);
std::vector<int> output = write(matrix);
}
The compilation error:
In file included from test.cpp:3:
In file included from /usr/local/Cellar/eigen/3.3.7/include/eigen3/Eigen/Eigen:1:
In file included from /usr/local/Cellar/eigen/3.3.7/include/eigen3/Eigen/Dense:1:
In file included from /usr/local/Cellar/eigen/3.3.7/include/eigen3/Eigen/Core:457:
/usr/local/Cellar/eigen/3.3.7/include/eigen3/Eigen/src/Core/PlainObjectBase.h:903:7: error: static_assert failed "INVALID_MATRIX_TEMPLATE_PARAMETERS"
EIGEN_STATIC_ASSERT((EIGEN_IMPLIES(MaxRowsAtCompileTime==1 && MaxColsAtCompileTime!=1, (Options&RowMajor)==RowMajor)
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/local/Cellar/eigen/3.3.7/include/eigen3/Eigen/src/Core/util/StaticAssert.h:33:40: note: expanded from macro 'EIGEN_STATIC_ASSERT'
#define EIGEN_STATIC_ASSERT(X,MSG) static_assert(X,#MSG);
^ ~
/usr/local/Cellar/eigen/3.3.7/include/eigen3/Eigen/src/Core/PlainObjectBase.h:535:7: note: in instantiation of member function 'Eigen::PlainObjectBase<Eigen::Matrix<int, -1, 1, 1, -1, 1>
>::_check_template_params' requested here
_check_template_params();
^
/usr/local/Cellar/eigen/3.3.7/include/eigen3/Eigen/src/Core/Matrix.h:377:9: note: in instantiation of function template specialization 'Eigen::PlainObjectBase<Eigen::Matrix<int, -1, 1, 1, -1, 1>
>::PlainObjectBase<Eigen::Matrix<int, -1, 1, 0, -1, 1> >' requested here
: Base(other.derived())
^
test.cpp:14:79: note: in instantiation of function template specialization 'Eigen::Matrix<int, -1, 1, 1, -1, 1>::Matrix<Eigen::Matrix<int, -1, 1, 0, -1, 1> >' requested here
Eigen::Matrix<T, Rows, Cols, Eigen::RowMajor, MaxRows, MaxCols> tmp = matrix;
^
test.cpp:23:31: note: in instantiation of function template specialization 'write<int, -1, 1, 0, -1, 1>' requested here
std::vector<int> output = write(matrix);
^
1 error generated.
Understanding the static assertion
Unfortunately the assertion is really not self-explanatory and the only thing you can get from it is the hint, that something is wrong with your template parameters. If we look into Eigen's source code we find the following beginning on line 903:
EIGEN_STATIC_ASSERT((EIGEN_IMPLIES(MaxRowsAtCompileTime==1 && MaxColsAtCompileTime!=1, (Options&RowMajor)==RowMajor)
&& EIGEN_IMPLIES(MaxColsAtCompileTime==1 && MaxRowsAtCompileTime!=1, (Options&RowMajor)==0)
&& ((RowsAtCompileTime == Dynamic) || (RowsAtCompileTime >= 0))
&& ((ColsAtCompileTime == Dynamic) || (ColsAtCompileTime >= 0))
&& ((MaxRowsAtCompileTime == Dynamic) || (MaxRowsAtCompileTime >= 0))
&& ((MaxColsAtCompileTime == Dynamic) || (MaxColsAtCompileTime >= 0))
&& (MaxRowsAtCompileTime == RowsAtCompileTime || RowsAtCompileTime==Dynamic)
&& (MaxColsAtCompileTime == ColsAtCompileTime || ColsAtCompileTime==Dynamic)
&& (Options & (DontAlign|RowMajor)) == Options),
INVALID_MATRIX_TEMPLATE_PARAMETERS)
Even though the compiler indicates that
EIGEN_IMPLIES(MaxRowsAtCompileTime==1 && MaxColsAtCompileTime!=1, (Options&RowMajor)==RowMajor)
causes the error, the following line really does:
EIGEN_IMPLIES(MaxColsAtCompileTime==1 && MaxRowsAtCompileTime!=1, (Options&RowMajor)==0)
Understanding what triggers the assertion
You provide Eigen::VectorXi as an input for write. Eigen::VectorXi is really just a typedef for
Eigen::Matrix<int, Eigen::Dynamic, 1, Eigen::ColMajor, Eigen::Dynamic, 1>
Therefore the line
Eigen::Matrix<T, Rows, Cols, Eigen::RowMajor, MaxRows, MaxCols> tmp = matrix;
in write expands to
Eigen::Matrix<int, Eigen::Dynamic, 1, Eigen::RowMajor, Eigen::Dynamic, 1> tmp = matrix;
which triggers the assertion, since a matrix with MaxColsAtCompileTime==1 and MaxRowsAtCompileTime!=1 must not be RowMajor.
Solve your problem
The problem now is that even though you can check if your input matrix is a vector, row-major or column-major, you cannot declare
Eigen::Matrix<T, Rows, Cols, Eigen::RowMajor, MaxRows, MaxCols>
if it is no legal to do so at compile-time (and it isn't due to the static assertion).
You have the following options to make your code work:
1. if constexpr (C++17)
C++17 offers a way for detecting at compile-time if a certain conditional branch will be taken or not. The downside of this approach (beside the requirement for a C++17 compiler) is that you can only test for constant expressions.
In the concrete example this looks like this:
template <class T, int Rows, int Cols, int Options, int MaxRows, int MaxCols>
std::vector<T> write(const Eigen::Matrix<T, Rows, Cols, Options, MaxRows, MaxCols>& matrix)
{
typedef Eigen::Matrix<T, Rows, Cols, Options, MaxRows, MaxCols> MatrixType;
std::vector<T> data(static_cast<size_t>(matrix.size()));
if constexpr (MatrixType::MaxRowsAtCompileTime == 1 ||
MatrixType::MaxColsAtCompileTime ==1 ||
(MatrixType::Options&Eigen::RowMajor) == Eigen::RowMajor) {
std::copy(matrix.data(), matrix.data() + matrix.size(), data.begin());
return data;
} else {
Eigen::Matrix<T, Rows, Cols, Eigen::RowMajor, MaxRows, MaxCols> tmp = matrix;
return write(tmp);
}
}
2. SFINAE
You can dispatch the call to write at compile-time using SFINAE by using std::enable_if. The following example uses a slightly modified version of your original code but everything should be clear from context:
// matrix is either a vector or in row-major
template <typename Derived>
std::vector<typename Derived::Scalar> write(const Eigen::MatrixBase<Derived>& matrix,
typename std::enable_if<Derived::MaxRowsAtCompileTime == 1 ||
Derived::MaxColsAtCompileTime == 1 ||
(Derived::Options & Eigen::RowMajor) == Eigen::RowMajor,
Derived>::type* = 0)
{
std::vector<typename Derived::Scalar> data(
static_cast<size_t>(matrix.size()));
std::copy(matrix.derived().data(), matrix.derived().data() + matrix.size(),
data.begin());
return data;
}
// matrix is neither a vector nor in row-major
template <typename Derived>
std::vector<typename Derived::Scalar> write(const Eigen::MatrixBase<Derived>& matrix,
typename std::enable_if<Derived::MaxRowsAtCompileTime != 1 &&
Derived::MaxColsAtCompileTime != 1 &&
(Derived::Options & Eigen::RowMajor) == 0,
Derived>::type* = 0)
{
Eigen::Matrix<typename Derived::Scalar, Derived::RowsAtCompileTime,
Derived::ColsAtCompileTime, Eigen::RowMajor,
Derived::MaxRowsAtCompileTime, Derived::MaxColsAtCompileTime> tmp = matrix;
return write(tmp);
}
This works using a C++11 compiler.
Other options would be to specialise the template but it will get even more lengthy than the SFINAE approach.
Some test cases:
Eigen::Matrix<int, 3, 3, Eigen::RowMajor> m;
m << 1, 2, 3,
1, 2, 3,
1, 2, 3;
std::vector<int> output = write(m);
for (const auto& element : output) {
std::cout << element << " ";
}
Output: 1 2 3 1 2 3 1 2 3
Eigen::Matrix<int, 3, 3, Eigen::ColMajor> m;
m << 1, 2, 3,
1, 2, 3,
1, 2, 3;
std::vector<int> output = write(m);
for (const auto& element : output) {
std::cout << element << " ";
}
Output: 1 2 3 1 2 3 1 2 3
Eigen::VectorXi m = Eigen::VectorXi::LinSpaced(10, 0, 9);
std::vector<int> output = write(m);
for (const auto& element : output) {
std::cout << element << " ";
}
Output: 0 1 2 3 4 5 6 7 8 9
Eigen::RowVectorXi m = Eigen::RowVectorXi::LinSpaced(10, 0, 9);
std::vector<int> output = write(m);
for (const auto& element : output) {
std::cout << element << " ";
}
Output: 0 1 2 3 4 5 6 7 8 9
A simpler solution is to let Eigen::Ref does all the job for you:
Ref<const Matrix<T,Rows,Cols,Cols==1?ColMajor:RowMajor,MaxRows,MaxCols>,0, InnerStride<1> > row_maj(matrix);
Then row_maj will be guaranteed to be sequentially stored in row-major order. If matrix is compatible, then no copy occurs. No branch, no SFINAE, etc.
Here matrix can be any expression, not only a Matrix<...> but also sub-matrices, Map, another Ref, etc.
To handle any expressions, just replace Rows and the likes with XprType::RowsAtCompileTime where XprType is the type of matrix.
template <class XprType>
std::vector<typename XprType::Scalar> write(const Eigen::MatrixBase<XprType>& matrix)
{...}

How to fix "Assertion failed" when training cv:ml::DTrees

I want to train a decision tree with hog features.
When I try to train my cv::ml::DTrees model, I receive the following error:
OpenCV(3.4.3) Error: Assertion failed (n >= 0) in cv::ml::TrainDataImpl::getValues, file C:\build\3_4_winpack-build-win64-vc14\opencv\modules\ml\src\data.cpp, line 890
I understand, that the number of samples(features) have to match the number of responses(labels) and both have to be >=0.
My cv::Mat feats and cv::Mat labels have the same number of rows (388).
This is how I try to do it:
cv::Ptr<cv::ml::DTrees> model = cv::ml::DTrees::create();
cv::Ptr<cv::ml::TrainData> data = cv::ml::TrainData::create(feats, cv::ml::ROW_SAMPLE, labels);
model->train(data);
model->train(data) fails, when it calls this function:
void getValues( int vi, InputArray _sidx, float* values ) const CV_OVERRIDE
{
Mat sidx = _sidx.getMat();
int i, n = sidx.checkVector(1, CV_32S), nsamples = getNSamples();
CV_Assert( 0 <= vi && vi < getNAllVars() );
CV_Assert( n >= 0 );
const int* s = n > 0 ? sidx.ptr<int>() : 0;
if( n == 0 )
n = nsamples;
}
}
Can anyone point me in the right direction to fix this problem?
EDIT:
If I set the MinSampleCount to 388 (the number of samples) I don't get an error, but the prediction won't work correctly (it always returns label 5).

OpenCV Mahalanobis function to calculate distance between two images

I'm using OpenCV to test the similarity between two images taken from the same environment.
I have a series of photos of the same moving environment. So being A and B two binary images of the edges of two sequential images of this environment, I do the following:
Mat mean;
mean.create(a.rows, a.cols, a.type());
mean += a;
mean += b;
mean/=2;
Mat covar, mean;
calcCovarMatrix(mean, covar, mean, COVAR_NORMAL | COVAR_ROWS, CV_8UC1);
Mat icovar = covar.inv();
std::cout<<"type a:"<<a.type()<<"\n";
std::cout<<"type b:"<<b.type()<<"\n";
std::cout<<"icovar type:"<<icovar.type()<<"\n";
std::cout<<"a cols:"<<a.cols<<"\n";
std::cout<<"a rows:"<<a.rows<<"\n";
std::cout<<"b cols:"<<b.cols<<"\n";
std::cout<<"b rows:"<<b.rows<<"\n";
std::cout<<"icovar cols:"<<icovar.cols<<"\n";
std::cout<<"icovar rows:"<<icovar.rows<<"\n";
double mahalDistance = Mahalanobis(a, b, icovar);
The matrixes are all the same type and have the following values:
type a:5
type b:5
icovar type:5
a cols:1280
a rows:400
b cols:1280
b rows:400
icovar cols:1280
icovar rows:1280
The mahalanobis distance function throws an error as following:
OpenCV Error: Assertion failed (type == v2.type() && type == icovar.type() && sz == v2.size() && len == icovar.rows && len == icovar.cols) in Mahalanobis, file /Users/felipefujioka/Documents/Developer/tg/opencv-3.0.0-beta/modules/core/src/matmul.cpp, line 2486
libc++abi.dylib: terminating with uncaught exception of type cv::Exception: /Users/felipefujioka/Documents/Developer/tg/opencv-3.0.0-beta/modules/core/src/matmul.cpp:2486: error: (-215) type == v2.type() && type == icovar.type() && sz == v2.size() && len == icovar.rows && len == icovar.cols in function Mahalanobis
I'd apreciate to know where I'm wrong. Thanks in advance.
You mix a with ma and b with mb in your code. Have you tried with Mahalanobis(ma, mb, icovar)?
According with docs:
http://docs.opencv.org/modules/core/doc/operations_on_arrays.html#double%20Mahalanobis%28InputArray%20v1,%20InputArray%20v2,%20InputArray%20icovar%29
A and B must be 1D array not matrices

cv::Mat causes crash when assigned to with 'at<unsigned char>(0,0)'

I have the following simple bit of code that is crashing and it's not immediately clear to me why this shouldn't work.
cv::Mat *test_bug = new cv::Mat(img->rows, img->cols, CV_32F);
test_bug->at<unsigned char>(0,0) = 4;
test_bug ends up being a 207 by 207 matrix..so I know that the array index is valid. This is the copy/paste error.
OpenCV Error: Assertion failed (dims <= 2 && data && (unsigned)i0 <
(unsigned)size.p[0] && (unsigned)(i1*DataType<_Tp>::channels) <
(unsigned)(size.p[1]*channels()) && ((((sizeof(size_t)<<28)|0x8442211)
((DataType<_Tp>::depth) & ((1 << 3) - 1))*4) & 15) == elemSize1()) in unknown function, file
C:\opencv231\build\include\opencv2/core/mat.hpp, line 552
Cause:
cv::Mat *test_bug = new cv::Mat(img->rows, img->cols, CV_32F);
Note the CV_32F; that's a floating point matrix.
test_bug->at<unsigned char>(0,0) = 4;
Note unsigned char (or uchar) ; now you're treating it like an unsigned char matrix (CV_8U).
Fix:
So if your matrix is supposed to be floating-point, you need to access elements with:
test_bug->at<float>(0,0) = 4;
Or, if you wanted to declare a uchar matrix:
cv::Mat *test_bug = new cv::Mat(img->rows, img->cols, CV_8U);
I ran your code exactly, and had no trouble, but I'm using the OpenCV trunk on Linux.
A few things to try:
What happens if instead of an unsigned char you use a float:
test_bug->at< float >(0, 0) = 4.0;
Declare the object on the stack to see if the behavior changes.
Mat test_bug(img->size(), CV_32F);
test_bug.at< unsigned char >(0, 0) = 4; // this will work, but it will only set one byte of the 32-bit floating point number.
NOTE : Only setting the first byte of the float pixel you will see some interesting data because the at() function advances the pixel pointer by the number bytes you specified for the type (in your case sizeof(float) == 4). You could (not that you would want to) set the floating point value using an unsigned int like so:
test_bug->at< unsigned int >(0, 0) = 0x41CA0000; // same as 25.25 in IEEE 754...
Also, make sure that img->size() != Size(0, 0) that could also be something that is a problem.
Hope that is helpful!