Классификатор SVM на основе функций HOG для "обнаружения объектов" в OpenCV

у меня есть проект, который я хочу обнаружить объекты на изображениях; моя цель-использовать функции HOG. Используя реализацию OpenCV SVM, я мог найти код для обнаружения людей, и я прочитал некоторые статьи о настройке параметров для обнаружения объекта вместо людей. К сожалению, я не мог этого сделать по нескольким причинам; Во-первых, я, вероятно, неправильно настраиваю параметры, во-вторых, я не хороший программист на C++, но я должен сделать это с C++/OpenCV... здесь вы можете найти код для обнаружения функций HOG для людей с помощью C++ / OpenCV.

предположим, что я хочу обнаружить объект в этом изображения. Теперь я покажу вам, что я пытался изменить в коде, но это не сработало со мной.

код, который я пытался изменить:

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

пожалуйста, мне нужна помощь в решении этой проблемы.

3 ответов


для обнаружения произвольных объектов с использованием дескрипторов opencv HOG и классификатора SVM вам необходимо сначала обучить классификатор. Игра с параметрами здесь не поможет, извините : (.

в широком смысле вам нужно будет выполнить следующие шаги:

Шаг 1) подготовьте несколько обучающих изображений объектов, которые вы хотите обнаружить (положительные образцы). Также вам нужно будет подготовить несколько изображений без объектов интереса (отрицательный образцы продукции.)

Шаг 2) обнаружение функций HOG учебной выборки и использование этих функций для обучения классификатора SVM (также предоставляется в OpenCV).

Шаг 3) используйте коэффициенты обученного классификатора SVM в методе HOGDescriptor::setSVMDetector ().

только тогда, вы можете использовать peopledetector.пример кода cpp, чтобы обнаружить объекты, которые вы хотите обнаружить.


Я имел дело с той же проблемой и удивлен отсутствием некоторых чистых решений на C++, которые я создал ~> эта обертка SVMLight , которая является статической библиотекой, предоставляющей классы SVMTrainer и SVMClassifier это упрощает обучение чему-то вроде:

// 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 для получения дополнительной информации :)


Я сделал похожие вещи, как вы сделали: сбор образцов положительных и отрицательных изображений с помощью HOG для извлечения особенностей автомобиля, обучить набор функций с помощью линейного SVM (я использую свет SVM), а затем использовать модель для обнаружения автомобиля с помощью функции Hog multidetect.

Я получаю много ложных срабатываний, затем я переобучаю данные, используя положительные образцы и ложные положительные+отрицательные образцы. Полученная модель затем тестируется снова. Результирующее обнаружение улучшается (меньше ложных срабатываний), но результат не удовлетворяет (средняя скорость попадания 50% и 50% ложных срабатываний). Настройка параметров multidetect улучшает результат, но не намного (на 10% меньше ложных срабатываний и увеличение скорости попадания).

редактировать Я могу поделиться с вами исходным кодом, если хотите, и я очень открыт для обсуждения, поскольку я не получил удовлетворительных результатов с помощью HOG. Во всяком случае, я думаю, что код может быть хорошей отправной точкой для использования HOG для обучения и обнаружения

Edit: добавление код

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