Ask Your Question

Revision history [back]

Completely random resultof HOGDescriptor

Using HOGDescriptor with C++. First of all, how can I identify class of detected object? There is no information in doc and example. I wrote simple program (according to train_HOG.cpp) and trained with vehicles and non-vehicles. So its "one-class"? How descriptor know it found vehicle or non-vehicle?

I did like in example, but it seems to not work (I hoped t see maybe higher density around cars - but it looks completely random!). One thing different is class name - I used 1 and 2 (for every class folder) but similar results are with classes like in example (-1 and 1). Tried different svm params (default and as in example...). The only thing I can do is show you my code :|

#include <HogClassifier.h>

#include <iostream>

#include <boost/filesystem.hpp>
#include <boost/range/iterator_range.hpp>
#include <boost/range/adaptor/indexed.hpp>
#include <boost/assert.hpp>

#include <opencv2/imgproc.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/core/ocl.hpp>

#include <opencv2/highgui.hpp>

namespace bfs = boost::filesystem;

HogClassifier::HogClassifier(cv::Size winSize)
{
    mHogs.winSize = winSize;
    //cv::ocl::setUseOpenCL(false);
}

auto HogClassifier::GetImages(std::vector<std::string> dirPaths, std::vector<int>& labels)
{
    int currClassDir = 0;
    std::vector<cv::Mat> images;

    for(auto& root: dirPaths)
    {
        if(bfs::is_directory(root))
        {
            for(const auto& record: boost::make_iterator_range(bfs::recursive_directory_iterator(root, {})))
                if(!bfs::is_directory(record))
                {
                    images.push_back(cv::imread(record.path().string()));
                    labels.push_back(currClassDir);
                }
        }
        else
        {
            std::cout << root << " is not a directory!" << std::endl;
        }
        ++currClassDir;
    }
    return images;
}

auto HogClassifier::GetDescriptors(const std::vector<cv::Mat> &images)
{
    std::vector<cv::Mat> allDdescriptors;
    std::vector<float> singleDescriptors;
    cv::Mat grayImage;

    for(const auto& img: images)
    {
        if(img.cols >= mHogs.winSize.width && img.rows >= mHogs.winSize.height)
        {
            cv::Rect extracted{(img.cols - mHogs.winSize.width) / 2,
                               (img.rows - mHogs.winSize.height) / 2,
                                mHogs.winSize.width, mHogs.winSize.height};
            cv::cvtColor(img(extracted), grayImage, cv::COLOR_BGR2GRAY);
            if(grayImage.data)
            {
                mHogs.compute(grayImage, singleDescriptors, cv::Size(4, 4), cv::Size(0, 0));
                allDdescriptors.push_back(cv::Mat{singleDescriptors}.clone());
            }
        }
    }
    return allDdescriptors;
}

auto HogClassifier::CumulateData(const std::vector<cv::Mat> &uncumulatedData)
{
    using namespace boost::adaptors;
    int columns = std::max(uncumulatedData.at(0).rows, uncumulatedData.at(0).cols);
    int rows = uncumulatedData.size();
    cv::Mat cumulatedData(rows, columns, CV_32F);
    cv::Mat temp(1, columns, CV_32F);

    for(const auto& sample: uncumulatedData | indexed(0))
    {
        if(sample.value().cols == 1)
        {
            cv::transpose(sample.value(), temp);
            temp.copyTo(cumulatedData.row(sample.index()));
        }
        else if(sample.value().rows == 1)
            sample.value().copyTo(cumulatedData.row(sample.index()));
        else
            std::cout << "Wrng data provided for cumulation" << std::endl;
    }

    return cumulatedData;
}

auto HogClassifier::GetSVM(SVMParams svmParams)
{
    auto svm = cv::ml::SVM::create();
    svm->setType(svmParams.type);
    svm->setKernel(svmParams.kernel);
    svm->setTermCriteria(svmParams.termCriteria);
    svm->setDegree(svmParams.degree);
    svm->setCoef0(svmParams.coef0);
    svm->setNu(svmParams.ny);
    svm->setP(svmParams.epsilon);
    svm->setC(svmParams.C);
    return svm;
}

auto HogClassifier::PrepateTrainingData(std::vector<std::string> dirPaths)
{
    std::vector<int> labels;

    auto images = GetImages(dirPaths, labels);
    auto uncumulatedData = GetDescriptors(images);
    auto trainingData = CumulateData(uncumulatedData);

    return std::make_tuple(trainingData, labels);
}

