如何正确使用BoW训练OpenCV SVM [英] How to train OpenCV SVM with BoW Properly

查看:121
本文介绍了如何正确使用BoW训练OpenCV SVM的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我无法训练SVM识别我的对象.我正在尝试使用SURF +单词袋+ SVM来执行此操作.我的问题是分类器没有检测到任何东西.所有结果均为0.

I can't train the SVM to recognize my object. I'm trying to do this using SURF + Bag Of Words + SVM. My problem is that the classifier does not detect anything. All the results are 0.

这是我的代码:

Ptr<FeatureDetector> detector = FeatureDetector::create("SURF");
Ptr<DescriptorExtractor> descriptors = DescriptorExtractor::create("SURF");

string to_string(const int val) {
    int i = val;
    std::string s;
    std::stringstream out;
    out << i;
    s = out.str();
    return s;
}

Mat compute_features(Mat image) {
    vector<KeyPoint> keypoints;
    Mat features;

    detector->detect(image, keypoints);
    KeyPointsFilter::retainBest(keypoints, 1500);
    descriptors->compute(image, keypoints, features);

    return features;
}

BOWKMeansTrainer addFeaturesToBOWKMeansTrainer(String dir, BOWKMeansTrainer& bowTrainer) {
    DIR *dp;
    struct dirent *dirp;
    struct stat filestat;

    dp = opendir(dir.c_str());


    Mat features;
    Mat img;

    string filepath;
    #pragma loop(hint_parallel(4))
    for (; (dirp = readdir(dp));) {
        filepath = dir + dirp->d_name;

        cout << "Reading... " << filepath << endl;

        if (stat( filepath.c_str(), &filestat )) continue;
        if (S_ISDIR( filestat.st_mode ))         continue;

        img = imread(filepath, 0);

        features = compute_features(img);
        bowTrainer.add(features);
    }


    return bowTrainer;
}

void computeFeaturesWithBow(string dir, Mat& trainingData, Mat& labels, BOWImgDescriptorExtractor& bowDE, int label) {
    DIR *dp;
    struct dirent *dirp;
    struct stat filestat;

    dp = opendir(dir.c_str());

    vector<KeyPoint> keypoints;
    Mat features;
    Mat img;

    string filepath;

    #pragma loop(hint_parallel(4))
    for (;(dirp = readdir(dp));) {
        filepath = dir + dirp->d_name;

        cout << "Reading: " << filepath << endl;

        if (stat( filepath.c_str(), &filestat )) continue;
        if (S_ISDIR( filestat.st_mode ))         continue;

        img = imread(filepath, 0);

        detector->detect(img, keypoints);
        bowDE.compute(img, keypoints, features);

        trainingData.push_back(features);
        labels.push_back((float) label);
    }

    cout << string( 100, '\n' );
}

int main() {
    initModule_nonfree();

    Ptr<DescriptorMatcher> matcher = DescriptorMatcher::create("FlannBased");

    TermCriteria tc(CV_TERMCRIT_ITER + CV_TERMCRIT_EPS, 10, 0.001);
    int dictionarySize = 1000;
    int retries = 1;
    int flags = KMEANS_PP_CENTERS;
    BOWKMeansTrainer bowTrainer(dictionarySize, tc, retries, flags);
    BOWImgDescriptorExtractor bowDE(descriptors, matcher);

    string dir = "./positive_large", filepath;
    DIR *dp;
    struct dirent *dirp;
    struct stat filestat;

    cout << "Add Features to KMeans" << endl;
    addFeaturesToBOWKMeansTrainer("./positive_large/", bowTrainer);
    addFeaturesToBOWKMeansTrainer("./negative_large/", bowTrainer);

    cout << endl << "Clustering..." << endl;

    Mat dictionary = bowTrainer.cluster();
    bowDE.setVocabulary(dictionary);

    Mat labels(0, 1, CV_32FC1);
    Mat trainingData(0, dictionarySize, CV_32FC1);


    cout << endl << "Extract bow features" << endl;

    computeFeaturesWithBow("./positive_large/", trainingData, labels, bowDE, 1);
    computeFeaturesWithBow("./negative_large/", trainingData, labels, bowDE, 0);

    CvSVMParams params;
    params.kernel_type=CvSVM::RBF;
    params.svm_type=CvSVM::C_SVC;
    params.gamma=0.50625000000000009;
    params.C=312.50000000000000;
    params.term_crit=cvTermCriteria(CV_TERMCRIT_ITER,100,0.000001);
    CvSVM svm;

    cout << endl << "Begin training" << endl;

    bool res=svm.train(trainingData,labels,cv::Mat(),cv::Mat(),params);

    svm.save("classifier.xml");

    //CvSVM svm;
    svm.load("classifier.xml");

    VideoCapture cap(0); // open the default camera

    if(!cap.isOpened())  // check if we succeeded
        return -1;

    Mat featuresFromCam, grey;
    vector<KeyPoint> cameraKeyPoints;
    namedWindow("edges",1);
    for(;;)
    {
        Mat frame;
        cap >> frame; // get a new frame from camera
        cvtColor(frame, grey, CV_BGR2GRAY);
        detector->detect(grey, cameraKeyPoints);
        bowDE.compute(grey, cameraKeyPoints, featuresFromCam);

        cout << svm.predict(featuresFromCam) << endl;
        imshow("edges", frame);
        if(waitKey(30) >= 0) break;
    }   

        return 0;
}

