opencv softcascade怎么用cascade检测物体

首先说一下这个级联分类器,OpenCV中级联分类器是根据VJ 04年的那篇论文(Robust Real-Time Face Detection)编写的,查看那篇论文,知道构建分类器的步骤如下:
1、根据haar-like特征训练多个弱分类器
2、使用adaboost算法将多个弱分类器组合成一个强分类器
3、最终的分类器是由多个强分类器级联而成
下面这幅图是弱分类器组合成强分类器的示意图(图片来源于网络):
下面这张是多个强分类器级联的示意图(图片来源于网络):
在了解了级联分类器是怎么一回事后,我们来看一看OpenCV里面级联分类器的结构
在调用OpenCV中的级联分类器对目标进行分类时,都会将一个训练好的分类器(一个训练好的.xml文件)读入到一个CvHaarClassifierCascade结构中,如下:
&1 CvHaarClassifierCascade* cascade = (CvHaarClassifierCascade*)cvLoad( "haarcascade_frontalface_alt.xml", 0, 0, 0 ); &
那么这个CvHaarClassifierCascade结构体里面的内容都有哪些呢?
1 typedef struct CvHaarClassifierCascade
/* 标志位 */
/* 分级分类器中强分类器的数量 */
CvSize orig_window_
/* 训练中原始目标的大小 */ 
/* these two parameters are set by cvSetImagesForHaarClassifierCascade */
CvSize real_window_
/* 待检测物体的大小 */
/* Haar块缩放的尺寸 */
CvHaarStageClassifier* stage_ /* 定义强分类器数组 */
CvHidHaarClassifierCascade* hid_
13 }CvHaarClassifierC
第一个flags,还不是很清楚,在debug模式下,flags=(好吧,这个值很诡异),我也不是很清楚
第二个count,表示整个分级分类器中强分类器的数量,即最后参与级联的强分类器的个数
第三个orig_window_size,表示的是在训练时用的正样本的尺寸,OpenCV中的尺寸是20x20
第四个和第五个,注释中说了,这两个参数需要自己设置,具体每个参数看注释
第六个stage_classifier,是强分类器指针,指向一个强分类器数组,之前的count是多少,那么此处的强分类器就有多少
最后一个hid_cascade,还不是很清楚
下面来看上面第六个参数的强分类器结构体
1 typedef struct CvHaarStageClassifier
/* number of classifiers in the battery 构成强分类器的弱分类器的数量*/
float /* threshold for the boosted classifier 叠加分类器的阈值*/
CvHaarClassifier* /* array of classifiers 定义分类器数组*/
/* these fields are used for organizing trees of stage classifiers,
rather than just stright cascades */
11 }CvHaarStageC
第一个count,表示该强分类器中,弱分类器的数量,即该强分类器由多少个弱分类器组成
第二个threshold,叠加分类器的阈值(好吧。。这个我也不知道)
第三个classifier,是一个指针,指向的是一个弱分类器数组,之前的count是多少,此处的弱分类器就有多少
后面3个都不清楚。。。(望知道的网友给予帮助)
下面是弱分类器的结构
1 typedef struct CvHaarClassifier
/* number of nodes in the decision tree */
/* these are "parallel" arrays. Every index i corresponds to a node of the decision tree (root has 0-th index).
left[i] - index of the left child (or negated index if the left child is a leaf)
right[i] - index of the right child (or negated index if the right child is a leaf)
threshold[i] - branch threshold. if feature responce is &= threshold, left branch is chosen, otherwise right branch is chosed.
alpha[i] - output value correponding to the leaf. */
CvHaarFeature* haar_
15 }CvHaarC
第一个count,在opencv里,发现始终都是1,自己想了想,因为这个结构体记录的是一个弱分类器,自然弱分类器的个数就是1了。
第二个haar_feature,也是一个指针,指向一个(因为count是1)特征结构体CvHaarFeature,这个结构体中记录的内容是弱分类器的类型(包括该haar-like特征的位置,大小,以及种类,这个结构体会在下面给出,再细说)
第三个threshold,就是那个判别函数:h(x,f,p,theta) = (p*f(x) & p*theta ? 1 : 0),中的阈值theta
第四个left,第五个right不是很清楚(求知道的同学详解啊~~~~)
第六个alpha,就是这个弱分类器的权重(每一个强分类器都是由多个弱分类器按照各自的权重进行表决,而得到的)
特征的结构体如下
1 #define CV_HAAR_FEATURE_MAX
2 // 一个Haar特征由2~3个具有相应权重的矩形组成
3 typedef struct CvHaarFeature
// 0 means up-right feature, 1 means 45-rotated feature
} rect[CV_HAAR_FEATURE_MAX];
// 2-3 rectangles with weights of opposite signs and with absolute values inversely proportional to the areas of the rectangles. if rect[2].weight != 0, then the feature     consists of 3 rectangles, otherwise it consists of 2.
12 }CvHaarF
第一个参数titled,0表示该特征是标准的haar-like特征,1表示旋转45&后的特征
  标准的haar-like特征如下:
      
  而旋转45&后的特征如下:
      
