概述:
级联分类器原理
视频文件与相机中帧图像处理
级联分类器的使用
CascadeClassifier与XML文件结构
视频中人脸和眼睛的检测与跟踪,人脸检测比检测到后的人脸跟踪算法要耗时的
级联分类器的训练(HAAR/LBP)
Haar 特征与LBP特征
级联分类器原理, 不光是人脸检测,只要是有明显特征的对象,都能通过此方式训练,检测
Viola和Jones – 2001在CVPR提出
一种实时对象(人脸)检测框架
算法,训练速度非常慢,检测速度非常快
5000个正向人脸样本与300万个非人脸负样本数据
精准度非常高
OpenCV中级联分类器使用- CascadeClassifier
AdaBoost(自适应分类器)
弱分类器 – weak classifier = Feature
强分类器 – 多个弱分类器的线性组合
级联分类器 – 多个强分类器组合
代码: 相机预览中检测人脸
#include "../common/common.hpp"
static CascadeClassifier face_cascader; // 级联分类检测器
static CascadeClassifier eye_cascader;
static String facefile = "project/workspace_vs/OpenCV310Sources_contrib/install/etc/haarcascades/haarcascade_frontalface_alt.xml"; // 训练好了的人脸数据
static String eyefile = "project/workspace_vs/OpenCV310Sources_contrib/install/etc/haarcascades/haarcascade_eye.xml";
void main(int argc, char** argv)
{
if (!face_cascader.load(getCVImagesPath(facefile))) // 加载训练的数据
{
printf("could not load face feature data...\n");
}
if (!eye_cascader.load(getCVImagesPath(eyefile)))
{
printf("could not load eye feature data...\n");
}
VideoCapture capture(0); // 相机预览,参数 0 是只有一个摄像头的时候传,更具体的去看源代码注释
Mat frame; // 每一帧图像
Mat gray;
vector<Rect> faces; // 保存检测到的人脸
vector<Rect> eyes; // 保存检测到的人眼
while (capture.read(frame)) // 从相机中读取每一帧数据
{
cvtColor(frame, gray, COLOR_BGR2GRAY);
equalizeHist(gray, gray); // 直方图均衡化,提升对比度,提升图像特征提取的准确率
face_cascader.detectMultiScale(gray, faces, 1.2, 3, 0, Size(30, 30)); // 在不同尺度空间检测,尺度空间比例不要设置太大,否则可能漏掉脸
for (size_t t = 0; t < faces.size(); t++)
{
Rect roi;
roi.x = faces[static_cast<int>(t)].x;
roi.y = faces[static_cast<int>(t)].y;
roi.width = faces[static_cast<int>(t)].width;
roi.height = faces[static_cast<int>(t)].height / 2; // 眼睛在人脸的上半部分
Mat faceROI = frame(roi); // 在每帧数据中截取人脸框的上半部分,再做人眼检测,减少检测数据,提高速度
eye_cascader.detectMultiScale(faceROI, eyes, 1.2, 3, 0, Size(20, 20)); // 检测人眼
for (size_t k = 0; k < eyes.size(); k++)
{
Rect rect; // 由于是在人脸框内检测人眼,所以需要在整帧数据上重新定位人眼眶的位置
rect.x = faces[static_cast<int>(t)].x + eyes[k].x;
rect.y = faces[static_cast<int>(t)].y + eyes[k].y;
rect.width = eyes[k].width;
rect.height = eyes[k].height;
rectangle(frame, rect, Scalar(0, 255, 0), 2, 8, 0);
}
rectangle(frame, faces[static_cast<int>(t)], Scalar(0, 0, 255), 2, 8, 0); // 绘制人脸框
}
imshow("camera4-2", frame);
if (waitKey(30) == 27) // 间隔30毫秒获取一帧数据,esc推出
{
break;
}
}
waitKey(0);
}
android代码: 图像中检测人脸
@BindView(R.id.iv_cv4_2_input) ImageView mInputIv;
@BindView(R.id.iv_cv4_2_equalize) ImageView mEqualizeIv;
@BindView(R.id.iv_cv4_2_detect) ImageView mDetectIv;
private Bitmap mInputBmp;
private Bitmap mEqualizeBmp;
private Bitmap mDetectBmp;
private Mat mInputMat = new Mat();
private Mat mGrayMat = new Mat();
private Mat mEqualizeMat = new Mat();
private Mat mDetectMat = new Mat();
private CascadeClassifier mFaceCascader = new CascadeClassifier();// 级联分类检测器
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_cv4_2);
mUnbinder = ButterKnife.bind(this);
//input
mInputBmp = CV310Utils.getBitmapFromAssets(this, "opencv/images/test1_3.png");
mInputIv.setImageBitmap(mInputBmp);
Utils.bitmapToMat(mInputBmp, mInputMat);
initBitmaps(mInputBmp);
//转灰度,直方图均衡化
Imgproc.cvtColor(mInputMat, mGrayMat, Imgproc.COLOR_BGR2GRAY);
long start = System.currentTimeMillis();
Imgproc.equalizeHist(mGrayMat, mEqualizeMat); // 直方图均衡化,提升对比度,提升图像特征提取的准确率
LogUtils.d("mydebug---", "1 time : "+(System.currentTimeMillis()-start)); // 0
CV310Utils.mat2bitmapAndShowInIv(mEqualizeMat, mEqualizeBmp, mEqualizeIv);
//人脸检测
String sdPath = CV310Utils.asset2SDcard(this, "opencv/etc/haarcascades/haarcascade_frontalface_alt.xml",
SDCardStoragePath.DEFAULT_OPENCV_CASCADE_HAAR, "haarcascade_frontalface_alt.xml");// 训练好了的人脸数据,从assets转存到sdcard
boolean isLoad = mFaceCascader.load(sdPath);
LogUtils.d("mydebug---", "isLoad="+isLoad+", sdPath="+sdPath);
if (isLoad) {
mInputMat.copyTo(mDetectMat);
MatOfRect faces = new MatOfRect();
start = System.currentTimeMillis();
// 在不同尺度空间检测,尺度空间比例不要设置太大,否则可能漏掉脸
mFaceCascader.detectMultiScale(mEqualizeMat, faces, 1.1, 3, 0, new Size(24, 24), new Size());
LogUtils.d("mydebug---", "2 time : "+(System.currentTimeMillis()-start)); // 96 毫秒,擦 这么快
List<Rect> facesRect = faces.toList();
LogUtils.d("mydebug---", "faces.size="+facesRect.size()); // 1
for (int i = 0; i < facesRect.size(); i++) {
//Rect rect = new Rect(200, 100, 300, 300);//起始位置x、y,宽,高
Rect rect = facesRect.get(i);
Scalar color = new Scalar(255, 0, 0, 255);
//OpenCV的坐标系,原点在屏幕左上角,x朝右正,y朝下正,参数:Mat,矩形坐上点 右下点
Imgproc.rectangle(mDetectMat, new Point(rect.x, rect.y), new Point(rect.x+rect.width, rect.y+rect.height),
color, 2, 8, 0);//绘制矩形到Mat,自带圆角。。
}
CV310Utils.mat2bitmapAndShowInIv(mDetectMat, mDetectBmp, mDetectIv);
}
}
/*初始化bitmap*/
private void initBitmaps(Bitmap bmp){
mEqualizeBmp = Bitmap.createBitmap(bmp.getWidth(), bmp.getHeight(), Bitmap.Config.RGB_565);
mDetectBmp = Bitmap.createBitmap(bmp.getWidth(), bmp.getHeight(), Bitmap.Config.RGB_565);
}