Перейти к содержимому
Compvision.ru
ProgRoman

Haar-like features

Recommended Posts

Добрый день! Подумал написать функцию выделения признаков Хаара из изображения для обучения классификаторов как это сделано с HOG к примеру, т.е. на вход подаём просто изображение, а на выходе к примеру cv::Mat с признаками что бы потом подать на вход классификатору. В Opencv есть класс CvHaarEvaluator вот его я взял за основу и немного изменил, точнее удалял  многое, приведу код 

/*
haarfeatures.h
*/
#ifndef _OPENCV_HAARFEATURES_H_
#define _OPENCV_HAARFEATURES_H_

#include "opencv2/core/core.hpp"
#include "opencv2\core\mat.hpp"
#include "opencv2\imgproc\imgproc.hpp"

#define CV_HAAR_FEATURE_MAX      3

#define HFP_NAME "haarFeatureParams"

using namespace cv;

#define CV_SUM_OFFSETS( p0, p1, p2, p3, rect, step )                      \
    /* (x, y) */                                                          \
    (p0) = (rect).x + (step) * (rect).y;                                  \
    /* (x + w, y) */                                                      \
    (p1) = (rect).x + (rect).width + (step) * (rect).y;                   \
    /* (x + w, y) */                                                      \
    (p2) = (rect).x + (step) * ((rect).y + (rect).height);                \
    /* (x + w, y + h) */                                                  \
    (p3) = (rect).x + (rect).width + (step) * ((rect).y + (rect).height);

#define CV_TILTED_OFFSETS( p0, p1, p2, p3, rect, step )                   \
    /* (x, y) */                                                          \
    (p0) = (rect).x + (step) * (rect).y;                                  \
    /* (x - h, y + h) */                                                  \
    (p1) = (rect).x - (rect).height + (step) * ((rect).y + (rect).height);\
    /* (x + w, y + w) */                                                  \
    (p2) = (rect).x + (rect).width + (step) * ((rect).y + (rect).width);  \
    /* (x + w - h, y + w + h) */                                          \
    (p3) = (rect).x + (rect).width - (rect).height                        \
           + (step) * ((rect).y + (rect).width + (rect).height);

inline float calcNormFactor( const Mat& sum, const Mat& sqSum )
{
    CV_Assert( sum.cols > 3 && sqSum.rows > 3 );
    Rect normrect( 1, 1, sum.cols - 3, sum.rows - 3 );
    size_t p0, p1, p2, p3;
    CV_SUM_OFFSETS( p0, p1, p2, p3, normrect, sum.step1() )
    double area = normrect.width * normrect.height;
    const int *sp = (const int*)sum.data;
    int valSum = sp[p0] - sp[p1] - sp[p2] + sp[p3];
    const double *sqp = (const double *)sqSum.data;
    double valSqSum = sqp[p0] - sqp[p1] - sqp[p2] + sqp[p3];
    return (float) sqrt( (double) (area * valSqSum - (double)valSum * valSum) );
}

class CvHaarEvaluator
{
public:	
    void init(const int _maxSampleCount = 1, const cv::Size& _winSize = Size(24,24));
	void CvHaarEvaluator::setImage(const Mat& img/*, uchar clsLabel*/, int idx = 1);
    float operator()(int featureIdx, int sampleIdx) const;	

protected:    

	void generateFeatures();

    class Feature
    {
    public:
        Feature();
        Feature( int offset, bool _tilted,
            int x0, int y0, int w0, int h0, float wt0,
            int x1, int y1, int w1, int h1, float wt1,
            int x2 = 0, int y2 = 0, int w2 = 0, int h2 = 0, float wt2 = 0.0F );
        float calc( const cv::Mat &sum, const cv::Mat &tilted, size_t y) const;

        bool  tilted;
        struct
        {
            cv::Rect r;
            float weight;
        } rect[CV_HAAR_FEATURE_MAX];

        struct
        {
            int p0, p1, p2, p3;
        } fastRect[CV_HAAR_FEATURE_MAX];
    };

    std::vector<Feature> features;
    cv::Mat  sum;         /* sum images (each row represents image) */
    cv::Mat  tilted;      /* tilted sum images (each row represents image) */
    cv::Mat  normfactor;  /* normalization factor */
	cv::Mat  sumimg;	  /*integral images*/
	cv::Size winSize;
	int numFeatures;
};

inline float CvHaarEvaluator::operator()(int featureIdx, int sampleIdx) const
{	
    float nf = normfactor.at<float>(0, sampleIdx);
    return !nf ? 0.0f : (features[featureIdx].calc( sum, tilted, sampleIdx)/nf);
}

