内涵:OPENCV之人眼检测

本篇博客主要是对前段时间数字图像课程大作业-疲劳检测所做工作的一次总结整理。主要涉及到的内容有1、基于图片的人脸、人眼检测;2、利用OPENCV实现本地视频与图片帧之间的相互转换;3、基于本地视频的人脸、人眼检测;4、操作笔记本摄像头,实现人脸、人眼检测。

源代码及haar检测器下载见http://download.csdn.net/detail/u011345885/9444335


1、基于图片的人脸、人眼检测
原理: OpenCV利用样本的Haar特征进行分类器训练,得到级联boosted分类器(CascadeClassification),可以检测图片中的眼睛(还支持的有人脸、嘴、鼻子、身体)。
具体操作步骤: 1、将分类器.xml文件放到源程序工程下与你的代码文件处于同一文件(这样在代码中定义路径时,可以免去其他硬盘路径,当然也可以放在任何位置,自己知道位置即可,在代码中做相应路径修改即可)。这些分类器.xml文件在opencv安装包文件下即可找到,以我的电脑为例:opencv-2.4.8->opencv->sources->data->haarcascades;也可以从此处下载:
然后就可以加载分类器进行,特征检测具体代码如下:

// face_detect.cpp : 定义控制台应用程序的入口点。
//

//#include "stdafx.h"

#include "opencv2/objdetect/objdetect.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/ml/ml.hpp"

#include <iostream>
#include <stdio.h>

using namespace std;
using namespace cv;

void detectAndDraw(Mat& img,
	CascadeClassifier& cascade, CascadeClassifier& nestedCascade,
	double scale);

String cascadeName = "./haarcascade_frontalface_alt2.xml";//人脸的训练数据
//String nestedCascadeName = "./haarcascade_eye_tree_eyeglasses.xml";//人眼的训练数据
String nestedCascadeName = "./haarcascade_eye.xml";//人眼的训练数据

int main(int argc, const char** argv)
{
	Mat image;
	CascadeClassifier cascade, nestedCascade;//创建级联分类器对象
	double scale = 1.3;

	//image = imread( "lena.jpg", 1 );//读入lena图片
	image = imread("2.jpg", 1);
	namedWindow("result", 1);//opencv2.0以后用namedWindow函数会自动销毁窗口

	if (!cascade.load(cascadeName))//从指定的文件目录中加载级联分类器
	{
		cerr << "ERROR: Could not load classifier cascade唉唉出错了" << endl;
		return 0;
	}

	if (!nestedCascade.load(nestedCascadeName))
	{
		cerr << "WARNING: Could not load classifier cascade for nested objects" << endl;
		return 0;
	}

	if (!image.empty())//读取图片数据不能为空
	{
		detectAndDraw(image, cascade, nestedCascade, scale);
		waitKey(0);
	}

	return 0;
}

