OpenCV Mahalanobis function to calculate distance between two images - c++

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

Related

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 Kalman filter multiple object tracking error

I have been trying to work on multiple object tracking using Kalman Filter. Here is my code,
for (int i =0; i<vGlobal.size(); i++) // Vector of objects of interest
{
cv::Point pTemp = cv::Point(vGlobal[i].iX, vGlobal[i].iY);
cv::KalmanFilter kTempKF(4,2,0);
kTempKF.statePre.at<floatt>(0) = pTemp.x;
kTempKF.statePre.at<float>(1) = pTemp.y;
kTempKF.statePre.at<float>(2) = 0;
kTempKF.statePre.at<float>(3) = 0;
kTempKF.transitionMatrix = *(cv::Mat_<float>(4,4)<< 1,0,1,0, 0,1,0,1, 0,0,1,0, 0,0,0,1);
cv::setIdentity(kTempKF.measurementMatrix);
cv::setIdentity(kTempKF.processNoiseCov, cv::Scalar::all(1e-4));
cv::setIdentity(kTempKF.measurementNoiseCov, cv::Scalar::all(10));
cv::setIdentity(kTempKF.errorCovPost, cv::Scalar::all(.1));
vKalmanFilters.push_back(kTempKF);
}
I am using vector of Kalman filters to track each of my objects. I have done initialization of the filters as above. Now I try to work on predition and measurement as follows in the below code.
for (int i=0; i<vKalmanFilters.size();i++)
{
cv::Mat mPrediction = vKalmanFilters[i].predict();
cv::Point pPredict(mPrediction.at<float>(0), mPrediction.at<float>(1));
mMeasurement(0) = vGlobal[i].iX;
mMeasurement(1) = vGlobal[i].iY;
cv::Mat mEstimated;
mEstimated = vKalmanFilters[i].correct(mPrediction); // Run time Error occurs here
}
When I try to run this program, I get a runtime error in the correct(Prediction)
OpenCV Error: Assertion failed (C.type() == type && (((flags&GEMM_3_T) == 0 && C.rows == d_size.height && C.cols == d_size.width) || ((flags&GEMM_3_T) != 0 && C.rows == d_size.width && C.cols == d_size.height))) in gemm, file /build/buildd/opencv-2.4.8+dfsg1/modules/core/src/matmul.cpp, line 741
terminate called after throwing an instance of 'cv::Exception'
what(): /build/buildd/opencv-2.4.8+dfsg1/modules/core/src/matmul.cpp:741: error: (-215) C.type() == type && (((flags&GEMM_3_T) == 0 && C.rows == d_size.height && C.cols == d_size.width) || ((flags&GEMM_3_T) != 0 && C.rows == d_size.width && C.cols == d_size.height)) in function gemm
I am still a beginner in Kalman Filter. The error occurs at the point of prediction. Is my approach totally wrong ? Please someone explain where I am going wrong.
You should use mMeasurement (2x1 matrix) instead of mPrediction (4x1 matrix) in the correction step:
mEstimated = vKalmanFilters[i].correct(mMeasurement);
Given that you did:
cv::KalmanFilter kTempKF(4,2,0);
// 4 dynamic params <-- your state, e.g. [x y dx dy]
// 2 measurements params <-- your mMeasurement [x y]

opencv C++ neural network predict() function throws "Bad argument" error

I have managed to train a neural network to recognize numbers in an image and have saved the network parameters to an .xml file.
However, when testing the network against a new image the code fails at the predict() stage with the error:
OpenCV Error: Bad argument (Both input and output must be floating-point matrices of the same type and have the same number of rows) in CvANN_MLP::predict, file ........\opencv\modules\ml\src\ann_mlp.cpp, line 279.
ann_mlp.cpp line 279 is:
if( !CV_IS_MAT(_inputs) || !CV_IS_MAT(_outputs) ||
!CV_ARE_TYPES_EQ(_inputs,_outputs) ||
(CV_MAT_TYPE(_inputs->type) != CV_32FC1 &&
CV_MAT_TYPE(_inputs->type) != CV_64FC1) ||
_inputs->rows != _outputs->rows )
CV_Error( CV_StsBadArg, "Both input and output must be floating-point matrices "
"of the same type and have the same number of rows" );
I have checked input rows by running this code:
cv::Size s = newVec.size();
int rows = s.height;
int cols = s.width;
cout << "newVec dimensions: " << rows << " x " << cols << endl;
...and it comes out with the expected 1 x 900 vector / matrix.
I have set the input and output matrices to be CV_32FC1 as per the error dialog like this:
Input matrix
cv::Mat newVec(1, 900, CV_32FC1);
newVec = crop_img.reshape(0, 1); //reshape / unroll image to vector
CvMat n = newVec;
newVec = cv::Mat(&n);
Output matrix
cv::Mat classOut = cvCreateMatHeader(1, CLASSES, CV_32FC1);
And I try to run the prediction function like this:
CvANN_MLP* nnetwork = new CvANN_MLP;
nnetwork->load("nnetwork.xml", "nnetwork");
int maxIndex = 0;
cv::Mat classOut = cvCreateMatHeader(1, CLASSES, CV_32FC1);
//prediction
nnetwork->predict(newVec, classOut);
float value;
float maxValue = classOut.at<float>(0, 0);
for (int index = 1; index<CLASSES; index++)
{
value = classOut.at<float>(0, index);
if (value>maxValue)
{
maxValue = value;
maxIndex = index;
}
}
Any ideas? Much appreciated...
I suspect the problem is your input, not your output.
First it's important to understand that OpenCV deserves a lot of the blame for this, not you. Their C++ API is quite mediocre, and it caused major confusion to you.
See, normally in C++ when you define a 1x900 matrix of floats, it stays a matrix of floats. C++ has strong type safety.
OpenCV does not. If you assign a matrix of bytes to a matrix of floats, the latter will change its type (!).
Your code initializes newVec to such a matrix of floats, then assigns a second matrix, and then yet another matrix. I suspect that crop_img is still an image, i.e. 8 bits. Reshaping it will make it 1x900, but not floating point. That's the job of .convertTo.

OpenCV 2.3.1. cv::Mat to std::vector cast

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...

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!