inline float CvHaarEvaluator::Feature::calc( const cv::Mat &_sum, const cv::Mat &_tilted, size_t y) const
{
    const int* img = tilted ? _tilted.ptr<int>((int)y) : _sum.ptr<int>((int)y);
    float ret = rect[0].weight * (img[fastRect[0].p0] - img[fastRect[0].p1] - img[fastRect[0].p2] + img[fastRect[0].p3] ) +
        rect[1].weight * (img[fastRect[1].p0] - img[fastRect[1].p1] - img[fastRect[1].p2] + img[fastRect[1].p3] );
    if( rect[2].weight != 0.0f )
        ret += rect[2].weight * (img[fastRect[2].p0] - img[fastRect[2].p1] - img[fastRect[2].p2] + img[fastRect[2].p3] );
    return ret;
}

#endif

ну и его реализация 

/*
haarfeatures.cpp
*/

#include "opencv2\core\core.hpp"
#include "opencv2\core\internal.hpp"
#include "opencv2\imgproc\imgproc.hpp"


#include "haarfeatures.h"

using namespace std;
using namespace cv;

//--------------------- HaarFeatureEvaluator ----------------

void CvHaarEvaluator::init(const int _maxSampleCount, const Size& _winSize)
{
    CV_Assert(_maxSampleCount > 0);
	winSize = _winSize;
	numFeatures = 0;
	int cols = (_winSize.width + 1) * (_winSize.height + 1);

	sum.create((int)_maxSampleCount, cols, CV_32SC1);
    tilted.create((int)_maxSampleCount, cols, CV_32SC1);
    normfactor.create(1, (int)_maxSampleCount, CV_32FC1);    
	generateFeatures();
}

void CvHaarEvaluator::setImage(const Mat& img/*, uchar clsLabel*/, int idx)
{
    CV_Assert( !sum.empty() && !tilted.empty() && !normfactor.empty() );

    Mat innSum(winSize.height + 1, winSize.width + 1, sum.type()/*, sum.ptr<int>((int)idx)*/);
    Mat innTilted(winSize.height + 1, winSize.width + 1, tilted.type()/*, tilted.ptr<int>((int)idx)*/);
    Mat innSqSum;
    integral(img, innSum, innSqSum, innTilted);
	sumimg = innSum.clone();	
    normfactor.ptr<float>(0)[idx] = calcNormFactor( innSum, innSqSum );
}



void CvHaarEvaluator::generateFeatures()
{    
    int offset = winSize.width + 1;

    for( int x = 0; x < winSize.width; x++ )
    {
        for( int y = 0; y < winSize.height; y++ )
        {
            for( int dx = 1; dx <= winSize.width; dx++ )
            {
                for( int dy = 1; dy <= winSize.height; dy++ )
                {
                    // haar_x2
                    if ( (x+dx*2 <= winSize.width) && (y+dy <= winSize.height) )
                    {
                        features.push_back( Feature( offset, false,
                            x,    y, dx*2, dy, -1,
                            x+dx, y, dx  , dy, +2 ) );
                    }
                    // haar_y2
                    if ( (x+dx <= winSize.width) && (y+dy*2 <= winSize.height) )
                    {
                        features.push_back( Feature( offset, false,
                            x,    y, dx, dy*2, -1,
                            x, y+dy, dx, dy,   +2 ) );
                    }
                    // haar_x3
                    if ( (x+dx*3 <= winSize.width) && (y+dy <= winSize.height) )
                    {
                        features.push_back( Feature( offset, false,
                            x,    y, dx*3, dy, -1,
                            x+dx, y, dx  , dy, +3 ) );
                    }
                    // haar_y3
                    if ( (x+dx <= winSize.width) && (y+dy*3 <= winSize.height) )
                    {
                        features.push_back( Feature( offset, false,
                            x, y,    dx, dy*3, -1,
                            x, y+dy, dx, dy,   +3 ) );
                    }                    
                    {
                        // haar_x4
                        if ( (x+dx*4 <= winSize.width) && (y+dy <= winSize.height) )
                        {
                            features.push_back( Feature( offset, false,
                                x,    y, dx*4, dy, -1,
                                x+dx, y, dx*2, dy, +2 ) );
                        }
                        // haar_y4
                        if ( (x+dx <= winSize.width ) && (y+dy*4 <= winSize.height) )
                        {
                            features.push_back( Feature( offset, false,
                                x, y,    dx, dy*4, -1,
                                x, y+dy, dx, dy*2, +2 ) );
                        }
                    }
                    // x2_y2
                    if ( (x+dx*2 <= winSize.width) && (y+dy*2 <= winSize.height) )
                    {
                        features.push_back( Feature( offset, false,
                            x,    y,    dx*2, dy*2, -1,
                            x,    y,    dx,   dy,   +2,
                            x+dx, y+dy, dx,   dy,   +2 ) );
                    }                    
                    {
                        if ( (x+dx*3 <= winSize.width) && (y+dy*3 <= winSize.height) )
                        {
                            features.push_back( Feature( offset, false,
                                x   , y   , dx*3, dy*3, -1,
                                x+dx, y+dy, dx  , dy  , +9) );
                        }
                    }                    
                    {
                        // tilted haar_x2
                        if ( (x+2*dx <= winSize.width) && (y+2*dx+dy <= winSize.height) && (x-dy>= 0) )
                        {
                            features.push_back( Feature( offset, true,
                                x, y, dx*2, dy, -1,
                                x, y, dx,   dy, +2 ) );
                        }
                        // tilted haar_y2
                        if ( (x+dx <= winSize.width) && (y+dx+2*dy <= winSize.height) && (x-2*dy>= 0) )
                        {
                            features.push_back( Feature( offset, true,
                                x, y, dx, 2*dy, -1,
                                x, y, dx, dy,   +2 ) );
                        }
                        // tilted haar_x3
                        if ( (x+3*dx <= winSize.width) && (y+3*dx+dy <= winSize.height) && (x-dy>= 0) )
                        {
                            features.push_back( Feature( offset, true,
                                x,    y,    dx*3, dy, -1,
                                x+dx, y+dx, dx,   dy, +3 ) );
                        }
                        // tilted haar_y3
                        if ( (x+dx <= winSize.width) && (y+dx+3*dy <= winSize.height) && (x-3*dy>= 0) )
                        {
                            features.push_back( Feature( offset, true,
                                x,    y,    dx, 3*dy, -1,
                                x-dy, y+dy, dx, dy,   +3 ) );
                        }
                        // tilted haar_x4
                        if ( (x+4*dx <= winSize.width) && (y+4*dx+dy <= winSize.height) && (x-dy>= 0) )
                        {
                            features.push_back( Feature( offset, true,
                                x,    y,    dx*4, dy, -1,
                                x+dx, y+dx, dx*2, dy, +2 ) );
                        }
                        // tilted haar_y4
                        if ( (x+dx <= winSize.width) && (y+dx+4*dy <= winSize.height) && (x-4*dy>= 0) )
                        {
                            features.push_back( Feature( offset, true,
                                x,    y,    dx, 4*dy, -1,
                                x-dy, y+dy, dx, 2*dy, +2 ) );
                        }
                    }
                }
            }
        }
    }    

	numFeatures = (int)features.size();
}

