概述

级联分类器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进行训练的分类器。

  1. class CV_EXPORTS_W CascadeClassifier
  2. {
  3. public:
  4. CV_WRAP CascadeClassifier();
  5. CV_WRAP CascadeClassifier(const String& filename); //从文件中加载级联分类器
  6. ~CascadeClassifier();
  7. CV_WRAP bool empty() const; //检测级联分类器是否被加载
  8. CV_WRAP bool load( const String& filename ); //从文件中加载级联分类器
  9. CV_WRAP bool read( const FileNode& node ); //从FileStorage节点读取分类器
  10. /** 检测输入图像中不同大小的对象。检测到的对象以矩形列表的形式返回。
  11. 参数:
  12. image: 包含检测对象的图像的CV_8U类型矩阵
  13. objects: 矩形的向量,其中每个矩形包含被检测的对象,矩形可以部分位于原始图像之外
  14. scaleFactor: 指定在每个图像缩放时的缩放比例
  15. minNeighbors:指定每个候选矩形需要保留多少个相邻矩形
  16. flags:含义与函数cvHaarDetectObjects中的旧级联相同。它不用于新的级联。
  17. minSize:对象最小大小,小于该值的对象被忽略。
  18. maxSize:最大可能的对象大小,大于这个值的对象被忽略。
  19. */
  20. CV_WRAP void detectMultiScale( InputArray image,
  21. CV_OUT std::vector<Rect>& objects,
  22. double scaleFactor = 1.1,
  23. int minNeighbors = 3, int flags = 0,
  24. Size minSize = Size(),
  25. Size maxSize = Size() );
  26. /**
  27. detectMultiScale重载函数
  28. 参数:
  29. image:包含检测对象的图像的CV_8U类型矩阵
  30. objects: 矩形的向量,其中每个矩形包含被检测的对象,矩形可以部分位于原始图像之外
  31. numDetections: 对应对象的检测编号向量。一个物体被探测到的次数是相邻的被积极分类的矩形的数量,这些矩形被连接在一起形成物体
  32. scaleFactor: 指定在每个图像缩放时的缩放比例
  33. minNeighbors:指定每个候选矩形需要保留多少个相邻矩形
  34. flags:含义与函数cvHaarDetectObjects中的旧级联相同。它不用于新的级联
  35. minSize:对象最小大小,小于该值的对象被忽略。
  36. maxSize:最大可能的对象大小,大于这个值的对象被忽略
  37. */
  38. CV_WRAP_AS(detectMultiScale2) void detectMultiScale( InputArray image,
  39. CV_OUT std::vector<Rect>& objects,
  40. CV_OUT std::vector<int>& numDetections,
  41. double scaleFactor=1.1,
  42. int minNeighbors=3, int flags=0,
  43. Size minSize=Size(),
  44. Size maxSize=Size() );
  45. /*
  46. detectMultiScale重载函数,此函数允许您检索分类的最终阶段决策确定性
  47. 为此,需要将' outputRejectLevels '设置为true,并提供' rejectLevels '和' levelWeights '参数。
  48. 对于每一个结果检测,‘levelWeights’将在最后阶段包含分类的确定性。这个值可以用来区分强分类和弱分类。 */
  49. CV_WRAP_AS(detectMultiScale3) void detectMultiScale( InputArray image,
  50. CV_OUT std::vector<Rect>& objects,
  51. CV_OUT std::vector<int>& rejectLevels,
  52. CV_OUT std::vector<double>& levelWeights,
  53. double scaleFactor = 1.1,
  54. int minNeighbors = 3, int flags = 0,
  55. Size minSize = Size(),
  56. Size maxSize = Size(),
  57. bool outputRejectLevels = false );
  58. 具体使用示例代码
  59. Mat img;
  60. vector<double> weights;
  61. vector<int> levels;
  62. vector<Rect> detections;
  63. CascadeClassifier model("/path/to/your/model.xml");
  64. model.detectMultiScale(img, detections, levels, weights, 1.1, 3, 0, Size(), Size(), true);
  65. cerr << "Detection " << detections[0] << " with weight " << weights[0] << endl;
  66. };

步骤

调用opencv训练好的分类器和自带的检测函数检测人脸人眼等的步骤简单直接:

加载分类器

