这学期上《数字图像处理》这门课,然后想做一个人脸识别系统,从博客和相关书籍中看到OpenCV实现人脸识别系统十分简单。下面就OpenCV的安装及实现人脸识别功能进行说明。由于时间限制,本程序各个步骤未封装成函数,后续更新将把每步更新成函数。本人水平有限,如有错误,欢迎改正。
参考书籍《深入理解OpenCV—实用计算机视觉项目解析》
我是在Ubuntu 18.04环境下安装OpenCV库的,看许多教程都是使用OpenCV+VS开发环境,我这里使用的是Qt+OpenCV进行开发。每个人电脑不同,所以安装时遇到的问题也就各种各样。第一部分仅做参考。
1.安装OpenCV和Cmake各种包
2.解压OpenCV和OpenCV_contrib文件。(其中人脸识别需要用到opencv_contrib中的face包,opencv包内含有人脸检测的文件)
3.在opencv中创建build文件夹,安装cmake,将opencv_contrib包链接起来。其shell命令为
cmake -D CMAKE_BUILD_TYPE=Release -D CMAKE_INSTALL_PREFIX=/usr/local -D OPENCV_EXTRA_MODULES_PATH=/media/antoniodc/Linux_File/opencv_contrib/modules ..
如果显示不存在,将-D中间的空格删除。/usr/local为安装的目的文件夹。/media/antoniodc/Linux_File/opencv_contrib/modules …为我contrib包的位置,根据每个人电脑情况来做。勿忘最后空格加…
4.make成功后,配置环境变量。
其中引用的头文件为
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "QDebug"
人脸检测是在图像中定位人脸区域的过程,这一步不关心人是谁,只是关心是不是人脸。
读入图像—>灰度图像转换—>直方图均衡化—>分类器进行对象检测
(1)当配置好OpenCV库与Qt Creater开发环境后,调用OpenCV的库函数,调用cv::Mat image = cv::imread(“1.jpg”, cv::IMREAD_COLOR);打开图片,其中:IMREAD_COLOR为图片的通道描述,将打开的图片以RGB三通道的方式读取
(2)灰度图像转换
灰度图像转换一方面是为了简化矩阵,增强运算速度,一方面是OpenCV中简单的识别算法对于颜色的依赖性不强。
(3)直方图均衡化
使用直方图均衡化改善图像对比图和亮度
(4)分类器
OpenCV提供了对象检测器,通过LBP的特征检测器可以能通过训练从大的图像集中找到人脸,这些图像存在于XML文件中。一般情况下图像分类检测器通常至少使用1000个独特的人脸图像和10000个非人脸图像作为训练才能产生训练数据。OpenCV自带训练好的额LBP检测器供用户使用,只要加载不同级联的分类器的XML文件给对象检测器就可以检测正面人脸、轮廓的面孔,眼睛或鼻子。为了便于后面的人脸图像预处理。在这里检测出人脸和人眼。加载OpenCV给定的XML数据集,使用矩形窗标记出人脸和眼睛。其中人脸的数据集在其中haarcascade_frontalface_alt.xml文件中,由于个人佩戴眼镜,所以选择眼睛所在的数据集为haarcascade_eye_tree_eyeglasses.xml,打开数据集会发现大量训练好的数据。检测时是灰色图像检测,我最终在彩色图像上显示效果图如下所示。
人脸检测代码
//step 1 -----read picture
cv::Mat printImg;
cv::Mat image = cv::imread("3.jpg", cv::IMREAD_COLOR); //read picture
if(image.empty())
qDebug()<<"init photo is empty";
resize(image,printImg,Size(600,800));
//cv::imshow("1",printImg);
cv::Mat image_gray;
cv::Mat Myface;
//step 2 -----convert gray image
cv::cvtColor(image, image_gray,cv::COLOR_BGR2GRAY);
resize(image_gray,printImg,Size(600,800));
//cv::imshow("2",printImg);
//step 3 ------equalizeHis
cv::equalizeHist(image_gray, image_gray); //to improve contrast ratio and brightness
resize(image_gray,printImg,Size(600,800));
//cv::imshow("3",printImg);
//step 4-----detection face
cv::CascadeClassifier face_cascade;
if (!face_cascade.load("haarcascade_frontalface_alt.xml")) //需要将xml文档放在自己指定的路径下
{
qDebug()<< "Load haarcascade_face.xml failed!";
}
std::vector faceRect;
face_cascade.detectMultiScale(image_gray, faceRect, 1.1, 3, 0 | CASCADE_SCALE_IMAGE, Size(30, 30)); //detect face
//if you only want get one face pelase set CASCADE_FIND_BIGGEST_OBJECT |CASCADE_DO_ROUGH_SEARCH;
for (size_t i = 0; i < faceRect.size(); i++)
{
cv::Mat faceROI = image(faceRect[i]);
if(faceROI.cols > 100)
{
resize(faceROI, Myface, Size(500, 500)); //调整图像大小为500 * 500
//imshow("4", Myface);
}
rectangle(image, faceRect[i], Scalar(0, 0, 255)); //用矩形画出检测到的位置
}
//cv::imshow("5",image); //show the image of detect face
脸预处理是调整人脸图像,使其看起来更加清楚,也更加符合人识别人脸的过程。
人眼检测对于人脸的预处理非常有用,对于人脸而言,面部表情、光照条件、摄像机的属性、与相机的距离等都会发生变化,但是人眼是水平的,并且对称分布在人脸上,两只眼睛在人脸的位置和大小是相当标准的。当人脸检测器将别的对象当成人脸时,可以通过眼部检测丢弃这种误判。人脸检测完成后,截取出人脸所在的区域,为了保持人脸在水平线上,将检测到眼睛中心位置的x为准线,将图像进行旋转和平移操作,以使眼睛能被对齐,保证训练图像和测试图像在同一位置。由于人脸识别的特征都在虹膜、鼻翼、嘴角等面像五官轮廓的大小、位置、距离等属性。对于截取的人脸图像在经过删除额头、下巴、耳朵和北京的操作,对于图像中含有噪声的影响,使用双边滤波器进行平滑操作减少噪声。最后使用椭圆掩码将图像中剩余的头发和人脸的背景去掉。
检测人眼代码为
cv::CascadeClassifier eye_cascade;
if (!eye_cascade.load("haarcascade_eye_tree_eyeglasses.xml")) //wear glass
{
qDebug()<< "Load haarcascade_face.xml failed!";
}
std::vector eyeRect;
eye_cascade.detectMultiScale(Myface, eyeRect, 1.1, 4, 0 | CASCADE_SCALE_IMAGE, Size(30, 30)); //detect eye //4 depends the correct rate of eye,the num is bigger,the correct rate is higher
int eye_num = 0;
cv::Point Myeye[2]; //eye center
for (size_t i = 0; i < eyeRect.size(); i++)
{
rectangle(Myface, eyeRect[i], Scalar(0, 255, 255)); //用矩形画出检测到的位置
Myeye[eye_num] =getCenterPoint(eyeRect[i]);
cv::circle(Myface,Myeye[eye_num],4,cv::Scalar(255,0,255));
eye_num = eye_num+1;
}
resize(Myface,printImg,Size(500,500));
//cv::imshow("6",printImg);
找到正方形中心代码为
cv::Point getCenterPoint(Rect rect)
{
cv::Point cpt;
cpt.x = rect.x + cvRound(rect.width/2.0);
cpt.y = rect.y + cvRound(rect.height/2.0);
return cpt;
}
人脸预处理其步骤效果如图
识别出人眼截取掉额头和下巴信息,对人脸进行平滑滤波。
处理代码如下
cv::Point2f eyesCenter;
eyesCenter.x = (Myeye[0].x+Myeye[1].x)*0.5f;
eyesCenter.y = (Myeye[0].y+Myeye[1].y)*0.5f;
double dy = (Myeye[1].y - Myeye[0].y);
double dx = (Myeye[1].x - Myeye[0].x);
double len = sqrt(dx*dx + dy*dy);
double angle=atan2(dy,dx)*180.0/CV_PI;
const double DESIRED_LEFT_EYE_X = 0.16;
const double DESIRED_LEFT_EYE_Y = 0.14;
const double DESIRED_RIGHT_EYE_X = (1.0f - 0.16);
const int DESIRED_FACE_WIDTH = 150;
const int DESIRED_FACE_HEIGHT= 150;
double desiredLen = (DESIRED_RIGHT_EYE_X - 0.16);
double scale = desiredLen * DESIRED_FACE_WIDTH /len;
cv::Mat rot_mat = getRotationMatrix2D(eyesCenter,angle,scale);
double ex = DESIRED_FACE_WIDTH * 0.5f - eyesCenter.x;
double ey = DESIRED_FACE_HEIGHT* DESIRED_LEFT_EYE_Y - eyesCenter.y;
rot_mat.at(0,2) += ex;
rot_mat.at(1,2) += ey;
cv::Mat warped = Mat(DESIRED_FACE_HEIGHT,DESIRED_FACE_WIDTH,CV_8U,Scalar(128));
warpAffine(Myface,warped,rot_mat,warped.size());
cv::Mat Smoothing_Myface;
bilateralFilter(warped,Smoothing_Myface,0,20.0,2.0);
resize(Smoothing_Myface,printImg,Size(500,500));
//cv::imshow("smoothing processing",printImg); //smoothing processing
cv::Mat mask = Mat(warped.size(),CV_8UC1,Scalar(255));
double dw = DESIRED_FACE_WIDTH;
double dh = DESIRED_FACE_HEIGHT;
Point faceCenter = Point(cvRound(dw*0.5),cvRound(dh*0.4));
Size size = Size(cvRound(dw*0.5),cvRound(dh*0.8));
ellipse(mask,faceCenter,size,0,0,360,Scalar(0),cv::FILLED);
Smoothing_Myface.setTo(Scalar(0),mask);
resize(Smoothing_Myface,printImg,Size(500,500));
cv::Mat preprocess_IMG;
resize(Smoothing_Myface,preprocess_IMG,Size(128,128));
cv::cvtColor(preprocess_IMG,preprocess_IMG,cv::COLOR_BGR2GRAY);
resize(mask,printImg,Size(500,500));
//cv::imshow("mask",printImg); //add mask processing
这里用了OpenCV中PCA分类器。PCA通过方差信息实现降维操作。
其流程为:导入样本图片---->增加标签----->人脸分类器训练----->保存数据------>识别器预测
代码为
//step 1------>input sample
cv::Mat src_1 = imread("./cuiandong/1.JPG",cv::IMREAD_GRAYSCALE); //Me
cv::Mat src_2 = imread("./cuiandong/C1.jpg",cv::IMREAD_GRAYSCALE);
cv::Mat src_3 = imread("./cuiandong/C2.jpg",cv::IMREAD_GRAYSCALE);
cv::Mat src_4 = imread("./cuiandong/C3.jpg",cv::IMREAD_GRAYSCALE);
cv::Mat src_5 = imread("./cuiandong/C4.jpg",cv::IMREAD_GRAYSCALE);
cv::Mat src_6 = imread("./cuiandong/2.jpeg",cv::IMREAD_GRAYSCALE); //Liu Shishi
cv::Mat src_7 = imread("./cuiandong/3.jpeg",cv::IMREAD_GRAYSCALE); //nomal man
cv::Mat src_8 = imread("./cuiandong/4.jpg",cv::IMREAD_GRAYSCALE); //Liu Yifei
cv::Mat src_9 = imread("./cuiandong/5.jpg",cv::IMREAD_GRAYSCALE); //nomal woman
//modify size
cv::resize(src_1,src_1,Size(128,128));
cv::resize(src_2,src_2,Size(128,128));
cv::resize(src_3,src_3,Size(128,128));
cv::resize(src_4,src_4,Size(128,128));
cv::resize(src_5,src_5,Size(128,128));
cv::resize(src_6,src_6,Size(128,128));
cv::resize(src_7,src_7,Size(128,128));
cv::resize(src_8,src_8,Size(128,128));
cv::resize(src_9,src_9,Size(128,128));
images.push_back(src_1);
images.push_back(src_2);
images.push_back(src_3);
images.push_back(src_4);
images.push_back(src_5);
images.push_back(src_6);
images.push_back(src_7);
images.push_back(src_8);
images.push_back(src_9);
//step 2 -----add labels
labels.push_back(1);
labels.push_back(1);
labels.push_back(1);
labels.push_back(1);
labels.push_back(1);
labels.push_back(2);
labels.push_back(3);
labels.push_back(4);
labels.push_back(5);
//step 3 -----faceRecognzer
Ptr fisherClass = FisherFaceRecognizer::create();
fisherClass->train(images,labels);
//step 4-----save xml
fisherClass->save("fisherClass.xml");
//step 5-----predict
int faceResult = fisherClass->predict(preprocess_IMG);
qDebug()<<"the face result is "<