auto HogClassifier::TrainClassifier(cv::Ptr<cv::ml::SVM> svm, cv::Mat& data, std::vector<int>& labels)
{
    BOOST_ASSERT_MSG(data.rows == labels.size(), "Number of samples doesn't match numer of labels");
    BOOST_ASSERT(svm);
    if(svm)
    {
        svm->train(data, cv::ml::ROW_SAMPLE, labels);

        cv::Mat sv = svm->getSupportVectors();
        cv::Mat alpha, svidx;
        double rho = svm->getDecisionFunction( 0, alpha, svidx );
        CV_Assert( alpha.total() == 1 && svidx.total() == 1 && sv.rows == 1 );
        CV_Assert( (alpha.type() == CV_64F && alpha.at<double>(0) == 1.) ||
                   (alpha.type() == CV_32F && alpha.at<float>(0) == 1.f) );
        CV_Assert( sv.type() == CV_32F );
        std::vector<float> hogDetector(sv.cols + 1);
        memcpy(&hogDetector[0], sv.ptr(), sv.cols*sizeof(hogDetector[0]));
        hogDetector[sv.cols] = (float)-rho;
        mHogs.setSVMDetector(hogDetector);
        mTrained = true;
    }
}

auto HogClassifier::Train(std::vector<std::string> dirPaths, SVMParams svmParams)
{
    auto [trainingData, labels] = PrepateTrainingData(dirPaths);
    auto svm = GetSVM(svmParams);
    TrainClassifier(svm, trainingData, labels);
}

auto HogClassifier::Save(std::string destFile)
{
    mHogs.save(destFile);
}

auto HogClassifier::Load(std::string srcFile)
{
    if(mHogs.load(srcFile))
        mTrained = true;
    else
    {
        mTrained = false;
        std::cout << "Problem reading classifier file." << std::endl;
    }
    return mTrained;
}

auto HogClassifier::FindObjects(cv::Mat& image)
{
    std::vector<cv::Rect> detections;
    std::vector<double> weights;
    if(mTrained && image.data)
    {
        cv::Mat grayImg;
        cv::cvtColor(image, grayImg, cv::COLOR_BGR2GRAY);
        mHogs.detectMultiScale(grayImg, detections, weights);
        //mHogs.detectMultiScale(image, detections, weights);
        for(const auto& occurence: detections | boost::adaptors::indexed(0))
        {
            cv::Scalar color{0, weights[occurence.index()] * weights[occurence.index()] * 200, 0};
            cv::rectangle(image, occurence.value(), color, image.cols/400 + 1);
        }
    }
    return detections.size();
}

/*=========================================*/
//#define DOCTEST_CONFIG_DISABLE
#include <doctest.h>
TEST_CASE("Train and check")
{
    HogClassifier classifier{cv::Size{64, 64}};
    cv::Mat image = cv::imread("../cars-on-road2.jpg");

    classifier.Train({"../dataset/vehicles", "../dataset/non-vehicles"});
    int found = classifier.FindObjects(image);
    if(found)
    {
        cv::imshow("found cars", image);
        cv::waitKey();
    }

    CHECK(found > 0);
}

//header

struct SVMParams
{
    using SVM = cv::ml::SVM;

    SVM::Types type = SVM::C_SVC;
    SVM::KernelTypes kernel = SVM::LINEAR;
    cv::TermCriteria termCriteria = {cv::TermCriteria::MAX_ITER, 100, 1e-4};
    int degree = 0;
    double coef0 = 0;
    double ny = 0;
    double epsilon = 0;
    double C = 0.01;
};

class HogClassifier
{
public:
    HogClassifier(cv::Size winSize);
    auto Train(std::vector<std::string> dirPaths, SVMParams svmParams = SVMParams{});
    auto Save(std::string destFile);
    auto Load(std::string srcFile);
    auto Test(std::vector<std::string> dirPaths);
    auto FindObjects(cv::Mat &image);

private:
    auto PrepateTrainingData(std::vector<std::string> dirPaths);
    auto GetImages(std::vector<std::string> dirPaths, std::vector<int>& labels);
    auto GetDescriptors(const std::vector<cv::Mat>& images);
    auto CumulateData(const std::vector<cv::Mat>& uncumulatedData);
    auto GetSVM(SVMParams svmParams);
    auto TrainClassifier(cv::Ptr<cv::ml::SVM>svm, cv::Mat& data, std::vector<int>& labels);

    cv::HOGDescriptor mHogs;
    bool mTrained{false};
};