您应该知道我从现有项目中获得的参数具有良好的效果,因此我认为它们在我的代码中也将很有用(但最终可能没有用).

You should know that I got the parameters from an existing project with a good results, so I thought they'll be useful in my code too (but eventually maybe not).

我有310张正像和508张负像.我尝试使用相等数量的正负图像,但结果是相同的. 我要检测的对象是汽车方向盘.这是我的数据集.

I have 310 positive images and 508 negative images. I tried to use equal numbers of positive and negative images but the result is the same. The object I want to detect is car steering wheel. Here is my dataset.

您知道我在做什么错吗?谢谢!

Do you have any idea what I'm doing wrong? Thank you!

推荐答案

首先,使用现有项目中的相同参数并不能证明您使用的参数正确.实际上,在我看来,这是一种完全胡说八道的方法(没有违法行为).这是因为,SVM参数直接受到数据集和分枝器提取方法的影响.为了获得正确的参数,您必须进行交叉验证.因此,如果这些参数是从其他识别任务中获得的,则将毫无意义.例如,在我的人脸验证项目中,gammaC的最佳参数分别为0.0625和10.

First of all, using same parameters from an existing project doesn't prove that you are using correct parameters. In fact, in my opinion it is a completely nonsense approach (no offense). It is because, SVM parameters are affected from dataset and decriptor extraction method directly. In order to get correct parameters you have to do cross-validation. So if those parameters are obtained from a different recognition task it won't make any sense. For example in my face verification project optimal parameters were 0.0625 and 10 for gamma and C respectively.

您的方法的其他重要问题是测试图像.据我从您的代码中看到的那样,您没有使用磁盘上的图像来测试分类器,因此在此其余部分中,我将做一些假设.如果从相机获取的测试图像与正像不同,则它将失败.我的意思是不同的.您必须确保测试图像仅由方向盘组成,因为您的训练图像仅包含方向盘.如果您的测试图像包含例如汽车座椅,则您的测试图像的BoW描述符将与火车图像的BoW描述符完全不同.因此,简单来说,您的测试图像不应包含带有其他物体的方向盘,而应仅包含方向盘.

Other important issue with your approach is test images. As far as I see from your code, you are not using images from disk to test your classifier, so from rest of here I'll do some assumptions. If your test images, that you are acquired from camera are different from your positive images, it will fail. By different I mean this; you have to be sure that your test images are composed only of steering wheels, because your training images contain only steering wheels. If your test image contains, for instance car seat with it, your BoW descriptor for test image will be completely different from your train images BoW descriptor. So, simply, your test images shouldn't contain steering wheels with some other objects, they should only contain steering wheels.

如果您满足这些要求,那么使用训练图像来测试系统是最基本的方法.即使在这种情况下,您可能也会遇到一些实现问题.其他方法可以是这样;将您的训练数据分为两个,这样您就可以分为四个分区:

If you satisfy these, using training images for testing your system is the most basic approach. Even in that scenario you are failing you probably have some implenetation issues. Other approach can be this; split your training data into two, such that you have four partitions:

  • 正面火车图像
  • 火车负像
  • 正面测试图像
  • 阴性测试图像

仅使用训练图像来训练系统,并使用测试图像对其进行测试.同样,您必须通过交叉验证来指定参数.

Use only train images for training the system and test it with the test images. And again, you have to specify parameters via cross-validation.

除这些以外,您可能需要先检查一些特定步骤以定位问题,然后再做我之前写的事情:

Other than these, you might want to check some specific steps in order to localize the problem, before doing the previous things that I wrote:

  1. 每个图像检测到多少个关键点?相似的图像应该会产生相似数量的关键点.
  2. 您知道BoW描述符是图像的SURF描述符的直方图.确保相似的图像导致相似的直方图(BoW描述符).最好通过可视化直方图进行检查.
  3. 如果上一步得到满足,则问题很可能出在SVM培训步骤上,这是非常重要的一步(也许是最重要的一步).

我希望我能够强调交叉验证的重要性.进行交叉验证!

I hope I was able to emphasize the importance of the cross-validation. Do the cross-validation!

祝你好运!

这篇关于如何正确使用BoW训练OpenCV SVM的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