void detectAndDraw(Mat& img,
	CascadeClassifier& cascade, CascadeClassifier& nestedCascade,
	double scale)
{
	int i = 0;
	double t = 0;
	vector<Rect> faces;
	const static Scalar colors[] = { CV_RGB(0, 0, 255),
		CV_RGB(0, 128, 255),
		CV_RGB(0, 255, 255),
		CV_RGB(0, 255, 0),
		CV_RGB(255, 128, 0),
		CV_RGB(255, 255, 0),
		CV_RGB(255, 0, 0),
		CV_RGB(255, 0, 255) };//用不同的颜色表示不同的人脸

	Mat gray, smallImg(cvRound(img.rows / scale), cvRound(img.cols / scale), CV_8UC1);//将图片缩小,加快检测速度

	cvtColor(img, gray, CV_BGR2GRAY);//因为用的是类haar特征,所以都是基于灰度图像的,这里要转换成灰度图像
	resize(gray, smallImg, smallImg.size(), 0, 0, INTER_LINEAR);//将尺寸缩小到1/scale,用线性插值
	equalizeHist(smallImg, smallImg);//直方图均衡

	t = (double)cvGetTickCount();//用来计算算法执行时间



	//检测人脸
	//detectMultiScale函数中smallImg表示的是要检测的输入图像为smallImg,faces表示检测到的人脸目标序列,1.1表示
	//每次图像尺寸减小的比例为1.1,2表示每一个目标至少要被检测到3次才算是真的目标(因为周围的像素和不同的窗口大
	//小都可以检测到人脸),CV_HAAR_SCALE_IMAGE表示不是缩放分类器来检测,而是缩放图像,Size(30, 30)为目标的
	//最小最大尺寸
	cascade.detectMultiScale(smallImg, faces,
		1.1, 2, 0
		//|CV_HAAR_FIND_BIGGEST_OBJECT
		//|CV_HAAR_DO_ROUGH_SEARCH
		| CV_HAAR_SCALE_IMAGE
		,
		Size(30, 30));

	t = (double)cvGetTickCount() - t;//相减为算法执行的时间
	printf("detection time = %g ms\n", t / ((double)cvGetTickFrequency()*1000.));
	for (vector<Rect>::const_iterator r = faces.begin(); r != faces.end(); r++, i++)
	{
		Mat smallImgROI;
		vector<Rect> nestedObjects;
		Point center;
		Scalar color = colors[i % 8];
		int radius;
		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);

		//检测人眼,在每幅人脸图上画出人眼
		if (nestedCascade.empty())
			continue;
		smallImgROI = smallImg(*r);

		//和上面的函数功能一样
		nestedCascade.detectMultiScale(smallImgROI, nestedObjects,
			1.1, 2, 0
			//|CV_HAAR_FIND_BIGGEST_OBJECT
			//|CV_HAAR_DO_ROUGH_SEARCH
			//|CV_HAAR_DO_CANNY_PRUNING
			| CV_HAAR_SCALE_IMAGE
			,
			Size(30, 30));
		for (vector<Rect>::const_iterator nr = nestedObjects.begin(); nr != nestedObjects.end(); nr++)
		{
			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);//将眼睛也画出来,和对应人脸的图形是一样的
		}
	}
	cv::imshow("result", img);
}

2.利用OPENCV实现本地视频与图片帧之间的相互转换
直接上代码:

// test3.cpp  
//  
// 该程序实现视频和图片的相互转换.  
// Image_to_video()函数将一组图片合成AVI视频文件.  
// Video_to_image()函数将AVI视频文件读入,将每一帧存储为jpg文件.  
//  
  
//#include "stdafx.h"
#include <stdlib.h>  
#include <stdio.h>  
#include <math.h>  
#include <cv.h>  
#include <highgui.h>  
#define NUM_FRAME 300 //只处理前300帧,根据视频帧数可修改  

void Video_to_image(char* filename)
{
	printf("------------- video to image ... ----------------n");
	//初始化一个视频文件捕捉器  
	CvCapture* capture = cvCaptureFromAVI(filename);
	//获取视频信息  
	cvQueryFrame(capture);
	int frameH = (int)cvGetCaptureProperty(capture, CV_CAP_PROP_FRAME_HEIGHT);
	int frameW = (int)cvGetCaptureProperty(capture, CV_CAP_PROP_FRAME_WIDTH);
	int fps = (int)cvGetCaptureProperty(capture, CV_CAP_PROP_FPS);
	int numFrames = (int)cvGetCaptureProperty(capture, CV_CAP_PROP_FRAME_COUNT);
	printf("tvideo height : %dntvideo width : %dntfps : %dntframe numbers : %dn", frameH, frameW, fps, numFrames);
	//定义和初始化变量  
	int i = 0;
	IplImage* img = 0;
	char image_name[13];

	cvNamedWindow("mainWin", CV_WINDOW_AUTOSIZE);
	//读取和显示  
	while (1)
	{

		img = cvQueryFrame(capture); //获取一帧图片  
		cvShowImage("mainWin", img); //将其显示  
		char key = cvWaitKey(20);

		sprintf(image_name, "%s%d%s", "image", ++i, ".jpg");//保存的图片名  

		cvSaveImage(image_name, img);   //保存一帧图片  

		if (i == NUM_FRAME) break;
	}
	cvReleaseCapture(&capture);
	cvDestroyWindow("mainWin");
}

