web-dev-qa-db-fra.com

Classificateur SVM basé sur les fonctionnalités HOG pour la "détection d'objet" dans OpenCV

J'ai un projet, que je veux détecter des objets dans les images; mon objectif est d'utiliser les fonctionnalités HOG. En utilisant l'implémentation OpenCV SVM, j'ai pu trouver le code pour détecter les personnes, et j'ai lu quelques articles sur le réglage des paramètres afin de détecter un objet au lieu de personnes. Malheureusement, je n'ai pas pu le faire pour plusieurs raisons; tout d'abord, je suis probablement en train de régler les paramètres incorrectement, deuxièmement, je ne suis pas un bon programmeur en C++ mais je dois le faire avec C++/OpenCV ... ici vous pouvez trouver le code pour détecter les fonctionnalités HOG pour les personnes à l'aide de C++/OpenCV.

Disons que je veux détecter l'objet dans ce image . Maintenant, je vais vous montrer ce que j'ai essayé de changer dans le code, mais cela n'a pas fonctionné avec moi.

Le code que j'ai essayé de changer:

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

J'ai essayé de modifier getDefaultPeopleDetector() avec les paramètres suivants, mais cela n'a pas fonctionné:

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

J'ai ensuite essayé de faire un vecteur, mais quand j'ai voulu imprimer les résultats, il semble être vide.

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);

S'il vous plaît, j'ai besoin d'aide pour résoudre ce problème.

21
Mario

Afin de détecter des objets arbitraires à l'aide des descripteurs HOG opencv et du classificateur SVM, vous devez d'abord former le classificateur. Jouer avec les paramètres n'aidera pas ici, désolé :(.

En termes généraux, vous devrez effectuer les étapes suivantes:

Étape 1) Préparez des images d'apprentissage des objets que vous souhaitez détecter (échantillons positifs). Vous devrez également préparer des images sans objets d'intérêt (échantillons négatifs).

Étape 2) Détectez les fonctionnalités HOG de l'exemple de formation et utilisez ces fonctionnalités pour former un classificateur SVM (également fourni dans OpenCV).

Étape 3) Utilisez les coefficients du classificateur SVM formé dans la méthode HOGDescriptor :: setSVMDetector ().

Ce n'est qu'alors que vous pouvez utiliser l'exemple de code peopledetector.cpp pour détecter les objets que vous souhaitez détecter.

36
Hakan Serce

J'ai été confronté au même problème et surpris par le manque de solutions C++ propres que j'ai créées ~> ce wrapper de SVMLight <~, qui est une bibliothèque statique qui fournit des classes SVMTrainer et SVMClassifier qui simplifient la formation à quelque chose comme:

// 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");

puis pour chaque échantillon de formation:

// 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

jusqu'au features.dat le fichier contient des vecteurs de fonctionnalités pour tous les échantillons et à la fin vous appelez simplement:

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

Une fois que vous avez un fichier avec le modèle (ou features.dat avec lequel vous pouvez simplement former le classificateur):

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);

il suffit de vérifier la documentation de HOGDescriptor pour plus d'informations :)

9
LihO

J'ai fait des choses similaires à vous: collecter des échantillons d'images positives et négatives à l'aide de HOG pour extraire les caractéristiques de la voiture, former l'ensemble de fonctionnalités à l'aide de SVM linéaire (j'utilise la lumière SVM), puis utiliser le modèle pour détecter la voiture à l'aide de la fonction multidétecter HOG.

J'obtiens beaucoup de faux positifs, puis je recycle les données en utilisant des échantillons positifs et des échantillons faux positifs + négatifs. Le modèle résultant est ensuite testé à nouveau. La détection résultante s'améliore (moins de faux positifs) mais le résultat n'est pas satisfaisant (taux de réussite moyen de 50% et 50% de faux positifs). Le réglage des paramètres multidétecteurs améliore le résultat mais pas beaucoup (10% de faux positifs en moins et augmentation du taux de réussite).

Edit Je peux vous partager le code source si vous le souhaitez, et je suis très ouvert à la discussion car je n'ai pas obtenu de résultats satisfaisants en utilisant HOG. Quoi qu'il en soit, je pense que le code peut être un bon point de départ pour utiliser HOG pour la formation et la détection

Modifier: ajouter du code

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;
}
7
bonchenko