opencv / opencv_contrib

Repository for OpenCV's extra modules
Apache License 2.0
9.42k stars 5.76k forks source link

LDA class faulty - "wrong shapes for given matrices" error due to unitialised variable #429

Closed karsten-burger closed 9 years ago

karsten-burger commented 9 years ago

Using OpenCV 2.4.9 I wrote a small test program for the LDA (Linear Discriminant Analysis) and got strange runtime errors,

OpenCV Error: Bad argument (Wrong shapes for given matrices. Was size(src) = (2,1), size(W) = (2,1).) in subspaceProject, file /home/abuild/rpmbuild/BUILD/opencv-2.4.9/modules/contrib/src/lda.cpp, line 187

I noticed that it was dependent on a imshow() call before: if I removed it, the error vanished. I used valgrind and got a warning:

Conditional jump or move depends on uninitialised value(s) at cv::LDA::project(cv::_InputArray const&) (lda.cpp:1108) by main (simple_lda_example.cpp:125)

I then inspected the original source code lda.cpp, and found that a class member _dataAsRow is used uninitialised and transposes the data passed to the algorithm. This member is clearly a remnant of some test. It should be removed completely.

I also found that the same error is contained in OpenCV 3.0.0.

I am not allowed to attach the sample program, not even as txt, so I copy it into this description: I copied the original lda.cpp locally and tested the fix in this way:

Makefile:

LOC= simple_lda_example all: $(LOC) clean: rm $(LOC)

CXXFLAGS += -Wall -g cppcheck: cppcheck --enable=all *.cpp $(LOC): LDLIBS += -lopencv_core -lopencv_contrib -lopencv_highgui $(LOC): lda.o

Test program: simple_lda_example.cpp:

// Linear Discriminant Analysis example // // based on https://github.com/s4kibs4mi/opencv-2/blob/master/lda/src/main.cpp // // See overview http://www.bytefish.de/blog/pca_lda_with_gnu_octave/ // K Burger 2015 tested with Opencv 2.4.9

include "opencv2/opencv.hpp"

include

include

include

include

include

using namespace std; using cv::Mat;

typedef vectorcv::Point Contour;

void drawdots(cv::Mat & annot_image, const Contour& points, const cv::Scalar& color) { const int radius = 5, thick = -1; for (unsigned i=0; i < points.size(); ++i) cv::circle(annot_image, points[i], radius, color, thick); }

static void addToContour(Contour& cont, const Mat& row) { cont.push_back( cv::Point(row.at(0,0)_10, row.at(0,1)_10) ); }

int main(int argc, const char *argv[]) { // Example for a Linear Discriminant Analysis // (example taken from: http://www.bytefish.de/wiki/pca_lda_with_gnu_octave) double d[][2] = { //{2, 0}, // test outlier for fun {2, 3}, {3, 4}, {4, 5}, {5, 6}, {5, 7},

        {2, 1},
        {3, 2},
        {4, 2},
        {4, 3},
        {6, 4},
        {7, 6}
       //,{5, 7}  // test outlier for fun
};
int c[] = { //0, // test outlier
           0,0,0,0,0,
           1,1,1,1,1,1
           //,1   // test outlier
           };
const int n  = sizeof(c) / sizeof(int);
const int n2 = sizeof(d) / (2*sizeof(double));
cout << "n=" << n << endl;
if (n2 != n)
{
  cout << " Array sizes of d and c are not equal!" << endl;
  exit(1);
}

// convert into OpenCV representation
Mat data = Mat(n, 2, CV_64FC1, d);

vector<int> classes(c, c + n); // copy n elements into vector

// perform the lda
const int num_components = 1; // no of classes - 1, but we could omit this parameter
cv::LDA lda(data, classes, num_components);

// GNU Octave finds the following Eigenvalue:
//octave> d
//d =
//   1.5195e+00
//
// Eigen finds the following Eigenvalue:
// [1.519536390756363]
//
// Since there's only 1 discriminant, this is correct.

cout << "Eigenvalues:" << endl << lda.eigenvalues() << endl;

// GNU Octave finds the following Eigenvectors:
//  octave:13> V(:,1)
//  V =
//
//     0.71169  -0.96623
//    -0.70249  -0.25766
//
// Eigen finds the following Eigenvector:
// [0.7116932742510111;
//  -0.702490343980524 ]
//

/* Opencv 2.4.9:
 Eigenvalues:
  [1.519536390756357]
 Eigenvectors:
  [0.8254890051644111;
    -0.814814578373492]
 Eigenvectors normalised:
  [0.7322600386582955;
    -0.7227911588480589]
 */

const Mat& ev = lda.eigenvectors();
cout << "Eigenvectors:" << endl << ev << endl;
cout << "Eigenvectors normalised:" << endl;
double x0 = ev.at<double>(0,0), x1 = ev.at<double>(0,1);
double len = sqrt(x0*x0 + x1*x1);
cout << ev/sqrt(len) << endl;

// lists of points for drawings: original training data class and predicted class
Contour class0, class1, class0pred, class1pred;

for (int i=0; i < n; ++i)
{
  // project a data sample onto the subspace identified by LDA
  Mat x = data.row(i);
  Mat predicted = lda.project(x);
  //cout << predicted << endl;

  // Caution: this would cause chaos: predicted.at<float>(0,0);
  double pred = predicted.at<double>(0,0);
  cout << "Projection of " << x << ": real class=" << classes[i] << "    predicted f=: "<< pred << " class=" << (pred <= 0 ? 0 : 1)<< endl;

  if (classes[i] == 0) addToContour(class0, x);
  else                 addToContour(class1, x);

  if (pred <= 0) addToContour(class0pred, x);
  else           addToContour(class1pred, x);
}

// image of original data
Mat image = Mat::zeros( 100, 100, CV_8UC3 );
drawdots( image, class0, cv::Scalar(255,0,0)); // blue
drawdots( image, class1, cv::Scalar(0,255,0)); // green

// image of predictions
Mat imageResult = Mat::zeros( 100, 100, CV_8UC3 );
drawdots( imageResult, class0pred, cv::Scalar(255,0,0)); // blue
drawdots( imageResult, class1pred, cv::Scalar(0,255,0)); // green

cv::imshow("data (class0 in blue)", image);
cv::imshow("prediction (class0 in blue)", imageResult);
cv::waitKey(0);

}

alalek commented 9 years ago

It is similar to https://github.com/Itseez/opencv/issues/5318 Fix for master is here: https://github.com/Itseez/opencv/pull/5320

karsten-burger commented 9 years ago

ok thanks, did not see this. we could add my test-program to the sources.

karsten-burger commented 9 years ago

closed as duplicate