Completely random resultof result of HOGDescriptor

Using HOGDescriptor with C++. First of all, how can I identify class of detected object? There is no information in doc and example. I wrote simple program (according to train_HOG.cpp) and trained with vehicles and non-vehicles. So its "one-class"? How descriptor know it found vehicle or non-vehicle?

I did like in example, but it seems to not work (I hoped t see maybe higher density around cars - but it looks completely random!). One thing different is class name - I used 1 and 2 (for every class folder) but similar results are with classes like in example (-1 and 1). Tried different svm params (default and as in example...). The only thing I can do is show you my code :|

Example positives: pos pos

#include <HogClassifier.h>

#include <iostream>

#include <boost/filesystem.hpp>
#include <boost/range/iterator_range.hpp>
#include <boost/range/adaptor/indexed.hpp>
#include <boost/assert.hpp>

#include <opencv2/imgproc.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/core/ocl.hpp>

#include <opencv2/highgui.hpp>

namespace bfs = boost::filesystem;

HogClassifier::HogClassifier(cv::Size winSize)
{
    mHogs.winSize = winSize;
    //cv::ocl::setUseOpenCL(false);
}

auto HogClassifier::GetImages(std::vector<std::string> dirPaths, std::vector<int>& labels)
{
    int currClassDir = 0;
    std::vector<cv::Mat> images;

    for(auto& root: dirPaths)
    {
        if(bfs::is_directory(root))
        {
            for(const auto& record: boost::make_iterator_range(bfs::recursive_directory_iterator(root, {})))
                if(!bfs::is_directory(record))
                {
                    images.push_back(cv::imread(record.path().string()));
                    labels.push_back(currClassDir);
                }
        }
        else
        {
            std::cout << root << " is not a directory!" << std::endl;
        }
        ++currClassDir;
    }
    return images;
}

auto HogClassifier::GetDescriptors(const std::vector<cv::Mat> &images)
{
    std::vector<cv::Mat> allDdescriptors;
    std::vector<float> singleDescriptors;
    cv::Mat grayImage;

    for(const auto& img: images)
    {
        if(img.cols >= mHogs.winSize.width && img.rows >= mHogs.winSize.height)
        {
            cv::Rect extracted{(img.cols - mHogs.winSize.width) / 2,
                               (img.rows - mHogs.winSize.height) / 2,
                                mHogs.winSize.width, mHogs.winSize.height};
            cv::cvtColor(img(extracted), grayImage, cv::COLOR_BGR2GRAY);
            if(grayImage.data)
            {
                mHogs.compute(grayImage, singleDescriptors, cv::Size(4, 4), cv::Size(0, 0));
                allDdescriptors.push_back(cv::Mat{singleDescriptors}.clone());
            }
        }
    }
    return allDdescriptors;
}

auto HogClassifier::CumulateData(const std::vector<cv::Mat> &uncumulatedData)
{
    using namespace boost::adaptors;
    int columns = std::max(uncumulatedData.at(0).rows, uncumulatedData.at(0).cols);
    int rows = uncumulatedData.size();
    cv::Mat cumulatedData(rows, columns, CV_32F);
    cv::Mat temp(1, columns, CV_32F);

    for(const auto& sample: uncumulatedData | indexed(0))
    {
        if(sample.value().cols == 1)
        {
            cv::transpose(sample.value(), temp);
            temp.copyTo(cumulatedData.row(sample.index()));
        }
        else if(sample.value().rows == 1)
            sample.value().copyTo(cumulatedData.row(sample.index()));
        else
            std::cout << "Wrng data provided for cumulation" << std::endl;
    }

    return cumulatedData;
}

auto HogClassifier::GetSVM(SVMParams svmParams)
{
    auto svm = cv::ml::SVM::create();
    svm->setType(svmParams.type);
    svm->setKernel(svmParams.kernel);
    svm->setTermCriteria(svmParams.termCriteria);
    svm->setDegree(svmParams.degree);
    svm->setCoef0(svmParams.coef0);
    svm->setNu(svmParams.ny);
    svm->setP(svmParams.epsilon);
    svm->setC(svmParams.C);
    return svm;
}

auto HogClassifier::PrepateTrainingData(std::vector<std::string> dirPaths)
{
    std::vector<int> labels;

    auto images = GetImages(dirPaths, labels);
    auto uncumulatedData = GetDescriptors(images);
    auto trainingData = CumulateData(uncumulatedData);

    return std::make_tuple(trainingData, labels);
}

