0

I am trying to use an SVM in Android using OpenCV's Android SDK. Everything seems to work correctly besides the predict() function.

I get the following error:

svm CvException error: (-215) samples.cols == var_count && samples.type() == CV_32F in function virtual float cv::ml::SVMImpl::predict(cv::InputArray, cv::OutputArray, int) const ]

I am rather certain that the dimensions and types are correct, but I have a feeling I am overlooking something simple. Below is the description of my code with the relevant code.

I have 60 training examples with 150 features each. All examples and labels are in a 2D float array of dimension 60x151 called dataset_2Darr. The right-most column has the labels stored as -1, 0, +1 for the three classes.

I first place the 60x150 sub-matrix in the Mat object X. Then, I place the labels in the 60x1 Mat object Y.

X is float - CV_ 32FC1

Y is int - CV_32SC1

The relevant code is executed in 3 parts of the overall program. Here are the three parts:

Part 1: Global Variables in top of MainActivity.java:

static int M = 60;   // Rows - examples
static int N = 150;  // Cols - features
public static Mat X;   // Data
public static Mat Y;   // Labels

// Instantiate SVM object globally
static SVM classifier = SVM.create();

Part 2: Inside OnCreate():

// SVM Stuff:
classifier.setKernel(SVM.LINEAR);
classifier.setType(SVM.C_SVC);
classifier.setGamma(0.5);
classifier.setNu(0.5);
classifier.setC(1);
//classifier.setTermCriteria(criteria);

// Dataset stuff:
Y = new Mat(new Size(1,M),CvType.CV_32SC1); // Integer {-1, 0, +1}
int Y_rows = Y.rows(); // 60
int Y_cols = Y.cols(); // 1

X = new Mat(new Size(N,M),CvType.CV_32FC1); // Float
int X_rows = X.rows(); // 60
int X_cols = X.cols(); // 150

Part 3: Inside method that executes code:

for (int i=0; i < 60; i++) {
    for(int j=0; j < 150; j++) {
        X.put(i, j, dataset_2Darr[i][j]); // Copy 2D array into mat object
    }
}

// Iterate down rows of right-most column
for (int i = 0; i < 60; ++i)
    Y.put(i,0, (int)dataset_2Darr[i][150]); // Copy right most column into label array

// Train the model using X and Y
classifier.train(X, Ml.ROW_SAMPLE, Y);

// Create 1x150 feature vector to test:
Mat x = new Mat(new Size(150, 1),CvType.CV_32FC1); // Float
int x_rows = x.rows(); // 1
int x_cols = x.cols(); // 150

// Place dummy values inside matrix x
for (int i = 0; i < 150; i++) {
    x.put(0, i, 0.2f);
}

//Mat outMat = new Mat();
//float response = classifier.predict(x, outMat, 0);
float prediction = classifier.predict(x);
user8919
  • 67
  • 2
  • 9
  • The assertion says `OpenCV Error: Assertion failed (samples.cols == var_count && samples.type() == CV_32F)` which means that the sample either doesn't have the right number of columns or doesn't have type `CV_32F`. It looks like you forgot the reshape function, so your data violates the first condition. I think in order to apply svm, the data needs to be a vector, i.e. 1 x n matrix. – Rick M. Apr 20 '18 at 07:08
  • Rick, I pass in x into the predict() function. It is a 1x150 Mat object. It is of type CV_32FC1. This seems to be both the correct type and dimension. Note that the Size(150, 1) parameter produces a 1x150 matrix, which is odd, but it is checked on the following lines. Am I missing something obvious? Your help is greatly appreciated. – user8919 Apr 20 '18 at 07:55
  • Have a look at [this](https://stackoverflow.com/questions/42233031/how-to-svm-train-my-edge-images-using-java-code/42270916#42270916) answer and [this](https://stackoverflow.com/questions/14694810/using-opencv-and-svm-with-images) answer and see what you can find. If you can't solve it, I will be happy to help :) – Rick M. Apr 20 '18 at 08:45
  • Rick, that is an excellent explanation. I read through the whole thing and I am pretty sure I am following all of the advice. My 60x150 matrix X already has the 60 examples layed out in the rows, each with 150 features. The 60x1 matrix Y has the corresponding labels. The test vector I am trying to use is a 1x150 Mat object. I still do not see where my problem is at. Can you please help me? – user8919 Apr 20 '18 at 08:57
  • Also, as I am following the format as described here: http://answers.opencv.org/question/63715/svm-java-opencv-3/ . In this post they say that I need to use CV_32FC1 dataype for the training matrix (I call this X) and CV_32SC1 datatype for the label vector (I call this Y). – user8919 Apr 20 '18 at 09:02
  • Are your training samples and the test sample in the same range? I think you have to normalize the data, _both training and testing_. I am also not sure what you are passing in the labels array. Since you are using classification, it has to be `{-1, 1}` – Rick M. Apr 20 '18 at 09:11
  • Rick, I am using multi-class classification with 3 classes {-1, 0, +1}. I have read that the SVM functions scale up to a large number of classes. I am passing in the labels as the third argument to train(). The data is in X with each row as an example. The labels are in Y. X is 60x150. Y is 60x1. – user8919 Apr 20 '18 at 09:18
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/169419/discussion-between-rick-m-and-user8919). – Rick M. Apr 20 '18 at 09:19
  • My final problem was related to every line where I declare a Mat object using Size() as an argument. I replaced those lines all with zeros constructors. Concretely, these lines were replaced with: Y = Mat.zeros(60, 1, CvType.CV_32SC1); X = Mat.zeros(60, 150, CvType.CV_32FC1); Mat x = Mat.zeros(1, 150, CvType.CV_32FC1); – user8919 Apr 20 '18 at 12:25

1 Answers1

0

I met the same problem recently. The shape and type of test data are fine, but somehow the SVM model cannot work with the test data. The solution is that you need to make sure the dimension of test data is the same as the training data. Note that the dimension of [1,2,3] is 1, and the dimension of [[1,2,3]] is 2.