CvHaarEvaluator::Feature::Feature()
{
    tilted = false;
    rect[0].r = rect[1].r = rect[2].r = Rect(0,0,0,0);
    rect[0].weight = rect[1].weight = rect[2].weight = 0;
}



CvHaarEvaluator::Feature::Feature( int offset, bool _tilted,
                                          int x0, int y0, int w0, int h0, float wt0,
                                          int x1, int y1, int w1, int h1, float wt1,
                                          int x2, int y2, int w2, int h2, float wt2 )
{
    tilted = _tilted;

    rect[0].r.x = x0;
    rect[0].r.y = y0;
    rect[0].r.width  = w0;
    rect[0].r.height = h0;
    rect[0].weight   = wt0;

    rect[1].r.x = x1;
    rect[1].r.y = y1;
    rect[1].r.width  = w1;
    rect[1].r.height = h1;
    rect[1].weight   = wt1;

    rect[2].r.x = x2;
    rect[2].r.y = y2;
    rect[2].r.width  = w2;
    rect[2].r.height = h2;
    rect[2].weight   = wt2;

    if( !tilted )
    {
        for( int j = 0; j < CV_HAAR_FEATURE_MAX; j++ )
        {
            if( rect[j].weight == 0.0F )
                break;
            CV_SUM_OFFSETS( fastRect[j].p0, fastRect[j].p1, fastRect[j].p2, fastRect[j].p3, rect[j].r, offset )
        }
    }
    else
    {
        for( int j = 0; j < CV_HAAR_FEATURE_MAX; j++ )
        {
            if( rect[j].weight == 0.0F )
                break;
            CV_TILTED_OFFSETS( fastRect[j].p0, fastRect[j].p1, fastRect[j].p2, fastRect[j].p3, rect[j].r, offset )
        }
    }
}

окошко я выбрал размера 24*24 и после запуска

CvHaarEvaluator haar;
haar.init(); 
....
haar.setImage(frame);

получаю массив с признаками std::vector<Feature> features; его размер 216600 признаков.. это как я понимаю не для всего изображения, а только для окошка размером 24*24, а что бы получить признаки из всего изображения надо будет окошком 24*24 иди по картинке  признаки сгенерированы поэтому надо будет только проходиться по массиву признаков и считать результаты по интегральному изображению..

с количеством признаков у меня что-то большие сомнения что их должно быть так много для небольшого участка изображения..  

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Их столько и должно быть.

Просто каскадная система отсекает почти все, остаются самые сильные на каждом каскаде.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Их столько и должно быть.

Просто каскадная система отсекает почти все, остаются самые сильные на каждом каскаде.

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Создайте учётную запись или войдите для комментирования

Вы должны быть пользователем, чтобы оставить комментарий

Создать учётную запись

Зарегистрируйтесь для создания учётной записи. Это просто!

Зарегистрировать учётную запись

Войти

Уже зарегистрированы? Войдите здесь.

Войти сейчас


  • Сейчас на странице   0 пользователей

    Нет пользователей, просматривающих эту страницу

×