OPENCV之人眼检测

来源:互联网 发布:企业管理优化建议 编辑:程序博客网 时间:2024/05/10 01:20
本篇博客主要是对前段时间数字图像课程大作业-疲劳检测所做工作的一次总结整理。主要涉及到的内容有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

include

include

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;

}

1 0
原创粉丝点击