基于 HOG 特征的 SVM 分类器用于 OpenCV 中的“对象检测”

Posted

技术标签:

【中文标题】基于 HOG 特征的 SVM 分类器用于 OpenCV 中的“对象检测”【英文标题】:SVM classifier based on HOG features for "object detection" in OpenCV 【发布时间】:2012-06-01 22:15:55 【问题描述】:

我有一个项目,我想检测图像中的对象;我的目标是使用 HOG 功能。通过使用 OpenCV SVM 实现,我可以找到检测人的代码,并且我阅读了一些关于调整参数以检测对象而不是人的论文。不幸的是,由于几个原因,我不能这样做。首先,我可能错误地调整了参数,其次,我不是 C++ 的优秀程序员,但我必须使用 C++/OpenCV...here 你可以找到用于检测 HOG 功能的代码人们使用 C++/OpenCV。

假设我要检测此image 中的对象。现在,我将向您展示我尝试在代码中更改的内容,但没有成功。

我尝试更改的代码:

HOGDescriptor hog;
hog.setSVMDetector(HOGDescriptor::getDefaultPeopleDetector());

我尝试使用以下参数更改getDefaultPeopleDetector(),但没有成功:

(Size(64, 128), Size(16, 16), Size(8, 8), Size(8, 8), 9, 0,-1, 0, 0.2, true, cv::HOGDescriptor::DEFAULT_NLEVELS)

然后我尝试制作一个向量,但是当我想打印结果时,它似乎是空的。

vector<float> detector;

HOGDescriptor hog(Size(64, 128), Size(16, 16), Size(8, 8), Size(8, 8), 9, 0,-1, 0, 0.2, true, cv::HOGDescriptor::DEFAULT_NLEVELS);

hog.setSVMDetector(detector);

拜托,我需要帮助解决这个问题。

【问题讨论】:

我被困在这里,我想知道该怎么做,甚至想给我看一个例子 只是想知道,如果我在编码方面做错了,我正在学习,这是这里问题的目的,以获得好处 【参考方案1】:

为了使用 opencv HOG 描述符和 SVM 分类器检测任意对象,您需要首先训练分类器。在这里玩参数无济于事,对不起:(。

从广义上讲,您需要完成以下步骤:

步骤 1) 准备一些您要检测的对象的训练图像(正样本)。此外,您还需要准备一些没有感兴趣对象的图像(负样本)。

第 2 步)检测训练样本的 HOG 特征并使用该特征来训练 SVM 分类器(OpenCV 中也提供)。

步骤 3) 在 HOGDescriptor::setSVMDetector() 方法中使用训练好的 SVM 分类器的系数。

只有这样,您才能使用 peopledetector.cpp 示例代码来检测您想要检测的对象。

【讨论】:

非常感谢您的精彩回答,但我有一些问题要问...首先,我是否必须使用默认参数检测功能,还是必须调整参数?其次,你指的是什么系数,能不能给我简单描述一下? 默认参数会在开始时完成这项工作。我的意思是你传递给hog.setSVMDetector() 方法的std::vector&lt;float&gt; 系数。 我遇到了一些问题,我可以通过您的电子邮件与您联系更多 请在这里提问。其他人可能会发现讨论也很有用。 我创建它,std::vector 然后在 HOGDescriptor hog(HOGDescriptor::getDefaultPeopleDetector());这似乎不正确并且我最初所遭受的痛苦,然后我将系数传递给 hog.setSVMDetector(detector);但它不起作用......【参考方案2】:

我一直在处理同样的问题,并对我创建的一些干净的 C++ 解决方案感到惊讶 ~> this wrapper of SVMLight <~,这是一个提供类SVMTrainer 和@987654324 的静态库@ 将训练简化为:

// we are going to use HOG to obtain feature vectors:
HOGDescriptor hog;
hog.winSize = Size(32,48);

// and feed SVM with them:
SVMLight::SVMTrainer svm("features.dat");

那么对于每个训练样本:

// obtain feature vector describing sample image:
vector<float> featureVector;
hog.compute(img, featureVector, Size(8, 8), Size(0, 0));

// and write feature vector to the file:
svm.writeFeatureVectorToFile(featureVector, true);      // true = positive sample

直到features.dat 文件包含所有样本的特征向量,最后您只需调用:

std::string modelName("classifier.dat");
svm.trainAndSaveModel(modelName);

一旦你有一个模型文件(或features.dat,你可以用它来训练分类器):

SVMLight::SVMClassifier c(classifierModelName);
vector<float> descriptorVector = c.getDescriptorVector();
hog.setSVMDetector(descriptorVector);
...
vector<Rect> found;
Size padding(Size(0, 0));
Size winStride(Size(8, 8));
hog.detectMultiScale(segment, found, 0.0, winStride, padding, 1.01, 0.1);