void Image_to_video()
{
	int i = 0;
	IplImage* img = 0;
	char image_name[13];
	printf("------------- image to video ... ----------------n");
	//初始化视频编写器,参数根据实际视频文件修改  
	CvVideoWriter *writer = 0;
	int isColor = 1;
	int fps = 30; // or 25  
	int frameW = 400; // 744 for firewire cameras  
	int frameH = 240; // 480 for firewire cameras  
	writer = cvCreateVideoWriter("out.avi", CV_FOURCC('X', 'V', 'I', 'D'), fps, cvSize(frameW, frameH), isColor);
	printf("tvideo height : %dntvideo width : %dntfps : %dn", frameH, frameW, fps);
	//创建窗口  
	cvNamedWindow("mainWin", CV_WINDOW_AUTOSIZE);
	while (i<NUM_FRAME)
	{
		sprintf(image_name, "%s%d%s", "image", ++i, ".jpg");
		img = cvLoadImage(image_name);
		if (!img)
		{
			printf("Could not load image file...n");
			exit(0);
		}
		cvShowImage("mainWin", img);
		char key = cvWaitKey(20);
		cvWriteFrame(writer, img);
	}
	cvReleaseVideoWriter(&writer);
	cvDestroyWindow("mainWin");
}

int main(int argc, char *argv[])
{
	char filename[13] = "tree.avi";
	Video_to_image(filename); //视频转图片  
	Image_to_video();    //图片转视频  
	return 0;
}

3.基于本地视频的人脸、人眼检测,事实上是1和2的结合,将视频转换为一帧、一帧的图片帧,再进行人脸、人眼检测。

// face_detect.cpp : 定义控制台应用程序的入口点。  
//该程序是读取本地文件视频并识别视频中人脸和眼睛,目前已经成功

//#include "stdafx.h"  

#include "opencv2/objdetect/objdetect.hpp"  
#include "opencv2/highgui/highgui.hpp"  
#include "opencv2/imgproc/imgproc.hpp"  
#include "opencv2/ml/ml.hpp"  
#include "cv.h"

#include <iostream>  
#include <stdio.h>  

using namespace std;
using namespace cv;
static CvMemStorage* storage = 0;
void detectAndDraw(IplImage* img,
	CascadeClassifier& cascade, CascadeClassifier& nestedCascade,
	double scale);

String cascadeName = "./haarcascade_frontalface_alt2.xml";//人脸的训练数据  
//String nestedCascadeName = "./haarcascade_eye_tree_eyeglasses.xml";//人眼的训练数据  
String nestedCascadeName = "./haarcascade_eye.xml";//人眼的训练数据  
int eyesopen = 0;
int eyesclose = 0;

int main(int argc, const char** argv)
{
	CvCapture *capture = 0;
	IplImage *frame, *frame_copy = 0;
	int optlen = strlen("--cascade=");
	const char* input_name;
	Mat image;
	CascadeClassifier cascade, nestedCascade;//创建级联分类器对象  
	double scale = 4;
	if (!cascade.load(cascadeName))//从指定的文件目录中加载级联分类器  
	{
		cerr << "ERROR: Could not load classifier cascade唉唉出错了" << endl;
		return 0;
	}
	if (!nestedCascade.load(nestedCascadeName))
	{
		cerr << "WARNING: Could not load classifier cascade for nested objects" << endl;
		return 0;
	}
	storage = cvCreateMemStorage(0);
	cvNamedWindow("result", 1);
	//检测视频
	
	capture = cvCaptureFromAVI("tree.avi");
	if (capture)
	{
		for (;;)
		{
			if (!cvGrabFrame(capture))
				break;
			frame = cvRetrieveFrame(capture);
			if (!frame)
				break;
			if (!frame_copy)
				frame_copy = cvCreateImage(cvSize(frame->width, frame->height), IPL_DEPTH_8U, frame->nChannels);
			if (frame->origin == IPL_ORIGIN_TL)
				cvCopy(frame, frame_copy, 0);
			else
				cvFlip(frame, frame_copy, 0);
			IplImage *equ = cvCreateImage(cvGetSize(frame_copy), 8, 1);
			IplImage *gray = cvCreateImage(cvGetSize(frame_copy), 8, 1);
			cvCvtColor(frame_copy, gray, CV_BGR2GRAY);
			cvEqualizeHist(gray,equ);
			detectAndDraw(frame_copy, cascade, nestedCascade, scale);
			if (cvWaitKey(10) >= 0)
				break;

		}
		cvReleaseImage(&frame_copy);
		cvReleaseCapture(&capture);

	}

	//image = imread( "lena.jpg", 1 );//读入lena图片  
	//image = imread("lena.jpg", 1);
	//namedWindow("result", 1);//opencv2.0以后用namedWindow函数会自动销毁窗口  





	//if (!image.empty())//读取图片数据不能为空  
	//{
	//	detectAndDraw(image, cascade, nestedCascade, scale);
	//	waitKey(0);
	//}
	//cout << "eye close:" << eyesclose<<endl<<"eye open:"<<eyesopen<<endl;

	cvWaitKey(-1);
	return 0;
}

