基于OpenCV 、VS2008 MFC对话框的USB摄像头的控制和视频播放、跟踪(logitech sphere AF网络摄像头)
1.opencv
2.vs2008 mfc
3.logitech sphere AF网络摄像头的pan tilt zoom控制
4.SIR粒子滤波
我已经做完了,总结完后上传。
草稿
PTZ (Pan, Tilt, Zoom )摄像机跟踪指图像工作站通过对摄像头所获取的视频图像序列处理,对运动目标进行检测、分割和跟踪,将得到的目标偏离视场中心的偏差值反馈给云台,控制其全方位转动,实现摄像机对目标的同步跟踪。
1. 界面设计
基于 Visual Studio 2008 的 MFC 技术, 在对话框GUI 界面,往里面添加若干Button 和一个Picture 控件,如图
图2.1 GUI 界面
各个控件的ID 号由Visual C++ 自动产生并存放于Resource.h 文件中,比如其中一句定义:
#define IDC_ShowImg 1002
定义了图片控件的ID 号,由图片控件显示图片时只需要使用如下命令:
CDC* pDC = GetDlgItem( IDC_ShowImg)->GetDC();// 获得显示控件的DC
HDC hDC = pDC->GetSafeHdc(); // 获取HDC( 设备句柄) 来进行绘图操作
img.DrawToHDC( hDC, &rect ); // 将图片绘制到显示控件的指定区域内
同时,为了实时显示各种参数,创建了状态栏。
HWND hStatusWindow;
HWND hDlg=GetSafeHwnd();
int IDS_STATUS =1;
hStatusWindow=CreateStatusWindow(WS_CHILD|WS_VISIBLE|WS_BORDER,TEXT(" 状态栏"),hDlg, IDS_STATUS);
int pint[4]={110,250,300,-1};//110,250,300 设定间隔
::SendMessage(hStatusWindow,SB_SETPARTS,4,(LPARAM)pint);
在状态栏中,将不断显示水平、垂直转动的角度和焦距变化值,这样可以使用户在使用系统的过程中得到一些关心的准确数据。
2. 人脸检测
人脸检测方法是一种基于积分图、级联检测器和AdaBoost 算法的方法,方法框架可以分为以下三大部分 :
第一部分,使用Harr-llke 特征表示人脸,使用“积分图’’实现特征数值的快速计算;
第二部分,使用Adaboost 算法挑选出一些最能代表人脸的矩形特征( 弱分类器) ,按照加权投票的方式将弱分类器构造为一个强分类器:
第三部分,将训练得到的若干强分类器串联组成一个级联结构的层叠分类器,级联结构能有效地提高分类器的检测速度。
基于AdaBoost 的人脸检测在OpenCV 中的具体实现步骤如图2.2 。
图 2.2 OpenCV2.0 实现人脸检测的基本步骤
1) 加载分类器。利用以下语句实现分类器的加载。
static CvHaarClassifierCascade* cascade = 0;
const char* cascade_name ="haarcascade_frontalface_alt.xml";
cascade = (CvHaarClassifierCascade*)cvLoad( cascade_name, 0, 0, 0 );
2) 同时,0penCV 在加载的时候将分类器转化成了内部格式。加载检测图像。在本检测中,首先将3 通道8 位的彩色图转为灰度图,然后将灰度图按缩小1.3 倍。
cvCvtColor( img, gray, CV_BGR2GRAY );// 将彩色图转化为灰度图
cvResize( gray, small_img, CV_INTER_LINEAR );// 将灰度图缩小
3) 检测人脸。通过以下函数实现人脸检测
vSeq* faces = cvHaarDetectObjects( small_img, cascade, storage,
1.1, 2, 0/*CV_HAAR_DO_CANNY_PRUNING*/,cvSize(30, 30) );
通过以下函数得到脸部坐标:
CvRect r = (CvRect*)cvGetSeqElem( faces,0);
图 2.3 人脸检测效果图
3. 基于多线程的视频文件播放
在多线程方法中,同一进程内所有线程共享惟一的进程地址空间资源,线程间共用内存空间、寄存器、进程表项等,不存在通过第三方进行信息交换的问题;而线程内部也能通过线程数据槽等方法实现各自的变量存储,所以多线程技术是并发程序设计方式中最为简单的一种,是首选工具。
在未采用多线程前,单击Capture 按钮,视频播放的过程中整个GUI 界面将失去消息响应,因为视频播放是一个死循环的过程。为了解决这个问题,本系统采用多线程技术,实现了视频播放与主程序的独立。
在Button 控件Capture 下添加了void CmymfcDlg::OnBnClickedReadimg() 按钮点击的消息响应程序,在程序中添加如下代码:
CreateThread(NULL,0,CaptureThread,Capture_param,0,NULL);
创建了一个线程,线程函数为C aptureThread ,传入参数为Capture_param 。在 DWORD WINAPI CaptureThread(LPVOID pParam) 函数中,实现了每帧视频的读取和粒子滤波的循环。
4. 基于OpenCV 2.0 的视频文件读取
OpenCV 是一种用于数字图像处理和计算机视觉的函数库。它由英特尔公司开发,是一套可免费获得的由一些C 函数和C++ 类所组成的库。OpenCV 在Windows 系统及Linux 系统下都可以使用,它提供了很多标准的图像处理算法,主要用于对图像进行一些高级处理。这些函数可以直接在具体的视频开发项目中调用,通过简单编程即可完成十分复杂庞大的开发任务,具有很好的效率。
CvCapture* pCapture;
pCapture = cvCaptureFromCAM(0) ;
IplImage* ipl=NULL;
ipl = cvQueryFrame( Capture_param->plistener->pCapture );
通过以上代码实现了从摄像头读取每帧图片。由于图片控件的大小是276 ×276 ,需要对摄像头采集的图像进行缩放。摄像头采集的图像大小是320 ×240 ,上下添加黑边至320 ×320, 然后进行256/320 的缩放。
cvSetImageROI( TheImage, cvRect( tlx, tly, nw, nh) );
cvResize( img, TheImage );
cvResetImageROI( TheImage );
通过OpenCV 设置感兴趣区域ROI 进行缩放, 可以很方便地实现图片以合适尺寸显示。
5. PTZ 摄像头的控制
在PTZ.CPP 文件中,主要通过如下函数实现摄像头的控制。
HRESULT set_mechanical_pan_relative(IAMCameraControl *pCameraControl, long value);
HRESULT set_mechanical_tilt_relative(IAMCameraControl *pCameraControl, long value);
以上两个函数,实现了摄像头的水平和垂直转动,输入参数分别为摄像头的ID 号和转动角度。
HRESULT set_digital_zoom_absolute(IAMCameraControl *pCameraControl, long value);
这个函数实现了摄像头的放大缩小倍数。
在Logitech 此款摄像头中,焦距的变化范围从50mm 到200mm 。函数传入的value 即设置的焦距值 。这样可以看到,此摄像头最大的放大倍数是4 倍。
6. 粒子滤波程序的实现
在Button 控件track 下添加了void CmymfcDlg:: OnBnClickedTrace () 按钮点击的消息响应程序,在程序中添加如下代码:
if(initPFTracking((PixelsInfor *)ImageSource->imageData,
&TrackLocation,
&goalFeatuerStuctAdr,
&G_MAIN_ParticlesAdr,
G_MAIN_PARNUMBER)!=0)
{
AfxMessageBox("SIR initialization error!");
}
启动粒子滤波程序。
其中TrackLocation 为目标位置,本系统由两种方式输入。
a) 由鼠标响应事件产生
在GUI 界面中,添加如下消息响应事件
void CmymfcDlg::OnLButtonDown(UINT nFlags, CPoint point)
void CmymfcDlg::OnLButtonUp(UINT nFlags, CPoint point)
由鼠标画框事件,记录下目标的坐标,再传给粒子滤波初始化程序。
b) 由AdaBoost 人脸检测程序的返回值确定。
人脸检测程序可以返回检测结果,通过一定的换算后可以得到目标位置,然后传递给粒子滤波程序,实现了人脸的自动检测和跟踪。
当初始化完成后,将粒子滤波的初始化完成标志位注为真,然后每帧图像显示前调用如下函数进行持续跟踪并返回位置坐标,画框程序根据位置坐标不断进行标注。
if(oncePFTracking((PixelsInfor *)(ImageSource->imageData),
Capture_param->plistener->G_MAIN_ParticlesAdr, G_MAIN_PARNUMBER,
Capture_param->plistener->goalFeatuerStuctAdr,
&Capture_param->plistener->TrackLocation)!=0)
{
AfxMessageBox("tracking code error!");}
四 总结
本系统在VS2008 下开发环境下,开发出了基于MFC 对话框的人机交互界面,使用OpenCV 2.0 函数库和多线程技术,在流畅播放视频的同时,能进行基于用户操作的Logitech USB 摄像头的PTZ 控制,能自动运行基于Adaboost 的人脸检测并具有非常高的准确性,能提供两种目标输入模式的粒子滤波程序的人脸跟踪,并能根据跟踪结果对摄像头进行水平、垂直控制,使跟踪实现了水平360 , 垂直90 的跟踪效果。
本系统只是完成了水平和垂直的转动跟踪,没有使云台根据转动参数实现目标跟踪过程中的自动镜头缩放,这个是下一步需要继续深入下去的工作。
五 部分代码
回答一个朋友的问题
1.怎么交换数据?
在窗体初始化的时候,创建守护线程,在守护线程中不断获取图像,并通过MFC显示。通过函数调用传递参数,不会产生延时。
以下代码是Capture_param的定义
typedef struct _tag_SOCKET_LISTEN_PARAM { CmymfcDlg* plistener; }SOCKETLISTENPARAM; SOCKETLISTENPARAM *Capture_param=(SOCKETLISTENPARAM*)pParam;
以下是显示代码
void CmymfcDlg::ShowImage( IplImage* img, UINT ID ) // ID 是Picture Control控件的ID号 { CDC* pDC = GetDlgItem( ID ) ->GetDC(); // 获得显示控件的 DC HDC hDC = pDC ->GetSafeHdc(); // 获取 HDC(设备句柄) 来进行绘图操作 CRect rect,rect2; ((CStatic*)GetDlgItem(ID))->GetWindowRect(&rect2); GetDlgItem(ID) ->GetClientRect( &rect ); int rw = rect.right - rect.left; // 求出图片控件的宽和高 int rh = rect.bottom - rect.top; int iw = img->width; // 读取图片的宽和高 int ih = img->height; tx = (int)(rw - iw)/2; // 使图片的显示位置正好在控件的正中 ty = (int)(rh - ih)/2; SetRect( rect, tx, ty, tx+iw, ty+ih ); if(TraceFlag==FALSE) DrawFrame(img,TraceLocation); CvvImage cimg; cimg.CopyOf( img ); // 复制图片 cimg.DrawToHDC( hDC, &rect ); // 将图片绘制到显示控件的指定区域内 cvWaitKey(50); ReleaseDC( pDC ); }