auto HogClassifier::TrainClassifier(cv::Ptr<cv::ml::SVM> svm, cv::Mat& data, std::vector<int>& labels)
{
    BOOST_ASSERT_MSG(data.rows == labels.size(), "Number of samples doesn't match numer of labels");
    BOOST_ASSERT(svm);
    if(svm)
    {
        svm->train(data, cv::ml::ROW_SAMPLE, labels);

        cv::Mat sv = svm->getSupportVectors();
        cv::Mat alpha, svidx;
        double rho = svm->getDecisionFunction( 0, alpha, svidx );
        CV_Assert( alpha.total() == 1 && svidx.total() == 1 && sv.rows == 1 );
        CV_Assert( (alpha.type() == CV_64F && alpha.at<double>(0) == 1.) ||
                   (alpha.type() == CV_32F && alpha.at<float>(0) == 1.f) );
        CV_Assert( sv.type() == CV_32F );
        std::vector<float> hogDetector(sv.cols + 1);
        memcpy(&hogDetector[0], sv.ptr(), sv.cols*sizeof(hogDetector[0]));
        hogDetector[sv.cols] = (float)-rho;
        mHogs.setSVMDetector(hogDetector);
        mTrained = true;
    }
}

auto HogClassifier::Train(std::vector<std::string> dirPaths, SVMParams svmParams)
{
    auto [trainingData, labels] = PrepateTrainingData(dirPaths);
    auto svm = GetSVM(svmParams);
    TrainClassifier(svm, trainingData, labels);
}

auto HogClassifier::Save(std::string destFile)
{
    mHogs.save(destFile);
}

auto HogClassifier::Load(std::string srcFile)
{
    if(mHogs.load(srcFile))
        mTrained = true;
    else
    {
        mTrained = false;
        std::cout << "Problem reading classifier file." << std::endl;
    }
    return mTrained;
}

auto HogClassifier::FindObjects(cv::Mat& image)
{
    std::vector<cv::Rect> detections;
    std::vector<double> weights;
    if(mTrained && image.data)
    {
        cv::Mat grayImg;
        cv::cvtColor(image, grayImg, cv::COLOR_BGR2GRAY);
        mHogs.detectMultiScale(grayImg, detections, weights);
        //mHogs.detectMultiScale(image, detections, weights);
        for(const auto& occurence: detections | boost::adaptors::indexed(0))
        {
            cv::Scalar color{0, weights[occurence.index()] * weights[occurence.index()] * 200, 0};
            cv::rectangle(image, occurence.value(), color, image.cols/400 + 1);
        }
    }
    return detections.size();
}

/*=========================================*/
//#define DOCTEST_CONFIG_DISABLE
#include <doctest.h>
TEST_CASE("Train and check")
{
    HogClassifier classifier{cv::Size{64, 64}};
    cv::Mat image = cv::imread("../cars-on-road2.jpg");

    classifier.Train({"../dataset/vehicles", "../dataset/non-vehicles"});
    int found = classifier.FindObjects(image);
    if(found)
    {
        cv::imshow("found cars", image);
        cv::waitKey();
    }

    CHECK(found > 0);
}

//header

struct SVMParams
{
    using SVM = cv::ml::SVM;

    SVM::Types type = SVM::C_SVC;
    SVM::KernelTypes kernel = SVM::LINEAR;
    cv::TermCriteria termCriteria = {cv::TermCriteria::MAX_ITER, 100, 1e-4};
    int degree = 0;
    double coef0 = 0;
    double ny = 0;
    double epsilon = 0;
    double C = 0.01;
};

class HogClassifier
{
public:
    HogClassifier(cv::Size winSize);
    auto Train(std::vector<std::string> dirPaths, SVMParams svmParams = SVMParams{});
    auto Save(std::string destFile);
    auto Load(std::string srcFile);
    auto Test(std::vector<std::string> dirPaths);
    auto FindObjects(cv::Mat &image);

private:
    auto PrepateTrainingData(std::vector<std::string> dirPaths);
    auto GetImages(std::vector<std::string> dirPaths, std::vector<int>& labels);
    auto GetDescriptors(const std::vector<cv::Mat>& images);
    auto CumulateData(const std::vector<cv::Mat>& uncumulatedData);
    auto GetSVM(SVMParams svmParams);
    auto TrainClassifier(cv::Ptr<cv::ml::SVM>svm, cv::Mat& data, std::vector<int>& labels);

    cv::HOGDescriptor mHogs;
    bool mTrained{false};
};

