OpenCV 实践——人脸检测与人脸图像提取

    人脸对比是现在比较常用的功能,比如出租车司机人脸与司机驾照照片对比,门禁系统中进入者的人脸与人脸库中的人脸进行对比。要实现人脸对比,首先要实现的是人脸检测,在摄像头拍摄到的一张图片中,正确的检测到人脸的位置,并且将人脸提取出来。考虑到免费开源,OpenCV 就可以很好的实现这个功能。OpenCVC 在linux 的安装可以参考前面的博客:ubuntu 16.04 OpenCV3.2.0完全编译安装

(一)人脸检测的实现:

    下面的代码由OpenCVC实例改进而来,它能够实现人脸检测和人眼睛的检测,将检测到的结果用圆圈圈出来。代码如下:

/*============================================================================= 
#     FileName: facecheck.cpp
#         Desc: detect faces and eyes by opencv ,and then cut the face      
#       Author: Licaibiao 
#      Version:  
#   LastChange: 2017-10-31 
#      History: 
=============================================================================*/ 
#include <opencv2/objdetect/objdetect.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <iostream>
#include <stdio.h>

using namespace std;
using namespace cv;
 
#define CASCADENAME			"./modle/haarcascade_frontalface_alt2.xml"
#define NESTEDCASCADENAME	"./modle/haarcascade_eye_tree_eyeglasses.xml"
#define FACEPHOTO_FACENAME  "./image/result.jpg"
#define DETECT_IMAGE		"./image/001.jpg"

 
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;
	
	/* 因为用的是类haar特征,所以都是基于灰度图像的,这里要转换成灰度图像 */
    cvtColor( img, gray, COLOR_BGR2GRAY );
	
	/* 将图片缩小,加快检测速度 */
    double fx = 1 / scale;
	/* 将尺寸缩小到1/scale, 用线性插值 */
    resize( gray, smallImg, Size(), fx, fx, INTER_LINEAR );
	/* 直方图均衡 */
    equalizeHist( smallImg, smallImg );
 
	/* 用来计算算法执行时间 */
    t = (double)getTickCount();
	
	/*人脸检测
		smallImg:输入的原图
		faces	:表示检测到的人脸目标序列
		1.1		:每次图像尺寸减小的比例为1.1
		2		:每一个目标至少要被检测到3次才算是真的目标
		CV_HAAR_SCALE_IMAGE:表示不是缩放分类器来检测,而是缩放图像
		Size(30, 30) 目标的最大最小尺寸
	*/
    cascade.detectMultiScale( smallImg, faces, 1.1, 2, CASCADE_SCALE_IMAGE, Size(30, 30) );
    if( tryflip )
    {
        flip(smallImg, smallImg, 1);
        cascade.detectMultiScale( smallImg, faces2,1.1, 2, 0|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;
		
		/* 人脸长宽比例,在0.75-1.3 间画圆,其他范围画矩形 */
        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, cvPoint(cvRound(r.x*scale), cvRound(r.y*scale)),
				cvPoint(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, 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 );
        }
    }
	/* 显示图像 img */
    imshow( "result", img );
}
 
 
int main( int argc, const char** argv )
{
    Mat frame, image;
    bool tryflip;
    CascadeClassifier cascade, nestedCascade;
    double scale = 1.3;
 
	/* 加载分类器 */
    if ( !nestedCascade.load( NESTEDCASCADENAME ) )
	{
		cerr << "WARNING: Could not load classifier cascade for nested objects" << endl;
	}   
    if( !cascade.load( CASCADENAME ) )
    {
        cerr << "ERROR: Could not load classifier cascade" << endl;
        return -1;
    }
	
	/* 加载图片 */
	image = imread(DETECT_IMAGE, 1 );
    if(image.empty()) 
	{
		cout << "Couldn't read iamge" << DETECT_IMAGE  <<  endl;
		
	}
	
    cout << "Detecting face(s) in " << DETECT_IMAGE << endl;
    
	/* 检测人脸及眼睛并画出检测到的区域 */
	if( !image.empty() )
    {
        detectAndDraw( image, cascade, nestedCascade, scale, tryflip );
        waitKey(0);
    }
    return 0;
}

要编译运行上面的代码需要注意几个地方
(1)haarcascade_frontalface_alt2.xml 和haarcascade_eye_tree_eyeglasses.xml 是opencv 中提供的文件。
(2)由于opencv源码安装的时候需要下载一些文件,网速慢的地方安装容易失败。如果使用sudo apt-get install libopencv-dev python-opencv 命令直接安装的,编译的时候需要带参数`pkg-config --cflags --libs opencv`

编译运行结果:

licaibiao@ubuntu:~/OpenCV/FaceTest$ make DetectDraw 
g++ FaceDetect_Draw.cpp -o TestDetectDraw `pkg-config --cflags --libs opencv` 
licaibiao@ubuntu:~/OpenCV/FaceTest$ ls
FaceCheck.cpp  FaceCheck_Draw.cpp  FaceDetect.cpp  FaceDetect_Draw.cpp  image  Makefile  modle  out  TestDetectDraw
licaibiao@ubuntu:~/OpenCV/FaceTest$ ./TestDetectDraw 
Detecting face(s) in ./image/001.jpg
detection time = 423.166 ms

实际检测效果如下:

