人脸检测的代码分析.docx
《人脸检测的代码分析.docx》由会员分享,可在线阅读,更多相关《人脸检测的代码分析.docx(16页珍藏版)》请在冰点文库上搜索。
![人脸检测的代码分析.docx](https://file1.bingdoc.com/fileroot1/2023-5/16/8009f0f0-8341-4d15-8c5c-3ab7026c9fe1/8009f0f0-8341-4d15-8c5c-3ab7026c9fe11.gif)
人脸检测的代码分析
OpenCV学习笔记(三)人脸检测的代码分析
一、预备知识:
1、动态内存存储及操作函数
CvMemStorage
typedefstructCvMemStorage
{
structCvMemBlock*bottom;/*firstallocatedblock*/
structCvMemBlock*top;/*thecurrentmemoryblock-topofthestack*/
structCvMemStorage*parent;/*borrowsnewblocksfrom*/
intblock_size;/*blocksize*/
intfree_space;/*freespaceinthetopblock(inbytes)*/
}CvMemStorage;
内存存储器是一个可用来存储诸如序列,轮廓,图形,子划分等动态增长数据结构的底层结构。
它是由一系列以同等大小的内存块构成,呈列表型---bottom域指的是列首,top域指的是当前指向的块但未必是列尾.在bottom和top之间所有的块(包括bottom,不包括top)被完全占据了空间;在top和列尾之间所有的块(包括块尾,不包括top)则是空的;而top块本身则被占据了部分空间--free_space指的是top块剩余的空字节数。
新分配的内存缓冲区(或显示的通过cvMemStorageAlloc函数分配,或隐示的通过cvSeqPush,cvGraphAddEdge等高级函数分配)总是起始于当前块(即top块)的剩余那部分,如果剩余那部分能满足要求(够分配的大小)。
分配后,free_space就减少了新分配的那部分内存大小,外加一些用来保存适当列型的附加大小。
当top块的剩余空间无法满足被分配的块(缓冲区)大小时,top块的下一个存储块被置为当前块(新的top块)--free_space被置为先前分配的整个块的大小。
如果已经不存在空的存储块(即:
top块已是列尾),则必须再分配一个新的块(或从parent那继承,见cvCreateChildMemStorage)并将该块加到列尾上去。
于是,存储器(memorystorage)就如同栈(Stack)那样,bottom指向栈底,(top,free_space)对指向栈顶。
栈顶可通过cvSaveMemStoragePos保存,通过cvRestoreMemStoragePos恢复指向,通过cvClearStorage重置。
CvMemBlock
内存存储块结构
typedefstructCvMemBlock
{
structCvMemBlock*prev;
structCvMemBlock*next;
}CvMemBlock;
CvMemBlock代表一个单独的内存存储块结构。
内存存储块中的实际数据存储在header块之后(即:
存在一个头指针head指向的块header,该块不存储数据),于是,内存块的第i个字节可以通过表达式((char*)(mem_block_ptr+1))[i]获得。
然而,通常没必要直接去获得存储结构的域。
CvMemStoragePos
内存存储块地址
typedefstructCvMemStoragePos
{
CvMemBlock*top;
intfree_space;
}CvMemStoragePos;
该结构(如以下所说)保存栈顶的地址,栈顶可以通过cvSaveMemStoragePos保存,也可以通过cvRestoreMemStoragePos恢复。
cvCreateMemStorage
创建内存块
CvMemStorage*cvCreateMemStorage(intblock_size=0);
block_size:
存储块的大小以字节表示。
如果大小是0byte,则将该块设置成默认值当前默认大小为64k.
函数cvCreateMemStorage创建一内存块并返回指向块首的指针。
起初,存储块是空的。
头部(即:
header)的所有域值都为0,除了block_size外.
cvCreateChildMemStorage
创建子内存块
CvMemStorage*cvCreateChildMemStorage(CvMemStorage*parent);
parent父内存块
函数cvCreateChildMemStorage创建一类似于普通内存块的子内存块,除了内存分配/释放机制不同外。
当一个子存储块需要一个新的块加入时,它就试图从parent那得到这样一个块。
如果parent中还未被占据空间的那些块中的第一个块是可获得的,就获取第一个块(依此类推),再将该块从parent那里去除。
如果不存在这样的块,则parent要么分配一个,要么从它自己parent(即:
parent的parent)那借个过来。
换句话说,完全有可能形成一个链或更为复杂的结构,其中的内存存储块互为child/parent关系(父子关系)。
当子存储结构被释放或清除,它就把所有的块还给各自的parent.在其他方面,子存储结构同普通存储结构一样。
子存储结构在下列情况中是非常有用的。
想象一下,如果用户需要处理存储在某个块中的动态数据,再将处理的结果存放在该块中。
在使用了最简单的方法处理后,临时数据作为输入和输出数据被存放在了同一个存储块中,于是该存储块看上去就类似下面处理后的样子:
Dynamicdataprocessingwithoutusingchildstorage.结果,在存储块中,出现了垃圾(临时数据)。
然而,如果在开始处理数据前就先建立一个子存储块,将临时数据写入子存储块中并在最后释放子存储块,那么最终在源/目的存储块(source/destinationstorage)中就不会出现垃圾,于是该存储块看上去应该是如下形式:
Dynamicdataprocessingusingachildstorage.
cvReleaseMemStorage
释放内存块
voidcvReleaseMemStorage(CvMemStorage**storage);
storage:
指向被释放了的存储块的指针
函数cvReleaseMemStorage释放所有的存储(内存)块或者将它们返回给各自的parent(如果需要的话)。
接下来再释放header块(即:
释放头指针head指向的块=free(head))并清除指向该块的指针(即:
head=NULL)。
在释放作为parent的块之前,先清除各自的child块。
cvClearMemStorage
清空内存存储块
voidcvClearMemStorage(CvMemStorage*storage);
storage:
存储存储块
函数cvClearMemStorage将存储块的top置到存储块的头部(注:
清空存储块中的存储内容)。
该函数并不释放内存(仅清空内存)。
假使该内存块有一个父内存块(即:
存在一内存块与其有父子关系),则函数就将所有的块返回给其parent.
cvMemStorageAlloc
在存储块中分配以内存缓冲区
void*cvMemStorageAlloc(CvMemStorage*storage,size_tsize);
storage:
内存块.
size:
缓冲区的大小.
函数cvMemStorageAlloc在存储块中分配一内存缓冲区。
该缓冲区的大小不能超过内存块的大小,否则就会导致运行时错误。
缓冲区的地址被调整为CV_STRUCT_ALIGN字节(当前为sizeof(double)).
cvMemStorageAllocString
在存储块中分配一文本字符串
typedefstructCvString
{
intlen;
char*ptr;
}
CvString;
CvStringcvMemStorageAllocString(CvMemStorage*storage,constchar*ptr,intlen=-1);
storage:
存储块
ptr:
字符串
len:
字符串的长度(不计算'\0')。
如果参数为负数,函数就计算该字符串的长度。
函数cvMemStorageAlloString在存储块中创建了一字符串的拷贝。
它返回一结构,该结构包含字符串的长度(该长度或通过用户传递,或通过计算得到)和指向被拷贝了的字符串的指针。
cvSaveMemStoragePos
保存内存块的位置(地址)
voidcvSaveMemStoragePos(constCvMemStorage*storage,CvMemStoragePos*pos);
storage:
内存块.
pos:
内存块顶部位置。
函数cvSaveMemStoragePos将存储块的当前位置保存到参数pos中。
函数cvRestoreMemStoragePos可进一步获取该位置(地址)。
cvRestoreMemStoragePos
恢复内存存储块的位置
voidcvRestoreMemStoragePos(CvMemStorage*storage,CvMemStoragePos*pos);
storage:
内存块.
pos:
新的存储块的位置
函数cvRestoreMemStoragePos通过参数pos恢复内存块的位置。
该函数和函数cvClearMemStorage是释放被占用内存块的唯一方法。
注意:
没有什么方法可去释放存储块中被占用的部分内存。
2、分类器结构及操作函数:
CvHaarFeature
#defineCV_HAAR_FEATURE_MAX3
typedefstructCvHaarFeature
{
inttilted;
struct
{
CvRectr;
floatweight;
}rect[CV_HAAR_FEATURE_MAX];
}
CvHaarFeature;
一个harr特征由2-3个具有相应权重的矩形组成
titled:
/*0meansup-rightfeature,1means45--rotatedfeature*/
rect[CV_HAAR_FEATURE_MAX];/*2-3rectangleswithweightsofoppositesignsandwithabsolutevaluesinverselyproportionaltotheareasoftherectangles.ifrect[2].weight!
=0,thenthefeatureconsistsof3rectangles,otherwiseitconsistsof2*/
CvHaarClassifier
typedefstructCvHaarClassifier
{
intcount;
CvHaarFeature*haar_feature;
float*threshold;
int*left;
int*right;
float*alpha;
}
CvHaarClassifier;
/*asingletreeclassifier(stumpinthesimplestcase)thatreturnstheresponseforthefeatureattheparticularimagelocation(i.e.pixelsumoversubrectanglesofthewindow)andgivesoutavaluedependingontheresponce*/
intcount;/*numberofnodesinthedecisiontree*/
/*theseare"parallel"arrays.Everyindexicorrespondstoanodeofthedecisiontree(roothas0-thindex).
left[i]-indexoftheleftchild(ornegatedindexiftheleftchildisaleaf)
right[i]-indexoftherightchild(ornegatedindexiftherightchildisaleaf)
threshold[i]-branchthreshold.iffeatureresponceis<=threshold,leftbranchischosen,otherwiserightbranchischosed.
alpha[i]-outputvaluecorrepondingtotheleaf.*/
CvHaarStageClassifier
typedefstructCvHaarStageClassifier
{
intcount;/*numberofclassifiersinthebattery*/
floatthreshold;/*thresholdfortheboostedclassifier*/
CvHaarClassifier*classifier;/*arrayofclassifiers*/
/*thesefieldsareusedfororganizingtreesofstageclassifiers,
ratherthanjuststrightcascades*/
intnext;
intchild;
intparent;
}
CvHaarStageClassifier;
/*aboostedbatteryofclassifiers(=stageclassifier):
thestageclassifierreturns1ifthesumoftheclassifiers'responcesisgreaterthanthresholdand0otherwise*/
intcount;/*numberofclassifiersinthebattery*/
floatthreshold;/*thresholdfortheboostedclassifier*/
CvHaarClassifier*classifier;/*arrayofclassifiers*/
/*thesefieldsareusedfororganizingtreesofstageclassifiers,ratherthanjuststrightcascades*/
CvHaarClassifierCascade
typedefstructCvHidHaarClassifierCascadeCvHidHaarClassifierCascade;
typedefstructCvHaarClassifierCascade
{
intflags;
intcount;
CvSizeorig_window_size;
CvSizereal_window_size;
doublescale;
CvHaarStageClassifier*stage_classifier;
CvHidHaarClassifierCascade*hid_cascade;
}
CvHaarClassifierCascade;
/*cascadeortreeofstageclassifiers*/
intflags;/*signature*/
intcount;/*numberofstages*/
CvSizeorig_window_size;/*originalobjectsize(thecascadeistrainedfor)*/
/*thesetwoparametersaresetbycvSetImagesForHaarClassifierCascade*/
CvSizereal_window_size;/*currentobjectsize*/
doublescale;/*currentscale*/
CvHaarStageClassifier*stage_classifier;/*arrayofstageclassifiers*/
CvHidHaarClassifierCascade*hid_cascade;/*hiddenoptimizedrepresentationofthecascade,createdbycvSetImagesForHaarClassifierCascade*/
所有的结构都代表一个级联boostedHaar分类器。
级联有下面的等级结构:
Cascade:
Stage1:
Classifier11:
Feature11
Classifier12:
Feature12
...
Stage2:
Classifier21:
Feature21
...
...
整个等级可以手工构建,也可以利用函数cvLoadHaarClassifierCascade从已有的磁盘文件或嵌入式基中导入。
特征检测用到的函数:
cvLoadHaarClassifierCascade
从文件中装载训练好的级联分类器或者从OpenCV中嵌入的分类器数据库中导入
CvHaarClassifierCascade*cvLoadHaarClassifierCascade(
constchar*directory,
CvSizeorig_window_size);
directory:
训练好的级联分类器的路径
orig_window_size:
级联分类器训练中采用的检测目标的尺寸。
因为这个信息没有在级联分类器中存储,所有要单独指出。
函数cvLoadHaarClassifierCascade用于从文件中装载训练好的利用海尔特征的级联分类器,或者从OpenCV中嵌入的分类器数据库中导入。
分类器的训练可以应用函数haartraining(详细察看opencv/apps/haartraining)
函数已经过时了。
现在的目标检测分类器通常存储在XML或YAML文件中,而不是通过路径导入。
从文件中导入分类器,可以使用函数cvLoad。
cvReleaseHaarClassifierCascade
释放haarclassifiercascade。
voidcvReleaseHaarClassifierCascade(CvHaarClassifierCascade**cascade);
cascade:
双指针类型指针指向要释放的cascade.指针由函数声明。
函数cvReleaseHaarClassifierCascade释放cascade的动态内存,其中cascade的动态内存或者是手工创建,或者通过函数cvLoadHaarClassifierCascade或cvLoad分配。
cvHaarDetectObjects
检测图像中的目标
typedefstructCvAvgComp
{
CvRectrect;/*boundingrectanglefortheobject(averagerectangleofagroup)*/
intneighbors;/*numberofneighborrectanglesinthegroup*/
}
CvAvgComp;
CvSeq*cvHaarDetectObjects(constCvArr*image,
CvHaarClassifierCascade*cascade,
CvMemStorage*storage,
doublescale_factor=1.1,
intmin_neighbors=3,intflags=0,
CvSizemin_size=cvSize(0,0));
image被检图像
cascadeharr分类器级联的内部标识形式
storage用来存储检测到的一序列候选目标矩形框的内存区域。
scale_factor在前后两次相继的扫描中,搜索窗口的比例系数。
例如1.1指将搜索窗口依次扩大10%。
min_neighbors构成检测目标的相邻矩形的最小个数(缺省-1)。
如果组成检测目标的小矩形的个数和小于min_neighbors-1都会被排除。
如果min_neighbors为0,则函数不做任何操作就返回所有的被检候选矩形框,这种设定值一般用在用户自定义对检测结果的组合程序上。
flags操作方式。
当前唯一可以定义的操作方式是CV_HAAR_DO_CANNY_PRUNING。
如果被设定,函数利用Canny边缘检测器来排除一些边缘很少或者很多的图像区域,因为这样的区域一般不含被检目标。
人脸检测中通过设定阈值使用了这种方法,并因此提高了检测速度。
min_size检测窗口的最小尺寸。
缺省的情况下被设为分类器训练时采用的样本尺寸(人脸检测中缺省大小是~20×20)。
函数cvHaarDetectObjects使用针对某目标物体训练的级联分类器在图像中找到包含目标物体的矩形区域,并且将这些区域作为一序列的矩形框返回。
函数以不同比例大小的扫描窗口对图像进行几次搜索(察看cvSetImagesForHaarClassifierCascade)。
每次都要对图像中的这些重叠区域利用cvRunHaarClassifierCascade进行检测。
有时候也会利用某些继承(heuristics)技术以减少分析的候选区域,例如利用Canny裁减(prunning)方法。
函数在处理和收集到候选的方框(全部通过级联分类器各层的区域)之后,接着对这些区域进行组合并且返回一系列各个足够大的组合中的平均矩形。
调节程序中的缺省参数(scale_factor=1.1,min_neighbors=3,flags=0)用于对目标进行更精确同时也是耗时较长的进一步检测。
为了能对视频图像进行更快的实时检测,参数设置通常是:
scale_factor=1.2,min_neighbors=2,flags=CV_HAAR_DO_CANNY_PRUNING,min_size=