Completely random result of HOGDescriptor

Using HOGDescriptor with C++. First of all, how can I identify class of detected object? There is no information in doc and example. I wrote simple program (according to train_HOG.cpp) and trained with vehicles and non-vehicles. So its "one-class"? How descriptor know it found vehicle or non-vehicle?

I did like in example, but it seems to not work (I hoped t see maybe higher density around cars - but it looks completely random!). One thing different is class name - I used 1 and 2 (for every class folder) but similar results are with classes like in example (-1 and 1). Tried different svm params (default and as in example...). The only thing I can do is show you my code :|

Example positives: pos posimage description image description

#include <HogClassifier.h>

#include <iostream>

#include <boost/filesystem.hpp>
#include <boost/range/iterator_range.hpp>
#include <boost/range/adaptor/indexed.hpp>
#include <boost/assert.hpp>

#include <opencv2/imgproc.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/core/ocl.hpp>

#include <opencv2/highgui.hpp>

namespace bfs = boost::filesystem;

HogClassifier::HogClassifier(cv::Size winSize)
{
    mHogs.winSize = winSize;
    //cv::ocl::setUseOpenCL(false);
}

auto HogClassifier::GetImages(std::vector<std::string> dirPaths, std::vector<int>& labels)
{
    int currClassDir = 0;
    std::vector<cv::Mat> images;

    for(auto& root: dirPaths)
    {
        if(bfs::is_directory(root))
        {
            for(const auto& record: boost::make_iterator_range(bfs::recursive_directory_iterator(root, {})))
                if(!bfs::is_directory(record))
                {
                    images.push_back(cv::imread(record.path().string()));
                    labels.push_back(currClassDir);
                }
        }
        else
        {
            std::cout << root << " is not a directory!" << std::endl;
        }
        ++currClassDir;
    }
    return images;
}

auto HogClassifier::GetDescriptors(const std::vector<cv::Mat> &images)
{
    std::vector<cv::Mat> allDdescriptors;
    std::vector<float> singleDescriptors;
    cv::Mat grayImage;

    for(const auto& img: images)
    {
        if(img.cols >= mHogs.winSize.width && img.rows >= mHogs.winSize.height)
        {
            cv::Rect extracted{(img.cols - mHogs.winSize.width) / 2,
                               (img.rows - mHogs.winSize.height) / 2,
                                mHogs.winSize.width, mHogs.winSize.height};
            cv::cvtColor(img(extracted), grayImage, cv::COLOR_BGR2GRAY);
            if(grayImage.data)
            {
                mHogs.compute(grayImage, singleDescriptors, cv::Size(4, 4), cv::Size(0, 0));
                allDdescriptors.push_back(cv::Mat{singleDescriptors}.clone());
            }
        }
    }
    return allDdescriptors;
}

auto HogClassifier::CumulateData(const std::vector<cv::Mat> &uncumulatedData)
{
    using namespace boost::adaptors;
    int columns = std::max(uncumulatedData.at(0).rows, uncumulatedData.at(0).cols);
    int rows = uncumulatedData.size();
    cv::Mat cumulatedData(rows, columns, CV_32F);
    cv::Mat temp(1, columns, CV_32F);

    for(const auto& sample: uncumulatedData | indexed(0))
    {
        if(sample.value().cols == 1)
        {
            cv::transpose(sample.value(), temp);
            temp.copyTo(cumulatedData.row(sample.index()));
        }
        else if(sample.value().rows == 1)
            sample.value().copyTo(cumulatedData.row(sample.index()));
        else
            std::cout << "Wrng data provided for cumulation" << std::endl;
    }

    return cumulatedData;
}

auto HogClassifier::GetSVM(SVMParams svmParams)
{
    auto svm = cv::ml::SVM::create();
    svm->setType(svmParams.type);
    svm->setKernel(svmParams.kernel);
    svm->setTermCriteria(svmParams.termCriteria);
    svm->setDegree(svmParams.degree);
    svm->setCoef0(svmParams.coef0);
    svm->setNu(svmParams.ny);
    svm->setP(svmParams.epsilon);
    svm->setC(svmParams.C);
    return svm;
}

auto HogClassifier::PrepateTrainingData(std::vector<std::string> dirPaths)
{
    std::vector<int> labels;

    auto images = GetImages(dirPaths, labels);
    auto uncumulatedData = GetDescriptors(images);
    auto trainingData = CumulateData(uncumulatedData);

    return std::make_tuple(trainingData, labels);
}

