基于opencv和QT的瞳孔精确检测程序

本文为作者为毕业设计所写的瞳孔精确检测程序,谢绝任何形式的转载。

本篇博客是在作者的前两篇博客 《基于QT和opencv的摄像头(本地图片)读取并输出程序》《 基于opencv和QT的人脸(人眼)检测程序》的基础上进行开发的。主要原理是:针对已经检测到的人眼区域图像,利用边缘检测和Hough变换实现瞳孔的精确检测。

首先建立一个图像处理类,对每一帧图像进行处理。

class ImgProcess
{
    private:
        Mat inimg;//输入图像
        Mat outimg;//输出结果
        Mat Leye;
        Mat Reye;
        Mat Leye_G;
        Mat Reye_G;
        CvRect drawing_box;

    public:
        vector<Vec3f> Lcircles;
        vector<Vec3f> Rcircles;
        ImgProcess(Mat image):inimg(image),drawing_box(cvRect(0, 0, 50, 20)){}
        void EyeDetect();//人眼检测
        Mat Outputimg();//输出结果
        void DivideEye();//分左右眼
        Mat OutLeye();//输出结果
        Mat OutReye();
        Mat EdgeDetect(Mat &edgeimg);//边缘检测
        void EyeEdge();//分别检测左右眼
        vector<Vec3f> Hough(Mat &midImage);//hough变换
        void FindCenter();//定位中心
        Mat PlotC(vector<Vec3f> circles,Mat &midImage);//画HOUGH变换的检测结果
};

#endif // IMGPROCESS_H

1.检测人眼

在图像处理时,首先利用上一篇博客所得到的人眼检测函数检测得到人眼区域。实现人眼检测的函数voidEyeDetect();

void ImgProcess::EyeDetect()
{
    detectAndDisplay( inimg,drawing_box );
    outimg=inimg;
}

其中的detectAndDisplay函数为:

void  detectAndDisplay( Mat &frame,CvRect &box )
{
    string face_cascade_name = "haarcascade_mcs_eyepair_big.xml";//导入已经训练完成的样本
    CascadeClassifier face_cascade;//建立分类器
    string window_name = "camera";
    if( !face_cascade.load( face_cascade_name ) ){
        printf("[error] no cascade\n");
    }

    std::vector<Rect> faces;//用于保存检测结果的向量
    Mat frame_gray;

    cvtColor( frame, frame_gray, CV_BGR2GRAY );//转换成灰度图
    equalizeHist( frame_gray, frame_gray );//直方图均值化

    face_cascade.detectMultiScale( frame_gray, faces, 1.1, 2, 0|CV_HAAR_SCALE_IMAGE, Size(30, 30) );//用于检测人眼的函数
    //画方框
    for( int i = 0; i < faces.size(); i++ ){
        Point centera( faces[i].x, faces[i].y);
        Point centerb( faces[i].x + faces[i].width, faces[i].y + faces[i].height );
        rectangle(frame,centera,centerb,Scalar(255,0,0));
        box=faces[0];
    }

    //imshow( window_name, frame );
}

运行完人眼检测函数后,检测结果将在label上显示出来,如图一。需要说明的是,opencv自带的检测函数存在误检或者漏检的情况,即检测到多个人眼区域(多讲眉毛部分检测为人眼),或者检测不到人眼。所以函数将检测到的第一个向量放入drawing_box中;如果没有检测到,就不赋值。另外需要注意的是,在佩戴眼镜的情况下,检测效果不是十分明显。

图一

2.左右眼图像分割

drawing_box是一个矩形数据结构,里面共有四个量,drawing_box.x和drawing_box.y分别是矩形框的x和y坐标,drawing_box.width和drawing_box.Height分别是矩形框的宽度和高度。检测到人眼区域后,利用voidDivideEye()函数将人眼区域分成左右眼两个区域,方便后续计算。其中leye_box表示左眼的矩形框,Leye表示左眼的图像,reye_box表示右眼的矩形框,Reye表示右眼的图像。

void ImgProcess::DivideEye()
{
    if (drawing_box.width>0)
    {
        CvRect leye_box;
        leye_box.x=drawing_box.x+1;
        leye_box.y=drawing_box.y+1;
        leye_box.height=drawing_box.height-1;
        leye_box.width=floor(drawing_box.width/2)-1;
        CvRect reye_box;
        reye_box.x=leye_box.x+leye_box.width;
        reye_box.y=drawing_box.y+1;
        reye_box.height=drawing_box.height-1;
        reye_box.width=leye_box.width-1;
        Leye=inimg(leye_box);
        Reye=inimg(reye_box);
//    imshow("L",Leye);
//    imshow("R",Reye);
    }
}