上面是比较正常的一张图片,人脸明显,图片背景比较的干净,所以检测出来的人脸和人的眼睛都是正确的。尝试换一张比较复杂的图片,它的检测功能就没有那么的好了。同样的代码,同样的训练文件,结果如下:

    从上图可以看出可以检测到人脸,但没有全部检测出来,同时还有好几个是误判的。从这里可以看出,使用Opencv原始的训练文件做人脸检测,可以检测到人脸,但是检测的效果并不是很好。

    在实际的使用OpenCVC做人脸检测的时候,为了提高识别率,可以通过检测到人脸再检测判断是否有人眼睛来加以判断。如果是在嵌入式设备中,还是不建议使用OpenCV来做人脸检测和人脸抠图这样的处理。一是OpenCV依赖的文件比较多,需要比较大的存储空间来放库文件和训练文件。二十它的检测效果并不是很好。

    接下来将介绍MTCNN,这也是一套开源的人脸识别代码,全用C和C++编写,代码量小,检测效果好,下一篇介绍。

    这一张的测试代码可以在这里下载:OpenCV测试代码

    里面有一套在可以在海思开发板上跑的和一些在PC机测试测代码,比较乱没有整理,有需要的可以参考一下,使用代码前需要在电脑上正确安装OpenCV。

licaibiao@ubuntu:~/OpenCV$ tree -L 2
.
├── arm_test
│   ├── 001.jpg
│   ├── example.cpp
│   ├── haarcascade_eye_tree_eyeglasses.xml
│   ├── haarcascade_frontalface_alt2.xml
│   ├── include
│   ├── lib
│   ├── Makefile
│   ├── OpencvFaceAPI.cpp
│   ├── OpencvFaceAPI.h
│   └── OpencvTestMain.cpp
├── face_test
│   ├── detect.cpp
│   ├── example_1.cpp
│   ├── example.cpp
│   ├── example.cpp_ok
│   ├── face_detect1.cpp
│   ├── face_detect.cpp
│   ├── facedetect.cpp
│   ├── haarcascade_eye_tree_eyeglasses.xml
│   ├── haarcascade_eye.xml
│   ├── haarcascade_frontalface_alt2.xml
│   ├── haarcascade_frontalface_default.xml
│   ├── Makefile
│   ├── run.sh
│   └── test
└── FaceTest
    ├── FaceCheck.cpp
    ├── FaceCheck_Draw.cpp
    ├── FaceDetect.cpp
    ├── FaceDetect_Draw.cpp
    ├── image
    ├── Makefile
    ├── modle
    ├── out
    └── TestDetectDraw

8 directories, 28 files
licaibiao@ubuntu:~/OpenCV$ 

-----------------------------------------------2022.08.28-----------------------------------------------

 新的文章内容和附件工程文件

已更新在博客首页和:

公众号 : liwen01

为了避免被夹,只能字体中间画横线了

-----------------------------------------------2022.08.28-----------------------------------------------

  • 8
    点赞
  • 140
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 9
    评论
可以使用Python中的OpenCV和Dlib库来实现自动抠图人脸的功能。以下是一个简单的示例代码: ```python import cv2 import dlib # 加载Haar级联分类器,用于检测人脸 face_cascade = cv2.CascadeClassifier('haarcascade_frontalface_default.xml') # 加载Dlib的人脸检测器和面部标志检测器 detector = dlib.get_frontal_face_detector() predictor = dlib.shape_predictor('shape_predictor_68_face_landmarks.dat') # 加载图像 img = cv2.imread('input.jpg') # 转换为灰度图像 gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 使用Haar级联分类器检测人脸 faces = face_cascade.detectMultiScale(gray, 1.3, 5) # 对每个检测到的人脸进行处理 for (x, y, w, h) in faces: # 使用Dlib人脸检测检测面部标志 rect = dlib.rectangle(int(x), int(y), int(x + w), int(y + h)) landmarks = predictor(gray, rect) # 提取面部标志中的左眼、右眼、鼻子和嘴巴的坐标 left_eye = (landmarks.part(36).x, landmarks.part(36).y, landmarks.part(39).x, landmarks.part(39).y) right_eye = (landmarks.part(42).x, landmarks.part(42).y, landmarks.part(45).x, landmarks.part(45).y) nose = (landmarks.part(27).x, landmarks.part(27).y, landmarks.part(35).x, landmarks.part(35).y) mouth = (landmarks.part(48).x, landmarks.part(48).y, landmarks.part(54).x, landmarks.part(54).y) # 根据面部标志的坐标抠出人脸 mask = np.zeros_like(gray) cv2.fillPoly(mask, [np.array([left_eye, right_eye, nose, mouth], dtype=np.int32)], 255) masked_img = cv2.bitwise_and(img, img, mask=mask) # 显示结果 cv2.imshow('Input', img) cv2.imshow('Output', masked_img) cv2.waitKey(0) ``` 在上面的代码中,我们首先使用Haar级联分类器检测人脸,然后使用Dlib人脸检测器和面部标志检测提取面部标志的坐标。最后,我们根据面部标志的坐标抠出人脸,并显示结果。注意,需要先安装OpenCV和Dlib库,并下载相应的分类器和模型文件。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

li_wen01

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

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

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

打赏作者

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

抵扣说明:

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

余额充值