auto HogClassifier::TrainClassifier(cv::Ptr<cv::ml::SVM> svm, cv::Mat& data, std::vector<int>& labels)
{
    BOOST_ASSERT_MSG(data.rows == labels.size(), "Number of samples doesn't match numer of labels");
    BOOST_ASSERT(svm);
    if(svm)
    {
        svm->train(data, cv::ml::ROW_SAMPLE, labels);

        cv::Mat sv = svm->getSupportVectors();
        cv::Mat alpha, svidx;
        double rho = svm->getDecisionFunction( 0, alpha, svidx );
        CV_Assert( alpha.total() == 1 && svidx.total() == 1 && sv.rows == 1 );
        CV_Assert( (alpha.type() == CV_64F && alpha.at<double>(0) == 1.) ||
                   (alpha.type() == CV_32F && alpha.at<float>(0) == 1.f) );
        CV_Assert( sv.type() == CV_32F );
        std::vector<float> hogDetector(sv.cols + 1);
        memcpy(&hogDetector[0], sv.ptr(), sv.cols*sizeof(hogDetector[0]));
        hogDetector[sv.cols] = (float)-rho;
        mHogs.setSVMDetector(hogDetector);
        mTrained = true;
    }
}

auto HogClassifier::Train(std::vector<std::string> dirPaths, SVMParams svmParams)
{
    auto [trainingData, labels] = PrepateTrainingData(dirPaths);
    auto svm = GetSVM(svmParams);
    TrainClassifier(svm, trainingData, labels);
}

auto HogClassifier::Save(std::string destFile)
{
    mHogs.save(destFile);
}

auto HogClassifier::Load(std::string srcFile)
{
    if(mHogs.load(srcFile))
        mTrained = true;
    else
    {
        mTrained = false;
        std::cout << "Problem reading classifier file." << std::endl;
    }
    return mTrained;
}

auto HogClassifier::FindObjects(cv::Mat& image)
{
    std::vector<cv::Rect> detections;
    std::vector<double> weights;
    if(mTrained && image.data)
    {
        cv::Mat grayImg;
        cv::cvtColor(image, grayImg, cv::COLOR_BGR2GRAY);
        mHogs.detectMultiScale(grayImg, detections, weights);
        //mHogs.detectMultiScale(image, detections, weights);
        for(const auto& occurence: detections | boost::adaptors::indexed(0))
        {
            cv::Scalar color{0, weights[occurence.index()] * weights[occurence.index()] * 200, 0};
            cv::rectangle(image, occurence.value(), color, image.cols/400 + 1);
        }
    }
    return detections.size();
}

/*=========================================*/
//#define DOCTEST_CONFIG_DISABLE
#include <doctest.h>
TEST_CASE("Train and check")
{
    HogClassifier classifier{cv::Size{64, 64}};
    cv::Mat image = cv::imread("../cars-on-road2.jpg");

    classifier.Train({"../dataset/vehicles", "../dataset/non-vehicles"});
    int found = classifier.FindObjects(image);
    if(found)
    {
        cv::imshow("found cars", image);
        cv::waitKey();
    }

    CHECK(found > 0);
}

//header

struct SVMParams
{
    using SVM = cv::ml::SVM;

    SVM::Types type = SVM::C_SVC;
    SVM::KernelTypes kernel = SVM::LINEAR;
    cv::TermCriteria termCriteria = {cv::TermCriteria::MAX_ITER, 100, 1e-4};
    int degree = 0;
    double coef0 = 0;
    double ny = 0;
    double epsilon = 0;
    double C = 0.01;
};

class HogClassifier
{
public:
    HogClassifier(cv::Size winSize);
    auto Train(std::vector<std::string> dirPaths, SVMParams svmParams = SVMParams{});
    auto Save(std::string destFile);
    auto Load(std::string srcFile);
    auto Test(std::vector<std::string> dirPaths);
    auto FindObjects(cv::Mat &image);

private:
    auto PrepateTrainingData(std::vector<std::string> dirPaths);
    auto GetImages(std::vector<std::string> dirPaths, std::vector<int>& labels);
    auto GetDescriptors(const std::vector<cv::Mat>& images);
    auto CumulateData(const std::vector<cv::Mat>& uncumulatedData);
    auto GetSVM(SVMParams svmParams);
    auto TrainClassifier(cv::Ptr<cv::ml::SVM>svm, cv::Mat& data, std::vector<int>& labels);

    cv::HOGDescriptor mHogs;
    bool mTrained{false};
};