第二个参数是个结构体数组,每个结构体中包括一个矩形和一个权重,这个数组的大小是CV_HAAR_FEATURE_MAX(3)(注释中说:此处可能有2~3个矩形,这里的矩形等看了下面的解释就知道了),这个矩形和权重有什么用呢?
  在debug模式下,查看第一个弱分类器数组内的元素
  第一个元素:
1 rect[0].r.x = 3
2 rect[0].r.y = 7
3 rect[0].r.width = 14
4 rect[0].r.height = 4
5 rect[0].weight = -1
  第二个元素:
1 rect[1].r.x = 3
2 rect[1].r.y = 9
3 rect[1].r.width = 14
4 rect[1].r.height = 2
5 rect[1].weight = 2
  第三个元素则全都是0
  这么看这些坐标,并不是很清楚,我们可以画个图:
  由图可见,第一个矩形表示的是A+B区域,第二个矩形表示的是B区域。
  此时再看一看每个元素的权重weight,结合积分图的概念,发现第一个矩形的积分图乘以其权重加上第二个积分图乘以其权重,恰好得到下述结果:
  (A+B)*(-1)+B*2=B-A
  看到这个公式,大家都不会陌生,这正式VJ论文中给出的众多haar-like模板中的其中一个模板的计算方法(此处不知如何表达,大家将就,看懂就行) 
我们继续考察第二个弱分类器的特征部分,其特征参数如下:
1 rect[0].r.x = 1, rect[0].r.y = 2
2 rect[0].r.width = 18, rect[0].r.height = 4
3 rect[0].weight = -1
5 rect[1].r.x = 7, rect[1].r.y = 2
6 rect[1].r.width = 6, rect[1].r.height = 4
7 rect[1].weight = 3
数组的第三个元素依然都是是0,对其绘图:
  第一块矩形区域是A+B+C,第二块矩形区域是B,积分图乘以权重,再相加,可得:
  (A+B+C)*(-1)+B*3 = 2*B-A-C
  也是haar-like特征模板之一(此处不知如何表达,大家将就,看懂就行)
刚刚找了好久,找到一个第三个元素权重不为0的,该数组三个元素如下:
1 rect[0].r.x = 0, rect[0].r.y = 2
2 rect[0].r.width = 20, rect[0].r.height = 6;
3 rect[0].weight = -1
5 rect[1].r.x = 10, rect[1].r.y = 2
6 rect[1].r.width = 10, rect[1].r.height = 3;
7 rect[1].weight = 2
9 rect[2].r.x = 0, rect[2].r.y = 5
10 rect[2].r.width = 10, rect[2].r.height = 3;
11 rect[2].weight = 2
&绘图可得:
  将每个矩形乘以相应的权重,相加可得:
  (A+B+C+D)*(-1)+2*B + 2*C = B+C-(A+D)
  也是模板之一
  (佩服设计这个结构体的程序员)