请查看HOGDescriptor 的文档了解更多信息:)

【讨论】:

@Liho,最后结果如何?检测效果好吗?你有没有像其他人提到的那样得到很多误报? @krips89 结果足以实时检测游戏片段(域:棋盘游戏)。不过那是学校项目的一部分。如果您打算在一些严肃的项目中使用它,您可能需要调整 SVN 的内部参数。你可能还想检查一些人在 github 上留下的 bug 和 cmets :)【参考方案3】:

我做过和你类似的事情:使用 HOG 收集正负图像样本以提取汽车的特征,使用线性 SVM 训练特征集(我使用 SVM 光),然后使用模型使用 HOG 检测汽车多重检测功能。

我得到很多误报,然后我使用正样本和误报+负样本重新训练数据。然后再次测试生成的模型。结果检测有所改善(误报减少),但结果并不令人满意(平均 50% 的命中率和 50% 的误报)。调整多检测参数可以改善结果,但效果不大(误报减少 10% 并提高命中率)。

编辑 如果您愿意,我可以与您分享源代码,并且我非常愿意讨论,因为我使用 HOG 没有得到令人满意的结果。无论如何,我认为代码可以作为使用 HOG 进行训练和检测的一个很好的起点

编辑:添加代码

static void calculateFeaturesFromInput(const string& imageFilename, vector<float>& featureVector, HOGDescriptor& hog) 

    Mat imageData = imread(imageFilename, 1);
    if (imageData.empty()) 
        featureVector.clear();
        printf("Error: HOG image '%s' is empty, features calculation skipped!\n", imageFilename.c_str());
        return;
    
    // Check for mismatching dimensions
    if (imageData.cols != hog.winSize.width || imageData.rows != hog.winSize.height) 
       featureVector.clear();
       printf("Error: Image '%s' dimensions (%u x %u) do not match HOG window size (%u x %u)!\n", imageFilename.c_str(), imageData.cols, imageData.rows, hog.winSize.width, hog.winSize.height);
        return;
    
    vector<Point> locations;
    hog.compute(imageData, featureVector, winStride, trainingPadding, locations);
    imageData.release(); // Release the image again after features are extracted

...