void detectAndDraw(IplImage* img1,
	CascadeClassifier& cascade, CascadeClassifier& nestedCascade,
	double scale)
{
	cv::Mat img(img1, 0);
	int i = 0;
	double t = 0;
	vector<Rect> faces;
	const static Scalar colors[] = { CV_RGB(0, 0, 255),
		CV_RGB(0, 128, 255),
		CV_RGB(0, 255, 255),
		CV_RGB(0, 255, 0),
		CV_RGB(255, 128, 0),
		CV_RGB(255, 255, 0),
		CV_RGB(255, 0, 0),
		CV_RGB(255, 0, 255) };//用不同的颜色表示不同的人脸  

	Mat gray, smallImg(cvRound(img.rows / scale), cvRound(img.cols / scale), CV_8UC1);//将图片缩小,加快检测速度  

	cvtColor(img, gray, CV_BGR2GRAY);//因为用的是类haar特征,所以都是基于灰度图像的,这里要转换成灰度图像  
	resize(gray, smallImg, smallImg.size(), 0, 0, INTER_LINEAR);//将尺寸缩小到1/scale,用线性插值  
	equalizeHist(smallImg, smallImg);//直方图均衡  

	t = (double)cvGetTickCount();//用来计算算法执行时间  


	//检测人脸  
	//detectMultiScale函数中smallImg表示的是要检测的输入图像为smallImg,faces表示检测到的人脸目标序列,1.1表示  
	//每次图像尺寸减小的比例为1.1,2表示每一个目标至少要被检测到3次才算是真的目标(因为周围的像素和不同的窗口大  
	//小都可以检测到人脸),CV_HAAR_SCALE_IMAGE表示不是缩放分类器来检测,而是缩放图像,Size(30, 30)为目标的  
	//最小最大尺寸  
	cascade.detectMultiScale(smallImg, faces,
		1.1, 2, 0
		//|CV_HAAR_FIND_BIGGEST_OBJECT  
		//|CV_HAAR_DO_ROUGH_SEARCH  
		| CV_HAAR_SCALE_IMAGE
		,
		Size(30, 30));

	t = (double)cvGetTickCount() - t;//相减为算法执行的时间  
	printf("detection time = %g ms\n", t / ((double)cvGetTickFrequency()*1000.));
	for (vector<Rect>::const_iterator r = faces.begin(); r != faces.end(); r++, i++)
	{
		Mat smallImgROI;
		vector<Rect> nestedObjects;
		Point center;
		Scalar color = colors[i % 8];
		int radius;
		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);

		//检测人眼,在每幅人脸图上画出人眼  
		if (nestedCascade.empty())
			continue;
	
		
		smallImgROI = smallImg(*r);

		//和上面的函数功能一样  
		nestedCascade.detectMultiScale(smallImgROI, nestedObjects,
			1.1, 2, 0
			//|CV_HAAR_FIND_BIGGEST_OBJECT  
			//|CV_HAAR_DO_ROUGH_SEARCH  
			//|CV_HAAR_DO_CANNY_PRUNING  
			| CV_HAAR_SCALE_IMAGE
			,
			Size(30, 30));
		if (nestedObjects.empty())
			eyesclose++;
		else
			eyesopen++;
		for (vector<Rect>::const_iterator nr = nestedObjects.begin(); nr != nestedObjects.end(); nr++)
		{
			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);//将眼睛也画出来,和对应人脸的图形是一样的  
			//eyesopen++;
		}
	}
	cv::imshow("result", img);
}