到此,四个结构体都说完了,如有不对,请大家指正
阅读(...) 评论()OpenCV中CascadeClassifier类实现多尺度检测源码解析
我的图书馆
OpenCV中CascadeClassifier类实现多尺度检测源码解析
级联分类器检测类CascadeClassifier,在2.4.5版本中使用Adaboost的方法+LBP、HOG、HAAR进行目标检测,加载的是使用traincascade进行训练的分类器
class CV_EXPORTS_W CascadeClassifier
& & CV_WRAP CascadeClassifier(); // 无参数构造函数,new自动调用该函数分配初试内存
& & CV_WRAP CascadeClassifier( const string& filename ); // 带参数构造函数,参数为XML的绝对名称
& & virtual ~CascadeClassifier(); // 析构函数,无需关心
& & CV_WRAP virtual bool empty() // 是否导入参数,只创建了该对象而没有加载或者加载失败时都是空的
& & CV_WRAP bool load( const string& filename ); // 加载分类器,参数为XML的绝对名称,函数内部调用read读取新格式的分类器,读取成功后直接返回,读取失败后调用cvLoad读取旧格式的分类器,读取成功返回true,否则返回false
& & virtual bool read( const FileNode& node ); & // load内部调用read解析XML中的内容,也可以自己创建节点然后调用Read即可,但是该函数只能读取新格式的分类器,不能读取旧格式的分类器
& & // 多尺度检测函数
& & CV_WRAP virtual void detectMultiScale( const Mat& image, & & & &// 图像,cvarrtoMat实现IplImage转换为Mat,必须为8位,内部可自行转换为灰度图像
& & & & & & & & & & & & & & & & & &CV_OUT vector&Rect&& objects, & &// 输出矩形,注意vector不是线程安全的
& & & & & & & & & & & & & & & & & &double scaleFactor=1.1, & & & & &// 缩放比例,必须大于1
& & & & & & & & & & & & & & & & & &int minNeighbors=3, & & & & & & &// 合并窗口时最小neighbor,每个候选矩阵至少包含的附近元素个数
& & & & & & & & & & & & & & & & & &int flags=0, & & & & & & & & & & // 检测标记,只对旧格式的分类器有效,与cvHaarDetectObjects的参数flags相同,默认为0,可能的取值为CV_HAAR_DO_CANNY_PRUNING(CANNY边缘检测)、CV_HAAR_SCALE_IMAGE(缩放图像)、CV_HAAR_FIND_BIGGEST_OBJECT(寻找最大的目标)、CV_HAAR_DO_ROUGH_SEARCH(做粗略搜索);如果寻找最大的目标就不能缩放图像,也不能CANNY边缘检测
& & & & & & & & & & & & & & & & & &Size minSize=Size(), & & & & & & // 最小检测目标
& & & & & & & & & & & & & & & & & &Size maxSize=Size() ); & & & & & // 最大检测目标
& & // 最好不要在这里设置最大最小,可能会影响合并的效果,因此可以在检测完毕后自行判断结果是否满足要求
& & CV_WRAP virtual void detectMultiScale( const Mat& image,
& & & & & & & & & & & & & & & & & &CV_OUT vector&Rect&& objects,
& & & & & & & & & & & & & & & & & &vector&int&& rejectLevels,
& & & & & & & & & & & & & & & & & &vector&double&& levelWeights,
& & & & & & & & & & & & & & & & & &double scaleFactor=1.1,
& & & & & & & & & & & & & & & & & &int minNeighbors=3, int flags=0,
& & & & & & & & & & & & & & & & & &Size minSize=Size(),
& & & & & & & & & & & & & & & & & &Size maxSize=Size(),
& & & & & & & & & & & & & & & & & &bool outputRejectLevels=false );
// 上述参数多了rejectLevels和levelWeights以及outputRejectLevels参数,只有在outputRejectLevels为true的时候才可能输出前两个参数
// 还有就是在使用旧分类器的时候必须设置flags为CV_HAAR_SCALE_IMAGE,可以通过haarcascade_frontalface_alt.xml检测人脸尝试
& & bool isOldFormatCascade() & & & &// 是否是旧格式的分类器
& & virtual Size getOriginalWindowSize() & &// 初始检测窗口大小,也就是训练的窗口
& & int getFeatureType() // 获取特征类型
& & bool setImage( const Mat& ); & &// 设置图像,计算图像的积分图
& & virtual int runAt( Ptr&FeatureEvaluator&& feval, Point pt, double& weight ); // 计算某检测窗口是否为目标
& & // 保存强分类器数据
& & class Data
& & public:
& & & & struct CV_EXPORTS DTreeNode // 节点
& & & & & & int featureI // 对应的特征编号
& & & & & & // for ordered features only 节点阈值
& & & & & & // 左子树
& & & & & & // 右子树
& & & & };
& & & & struct CV_EXPORTS DTree // 弱分类器
& & & & & & int nodeC // 弱分类器中节点个数
& & & & };
& & & & struct CV_EXPORTS Stage // 强分类器
& & & & & & // 在classifier中的起始位置
& & & & & & // 该强分类器中的弱分类器数
& & & & & & // 强分类器阈值
& & & & };
& & & & bool read(const FileNode &node); // 读取强分类器
& & & & bool isStumpB & &// 是否只有树桩
& & & & int stageT & & &// BOOST,boostType:GAB、RAB等
& & & & int featureT & &// HAAR、HOG、LBP
& & & & & &// maxCatCount,LBP为256,其余为0
& & & & Size origWinS
& & & & vector&Stage&
& & & & vector&DTree&
& & & & vector&DTreeNode&
& & & & vector&float&
& & & & vector&int&
& & Ptr&FeatureEvaluator& featureE
& & Ptr&CvHaarClassifierCascade& oldC
// 关于mask这块参考《OpenCV目标检测之MaskGenerator》
& & class CV_EXPORTS MaskGenerator
& & public:
& & & & virtual ~MaskGenerator() {}
& & & & virtual cv::Mat generateMask(const cv::Mat& src)=0;
& & & & virtual void initializeMask(const cv::Mat& /*src*/) {};
& & void setMaskGenerator(Ptr&MaskGenerator& maskGenerator);
& & Ptr&MaskGenerator& getMaskGenerator();
& & void setFaceDetectionMaskGenerator();
protected:
& & Ptr&MaskGenerator& maskG
注意:当在不同的分类器之间切换的时候,需要手动释放,因为read内部没有释放上一次读取的分类器数据!
关于新旧格式的分类器参考《OpenCV存储解读之Adaboost分类器》
使用CascadeClassifier检测目标的过程
1) load分类器并调用empty函数检测是否load成功
// 读取stages
bool CascadeClassifier::Data::read(const FileNode &root)
& & static const float THRESHOLD_EPS = 1e-5f;
& & // load stage params
& & string stageTypeStr = (string)root[CC_STAGE_TYPE];
& & if( stageTypeStr == CC_BOOST )
& & & & stageType = BOOST;
& & printf("stageType: %s\n", stageTypeStr.c_str());
& & string featureTypeStr = (string)root[CC_FEATURE_TYPE];
& & if( featureTypeStr == CC_HAAR )
& & & & featureType = FeatureEvaluator::HAAR;
& & else if( featureTypeStr == CC_LBP )
& & & & featureType = FeatureEvaluator::LBP;
& & else if( featureTypeStr == CC_HOG )
& & & & featureType = FeatureEvaluator::HOG;
& & printf("featureType: %s\n", featureTypeStr.c_str());
& & origWinSize.width = (int)root[CC_WIDTH];
& & origWinSize.height = (int)root[CC_HEIGHT];
& & CV_Assert( origWinSize.height & 0 && origWinSize.width & 0 );
& & isStumpBased = (int)(root[CC_STAGE_PARAMS][CC_MAX_DEPTH]) == 1 ? true :
& & printf("stumpBased: %d\n", isStumpBased);
& & // load feature params
& & FileNode fn = root[CC_FEATURE_PARAMS];
& & if( fn.empty() )
& & // LBP的maxCatCount=256,其余特征都等于0
& & ncategories = fn[CC_MAX_CAT_COUNT]; // ncategories=256/0
& & int subsetSize = (ncategories + 31)/32,// subsetSize=8/0 // 强制类型转换取整,不是四舍五入
& & & & nodeStep = 3 + ( ncategories&0 ? subsetSize : 1 ); //每组数值个数,nodeStep=11/4
& & printf("subsetSize: %d, nodeStep: %d\n", subsetSize, nodeStep);
& & // load stages
& & fn = root[CC_STAGES];
& & if( fn.empty() )
& & stages.reserve(fn.size());
& & classifiers.clear();
& & nodes.clear();
& & FileNodeIterator it = fn.begin(), it_end = fn.end();
& & for( int si = 0; it != it_ si++, ++it )
& & & & FileNode fns = *
& & & & stage.threshold = (float)fns[CC_STAGE_THRESHOLD] - THRESHOLD_EPS;
& & & & fns = fns[CC_WEAK_CLASSIFIERS];
& & & & if(fns.empty())
& & & & & &
& & & & stage.ntrees = (int)fns.size();
& & & & stage.first = (int)classifiers.size();
& & & & printf("stage %d: ntrees: %d, first: %d\n", si, stage.ntrees, stage.first);
& & & & stages.push_back(stage);
& & & & classifiers.reserve(stages[si].first + stages[si].ntrees);
& & & & FileNodeIterator it1 = fns.begin(), it1_end = fns.end();
& & & & for( ; it1 != it1_ ++it1 ) // weak trees
& & & & & & FileNode fnw = *it1;
& & & & & & FileNode internalNodes = fnw[CC_INTERNAL_NODES];
& & & & & & FileNode leafValues = fnw[CC_LEAF_VALUES];
& & & & & & if( internalNodes.empty() || leafValues.empty() )
& & & & & & & &
& & & & & & // 弱分类器中的节点
& & & & & & DT
& & & & & & tree.nodeCount = (int)internalNodes.size()/nodeS
& & & & & & classifiers.push_back(tree);
& & & & & & nodes.reserve(nodes.size() + tree.nodeCount);
& & & & & & leaves.reserve(leaves.size() + leafValues.size());
& & & & & & if( subsetSize & 0 ) // 针对LBP
& & & & & & & & subsets.reserve(subsets.size() + tree.nodeCount*subsetSize);
& & & & & & FileNodeIterator internalNodesIter = internalNodes.begin(), internalNodesEnd = internalNodes.end();
& & & & & & // 保存每一个node
& & & & & & for( ; internalNodesIter != internalNodesE ) // nodes
& & & & & & {
& & & & & & & & DTreeN
& & & & & & & & node.left = (int)*internalNodesI ++internalNodesI
& & & & & & & & node.right = (int)*internalNodesI ++internalNodesI
& & & & & & & & node.featureIdx = (int)*internalNodesI ++internalNodesI
& & & & & & & & // 针对LBP,获取8个数值
& & & & & & & & if( subsetSize & 0 )
& & & & & & & & {
& & & & & & & & & & for( int j = 0; j & subsetS j++, ++internalNodesIter )
& & & & & & & & & & & & subsets.push_back((int)*internalNodesIter);
& & & & & & & & & & node.threshold = 0.f;
& & & & & & & & }
& & & & & & & & else
& & & & & & & & {
& & & & & & & & & & node.threshold = (float)*internalNodesI ++internalNodesI
& & & & & & & & }
& & & & & & & & nodes.push_back(node);
& & & & & & }
& & & & & & // 保存叶子节点
& & & & & & internalNodesIter = leafValues.begin(), internalNodesEnd = leafValues.end();
& & & & & & for( ; internalNodesIter != internalNodesE ++internalNodesIter ) // leaves
& & & & & & & & leaves.push_back((float)*internalNodesIter);
// 读取stages与features
bool CascadeClassifier::read(const FileNode& root)
& & // load stages
& & if( !data.read(root) )
& & // load features,参考《图像特征-&XXX特征之OpenCV-估计》
& & featureEvaluator = FeatureEvaluator::create(data.featureType);
& & FileNode fn = root[CC_FEATURES];
& & if( fn.empty() )
& & return featureEvaluator-&read(fn);
// 外部调用的函数
bool CascadeClassifier::load(const string& filename)
& & oldCascade.release();
& & data = Data();
& & featureEvaluator.release();
& & // 读取新格式的分类器
& & FileStorage fs(filename, FileStorage::READ);
& & if( !fs.isOpened() )
& & if( read(fs.getFirstTopLevelNode()) )
& & fs.release();
& & // 读取新格式失败则读取旧格式的分类器
& & oldCascade = Ptr&CvHaarClassifierCascade&((CvHaarClassifierCascade*)cvLoad(filename.c_str(), 0, 0, 0));
& & return !oldCascade.empty();
2) 调用detectMultiScale函数进行多尺度检测,该函数可以使用老分类器进行检测也可以使用新分类器进行检测
2.1 如果load的为旧格式的分类器则使用cvHaarDetectObjectsForROC进行检测,flags参数只对旧格式的分类器有效,参考《OpenCV函数解读之cvHaarDetectObjects》
& & if( isOldFormatCascade() )
& & & & MemStorage storage(cvCreateMemStorage(0));
& & & & CvMat _image =
& & & & CvSeq* _objects = cvHaarDetectObjectsForROC( &_image, oldCascade, storage, rejectLevels, levelWeights, scaleFactor,
& & & & & & & & & & & & & & & & & & & & & & & minNeighbors, flags, minObjectSize, maxObjectSize, outputRejectLevels );
& & & & vector&CvAvgComp& vecAvgC
& & & & Seq&CvAvgComp&(_objects).copyTo(vecAvgComp);
& & & & objects.resize(vecAvgComp.size());
& & & & std::transform(vecAvgComp.begin(), vecAvgComp.end(), objects.begin(), getRect());
2.2 新格式分类器多尺度检测
& & &for( double factor = 1; ; factor *= scaleFactor )
& & & & Size originalWindowSize = getOriginalWindowSize();
& & & & Size windowSize( cvRound(originalWindowSize.width*factor), cvRound(originalWindowSize.height*factor) );
& & & & Size scaledImageSize( cvRound( grayImage.cols/factor ), cvRound( grayImage.rows/factor ) );
& & & & Size processingRectSize( scaledImageSize.width-originalWindowSize.width + 1, scaledImageSize.height-originalWindowSize.height + 1 );
& & & & if( processingRectSize.width &= 0 || processingRectSize.height &= 0 )
& & & & & &
& & & & if( windowSize.width & maxObjectSize.width || windowSize.height & maxObjectSize.height )
& & & & & &
& & & & if( windowSize.width & minObjectSize.width || windowSize.height & minObjectSize.height )
& & & & & &
& & & & // 缩放图像
& & & & Mat scaledImage( scaledImageSize, CV_8U, imageBuffer.data );
& & & & resize( grayImage, scaledImage, scaledImageSize, 0, 0, CV_INTER_LINEAR );
& & & & // 计算步长
& & & & int yS
& & & & if( getFeatureType() == cv::FeatureEvaluator::HOG )
& & & & & & yStep = 4;
& & & & else
& & & & & & yStep = factor & 2. ? 1 : 2;
& & & & // 并行个数以及大小,按照列进行并行处理
& & & & int stripCount, stripS
& & & & // 是否采用TBB进行优化
& & #ifdef HAVE_TBB
& & & & const int PTS_PER_THREAD = 1000;
& & & & stripCount = ((processingRectSize.width/yStep)*(processingRectSize.height + yStep-1)/yStep + PTS_PER_THREAD/2)/PTS_PER_THREAD;
& & & & stripCount = std::min(std::max(stripCount, 1), 100);
& & & & stripSize = (((processingRectSize.height + stripCount - 1)/stripCount + yStep-1)/yStep)*yS
& & & & stripCount = 1;
& & & & stripSize = processingRectSize.
& & #endif
& & & & // 调用单尺度检测函数进行检测
& & & & if( !detectSingleScale( scaledImage, stripCount, processingRectSize, stripSize, yStep, factor, candidates,
& & & & & & rejectLevels, levelWeights, outputRejectLevels ) )
& & & & & &
2.3 合并检测结果
& & objects.resize(candidates.size());
& & std::copy(candidates.begin(), candidates.end(), objects.begin());
& & if( outputRejectLevels )
& & & & groupRectangles( objects, rejectLevels, levelWeights, minNeighbors, GROUP_EPS );
& & & & groupRectangles( objects, minNeighbors, GROUP_EPS );
单尺度检测函数流程
2.2.1 根据所载入的特征计算积分图、积分直方图等
& & // 计算当前图像的积分图,参考《图像特征-&XXX特征之OpenCV-估计》
& & if( !featureEvaluator-&setImage( image, data.origWinSize ) )
2.2.2 根据是否输出检测级数并行目标检测
& & vector&Rect& candidatesV
& & vector&int& rejectL
& & vector&double& levelW
& & if( outputRejectLevels )
& & & & parallel_for_(Range(0, stripCount), CascadeClassifierInvoker( *this, processingRectSize, stripSize, yStep, factor,
& & & & & & candidatesVector, rejectLevels, levelWeights, true, currentMask, &mtx));
& & & & levels.insert( levels.end(), rejectLevels.begin(), rejectLevels.end() );
& & & & weights.insert( weights.end(), levelWeights.begin(), levelWeights.end() );
& & & & &parallel_for_(Range(0, stripCount), CascadeClassifierInvoker( *this, processingRectSize, stripSize, yStep, factor,
& & & & & & candidatesVector, rejectLevels, levelWeights, false, currentMask, &mtx));
& & candidates.insert( candidates.end(), candidatesVector.begin(), candidatesVector.end() );
CascadeClassifierInvoker函数的operator()实现具体的检测过程
& & // 对于没有并行时range.start=0,range.end=1
& & void operator()(const Range& range) const
& & & & Ptr&FeatureEvaluator& evaluator = classifier-&featureEvaluator-&clone();
& & & & Size winSize(cvRound(classifier-&data.origWinSize.width * scalingFactor),&
& & & & & & & & & & &cvRound(classifier-&data.origWinSize.height * scalingFactor));
& & & & // strip=processingRectSize.height
& & & & int y1 = range.start * stripS // 0
& & & & int y2 = min(range.end * stripSize, processingRectSize.height); // processSizeRect.height也就是可以处理的高度,已经减去窗口高度
& & & & for( int y = y1; y & y2; y += yStep )
& & & & & & for( int x = 0; x & processingRectSize. x += yStep )
& & & & & & {
& & & & & & & & if ( (!mask.empty()) && (mask.at&uchar&(Point(x,y))==0)) {
& & & & & & & & & &
& & & & & & & & }
& & & & & & & & // result=1表示通过了所有的分类器 &=0表示失败的级数
& & & & & & & & // gypWeight表示返回的阈值
& & & & & & & & double gypW
& & & & & & & & int result = classifier-&runAt(evaluator, Point(x, y), gypWeight);
// 输出LOG
#if defined (LOG_CASCADE_STATISTIC)
& & & & & & & & logger.setPoint(Point(x, y), result);
& & & & & & & & // 当返回级数的时候可以最后三个分类器不通过
& & & & & & & & if( rejectLevels )
& & & & & & & & {
& & & & & & & & & & if( result == 1 )
& & & & & & & & & & & & result = -(int)classifier-&data.stages.size();
& & & & & & & & & & // 可以最后三个分类器不通过
& & & & & & & & & & if( classifier-&data.stages.size() + result & 4 )
& & & & & & & & & & {
& & & & & & & & & & & & mtx-&lock();
& & & & & & & & & & & & rectangles-&push_back(Rect(cvRound(x*scalingFactor), cvRound(y*scalingFactor), winSize.width, winSize.height));
& & & & & & & & & & & & rejectLevels-&push_back(-result);
& & & & & & & & & & & & levelWeights-&push_back(gypWeight);
& & & & & & & & & & & & mtx-&unlock();
& & & & & & & & & & }
& & & & & & & & }
& & & & & & & & // 不返回级数的时候通过所有的分类器才保存起来
& & & & & & & & else if( result & 0 )
& & & & & & & & {
& & & & & & & & & & mtx-&lock();
& & & & & & & & & & rectangles-&push_back(Rect(cvRound(x*scalingFactor), cvRound(y*scalingFactor),
& & & & & & & & & & & & & & & & & & & & & & & &winSize.width, winSize.height));
& & & & & & & & & & mtx-&unlock();
& & & & & & & & }
& & & & & & & & // 如果一级都没有通过那么加大搜索步长
& & & & & & & & if( result == 0 )
& & & & & & & & & & x += yS
& & & & & & }
runAt函数实现某一检测窗口的检测
int CascadeClassifier::runAt( Ptr&FeatureEvaluator&& evaluator, Point pt, double& weight )
& & CV_Assert( oldCascade.empty() );
& & assert( data.featureType == FeatureEvaluator::HAAR ||
& & & & & & data.featureType == FeatureEvaluator::LBP ||
& & & & & & data.featureType == FeatureEvaluator::HOG );
& & // 设置某一点处的特征,参考《图像特征-&XXX特征之OpenCV-估计》
& & if( !evaluator-&setWindow(pt) )
& & & & return -1;
& & // 如果为树桩,没有树枝
& & if( data.isStumpBased )
& & & & if( data.featureType == FeatureEvaluator::HAAR )
& & & & & & return predictOrderedStump&HaarEvaluator&( *this, evaluator, weight );
& & & & else if( data.featureType == FeatureEvaluator::LBP )
& & & & & & return predictCategoricalStump&LBPEvaluator&( *this, evaluator, weight );
& & & & else if( data.featureType == FeatureEvaluator::HOG )
& & & & & & return predictOrderedStump&HOGEvaluator&( *this, evaluator, weight );
& & & & else
& & & & & & return -2;
& & // 每个弱分类器不止一个node
& & & & if( data.featureType == FeatureEvaluator::HAAR )
& & & & & & return predictOrdered&HaarEvaluator&( *this, evaluator, weight );
& & & & else if( data.featureType == FeatureEvaluator::LBP )
& & & & & & return predictCategorical&LBPEvaluator&( *this, evaluator, weight );
& & & & else if( data.featureType == FeatureEvaluator::HOG )
& & & & & & return predictOrdered&HOGEvaluator&( *this, evaluator, weight );
& & & & else
& & & & & & return -2;
predictOrdered*函数实现判断当前检测窗口的判断
// HAAR与HOG特征的多node检测
template&class FEval&
inline int predictOrdered( CascadeClassifier& cascade, Ptr&FeatureEvaluator& &_featureEvaluator, double& sum )
& & int nstages = (int)cascade.data.stages.size();
& & int nodeOfs = 0, leafOfs = 0;
& & FEval& featureEvaluator = (FEval&)*_featureE
& & float* cascadeLeaves = &cascade.data.leaves[0];
& & CascadeClassifier::Data::DTreeNode* cascadeNodes = &cascade.data.nodes[0];
& & CascadeClassifier::Data::DTree* cascadeWeaks = &cascade.data.classifiers[0];
& & CascadeClassifier::Data::Stage* cascadeStages = &cascade.data.stages[0];
& & // 遍历每个强分类器
& & for( int si = 0; si & si++ )
& & & & CascadeClassifier::Data::Stage& stage = cascadeStages[si];
& & & & int wi, ntrees = stage.
& & & & sum = 0;
& & & & // 遍历每个弱分类器
& & & & for( wi = 0; wi & wi++ )
& & & & & & CascadeClassifier::Data::DTree& weak = cascadeWeaks[stage.first + wi];
& & & & & & int idx = 0, root = nodeO
& & & & & & // 遍历每个节点
& & & & & & do
& & & & & & {
& & & & & & & & // 选择一个node:root和idx初始化为0,即第一个node
& & & & & & & & CascadeClassifier::Data::DTreeNode& node = cascadeNodes[root + idx];
& & & & & & & & // 计算当前node特征池编号下的特征值
& & & & & & & & double val = featureEvaluator(node.featureIdx);
& & & & & & & & // 如果val小于node阈值则选择左子树,否则选择右子树
& & & & & & & & idx = val & node.threshold ? node.left : node.
& & & & & & }&while( idx & 0 );
& & & & & & // 累加最终的叶子节点
& & & & & & sum += cascadeLeaves[leafOfs - idx];
& & & & & & nodeOfs += weak.nodeC
& & & & & & leafOfs += weak.nodeCount + 1;
& & & & // 判断所有叶子节点累加和是否小于强分类器阈值,小于强分类器阈值则失败
& & & & if( sum & stage.threshold )
& & & & & & return -
& & // 通过了所有的强分类器返回1,否则返回失败的分类器
& & return 1;
// LBP特征的多node检测
template&class FEval&
inline int predictCategorical( CascadeClassifier& cascade, Ptr&FeatureEvaluator& &_featureEvaluator, double& sum )
& & int nstages = (int)cascade.data.stages.size();
& & int nodeOfs = 0, leafOfs = 0;
& & FEval& featureEvaluator = (FEval&)*_featureE
& & size_t subsetSize = (cascade.data.ncategories + 31)/32;
& & int* cascadeSubsets = &cascade.data.subsets[0];
& & float* cascadeLeaves = &cascade.data.leaves[0];
& & CascadeClassifier::Data::DTreeNode* cascadeNodes = &cascade.data.nodes[0];
& & CascadeClassifier::Data::DTree* cascadeWeaks = &cascade.data.classifiers[0];
& & CascadeClassifier::Data::Stage* cascadeStages = &cascade.data.stages[0];
& & for(int si = 0; si & si++ )
& & & & CascadeClassifier::Data::Stage& stage = cascadeStages[si];
& & & & int wi, ntrees = stage.
& & & & sum = 0;
& & & & for( wi = 0; wi & wi++ )
& & & & & & CascadeClassifier::Data::DTree& weak = cascadeWeaks[stage.first + wi];
& & & & & & int idx = 0, root = nodeO
& & & & & & do
& & & & & & {
& & & & & & & & CascadeClassifier::Data::DTreeNode& node = cascadeNodes[root + idx];
& & & & & & & & // c为0-255之间的数
& & & & & & & & int c = featureEvaluator(node.featureIdx);
& & & & & & & & // 获取当前node的subset头位置
& & & & & & & & const int* subset = &cascadeSubsets[(root + idx)*subsetSize]; // LBP:subsetSize=8
& & & & & & & & // 判断选择左子树还是右子树
& & & & & & & & idx = (subset[c&&5] & (1 && (c & 31))) ? node.left : node.
& & & & & & & & // c&&5表示将c右移5位,选择高3位,0-7之间
& & & & & & & & // c&31表示低5位,1&&(c&31)选择低5位后左移1位
& & & & & & & & // 将上面的数按位与,如果最后结果不为0表示选择左子树,否则选择右子树
& & & & & & }
& & & & & & while( idx & 0 );
& & & & & & sum += cascadeLeaves[leafOfs - idx];
& & & & & & nodeOfs += weak.nodeC
& & & & & & leafOfs += weak.nodeCount + 1;
& & & & if( sum & stage.threshold )
& & & & & & return -
& & return 1;
// HAAR与HOG特征的单node检测
template&class FEval&
inline int predictOrderedStump( CascadeClassifier& cascade, Ptr&FeatureEvaluator& &_featureEvaluator, double& sum )
& & int nodeOfs = 0, leafOfs = 0;
& & FEval& featureEvaluator = (FEval&)*_featureE
& & float* cascadeLeaves = &cascade.data.leaves[0];
& & CascadeClassifier::Data::DTreeNode* cascadeNodes = &cascade.data.nodes[0];
& & CascadeClassifier::Data::Stage* cascadeStages = &cascade.data.stages[0];
& & int nstages = (int)cascade.data.stages.size();
& & // 遍历每个强分类器
& & for( int stageIdx = 0; stageIdx & stageIdx++ )
& & & & CascadeClassifier::Data::Stage& stage = cascadeStages[stageIdx];
& & & & sum = 0.0;
& & & & int ntrees = stage.
& & & & // 遍历每个弱分类器
& & & & for( int i = 0; i & i++, nodeOfs++, leafOfs+= 2 )
& & & & & & CascadeClassifier::Data::DTreeNode& node = cascadeNodes[nodeOfs];
& & & & & & double value = featureEvaluator(node.featureIdx);
& & & & & & sum += cascadeLeaves[ value & node.threshold ? leafOfs : leafOfs + 1 ];
& & & & if( sum & stage.threshold )
& & & & & & return -stageI
& & return 1;
// LBP特征的单node检测
template&class FEval&
inline int predictCategoricalStump( CascadeClassifier& cascade, Ptr&FeatureEvaluator& &_featureEvaluator, double& sum )
& & int nstages = (int)cascade.data.stages.size();
& & int nodeOfs = 0, leafOfs = 0;
& & FEval& featureEvaluator = (FEval&)*_featureE
& & size_t subsetSize = (cascade.data.ncategories + 31)/32;
& & int* cascadeSubsets = &cascade.data.subsets[0];
& & float* cascadeLeaves = &cascade.data.leaves[0];
& & CascadeClassifier::Data::DTreeNode* cascadeNodes = &cascade.data.nodes[0];
& & CascadeClassifier::Data::Stage* cascadeStages = &cascade.data.stages[0];
#ifdef HAVE_TEGRA_OPTIMIZATION
& & float tmp = 0; // float accumulator -- float operations are quicker
& & for( int si = 0; si & si++ )
& & & & CascadeClassifier::Data::Stage& stage = cascadeStages[si];
& & & & int wi, ntrees = stage.
#ifdef HAVE_TEGRA_OPTIMIZATION
& & & & tmp = 0;
& & & & sum = 0;
& & & & for( wi = 0; wi & wi++ )
& & & & & & CascadeClassifier::Data::DTreeNode& node = cascadeNodes[nodeOfs];
& & & & & & int c = featureEvaluator(node.featureIdx);
& & & & & & const int* subset = &cascadeSubsets[nodeOfs*subsetSize];
#ifdef HAVE_TEGRA_OPTIMIZATION
& & & & & & tmp += cascadeLeaves[ subset[c&&5] & (1 && (c & 31)) ? leafOfs : leafOfs+1];
& & & & & & sum += cascadeLeaves[ subset[c&&5] & (1 && (c & 31)) ? leafOfs : leafOfs+1];
& & & & & & nodeOfs++;
& & & & & & leafOfs += 2;
#ifdef HAVE_TEGRA_OPTIMIZATION
& & & & if( tmp & stage.threshold ) {
& & & & & & sum = (double)
& & & & & & return -
& & & & if( sum & stage.threshold )
& & & & & & return -
#ifdef HAVE_TEGRA_OPTIMIZATION
& & sum = (double)
& & return 1;
版权声明:本文为博主原创文章,未经博主允许不得转载。
发表评论:
TA的最新馆藏[转]&

我要回帖

更多关于 opencv3 traincascade 的文章

 

随机推荐