train_cascade 源码阅读之Haar特征
2015-01-19 21:43
399 查看
train_cascade中对Haar特征的处理与LBP特征类似,调用方法也相近,都是作为CvFeatureEvaluator的子类,框架性的东西都是一致的,具体调用过程可以参考
train_cascade
源码阅读之LBP feature ,下面主要讨论的是Haar特征的实现。
下面片段是生成用于在积分图中的矩形块的坐标,Feature类中存的是在积分图矩阵中的初始偏移量,矩形的左上角坐标和宽高,以及是否旋转。不同类型的Haar特征已经在代码中体现的很明确了,故不赘述。
SqSum是平方积分图。
最后,不同小块乘上权重系数,作为Haar特征值。
train_cascade
源码阅读之LBP feature ,下面主要讨论的是Haar特征的实现。
下面片段是生成用于在积分图中的矩形块的坐标,Feature类中存的是在积分图矩阵中的初始偏移量,矩形的左上角坐标和宽高,以及是否旋转。不同类型的Haar特征已经在代码中体现的很明确了,故不赘述。
<span style="font-size:14px;">void CvHaarEvaluator::generateFeatures() { int mode = ((const CvHaarFeatureParams*)((CvFeatureParams*)featureParams))->mode; 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 ) ); } if( mode != CvHaarFeatureParams::BASIC ) { // 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 (mode != CvHaarFeatureParams::BASIC) { 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) ); } } if (mode == CvHaarFeatureParams::ALL) { // 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(); }</span>接着,在 CvHaarEvaluator::Feature构造函数中,对刚刚求得的坐标做了偏移量上的转换。
<span style="font-size:14px;">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 ) } } } </span>CV_SUM_OFFSET和CV_TILTED_OFFSET是计算偏移量的宏,它们将左上角点和宽高转换成在单行sum或者tilted矩阵中的位置。sum矩阵的求法和LBP中是一样的,也是利用了OpenCV自带的cv::integral函数,而斜45度的矩阵也没有用到旋转图像之类的操作,而是…嗯,还是integral函数,自带重载功能,实现了45度倾斜操作。
<span style="font-size:14px;">void CvHaarEvaluator::setImage(const Mat& img, uchar clsLabel, int idx) { CV_DbgAssert( !sum.empty() && !tilted.empty() && !normfactor.empty() ); CvFeatureEvaluator::setImage( img, clsLabel, idx); 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); normfactor.ptr<float>(0)[idx] = calcNormFactor( innSum, innSqSum ); }</span>归一化因子计算如下:
<span style="font-size:14px;">float calcNormFactor( const Mat& sum, const Mat& sqSum ) { CV_DbgAssert( 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) ); }</span>
SqSum是平方积分图。
最后,不同小块乘上权重系数,作为Haar特征值。
<span style="font-size:14px;">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; }</span>
相关文章推荐
- train_cascade 源码阅读之Haar特征
- train_cascade 源码阅读之LBP特征
- train _cascade 源码阅读之HOG特征
- train_cascade 源码阅读之LBP特征
- train _cascade 源码阅读之HOG特征
- 用opencv的traincascade.exe训练行人的HAAR、LBP和HOG特征的xml
- 为什么opencv train_cascade训练级联分类器时,lbp特征比haar_like特征快?
- train_cascade 源码阅读系列
- 用opencv的traincascade.exe训练行人的HAAR、LBP和HOG特征的xml文件,并对分类器进行加载和检测
- train_cascade 源码阅读之级联训练
- 用opencv自带的traincascade.exe训练给予haar特征和LBP特征的分类器
- train_cascade 源码阅读之级联训练
- OpenCV源码中Haar训练及提取特征的代码
- PG2 BYPASS源码阅读 学习x64解密定时器、特征码定位
- 利用opencv训练基于Haar特征、LBP特征、Hog特征的分类器cascade.xml
- OpenCV源码中Haar训练及特征提取的代码说明
- OpenCV学习记录(二):自己训练haar特征的adaboost分类器进行人脸识别 标签: 脸部识别opencv 2017-07-03 21:38 26人阅读
- OpenCV源码中Haar训练及提取特征的代码
- haar_adaboost_cascade阅读资料
- Scala模式匹配、类型系统与Spark源码阅读