本篇博客是基于上一篇博客《基于opencv和QT的瞳孔精确检测程序》的进一步开发和完善,实现了瞳孔坐标和位置的输出功能,本文将给出完整的瞳孔定位及跟踪程序,本文是作者的独立开发成果,转载请注明出处http://blog.csdn.net/zyx1990412/article/details/51254127。
实现原理:通过上一篇博客的介绍,我们可以实现对每一帧图像进行瞳孔检测,并得到瞳孔的精确坐标,即实现了瞳孔定位的功能。为了实现瞳孔跟踪的功能,我们可以通过手动按钮记录下瞳孔当前帧的坐标,作为跟踪的参考标准,然后将参考坐标与后续定位的坐标作比较,得到后续帧的相对坐标。本文的瞳孔跟踪功能是简单地计算当前帧相对于参考帧的相对位置,以确定瞳孔的当前相对位置。其原理图可以用图一表示。
图一
2.首先利用QT搭建一个界面,如图二。
图二
2.实现摄像头视频的读取和显示功能,详细的方法在《基于QT和opencv的摄像头(本地图片)读取并输出程序》这篇博客中有介绍。
3.建立一个图像处理类,对每一帧图像进行处理。主要功能是检测瞳孔中心,显示瞳孔中心定位结果图,并将瞳孔中心的绝对坐标保存在 Lcircles 和 Rcircles中,方便外部函数读取。这个类的实现在后文给出。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变换的检测结果 };
4.在mainwindow中编写槽函数,触发定位和跟踪事件。
namespace Ui { class MainWindow; } class MainWindow : public QMainWindow { Q_OBJECT public: explicit MainWindow(QWidget *parent = 0); ~MainWindow(); private slots: void on_pushButton_clicked();//打开摄像头,进行瞳孔精确检测及定位 void on_pushButton_2_clicked();//打开本地图片 void on_pushButton_3_clicked();//瞳孔定位,建立标准坐标 void on_pushButton_4_clicked();//是够启动瞳孔跟踪 void on_pushButton_5_clicked();//关闭摄像头 private: Ui::MainWindow *ui; CvRect lpc_box=cvRect( 0,0,0,0);//保存当前帧的瞳孔坐标 CvRect rpc_box=cvRect( 0,0,0,0); CvRect lp_bz=cvRect( 0,0,0,0);//保存标准瞳孔坐标 CvRect rp_bz=cvRect( 0,0,0,0); Mat pic1;//坐标图 Mat pic2; bool Lstop=false; bool Tack=false; void eyeTack();//瞳孔跟踪函数 //cv::Mat image; };
(1)打开摄像头后,图像处理类对每一帧图像进行处理,并将图像处理类得到的瞳孔绝对坐标保存到lpc_box和rpc_box中,详细介绍参考《基于opencv和QT的瞳孔精确检测程序》。
(2)触发瞳孔定位函数后,将图像处理类中得到的瞳孔绝对坐标保存到参考坐标lp_bz和rp_bz中。并将结果在lcdNumber上输出
(3)触发瞳孔跟踪函数后,通过当前帧绝对坐标和参考坐标的计算,得到相对坐标。并将结果在lcdNumber上输出。
(4)关闭摄像头,循环终止。
图三
最终的效果如图三,由于参数的设置和函数的精确等问题,其定位的效果还不能达到最佳效果。佩戴眼镜时的检测效果不好。图中的坐标图是直接读取已经画好的坐标图,如图四,图中的蓝点是根据相对坐标在坐标图上用opencv的画圆函数画出的。
图四
PS:本文所编写的瞳孔跟踪函数实际上核心是瞳孔的实时检测和定位,并没有用到传统意义上的目标跟踪函数。作者在毕业设计中的研究重点是基于MeanShift算法的目标跟踪算法,针对瞳孔跟踪,进行了加入帧差法和LBP的改进,作者将在以后的博客中进行详细介绍,并利用vs2013+opencv进行编程验证。
源代码mainwindow.h
#ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> #include "ui_mainwindow.h" #include "detectanddisplay.h" #include "imgprocess.h" #include <QLabel> #include"Mat2QImage.h" namespace Ui { class MainWindow; } class MainWindow : public QMainWindow { Q_OBJECT public: explicit MainWindow(QWidget *parent = 0); ~MainWindow(); private slots: void on_pushButton_clicked();//打开摄像头,进行瞳孔精确检测及定位 void on_pushButton_2_clicked();//打开本地图片 void on_pushButton_3_clicked();//瞳孔定位,建立标准坐标 void on_pushButton_4_clicked();//是够启动瞳孔跟踪 void on_pushButton_5_clicked();//关闭摄像头 private: Ui::MainWindow *ui; CvRect lpc_box=cvRect( 0,0,0,0);//保存当前帧的瞳孔坐标 CvRect rpc_box=cvRect( 0,0,0,0); CvRect lp_bz=cvRect( 0,0,0,0);//保存标准瞳孔坐标 CvRect rp_bz=cvRect( 0,0,0,0); Mat pic1;//坐标图 Mat pic2; bool Lstop=false; bool Tack=false; void eyeTack();//瞳孔跟踪函数 //cv::Mat image; }; #endif // MAINWINDOW_H
#include "mainwindow.h" MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); } MainWindow::~MainWindow() { delete ui; } void MainWindow::on_pushButton_clicked()//打开摄像头,进行瞳孔精确检测及定位 { VideoCapture cap(0); //打开默认摄像头 if(!cap.isOpened()) { cout<<"no cap"<<endl; } Mat frame; //保存当前帧的图像 Lstop=false;//控制是否循环检测 while(!Lstop) { cap>>frame;//将摄像头的当前帧输出到frame 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); if (pro.Lcircles.size()>0)//将检测结果输出 { lpc_box.x=pro.Lcircles[0][0]; lpc_box.y=pro.Lcircles[0][1]; lpc_box.height=pro.Lcircles[0][2]; lpc_box.width=pro.Lcircles[0][2]; } ui->lcdNumber_6->display(lpc_box.x);//显示坐标 ui->lcdNumber_5->display(lpc_box.y); ui->lcdNumber_9->display(lpc_box.height); if (pro.Rcircles.size()>0) { rpc_box.x=pro.Rcircles[0][0]; rpc_box.y=pro.Rcircles[0][1]; rpc_box.height=pro.Rcircles[0][2]; rpc_box.width=pro.Rcircles[0][2]; } ui->lcdNumber_8->display(rpc_box.x); ui->lcdNumber_7->display(rpc_box.y); ui->lcdNumber_10->display(rpc_box.height); if( Tack==true)//是够启动跟踪功能 { eyeTack(); } int c = waitKey(30);//每一帧间隔30ms if (c >= 0)//任意键暂停 { waitKey(0); } } cvDestroyWindow("camera"); } void MainWindow::on_pushButton_2_clicked()//打开本地图片 { Mat image0; image0=cv::imread("IF.jpg");//图片的路径 ImgProcess pro(image0); pro.EyeDetect(); Mat image=pro.Outputimg(); QImage img=Mat2QImage(image); ui->label->setPixmap(QPixmap::fromImage(img)); // ui->label->setScaledContents(true); pro.DivideEye(); pro.EyeEdge(); pro.FindCenter(); 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); waitKey(1000); } void MainWindow::on_pushButton_3_clicked()//瞳孔定位,建立标准坐标 { lp_bz=lpc_box; rp_bz=rpc_box; ui->lcdNumber->display(lpc_box.x); ui->lcdNumber_2->display(lpc_box.y); ui->lcdNumber_4->display(rpc_box.x); ui->lcdNumber_3->display(rpc_box.y); pic1=cv::imread("zb.jpg");//读坐标图 cv::resize(pic1,pic1,Size(100,100)); QImage zbimg1=Mat2QImage(pic1); ui->label_4->setPixmap(QPixmap::fromImage(zbimg1)); ui->label_4->setScaledContents(true); pic2=cv::imread("zb1.jpg"); cv::resize(pic2,pic2,cvSize(100,100)); QImage zbimg2=Mat2QImage(pic2); ui->label_5->setPixmap(QPixmap::fromImage(zbimg2)); ui->label_5->setScaledContents(true); } void MainWindow::on_pushButton_4_clicked()//是够启动瞳孔跟踪 { Tack=true; } void MainWindow::eyeTack()//瞳孔跟踪函数 { float ldx,ldy,rdx,rdy,plx,ply,prx,pry;//左右眼瞳孔坐标,d相对坐标差 ldx=(lpc_box.x-lp_bz.x)/lp_bz.width; ldy=(lpc_box.y-lp_bz.y)/lp_bz.width; rdx=(rpc_box.x-rp_bz.x)/rp_bz.width; rdy=(rpc_box.y-rp_bz.y)/rp_bz.width; plx=ldx*10+50; ply=ldy*10+50; Point Lcenter(plx, ply);//瞳孔中心在坐标图中的位置 pic1=cv::imread("zb.jpg"); cv::resize(pic1,pic1,Size(100,100)); circle( pic1, Lcenter, 3, Scalar(255,0,0), -1,8);//画瞳孔中心 QImage zbimg1=Mat2QImage(pic1); ui->label_4->setPixmap(QPixmap::fromImage(zbimg1)); ui->label_4->setScaledContents(true); prx=rdx*10+50;//右眼 pry=rdy*10+50; Point Rcenter(prx, pry); pic2=cv::imread("zb1.jpg"); cv::resize(pic2,pic2,Size(100,100)); circle( pic2, Rcenter, 3, Scalar(255,0,0), -1,8); QImage zbimg2=Mat2QImage(pic2); ui->label_5->setPixmap(QPixmap::fromImage(zbimg2)); ui->label_5->setScaledContents(true); ui->lcdNumber->display(ldx); ui->lcdNumber_2->display(ldy); ui->lcdNumber_4->display(rdx); ui->lcdNumber_3->display(rdy); } void MainWindow::on_pushButton_5_clicked()//关闭摄像头 { Lstop=true; }
#ifndef IMGPROCESS_H #define IMGPROCESS_H #include <opencv2/opencv.hpp> #include <iostream> #include <stdio.h> #include "detectanddisplay.h" //视频处理类 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
imgprocess.cpp#include "imgprocess.h" void ImgProcess::EyeDetect() { detectAndDisplay( inimg,drawing_box ); outimg=inimg; } Mat ImgProcess::Outputimg() { return outimg; } 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); } } Mat ImgProcess::OutLeye() { return Leye; } Mat ImgProcess::OutReye() { return Reye; } 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); } 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; } 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; } void ImgProcess::FindCenter() { Lcircles=Hough(Leye_G); Rcircles=Hough(Reye_G); Leye=PlotC(Lcircles,Leye); Reye=PlotC(Rcircles,Reye); }
detectanddisplay.h
#ifndef DETECTANDDISPLAY_H #define DETECTANDDISPLAY_H #include <opencv2/opencv.hpp> #include <iostream> #include <stdio.h> using namespace std; using namespace cv; //检测人眼区域 void detectAndDisplay( Mat &frame,CvRect &box ); #endif // DETECTANDDISPLAY_H
detectanddisplay.cpp
#include "detectanddisplay.h" 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 ); }
Mat2QImage.h
#ifndef MAT2QIMAGE_H #define MAT2QIMAGE_H #include <QtGui> #include <QDebug> #include <iostream> #include <opencv/cv.h> #include <opencv/highgui.h> using namespace cv; using namespace std; QImage Mat2QImage(const Mat&);//mat格式转换为Qimage格式 #endif
Mat2QImage.cpp
#include "Mat2QImage.h" QImage Mat2QImage(const Mat& mat) { // 8-bits unsigned, NO. OF CHANNELS=1 if(mat.type()==CV_8UC1) { // cout<<"1"<<endl; // Set the color table (used to translate colour indexes to qRgb values) QVector<QRgb> colorTable; for (int i=0; i<256; i++) colorTable.push_back(qRgb(i,i,i)); // Copy input Mat const uchar *qImageBuffer = (const uchar*)mat.data; // Create QImage with same dimensions as input Mat QImage img(qImageBuffer, mat.cols, mat.rows, mat.step, QImage::Format_Indexed8); img.setColorTable(colorTable); return img; } // 8-bits unsigned, NO. OF CHANNELS=3 if(mat.type()==CV_8UC3) { // cout<<"3"<<endl; // Copy input Mat const uchar *qImageBuffer = (const uchar*)mat.data; // Create QImage with same dimensions as input Mat QImage img(qImageBuffer, mat.cols, mat.rows, mat.step, QImage::Format_RGB888); return img.rgbSwapped(); } else { qDebug() << "ERROR: Mat could not be converted to QImage."; return QImage(); } }main.cpp
#include "mainwindow.h" #include <QApplication> int main(int argc, char *argv[]) { QApplication a(argc, argv); MainWindow w; w.show(); return a.exec(); }test0.pro
#------------------------------------------------- # # Project created by QtCreator 2016-03-16T10:24:54 # #------------------------------------------------- QT += core gui greaterThan(QT_MAJOR_VERSION, 4): QT += widgets TARGET = test0 TEMPLATE = app SOURCES += main.cpp\ mainwindow.cpp \ imgprocess.cpp \ detectanddisplay.cpp \ others.cpp \ Mat2QImage.cpp HEADERS += mainwindow.h \ detectanddisplay.h \ imgprocess.h \ others.h \ Mat2QImage.h FORMS += mainwindow.ui INCLUDEPATH += d:\opencv\build\include\ INCLUDEPATH += d:\opencv\build\include\opencv\ INCLUDEPATH += d:\opencv\build\include\opencv2\ CONFIG(debug,debug|release) { LIBS += -Ld:\opencv\build\x64\vc12\lib \ -lopencv_core2410d \ -lopencv_highgui2410d \ -lopencv_imgproc2410d \ -lopencv_features2d2410d \ -lopencv_calib3d2410d \ -lopencv_video2410d \ -lopencv_objdetect2410d } else { LIBS += -Ld:\opencv\build\x64\vc12\lib \ -lopencv_core2410 \ -lopencv_highgui2410 \ -lopencv_imgproc2410 \ -lopencv_features2d2410 \ -lopencv_calib3d2410 \ -lopencv_video2410 \ -lopencv_objdetect2410 }