ProgRoman 9 Жалоба Опубликовано April 24, 2015 Добрый день! Подумал написать функцию выделения признаков Хаара из изображения для обучения классификаторов как это сделано с 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 иди по картинке признаки сгенерированы поэтому надо будет только проходиться по массиву признаков и считать результаты по интегральному изображению.. с количеством признаков у меня что-то большие сомнения что их должно быть так много для небольшого участка изображения.. Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
Smorodov 579 Жалоба Опубликовано April 24, 2015 Их столько и должно быть. Просто каскадная система отсекает почти все, остаются самые сильные на каждом каскаде. Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
ProgRoman 9 Жалоба Опубликовано April 25, 2015 Их столько и должно быть. Просто каскадная система отсекает почти все, остаются самые сильные на каждом каскаде. Ясно тогда понятно почему так много, у меня ещё вопрос т.е. правильно понимаю, что конечно можно выделить из изображения все признаки Хаара, но для распознавания это совсем не лучший вариант, т.е. имеет смысл не просто извлекать признаки, а делать выборку из них ну как это сделано в алгоритме Виолы Джонса Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах