概述
级联分类器CascadeClassifier
OpenCV官方文档:https://docs.opencv.org/3.4.3/d5/d54/group__objdetect.html
分类器:
判别某个事物是否属于某种分类的器件,两种结果:是、否 。
级联分类器:
可以理解为将N个单类的分类器串联起来。如果一个事物能属于这一系列串联起来的的所有分类器,则最终结果就是 是,若有一项不符,则判定为否。
比如人脸,它有很多属性,我们将每个属性做一成个分类器,如果一个模型符合了我们定义的人脸的所有属性,则我们人为这个模型就是一个人脸。那么这些属性是指什么呢? 比如人脸需要有两条眉毛,两只眼睛,一个鼻子,一张嘴,一个大概U形状的下巴或者是轮廓等等。
CascadeClassifier为OpenCV下用来做目标检测的级联分类器的一个类。该类中封装的目标检测机制,简而言之是滑动窗口机制+级联分类器的方式。
级联分类器检测类CascadeClassifier,在2.4.5版本中使用Adaboost的方法+LBP、HOG、HAAR进行目标检测,加载的是使用traincascade进行训练的分类器。
class CV_EXPORTS_W CascadeClassifier{public:CV_WRAP CascadeClassifier();CV_WRAP CascadeClassifier(const String& filename); //从文件中加载级联分类器~CascadeClassifier();CV_WRAP bool empty() const; //检测级联分类器是否被加载CV_WRAP bool load( const String& filename ); //从文件中加载级联分类器CV_WRAP bool read( const FileNode& node ); //从FileStorage节点读取分类器/** 检测输入图像中不同大小的对象。检测到的对象以矩形列表的形式返回。参数:image: 包含检测对象的图像的CV_8U类型矩阵objects: 矩形的向量,其中每个矩形包含被检测的对象,矩形可以部分位于原始图像之外scaleFactor: 指定在每个图像缩放时的缩放比例minNeighbors:指定每个候选矩形需要保留多少个相邻矩形flags:含义与函数cvHaarDetectObjects中的旧级联相同。它不用于新的级联。minSize:对象最小大小,小于该值的对象被忽略。maxSize:最大可能的对象大小,大于这个值的对象被忽略。*/CV_WRAP void detectMultiScale( InputArray image,CV_OUT std::vector<Rect>& objects,double scaleFactor = 1.1,int minNeighbors = 3, int flags = 0,Size minSize = Size(),Size maxSize = Size() );/**detectMultiScale重载函数参数:image:包含检测对象的图像的CV_8U类型矩阵objects: 矩形的向量,其中每个矩形包含被检测的对象,矩形可以部分位于原始图像之外numDetections: 对应对象的检测编号向量。一个物体被探测到的次数是相邻的被积极分类的矩形的数量,这些矩形被连接在一起形成物体scaleFactor: 指定在每个图像缩放时的缩放比例minNeighbors:指定每个候选矩形需要保留多少个相邻矩形flags:含义与函数cvHaarDetectObjects中的旧级联相同。它不用于新的级联minSize:对象最小大小,小于该值的对象被忽略。maxSize:最大可能的对象大小,大于这个值的对象被忽略*/CV_WRAP_AS(detectMultiScale2) void detectMultiScale( InputArray image,CV_OUT std::vector<Rect>& objects,CV_OUT std::vector<int>& numDetections,double scaleFactor=1.1,int minNeighbors=3, int flags=0,Size minSize=Size(),Size maxSize=Size() );/*detectMultiScale重载函数,此函数允许您检索分类的最终阶段决策确定性为此,需要将' outputRejectLevels '设置为true,并提供' rejectLevels '和' levelWeights '参数。对于每一个结果检测,‘levelWeights’将在最后阶段包含分类的确定性。这个值可以用来区分强分类和弱分类。 */CV_WRAP_AS(detectMultiScale3) void detectMultiScale( InputArray image,CV_OUT std::vector<Rect>& objects,CV_OUT std::vector<int>& rejectLevels,CV_OUT std::vector<double>& levelWeights,double scaleFactor = 1.1,int minNeighbors = 3, int flags = 0,Size minSize = Size(),Size maxSize = Size(),bool outputRejectLevels = false );具体使用示例代码Mat img;vector<double> weights;vector<int> levels;vector<Rect> detections;CascadeClassifier model("/path/to/your/model.xml");model.detectMultiScale(img, detections, levels, weights, 1.1, 3, 0, Size(), Size(), true);cerr << "Detection " << detections[0] << " with weight " << weights[0] << endl;};
步骤
调用opencv训练好的分类器和自带的检测函数检测人脸人眼等的步骤简单直接:
加载分类器
分类器就是一个XML文件,该文件中会描述人体各个部位的Haar特征值。包括人脸、眼睛、嘴唇、侧脸、微笑、上半身、下半身、全身等等。分类器本来的位置是在*\opencv\sources\data(harr分类器,也有其他的可以用;也可以自己训练,traincascade的独立应用程序可以从一组样本中训练一系列增强分类器,位置在“\opencv\sources\apps”)
在右上图中,文件夹的名字“haarcascades”、“haarcascades_cuda”、“hogcascades”和“lbpcascades”分别表示通过“haar”、 “harr”、“hog”和“lbp”三种不同的特征而训练出的分类器:即各文件夹里的文件。
“haar”特征主要用于人脸检测,
“hog”特征主要用于行人检测,
“lbp”特征主要用于人脸识别。
打开“haarcascades_cuda”文件夹。
图中的XML文件即是我们人脸检测所需要的分类器文件。在实际使用中,推荐使用上图中被标记的“haarcascade_frontalface_alt2.xml”分类器文件,准确率和速度都比较好。
/***************************************************************FunctionName: FaceDetectionPurpose: 人脸检测,在图像上框出矩形/圆形人脸Parameter: img 待检测图像cascade 级联器Return: 无****************************************************************/void FaceDetection(Mat& img, CascadeClassifier cascade){Mat gray;cvtColor(img, gray, COLOR_BGR2GRAY);vector<Rect> rect;cascade.detectMultiScale(gray, rect, 1.1, 2, 0|CV_HAL_CMP_GE, Size(100, 100), Size(500, 500));//cout << "检测到的人脸个数" << rect.size() << endl;for (int i = 0; i < rect.size(); i++){//Point center;//int radius;//center.x = cvRound((rect[i].x + rect[i].width * 0.5));//center.y = cvRound((rect[i].y + rect[i].height * 0.5));//radius = cvRound((rect[i].width + rect[i].height) * 0.25);//circle(img, center, radius, Scalar(255, 255, 255), 2);rectangle(img, rect[i], Scalar(255, 255, 255), 2);}}// OpenCV includes#include<opencv2\opencv.hpp>#include<iostream>#include<vector>#include <string>using namespace cv;using namespace std;int main(int argc, char* argv[]){Mat camera;CascadeClassifier cascade;string faceCascadeName = "D:/OpenCV/opencv/build/etc/haarcascades/haarcascade_frontalface_alt.xml";cascade.load(faceCascadeName);VideoCapture cap(0);if (!cap.isOpened()){cout << "No video!" << endl;return -1;}while (true){cap >> camera;FaceDetection(camera, cascade);imshow("camera", camera);if (waitKey(10) == 27){break;}}cap.release();destroyAllWindows();return 0;}
在检测时,先导入训练好的参数文件,其中haarcascade_frontalface_alt2.xml对正面脸的识别效果较好,haarcascade_profileface.xml对侧脸的检测效果较好。当然,如果要达到更高的分类精度,可以收集更多的数据进行训练。
自己训练分类器
在opencv的安装目录中的bin文件夹下有两个可执行文件opencv_createsamples.exe和opencv_traincascade.exe。traincascade快速使用详解:https://blog.csdn.net/guduruyu/article/details/70183372
如果对分类器的参数不满意,或者说想识别其他的物体例如车、人、飞机、苹果等等等等,只需要选择适当的样本训练,获取该物体的各个方面的参数,完整的细节在:https://docs.opencv.org/2.4/doc/user_guide/ug_traincascade.html
训练过程可以通过openCV的haartraining实现(参考haartraining参考文档opencv/apps/traincascade ),主要包括个步骤:
1. 收集打算学习的物体数据集(如正面人脸图,侧面汽车图等, 1000~10000个正样本为宜),把它们存储在一个或多个目录下面。
2. 使用createsamples来建立正样本的向量输出文件,通过这个文件可以重复训练过程,使用同一个向量输出文件尝试各种参数。
3. 获取负样本,即不包含该物体的图像。
4. 训练。
附:人脸识别+人眼识别代码
string harr_xmlPath = "D:\\OpenCV\\opencv\\build\\etc\\haarcascades\\haarcascade_frontalface_default.xml";string lbp_xmlPath = "D:\\OpenCV\\opencv\\sources\\data\\haarcascades_cuda\\haarcascade_eye.xml";CascadeClassifier face_cascade;CascadeClassifier eyes_cascade;void detectAndDisplay(Mat &dispFace);/*--------------main-----------------*/int main(){system("color F0");VideoCapture cam(0);if (!face_cascade.load(harr_xmlPath))//也可用Haar分类器{printf("人脸检测器加载失败\n");return -1;}if (!eyes_cascade.load(lbp_xmlPath)){printf("人眼检测器加载失败\n");return -1;};while (true){Mat frame;cam >> frame;detectAndDisplay(frame);imshow("camera", frame);if (waitKey(10) == 27){break;}}waitKey(0);return 0;}/*-------------函数实现----------------*/void detectAndDisplay(Mat &dispFace){//定义变量std::vector<Rect> faces;std::vector<Rect>eyes;Mat srcFace, grayFace, eqlHistFace;cvtColor(dispFace, grayFace, COLOR_BGR2GRAY);equalizeHist(grayFace, eqlHistFace); //直方图均衡化//人脸检测face_cascade.detectMultiScale(eqlHistFace, faces, 1.1, 3, 0 | CV_HAL_CMP_GE, Size(100, 100));//增大第四个参数可以提高检测精度,但也可能会造成遗漏//人脸尺寸minSize和maxSize,关键参数,自行设定,随图片尺寸有很大关系,for (unsigned int i = 0; i < faces.size(); i++){//用蓝色椭圆标记检测到的人脸Point center(faces[i].x + faces[i].width / 2, faces[i].y + faces[i].height / 2);ellipse(dispFace, center, Size(faces[i].width / 2, faces[i].height * 65 / 100), 0, 0, 360, Scalar(255, 0, 0), 2, 8, 0);//人眼检测,提取的子矩阵指定为矩形Mat faceROI = eqlHistFace(faces[i]);eyes_cascade.detectMultiScale(faceROI, eyes, 1.2, 3, 0 | CV_HAL_CMP_GE, Size(15, 15), Size(80, 80));//用绿色圆标记检测到的人眼for (unsigned int j = 0; j < eyes.size(); j++){Point center(faces[i].x + eyes[j].x + eyes[j].width / 2, faces[i].y + eyes[j].y + eyes[j].height / 2);circle(dispFace, center, 2, Scalar(0, 255, 0), 2);}}}

