本篇博客是基于上一篇博客《基于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
mainwindow.cpp
#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;
}
imgprocess.h
#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
}