Completely random result of HOGDescriptor

Using HOGDescriptor with C++. First of all, how can I identify class of detected object? There is no information in doc and example. I wrote simple program (according to train_HOG.cpp) and trained with vehicles and non-vehicles. So its "one-class"? How descriptor know it found vehicle or non-vehicle?

I did like in example, but it seems to not work (I hoped t see maybe higher density around cars - but it looks completely random!). random! - so even heat map is impossible). One thing different is class name - I used 1 and 2 (for every class folder) but similar results are with classes like in example (-1 and 1). Tried different svm params (default and as in example...). The only thing I can do is show you my code :|

Example positives: image description image description

#include <HogClassifier.h>

#include <iostream>

#include <boost/filesystem.hpp>
#include <boost/range/iterator_range.hpp>
#include <boost/range/adaptor/indexed.hpp>
#include <boost/assert.hpp>

#include <opencv2/imgproc.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/core/ocl.hpp>

#include <opencv2/highgui.hpp>

namespace bfs = boost::filesystem;

HogClassifier::HogClassifier(cv::Size winSize)
{
    mHogs.winSize = winSize;
    //cv::ocl::setUseOpenCL(false);
}

auto HogClassifier::GetImages(std::vector<std::string> dirPaths, std::vector<int>& labels)
{
    int currClassDir = 0;
    std::vector<cv::Mat> images;

    for(auto& root: dirPaths)
    {
        if(bfs::is_directory(root))
        {
            for(const auto& record: boost::make_iterator_range(bfs::recursive_directory_iterator(root, {})))
                if(!bfs::is_directory(record))
                {
                    images.push_back(cv::imread(record.path().string()));
                    labels.push_back(currClassDir);
                }
        }
        else
        {
            std::cout << root << " is not a directory!" << std::endl;
        }
        ++currClassDir;
    }
    return images;
}

auto HogClassifier::GetDescriptors(const std::vector<cv::Mat> &images)
{
    std::vector<cv::Mat> allDdescriptors;
    std::vector<float> singleDescriptors;
    cv::Mat grayImage;

    for(const auto& img: images)
    {
        if(img.cols >= mHogs.winSize.width && img.rows >= mHogs.winSize.height)
        {
            cv::Rect extracted{(img.cols - mHogs.winSize.width) / 2,
                               (img.rows - mHogs.winSize.height) / 2,
                                mHogs.winSize.width, mHogs.winSize.height};
            cv::cvtColor(img(extracted), grayImage, cv::COLOR_BGR2GRAY);
            if(grayImage.data)
            {
                mHogs.compute(grayImage, singleDescriptors, cv::Size(4, 4), cv::Size(0, 0));
                allDdescriptors.push_back(cv::Mat{singleDescriptors}.clone());
            }
        }
    }
    return allDdescriptors;
}

auto HogClassifier::CumulateData(const std::vector<cv::Mat> &uncumulatedData)
{
    using namespace boost::adaptors;
    int columns = std::max(uncumulatedData.at(0).rows, uncumulatedData.at(0).cols);
    int rows = uncumulatedData.size();
    cv::Mat cumulatedData(rows, columns, CV_32F);
    cv::Mat temp(1, columns, CV_32F);

    for(const auto& sample: uncumulatedData | indexed(0))
    {
        if(sample.value().cols == 1)
        {
            cv::transpose(sample.value(), temp);
            temp.copyTo(cumulatedData.row(sample.index()));
        }
        else if(sample.value().rows == 1)
            sample.value().copyTo(cumulatedData.row(sample.index()));
        else
            std::cout << "Wrng data provided for cumulation" << std::endl;
    }

    return cumulatedData;
}

auto HogClassifier::GetSVM(SVMParams svmParams)
{
    auto svm = cv::ml::SVM::create();
    svm->setType(svmParams.type);
    svm->setKernel(svmParams.kernel);
    svm->setTermCriteria(svmParams.termCriteria);
    svm->setDegree(svmParams.degree);
    svm->setCoef0(svmParams.coef0);
    svm->setNu(svmParams.ny);
    svm->setP(svmParams.epsilon);
    svm->setC(svmParams.C);
    return svm;
}

auto HogClassifier::PrepateTrainingData(std::vector<std::string> dirPaths)
{
    std::vector<int> labels;

    auto images = GetImages(dirPaths, labels);
    auto uncumulatedData = GetDescriptors(images);
    auto trainingData = CumulateData(uncumulatedData);

    return std::make_tuple(trainingData, labels);
}

