MLMLTL
MLMLTL

Reputation: 1559

SVM OpenCV c++ Predict returning nothing but 1's

I believe I have successfully trained an SVM, but when I try to predict with it, the output is entirely 1's.

My code for training looks like this:

for(size_t i = 0; i < (testPosArraySize); i++){
    testGivenImg = imread(imagePosDir[i]);
    detector->detect(testGivenImg, testKeypointsPos);
    bowDE.compute(testGivenImg, testKeypointsPos, testFeaturesPos);
    testFeaturesPos.reshape(1, 1);
    testFeaturesVec.push_back(testFeaturesPos);
}
for(size_t i = 0; i < (testNegaArraySize); i++){
    testGivenImg = imread(image[i]);
    detector->detect(testGivenImg, testKeypointsNega);
    bowDE.compute(testGivenImg, testKeypointsNega, testFeaturesNega);
    testFeaturesNega.reshape(1, 1);
    testFeaturesVec.push_back(testFeaturesNega);
}

Mat labels(numSamples, 1, CV_32F);
labels.rowRange(0, testPosArraySize).setTo(1);
labels.rowRange(testPosArraySize + 1, numSamples).setTo(-1);
SVM.model.train(fileTestFeat, labels, Mat(), Mat(), SVMParams());

My code for prediction looks like this:

vector<Mat> predictMatVec(predictArraySize); // -- amount of testing images

for(size_t i = 0; i < (predictArraySize); i++){
    predictImg = imread(imageNegaDir[i]);
    detector->detect(predictImg, predictKeypoints);
    bowDE.compute(predictImg, predictKeypoints, predictFeatures);
    predictFeatures.reshape(1, 1);
    predictMatVec[i].push_back(predictFeatures);

    Mat predictMat = Mat(predictMatVec);
    float* predictFloat1D = (float*)predictMat.data;
    Mat predictMat1D(1, fileTestFeat.cols, CV_32FC1, predictFloat1D);
    float predictFloat = model.predict(predictMat1D);
    cout << " -- SVM output: " << predictFloat << endl; 
}

But it is returning nothing but 1's.

enter image description here

What is wrong with it?

Upvotes: 3

Views: 5171

Answers (1)

Kornel
Kornel

Reputation: 5364

So, the vocabulary has been already created (e.g. by BOWKMeansTrainer) and you start to train you SVM classifier, right?

At this point you have a feature detector, extractor, matcher and a BOW image descriptor extractor (to compute an image descriptor using the bag of visual words) such as:

cv::Ptr<cv::FeatureDetector> detector = cv::FeatureDetector::create("SURF");
cv::Ptr<cv::DescriptorExtractor> extractor = cv::DescriptorExtractor::create("SURF");
cv::Ptr<cv::DescriptorMatcher> matcher = cv::DescriptorMatcher::create("BruteForce ");

cv::BOWImgDescriptorExtractor bowide(extractor, matcher);
bowide->setVocabulary(vocabulary);

First of all we need to scour the training set for our histograms:

cv::Mat samples;
cv::Mat labels(0, 1, CV_32FC1);

for(auto& it : imagePosDir)
{
    cv::Mat image = cv::imread(it);

    std::vector<cv::KeyPoint> keypoints;
    detector->detect(image, keypoints);

    if(keypoints.empty()) continue;

    // Responses to the vocabulary
    cv::Mat imgDescriptor;
    bowide.compute(image, keypoints, imgDescriptor);

    if(imgDescriptor.empty()) continue;

    if(samples.empty())
    {
        samples.create(0, imgDescriptor.cols, imgDescriptor.type());
    }

    // Copy class samples and labels
    std::cout << "Adding " << imgDescriptor.rows << " positive sample." << std::endl;
    samples.push_back(imgDescriptor);

    cv::Mat classLabels = cv::Mat::ones(imgDescriptor.rows, 1, CV_32FC1);
    labels.push_back(classLabels);
}

Do the same for imagePosNeg except that classLabels will have zero values, such as:

...
cv::Mat classLabels = cv::Mat::zeros(imgDescriptor.rows, 1, CV_32FC1);
labels.push_back(classLabels);
...

Note how I build the samples and the labels, I marked the positive samples with labels '1', and then the negatives with label '0'. So we have the training data for each class (here for positives and negatives) in samples. Lets's get training:

cv::Mat samples_32f; 
samples.convertTo(samples_32f, CV_32F);

CvSVM svm; 
svm.train(samples_32f, labels);
// Do something with the classifier, like saving it to file

Then testing let's get testing the classifier:

for(auto& it : testDir)
{
    cv::Mat image = cv::imread(it);

    std::vector<cv::KeyPoint> keypoints;
    detector->detect(image, keypoints);

    if(keypoints.empty()) continue;

    // Responses to the vocabulary
    cv::Mat imgDescriptor;
    bowide.compute(image, keypoints, imgDescriptor);

    if(imgDescriptor.empty()) continue;

    float res = svm.predict(imgDescriptor, true);

    std::cout << "- Result of prediction: " << res << std::endl;
}

Is it working?


Update #1:

Here I made a simple example about BOW+SVM under OpenCV 3.0: https://github.com/bkornel/OpenCV_BOW_SVM/blob/master/main.cpp

This works me fine for classifying bottles of Coca Cola / Pepsi. I also published the binaries so you can have a try on your database. Hope it works :)

Upvotes: 2

Related Questions