4.opencv操作摄像头实现人脸和人眼的检测
//#include “stdafx.h”
#include
#include <opencv2/imgproc/imgproc.hpp> // Gaussian Blur
#include <opencv2/core/core.hpp> // Basic OpenCV structures (cv::Mat, Scalar)
#include <opencv2/highgui/highgui.hpp> // OpenCV window I/O
#include “opencv2/objdetect/objdetect.hpp”//人脸识别的接口

using namespace cv;//必须加入,否则无法检找到OPENCV的各个函数
using namespace std;

string face_cascade_name = “haarcascade_frontalface_alt2.xml”;
string eye_cascade_name = “./haarcascade_eye.xml”;
CascadeClassifier face_cascade;
CascadeClassifier eye_cascade;
string window_name = “疲劳检测”;
double scale = 2;//在检测本地视频时发现该值为4时,效果比较好,但在摄像头实时视频中取2值比较好
void detectAndDisplay(Mat frame){
std::vector faces;
std::vector eyes;
Mat frame_gray,smallframe_gray(cvRound(frame.rows/scale),cvRound(frame.cols/scale),CV_8UC1);//将图片缩小,加快检测速度

cvtColor(frame, frame_gray, CV_BGR2GRAY);
equalizeHist(frame_gray, frame_gray);
resize(frame_gray, smallframe_gray, smallframe_gray.size(), 0, 0, INTER_LINEAR);//将尺寸缩小到1/scale,用线性差值

face_cascade.detectMultiScale(smallframe_gray, faces, 1.1, 2, 0 | CV_HAAR_SCALE_IMAGE, Size(30, 30));

for (int i = 0; i < faces.size(); i++){
	Point center(cvRound((faces[i].x + faces[i].width*0.5)*scale), cvRound((faces[i].y + faces[i].height*0.5)*scale));
	ellipse(frame, center, Size(faces[i].width*0.5*scale, faces[i].height*0.5*scale), 0, 0, 360, Scalar(255, 0, 255), 4, 8, 0);
	eye_cascade.detectMultiScale(smallframe_gray, eyes, 1.1, 2, 0 | CV_HAAR_SCALE_IMAGE, Size(30, 30));
	for (int i = 0; i < eyes.size(); i++){
		Point center(cvRound((eyes[i].x + eyes[i].width*0.5)*scale), cvRound((eyes[i].y + eyes[i].height*0.5)*scale));
		ellipse(frame, center, Size(eyes[i].width*0.5*scale, eyes[i].height*0.5*scale), 0, 0, 360, Scalar(255, 0, 255), 4, 8, 0);
	}

}

imshow(window_name, frame);

}

int main()
{
//double scale = 4;
VideoCapture cap(0); // open the default camera
if (!cap.isOpened()) // check if we succeeded
return -1;

Mat edges;
//namedWindow("edges", 1);
if (!face_cascade.load(face_cascade_name)){
	printf("[error] 无法加载face级联分类器文件!\n");
	return -1;
}
if (!eye_cascade.load(eye_cascade_name)){
	printf("[error] 无法加载eye级联分类器文件!\n");
	return -1;
}
int nTick = 0;
for (;;)
{
	if (!cap.isOpened())
	{//等等摄像头打开
		continue;
	}
	Mat frame;
	nTick = getTickCount();
	cap >> frame; // get a new frame from camera
	if (frame.data == NULL)
	{//等到捕获到数据
		continue;
	}
	cvtColor(frame, edges, CV_BGR2BGRA);

	detectAndDisplay(edges);

	if (waitKey(30) >= 0) break;
}
return 0;

}

  • 9
    点赞
  • 45
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

学弟

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

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

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

打赏作者

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

抵扣说明:

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

余额充值