auto HogClassifier::TrainClassifier(cv::Ptr<cv::ml::SVM> svm, cv::Mat& data, std::vector<int>& labels)
{
    BOOST_ASSERT_MSG(data.rows == labels.size(), "Number of samples doesn't match numer of labels");
    BOOST_ASSERT(svm);
    if(svm)
    {
        svm->train(data, cv::ml::ROW_SAMPLE, labels);

        cv::Mat sv = svm->getSupportVectors();
        cv::Mat alpha, svidx;
        double rho = svm->getDecisionFunction( 0, alpha, svidx );
        CV_Assert( alpha.total() == 1 && svidx.total() == 1 && sv.rows == 1 );
        CV_Assert( (alpha.type() == CV_64F && alpha.at<double>(0) == 1.) ||
                   (alpha.type() == CV_32F && alpha.at<float>(0) == 1.f) );
        CV_Assert( sv.type() == CV_32F );
        std::vector<float> hogDetector(sv.cols + 1);
        memcpy(&hogDetector[0], sv.ptr(), sv.cols*sizeof(hogDetector[0]));
        hogDetector[sv.cols] = (float)-rho;
        mHogs.setSVMDetector(hogDetector);
        mTrained = true;
    }
}

auto HogClassifier::Train(std::vector<std::string> dirPaths, SVMParams svmParams)
{
    auto [trainingData, labels] = PrepateTrainingData(dirPaths);
    auto svm = GetSVM(svmParams);
    TrainClassifier(svm, trainingData, labels);
}

auto HogClassifier::Save(std::string destFile)
{
    mHogs.save(destFile);
}

auto HogClassifier::Load(std::string srcFile)
{
    if(mHogs.load(srcFile))
        mTrained = true;
    else
    {
        mTrained = false;
        std::cout << "Problem reading classifier file." << std::endl;
    }
    return mTrained;
}

auto HogClassifier::FindObjects(cv::Mat& image)
{
    std::vector<cv::Rect> detections;
    std::vector<double> weights;
    if(mTrained && image.data)
    {
        cv::Mat grayImg;
        cv::cvtColor(image, grayImg, cv::COLOR_BGR2GRAY);
        mHogs.detectMultiScale(grayImg, detections, weights);
        //mHogs.detectMultiScale(image, detections, weights);
        for(const auto& occurence: detections | boost::adaptors::indexed(0))
        {
            cv::Scalar color{0, weights[occurence.index()] * weights[occurence.index()] * 200, 0};
            cv::rectangle(image, occurence.value(), color, image.cols/400 + 1);
        }
    }
    return detections.size();
}

/*=========================================*/
//#define DOCTEST_CONFIG_DISABLE
#include <doctest.h>
TEST_CASE("Train and check")
{
    HogClassifier classifier{cv::Size{64, 64}};
    cv::Mat image = cv::imread("../cars-on-road2.jpg");

    classifier.Train({"../dataset/vehicles", "../dataset/non-vehicles"});
    int found = classifier.FindObjects(image);
    if(found)
    {
        cv::imshow("found cars", image);
        cv::waitKey();
    }

    CHECK(found > 0);
}

//header

struct SVMParams
{
    using SVM = cv::ml::SVM;

    SVM::Types type = SVM::C_SVC;
    SVM::KernelTypes kernel = SVM::LINEAR;
    cv::TermCriteria termCriteria = {cv::TermCriteria::MAX_ITER, 100, 1e-4};
    int degree = 0;
    double coef0 = 0;
    double ny = 0;
    double epsilon = 0;
    double C = 0.01;
};

class HogClassifier
{
public:
    HogClassifier(cv::Size winSize);
    auto Train(std::vector<std::string> dirPaths, SVMParams svmParams = SVMParams{});
    auto Save(std::string destFile);
    auto Load(std::string srcFile);
    auto Test(std::vector<std::string> dirPaths);
    auto FindObjects(cv::Mat &image);

private:
    auto PrepateTrainingData(std::vector<std::string> dirPaths);
    auto GetImages(std::vector<std::string> dirPaths, std::vector<int>& labels);
    auto GetDescriptors(const std::vector<cv::Mat>& images);
    auto CumulateData(const std::vector<cv::Mat>& uncumulatedData);
    auto GetSVM(SVMParams svmParams);
    auto TrainClassifier(cv::Ptr<cv::ml::SVM>svm, cv::Mat& data, std::vector<int>& labels);

    cv::HOGDescriptor mHogs;
    bool mTrained{false};
};