3.图像边缘检测

本文利用canny算法对图像进行边缘检测,canny算法的原理就不详细介绍了,大家可以自行查阅网上的资料,本文直接使用opencv自带的检测函数进行检测。单个图像的边缘检测ImgProcess::EdgeDetect:首先将图像由彩色图转换成灰度图,然后进行高斯平滑,接着进行直方图均值化,最后利用canny函数进行边缘检测。这几个函数的用法和参数设置网上都有介绍,其中canny函数最重要的是低阈值和高阈值的设置,本文的参数是通过多次测试得出的结果。参数的设置与光照和背景都有较大的关系,本文的参数适用于作者所在是环境(图一的背景),在其他环境下是否适用,还需要读者自行探索。

Mat ImgProcess::EdgeDetect(Mat &edgeimg)
{
    Mat edgeout;
    cvtColor(edgeimg,edgeimg,CV_BGR2GRAY);// 彩色图转换成灰度图
    GaussianBlur( edgeimg,edgeimg, Size(9, 9), 2, 2 );// 高斯平滑
    equalizeHist( edgeimg, edgeimg );//直方图均值化
    Canny(edgeimg,edgeout,100,200,3);//输入图像,输出图像,低阈值,高阈值,opencv建议是低阈值的3倍,内部sobel滤波器大小
    return edgeout;
}

然后建立一个函数对左右眼图片分别调用边缘检测函数

void ImgProcess::EyeEdge()
{
    Leye_G=EdgeDetect(Leye);
    Reye_G=EdgeDetect(Reye);
  //imshow("L",Leye_G);
  //imshow("R",Reye_G);
}

检测结果如图二所示

图二

4.Hough变换检测圆心

得到人眼区域的边缘图像后,就可以用Hough变换求瞳孔圆心,Hough变换的基本原理这里同样不作介绍,直接调用opencv自带的检测函数进行检测。

vector<Vec3f> ImgProcess::Hough(Mat &midImage)
{
    vector<Vec3f> circles;

    HoughCircles( midImage, circles, CV_HOUGH_GRADIENT,1.5, 5, 100, 20, drawing_box.height/4, drawing_box.height/3 );
    return circles;
}

http://www.tuicool.com/articles/Mn2EBn这篇文章里有详细Hough变换的原理与函数的使用方法,下面是引用的这篇文章里对函数参数的解释。

voidHoughCircles(InputArray image,OutputArray circles,int method,doubledp,
double minDist,double param1=100,doubleparam2=100,int
minRadius=0,int maxRadius=0 )

·       第一个参数,InputArray类型的image,输入图像,即源图像,需为8位的灰度单通道图像。第二个参数,InputArray类型的circles,经过调用HoughCircles函数后存储了检测到的圆的输出矢量,每个矢量由包含了3个元素的浮点矢量(x,
y, radius)表示。

·       第三个参数,int类型的method,即使用的检测方法,目前OpenCV中就霍夫梯度法一种可以使用,它的标识符为CV_HOUGH_GRADIENT,在此参数处填这个标识符即可。

·       第四个参数,double类型的dp,累加器图像的分辨率和输入图像之比的倒数,且此参数允许创建一个比输入图像分辨率低的累加器。上述文字不好理解的话,来看例子吧。例如,如果dp=
1时,累加器和输入图像具有相同的分辨率。如果dp=2,累加器便有输入图像一半那么大的宽度和高度。

·       第五个参数,double类型的minDist,为霍夫变换检测到的圆的圆心之间的最小距离,即让我们的算法能明显区分的两个不同圆之间的最小距离。这个参数如果太小的话,多个相邻的圆可能被错误地检测成了一个重合的圆。反之,这个参数设置太大的话,某些圆就不能被检测出来了。

·       第六个参数,double类型的param1,有默认值100。它是第三个参数method设置的检测方法的对应的参数。对当前唯一的方法霍夫梯度法CV_HOUGH_GRADIENT,它表示传递给canny边缘检测算子的高阈值,而低阈值为它的一半。

·       第七个参数,double类型的param2,也有默认值100。它是第三个参数method设置的检测方法的对应的参数。对当前唯一的方法霍夫梯度法CV_HOUGH_GRADIENT,它表示在检测阶段圆心的累加器阈值。它越小的话,就可以检测到更多的“假圆”,而它越大的话,能通过检测的圆就更加接近完美的圆形了。

