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.
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)
{...}
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).
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
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!