分类器就是一个XML文件,该文件中会描述人体各个部位的Haar特征值。包括人脸、眼睛、嘴唇、侧脸、微笑、上半身、下半身、全身等等。分类器本来的位置是在*\opencv\sources\data(harr分类器,也有其他的可以用;也可以自己训练,traincascade的独立应用程序可以从一组样本中训练一系列增强分类器,位置在“\opencv\sources\apps”)
image.png
在右上图中,文件夹的名字“haarcascades”、“haarcascades_cuda”、“hogcascades”和“lbpcascades”分别表示通过“haar”、 “harr”、“hog”和“lbp”三种不同的特征而训练出的分类器:即各文件夹里的文件。
“haar”特征主要用于人脸检测,
“hog”特征主要用于行人检测,
“lbp”特征主要用于人脸识别。
打开“haarcascades_cuda”文件夹。
image.png
图中的XML文件即是我们人脸检测所需要的分类器文件。在实际使用中,推荐使用上图中被标记的“haarcascade_frontalface_alt2.xml”分类器文件,准确率和速度都比较好。

  1. /***************************************************************
  2. FunctionName: FaceDetection
  3. Purpose: 人脸检测,在图像上框出矩形/圆形人脸
  4. Parameter: img 待检测图像
  5. cascade 级联器
  6. Return: 无
  7. ****************************************************************/
  8. void FaceDetection(Mat& img, CascadeClassifier cascade)
  9. {
  10. Mat gray;
  11. cvtColor(img, gray, COLOR_BGR2GRAY);
  12. vector<Rect> rect;
  13. cascade.detectMultiScale(gray, rect, 1.1, 2, 0|CV_HAL_CMP_GE, Size(100, 100), Size(500, 500));
  14. //cout << "检测到的人脸个数" << rect.size() << endl;
  15. for (int i = 0; i < rect.size(); i++)
  16. {
  17. //Point center;
  18. //int radius;
  19. //center.x = cvRound((rect[i].x + rect[i].width * 0.5));
  20. //center.y = cvRound((rect[i].y + rect[i].height * 0.5));
  21. //radius = cvRound((rect[i].width + rect[i].height) * 0.25);
  22. //circle(img, center, radius, Scalar(255, 255, 255), 2);
  23. rectangle(img, rect[i], Scalar(255, 255, 255), 2);
  24. }
  25. }
  26. // OpenCV includes
  27. #include<opencv2\opencv.hpp>
  28. #include<iostream>
  29. #include<vector>
  30. #include <string>
  31. using namespace cv;
  32. using namespace std;
  33. int main(int argc, char* argv[])
  34. {
  35. Mat camera;
  36. CascadeClassifier cascade;
  37. string faceCascadeName = "D:/OpenCV/opencv/build/etc/haarcascades/haarcascade_frontalface_alt.xml";
  38. cascade.load(faceCascadeName);
  39. VideoCapture cap(0);
  40. if (!cap.isOpened())
  41. {
  42. cout << "No video!" << endl;
  43. return -1;
  44. }
  45. while (true)
  46. {
  47. cap >> camera;
  48. FaceDetection(camera, cascade);
  49. imshow("camera", camera);
  50. if (waitKey(10) == 27)
  51. {
  52. break;
  53. }
  54. }
  55. cap.release();
  56. destroyAllWindows();
  57. return 0;
  58. }

在检测时,先导入训练好的参数文件,其中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. 训练。

附:人脸识别+人眼识别代码

  1. string harr_xmlPath = "D:\\OpenCV\\opencv\\build\\etc\\haarcascades\\haarcascade_frontalface_default.xml";
  2. string lbp_xmlPath = "D:\\OpenCV\\opencv\\sources\\data\\haarcascades_cuda\\haarcascade_eye.xml";
  3. CascadeClassifier face_cascade;
  4. CascadeClassifier eyes_cascade;
  5. void detectAndDisplay(Mat &dispFace);
  6. /*--------------main-----------------*/
  7. int main()
  8. {
  9. system("color F0");
  10. VideoCapture cam(0);
  11. if (!face_cascade.load(harr_xmlPath))//也可用Haar分类器
  12. {
  13. printf("人脸检测器加载失败\n");
  14. return -1;
  15. }
  16. if (!eyes_cascade.load(lbp_xmlPath))
  17. {
  18. printf("人眼检测器加载失败\n");
  19. return -1;
  20. };
  21. while (true)
  22. {
  23. Mat frame;
  24. cam >> frame;
  25. detectAndDisplay(frame);
  26. imshow("camera", frame);
  27. if (waitKey(10) == 27)
  28. {
  29. break;
  30. }
  31. }
  32. waitKey(0);
  33. return 0;
  34. }
  35. /*-------------函数实现----------------*/
  36. void detectAndDisplay(Mat &dispFace)
  37. {
  38. //定义变量
  39. std::vector<Rect> faces;
  40. std::vector<Rect>eyes;
  41. Mat srcFace, grayFace, eqlHistFace;
  42. cvtColor(dispFace, grayFace, COLOR_BGR2GRAY);
  43. equalizeHist(grayFace, eqlHistFace); //直方图均衡化
  44. //人脸检测
  45. face_cascade.detectMultiScale(eqlHistFace, faces, 1.1, 3, 0 | CV_HAL_CMP_GE, Size(100, 100));
  46. //增大第四个参数可以提高检测精度,但也可能会造成遗漏
  47. //人脸尺寸minSize和maxSize,关键参数,自行设定,随图片尺寸有很大关系,
  48. for (unsigned int i = 0; i < faces.size(); i++)
  49. {
  50. //用蓝色椭圆标记检测到的人脸
  51. Point center(faces[i].x + faces[i].width / 2, faces[i].y + faces[i].height / 2);
  52. ellipse(dispFace, center, Size(faces[i].width / 2, faces[i].height * 65 / 100), 0, 0, 360, Scalar(255, 0, 0), 2, 8, 0);
  53. //人眼检测,提取的子矩阵指定为矩形
  54. Mat faceROI = eqlHistFace(faces[i]);
  55. eyes_cascade.detectMultiScale(faceROI, eyes, 1.2, 3, 0 | CV_HAL_CMP_GE, Size(15, 15), Size(80, 80));
  56. //用绿色圆标记检测到的人眼
  57. for (unsigned int j = 0; j < eyes.size(); j++)
  58. {
  59. Point center(faces[i].x + eyes[j].x + eyes[j].width / 2, faces[i].y + eyes[j].y + eyes[j].height / 2);
  60. circle(dispFace, center, 2, Scalar(0, 255, 0), 2);
  61. }
  62. }
  63. }


image.png