·       第八个参数,int类型的minRadius,有默认值0,表示圆半径的最小值。

·       第九个参数,int类型的maxRadius,也有默认值0,表示圆半径的最大值。

经过作者亲自对参数的调整,发现对检测结果影响最大的是第六个和第七个参数,作者已经将调节好的参数在函数中写了出来。

HoughCircles函数返回的是一个三维向量,其中保存了检测到的圆的x,y坐标和圆的半径r,然后需要建立一个绘制圆的函数将检测结果表示出来:

Mat ImgProcess::PlotC(vector<Vec3f> circles,Mat &midImage)
 {
     for( size_t i = 0; i < circles.size(); i++ )
       {
         Point center(cvRound(circles[i][0]), cvRound(circles[i][1]));
         int radius = cvRound(circles[i][2]);
         //cout<<i<<":"<<circles[i][0]<<","<<circles[i][1]<<","<<circles[i][2]<<endl;
         //绘制圆心cvRound进行四舍五入
         circle( midImage, center, 1, Scalar(255,0,0), -1,8);
         //绘制圆轮廓
         circle( midImage, center, radius, Scalar(255,0,0), 1,8 );
       }
     return midImage;
 }

最后分别建立一个对左右眼进行hough变换的函数对上述两个函数进行调用:

voidImgProcess::FindCenter()
{
    Lcircles=Hough(Leye_G);
    Rcircles=Hough(Reye_G);
    Leye=PlotC(Lcircles,Leye);
    Reye=PlotC(Rcircles,Reye);
}

最终的检测结果如图三所示。从结果中可以发现,检测结果不是非常稳定,存在漏检和误检的情况,这可能是参数设置的问题。利用本文所设置的参数,基本可以实现瞳孔的精确检测,通过调整人脸相对于摄像头的位置,一定可以检测到瞳孔,但是检测结果不一定是连续的。在误检的情况中,即使存在多余的检测结果,但是正确的结果也同时存在(如图三右图),说明正确相对于误检的结果,是最稳定的。

图三

在上一篇《基于QT和opencv的摄像头(本地图片)读取并输出程序》的程序中的图像处理函数的位置,对上述函数进行调用,可以得到图四所得的结果。

ImgProcess pro(frame);//建立视频处理类
pro.EyeDetect();//人眼检测
        Mat image=pro.Outputimg();//输出检测图像
        imshow( "camera", image );
        QImage img=Mat2QImage(image);//将mat格式转换为Qimage格式
        ui->label->setPixmap(QPixmap::fromImage(img));//将结果在label上显示
        //ui->label->setScaledContents(true);//使图像尺寸与label大小匹配
        pro.DivideEye();//分成左右眼
        pro.EyeEdge();//瞳孔边缘检测
        pro.FindCenter();//hough变换求圆心
        Mat mleye=pro.OutLeye();//输出瞳孔定位结果
        QImage qleye=Mat2QImage(mleye);
        ui->label_2->setPixmap(QPixmap::fromImage(qleye));
        //ui->label_2->setScaledContents(true);
        Mat mreye=pro.OutReye();
        QImage qreye=Mat2QImage(mreye);
        ui->label_3->setPixmap(QPixmap::fromImage(qreye));
        //ui->label_3->setScaledContents(true);

图四

本篇博客介绍的是对opencv自带的图像处理函数的应用,下一篇《基于QT和opencv的瞳孔定位及跟踪程序》将介绍对检测到的数据进行检测的处理,实现瞳孔的定位和跟踪功能。整个图像处理类函数和类的调用函数将在下一篇完整给出。

时间: 2024-10-10 06:56:07

基于opencv和QT的瞳孔精确检测程序的相关文章

基于opencv和QT的摄像头采集代码( GoQTtemplate3持续更新)

在Linux操作系统上,编写带界面的图像处理程序,选择opencv+QT是一种很好的选择.GoQTtemplate3是我为编写Linux下图像处理程序实现的框架,希望能够为大家解决Linux环境下桌面图像处理程序,提供一些帮助. 文中相关代码请参考:https://github.com/jsxyhelu/GOQTtemplate3 一.基本环境构建 a.我们需要完整地安装QT.具体方法,是从网站上下载在线安装程序,并且直接安装 b.下载并且安装OpenCV 直接使用编译好的OpenCV类库 su

基于opencv的人脸检测的web应用

参考资料 https://github.com/bsdnoobz/web-based-face-detect http://opencv-code.com/projects/web-based-interface-for-face-detection-with-opencv/ http://www.cnblogs.com/findingsea/archive/2012/03/31/2427833.html 流程如下图 背景知识 php调用exe的返回 <html> <body> &