int main(int argc, char** argv) 

    // <editor-fold defaultstate="collapsed" desc="Init">
    HOGDescriptor hog; // Use standard parameters here
    hog.winSize.height = 128;
    hog.winSize.width = 64;

    // Get the files to train from somewhere
    static vector<string> tesImages;
    static vector<string> positiveTrainingImages;
    static vector<string> negativeTrainingImages;
    static vector<string> validExtensions;
    validExtensions.push_back("jpg");
    validExtensions.push_back("png");
    validExtensions.push_back("ppm");
    validExtensions.push_back("pgm");
    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="Read image files">
    getFilesInDirectory(posSamplesDir, positiveTrainingImages, validExtensions);
    getFilesInDirectory(negSamplesDir, negativeTrainingImages, validExtensions);
    /// Retrieve the descriptor vectors from the samples
    unsigned long overallSamples = positiveTrainingImages.size() + negativeTrainingImages.size();
    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="Calculate HOG features and save to file">
    // Make sure there are actually samples to train
    if (overallSamples == 0) 
        printf("No training sample files found, nothing to do!\n");
        return EXIT_SUCCESS;
    

    /// @WARNING: This is really important, some libraries (e.g. ROS) seems to set the system locale which takes decimal commata instead of points which causes the file input parsing to fail
    setlocale(LC_ALL, "C"); // Do not use the system locale
    setlocale(LC_NUMERIC,"C");
    setlocale(LC_ALL, "POSIX");

    printf("Reading files, generating HOG features and save them to file '%s':\n", featuresFile.c_str());
    float percent;
    /**
     * Save the calculated descriptor vectors to a file in a format that can be used by SVMlight for training
     * @NOTE: If you split these steps into separate steps: 
     * 1. calculating features into memory (e.g. into a cv::Mat or vector< vector<float> >), 
     * 2. saving features to file / directly inject from memory to machine learning algorithm,
     * the program may consume a considerable amount of main memory
     */ 
    fstream File;
    File.open(featuresFile.c_str(), ios::out);
    if (File.good() && File.is_open()) 
        File << "# Use this file to train, e.g. SVMlight by issuing $ svm_learn -i 1 -a weights.txt " << featuresFile.c_str() << endl; // Remove this line for libsvm which does not support comments
        // Iterate over sample images
        for (unsigned long currentFile = 0; currentFile < overallSamples; ++currentFile) 
            storeCursor();
            vector<float> featureVector;
            // Get positive or negative sample image file path
            const string currentImageFile = (currentFile < positiveTrainingImages.size() ? positiveTrainingImages.at(currentFile) : negativeTrainingImages.at(currentFile - positiveTrainingImages.size()));
            // Output progress
            if ( (currentFile+1) % 10 == 0 || (currentFile+1) == overallSamples ) 
                percent = ((currentFile+1) * 100 / overallSamples);
                printf("%5lu (%3.0f%%):\tFile '%s'", (currentFile+1), percent, currentImageFile.c_str());
                fflush(stdout);
                resetCursor();
            
            // Calculate feature vector from current image file
            calculateFeaturesFromInput(currentImageFile, featureVector, hog);
            if (!featureVector.empty()) 
                /* Put positive or negative sample class to file, 
                 * true=positive, false=negative, 
                 * and convert positive class to +1 and negative class to -1 for SVMlight
                 */
                File << ((currentFile < positiveTrainingImages.size()) ? "+1" : "-1");
                // Save feature vector components
                for (unsigned int feature = 0; feature < featureVector.size(); ++feature) 
                    File << " " << (feature + 1) << ":" << featureVector.at(feature);
                
                File << endl;
            
        
        printf("\n");
        File.flush();
        File.close();
     else 
        printf("Error opening file '%s'!\n", featuresFile.c_str());
        return EXIT_FAILURE;
    
    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="Pass features to machine learning algorithm">
    /// Read in and train the calculated feature vectors
    printf("Calling SVMlight\n");
    SVMlight::getInstance()->read_problem(const_cast<char*> (featuresFile.c_str()));
    SVMlight::getInstance()->train(); // Call the core libsvm training procedure
    printf("Training done, saving model file!\n");
    SVMlight::getInstance()->saveModelToFile(svmModelFile);
    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="Generate single detecting feature vector from calculated SVM support vectors and SVM model">
    printf("Generating representative single HOG feature vector using svmlight!\n");
    vector<float> descriptorVector;
    vector<unsigned int> descriptorVectorIndices;
    // Generate a single detecting feature vector (v1 | b) from the trained support vectors, for use e.g. with the HOG algorithm
    SVMlight::getInstance()->getSingleDetectingVector(descriptorVector, descriptorVectorIndices);
    // And save the precious to file system
    saveDescriptorVectorToFile(descriptorVector, descriptorVectorIndices, descriptorVectorFile);
    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="Test detecting vector">

    cout << "Test Detecting Vector" << endl;
    hog.setSVMDetector(descriptorVector); // Set our custom detecting vector
    cout << "descriptorVector size: " << sizeof(descriptorVector) << endl;

    getFilesInDirectory(tesSamplesDir, tesImages, validExtensions);
    namedWindow("Test Detector", 1);

    for( size_t it = 0; it < tesImages.size(); it++ )
    
        cout << "Process image " << tesImages[it] << endl;
        Mat image = imread( tesImages[it], 1 );
        detectAndDrawObjects(image, hog);

        for(;;)
        
            int c = waitKey();
            if( (char)c == 'n')
                break;
            else if( (char)c == '\x1b' )
                exit(0);
        
    
    // </editor-fold>
    return EXIT_SUCCESS;

【讨论】:

我最终会建议你发布代码,并希望得到任何人的反馈——不仅仅是作者。 好主意。现在有点忙,我今晚就去。谢谢! 嘿@bonchenko 谢谢你的回答,我已经解决了这个问题,但似乎你在建议一个好方法。无论如何,分享你的代码,我也会分享我的,BOW怎么样,你有什么想法吗? @bonchenko,你的代码在哪里?我在这里发布了一个新问题***.com/questions/16214920/… 尝试查看您可能对它们有想法的问题, @TimeManx 我没有得到更好的检测百分比。此外我发现 HOG 很慢,我需要检测视频流(20 fps)。目前我正在研究 Haar 和 LBP 功能

以上是关于基于 HOG 特征的 SVM 分类器用于 OpenCV 中的“对象检测”的主要内容,如果未能解决你的问题,请参考以下文章

为 HOG 特征分配标签以训练 SVM 分类器

基于SVM支持向量机和HOG特征的交通标志检测算法matlab仿真

opencv学习笔记SVM+HOG

图像分类基于matlab HOG+SVM图像分类识别含Matlab源码 2141期

OpenCV和SVM分类器在自动驾驶中的车辆检测

如何在 Python 中使用 OpenCV 3.0 中的 HOG 功能训练 SVM 分类器?