用opencv将普通视频转换为带人脸检测效果的视频

本文详细介绍如何使用OpenCV库实现视频中的人脸检测,包括模型选择、数据集加载、视频帧处理及结果展示,同时分享了代码实现细节和检测效果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近几年AI比较火,而AI中人脸检测算是比较基础的部分。但是人脸检测很多模型以及训练的数据集其实是人脸检测准确率比较低的,所以将一个普通视频转换为带人脸检测效果的视频可以校验模型以及数据集的人脸识别准确率和漏检率等等。

好吧背景说的差不多,现在进入正题。

该怎么做呢?实际上不复杂,就是首先用opencv解析一个视频,拆分成每一帧,然后对每一帧进行人脸检测,检测完画上框,然后再把每一帧合并回去视频,搞定。

那么开始。

第一步,下载opencv,编译出库文件然后使用。很多人觉得编译麻烦,有现成的下载:

https://download.youkuaiyun.com/download/luohualiushui1/10930424

第二步,这次我使用windows环境的vs2015来弄,opencv版本4.0.0

第三步,首先opencv读取一个视频文件

	VideoCapture capture("E:\\video\\229728.mp4");
	if (!capture.isOpened())
		return -1;

好吧,opencv集成很完善,读取就是一行代码

第四步,拆分每一帧

while (!stop)
{
    if (!capture.read(frame)){
	break;
    }
     ... ...
      ... ...
}

第五步,人脸检测我使用的是opencv的模型和默认训练好的数据集

	cascadeName = "D:\\Virtual Machines\\share\\opencv-4.0.0\\data\\haarcascades\\haarcascade_frontalface_alt.xml";
	nestedCascadeName = "D:\\Virtual Machines\\share\\opencv-4.0.0\\data\\haarcascades\\haarcascade_eye_tree_eyeglasses.xml";
	scale = 5;
	tryflip = false;

	if (!nestedCascade.load(samples::findFileOrKeep(nestedCascadeName)))
		cerr << "WARNING: Could not load classifier cascade for nested objects" << endl;
	if (!cascade.load(samples::findFile(cascadeName)))
	{
		cerr << "ERROR: Could not load classifier cascade" << endl;
		return -1;
	}

第六步,检测和画框方法如下:

void detectAndDraw( Mat& img, CascadeClassifier& cascade,
                    CascadeClassifier& nestedCascade,
                    double scale, bool tryflip )
{
    double t = 0;
    vector<Rect> faces, faces2;
    const static Scalar colors[] =
    {
        Scalar(255,0,0),
        Scalar(255,128,0),
        Scalar(255,255,0),
        Scalar(0,255,0),
        Scalar(0,128,255),
        Scalar(0,255,255),
        Scalar(0,0,255),
        Scalar(255,0,255)
    };
    Mat gray, smallImg;

    cvtColor( img, gray, COLOR_BGR2GRAY );
    double fx = 1 / scale;
    resize( gray, smallImg, Size(), fx, fx, INTER_LINEAR_EXACT );
    equalizeHist( smallImg, smallImg );

    t = (double)getTickCount();
    cascade.detectMultiScale( smallImg, faces,
        1.1, 2, 0
        //|CASCADE_FIND_BIGGEST_OBJECT
        //|CASCADE_DO_ROUGH_SEARCH
        |CASCADE_SCALE_IMAGE,
        Size(30, 30) );
    if( tryflip )
    {
        flip(smallImg, smallImg, 1);
        cascade.detectMultiScale( smallImg, faces2,
                                 1.1, 2, 0
                                 //|CASCADE_FIND_BIGGEST_OBJECT
                                 //|CASCADE_DO_ROUGH_SEARCH
                                 |CASCADE_SCALE_IMAGE,
                                 Size(30, 30) );
        for( vector<Rect>::const_iterator r = faces2.begin(); r != faces2.end(); ++r )
        {
            faces.push_back(Rect(smallImg.cols - r->x - r->width, r->y, r->width, r->height));
        }
    }
    t = (double)getTickCount() - t;
    printf( "detection time = %g ms\n", t*1000/getTickFrequency());
    for ( size_t i = 0; i < faces.size(); i++ )
    {
        Rect r = faces[i];
        Mat smallImgROI;
        vector<Rect> nestedObjects;
        Point center;
        Scalar color = colors[i%8];
        int radius;

        double aspect_ratio = (double)r.width/r.height;
        if( 0.75 < aspect_ratio && aspect_ratio < 1.3 )
        {
            center.x = cvRound((r.x + r.width*0.5)*scale);
            center.y = cvRound((r.y + r.height*0.5)*scale);
            radius = cvRound((r.width + r.height)*0.25*scale);
            circle( img, center, radius, color, 3, 8, 0 );
        }
        else
            rectangle( img, Point(cvRound(r.x*scale), cvRound(r.y*scale)),
                       Point(cvRound((r.x + r.width-1)*scale), cvRound((r.y + r.height-1)*scale)),
                       color, 3, 8, 0);
        if( nestedCascade.empty() )
            continue;
        smallImgROI = smallImg( r );
        nestedCascade.detectMultiScale( smallImgROI, nestedObjects,
            1.1, 2, 0
            //|CASCADE_FIND_BIGGEST_OBJECT
            //|CASCADE_DO_ROUGH_SEARCH
            //|CASCADE_DO_CANNY_PRUNING
            |CASCADE_SCALE_IMAGE,
            Size(30, 30) );
        for ( size_t j = 0; j < nestedObjects.size(); j++ )
        {
            Rect nr = nestedObjects[j];
            center.x = cvRound((r.x + nr.x + nr.width*0.5)*scale);
            center.y = cvRound((r.y + nr.y + nr.height*0.5)*scale);
            radius = cvRound((nr.width + nr.height)*0.25*scale);
            circle( img, center, radius, color, 3, 8, 0 );
        }
    }
    //imshow( "result", img );
}

第七部,创建新的视频,输出视频的参数我设置为和输入参数一样

	const char* fileName = "E:\\video\\229728_1.avi";
	cv::VideoWriter out(fileName,
		CV_FOURCC('D', 'I', 'V', 'X'), 
		rate, 
		cv::Size(capture.get(CAP_PROP_FRAME_WIDTH), capture.get(CAP_PROP_FRAME_HEIGHT)),
		true 
	);
	while (!stop)
	{
		if (!capture.read(frame))
		{
			break;
		}
		detectAndDraw(frame, cascade, nestedCascade, scale, tryflip);
		out << frame;
		//string imgPath = "E:\\img\\";
		//imgPath += to_string(currentFrame);
		//imgPath += ".jpg";
		//imwrite(imgPath, frame);
	}

ok,搞定看看效果

视频过程中人脸被检测出来,不过也可以发现有一些误检和漏检的地方。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

路边草随风

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值