基于OpenCv的人脸检测、识别系统学习制作笔记之一

基于OpenCv从视频到摄像头的人脸检测 在OpenCv中读取视频文件和读取摄像头的的视频流然后在放在一个窗口中显示结果其实是类似的一个实现过程. 先创建一个指向CvCapture结构的指针 CvCapture *capture; 再用两个函数就可以分别获取到视频文件或者摄像头的一些状态信息,然后把这些信息放进去之前指向的结构体 视频文件 capture = cvCreateCameraCapture(0); 打开摄像头 capture = cvCreateFileCapture(argv[1]

基于OpenCv的人脸检测、识别系统学习制作笔记之三

1.在windows下编写人脸检测.识别系统.目前已完成:可利用摄像头提取图像,并将人脸检测出来,未进行识别. 2.在linux下进行编译在windows环境下已经能运行的代码. 为此进行了linux系统下OpenCv的安装. 在linux中安装OpenCv遇到了很多问题,已经解决,但是花费了不少时间.目前:可以在linux下编译OpenCv项目,但是运行生成的程序时出现问题.初步认定为采用了虚拟机而导致运行内存不足,程序直接崩溃,将继续解决这个问题. 花费较多时间安装OpenCv是有必要的,为

基于OpenCV读取摄像头进行人脸检测和人脸识别

前段时间使用OpenCV的库函数实现了人脸检测和人脸识别,笔者的实验环境为VS2010+OpenCV2.4.4,opencv的环境配置网上有很多,不再赘述.检测的代码网上很多,记不清楚从哪儿copy的了,识别的代码是从OpenCV官网上找到的:http://docs.opencv.org/trunk/modules/contrib/doc/facerec/facerec_api.html 需要注意的是,opencv的FaceRecogizer目前有三个类实现了它,特征脸和fisherface方法

基于OpenCV的图片卡通化处理

学习OpenCV已有一段时间,除了研究各种算法的内容,在空闲之余,根据书本及资料的引导,尝试结合图像处理算法和日常生活联系起来,首先在台式机上(带摄像头)完成一系列视频流处理功能,开发平台为Qt5.3.2+OpenCV2.4.9. 本次试验实现的功能主要有: 调用摄像头捕获视频流: 将帧图像转换为素描效果图片: 将帧图像卡通化处理: 简单地生成"怪物"形象: 人脸肤色变换. 本节所有的算法均由类cartoon中的函数cartoonTransform()来实现: // Frame:输入每

opencv学习---运动目标(前景)检测

opencv学习---运动目标(前景)检测 1.帧差法 原理:视频序列相邻两帧或三帧间采用基于像素的时间差分,通过闭值化来提取出图像中的运动区域. 优点:算法简单.计算量小,无需训练背景,对缓慢变换的光照不是很敏感. 缺点:容易受天气.阴影及杂乱背景干扰,阈值T的选择相当关键,稳定性差. 2.背景差分法 原理:用背景的参数模型来近似背景图像,将当前帧与背景图像进行差分比较实现对运动区域的检测 优点:计算量小,较高的实时性,利用已有帧信息进行背景动态更新 缺点:如何建立对于不同场景的动态变化均具有

【OpenCV入门教程之十七】OpenCV重映射 &amp; SURF特征点检测合辑

本系列文章由@浅墨_毛星云 出品,转载请注明出处. 文章链接:http://blog.csdn.net/poem_qianmo/article/details/30974513 作者:毛星云(浅墨)    微博:http://weibo.com/u/1723155442 知乎:http://www.zhihu.com/people/mao-xing-yun 邮箱: [email protected] 写作当前博文时配套使用的OpenCV版本: 2.4.9 本篇文章中,我们一起探讨了OpenCV中

基于 OpenCV 的人脸识别

基于 OpenCV 的人脸识别 一点背景知识 OpenCV 是一个开源的计算机视觉和机器学习库.它包含成千上万优化过的算法,为各种计算机视觉应用提供了一个通用工具包.根据这个项目的关于页面,OpenCV 已被广泛运用在各种项目上,从谷歌街景的图片拼接,到交互艺术展览的技术实现中,都有 OpenCV 的身影. OpenCV 起始于 1999 年 Intel 的一个内部研究项目.从那时起,它的开发就一直很活跃.进化到现在,它已支持如 OpenCL 和 OpenGL 的多种现代技术,也支持如 iOS