上一篇实现了在MFC的窗体内显示图片,本篇介绍如何在MFC窗体内实时显示摄像头的影像。html
要实现的功能是点击一个“开始”按钮,能够显示影像,再点击“中止”按钮,能够中止显示。多线程
由于实时显示影像须要在一个循环里执行,为了在显示影像的同时还能够干别的(好比,点击“中止”按钮),这里须要用到多线程,即显示影像的代码放到子线程中,与主线程并发执行。并发
重点已经说清楚了,下面是开发步骤:异步
一、先把Halcon中实时显示的程序搞定async
二、Halcon代码导出为C++代码函数
三、创建MFC工程优化
四、在MFC中添加Halcon代码ui
下面说细节:this
从资源选项卡能够看到检测到的接口为DirectShow,这是微软开发的视频设备驱动。spa
从链接选项卡能看到检测到相机,是笔记本自带的摄像头。点击上方的摄像机图标,Halcon的图形窗口就开始实时显示摄像头的画面了,很方便。
下面点击“代码生成”选项卡,点击“插入代码”按钮,就把实时显示的代码插入到代码窗口中了。
注意这里的采集模式是异步采集,在循环中采集图像的意思就是实时显示。
生成的Halcon代码以下:
* Image Acquisition 01: Code generated by Image Acquisition 01 open_framegrabber ('DirectShow', 1, 1, 0, 0, 0, 0, 'default', 8, 'rgb', -1, 'false', 'default', '[0] Integrated Camera', 0, -1, AcqHandle) grab_image_start (AcqHandle, -1) while (true) grab_image_async (Image, AcqHandle, -1) * Image Acquisition 01: Do something endwhile close_framegrabber (AcqHandle)
导出的C++代码中Action函数以下:
void action() { // Local iconic variables HObject ho_Image; // Local control variables HTuple hv_AcqHandle; //Image Acquisition 01: Code generated by Image Acquisition 01 OpenFramegrabber("DirectShow", 1, 1, 0, 0, 0, 0, "default", 8, "rgb", -1, "false", "default", "[0] Integrated Camera", 0, -1, &hv_AcqHandle); GrabImageStart(hv_AcqHandle, -1); while (0 != 1) { GrabImageAsync(&ho_Image, hv_AcqHandle, -1); //Image Acquisition 01: Do something } CloseFramegrabber(hv_AcqHandle); }
这就是咱们须要添加到MFC的代码,须要注意到while循环中只是获取了图像,并无显示图像,因此咱们还要添加显示图像的代码。
与上一篇相似,新建基于对话框的MFC项目,添加Picture Control 和两个按钮。
一、在文件开头添加Halcon头文件以及命名空间
#include "halconcpp.h"
using namespace HalconCpp;
二、在类外添加线程函数的声明
//线程函数的声明应在类CMultiThread1Dlg的外部
void ThreadFunc(LPVOID lpParam);
三、在类内添加Halcon变量为对话框类的Public成员
HObject ho_Image;
HTuple hv_AcqHandle;
HTuple m_HWindowID;
HTuple m_FGHandle,m_ImageWidth,m_ImageHeight;
四、添加线程函数的变量为对话框类的Protected成员
HANDLE hThread;
DWORD ThreadID;
一、首先添加Halcon头文件和命名空间,并定义全局变量
volatile BOOL m_bRun;
volatile BOOL m_bShowFlag;
Volatile关键词告诉编译器不对此变量进行优化,使该值可被多个线程修改,对于多线程意义重大。
二、为开始按钮添加单机响应函数
CWnd * pWnd = AfxGetApp()->GetMainWnd(); if(m_bShowFlag){ m_bRun=TRUE; }else{ hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) ThreadFunc, // this, //传入主窗口指针 0, &ThreadID ); }
为了能随时中止和开始实时监控,我设置了m_bShowFlag这个变量,第一次点击“开始”按钮时,m_bShowFlag为FALSE,执行CreateThread函数启动子线程,在子线程中m_bShowFlag被置为TRUE,因此下次点击“开始”按钮时不会再次开启子线程,而只是修改线程中的标志位来启动实时监控。
三、子线程函数的实现代码
void ThreadFunc(LPVOID pParam) { CHalconCameraDlg * pMainWindow; pMainWindow=(CHalconCameraDlg * ) pParam; //强制转化为主窗口指针 HTuple HWindowID; CRect Rect; CWnd * pWnd = pMainWindow->GetDlgItem( IDC_STATIC); HWindowID = (Hlong)pWnd->m_hWnd; pWnd->GetWindowRect(&Rect); OpenWindow(0,0,Rect.Width(),Rect.Height(),HWindowID,"visible","",&(pMainWindow->m_HWindowID) ); //显示相机捕捉的图像 OpenFramegrabber("DirectShow", 1, 1, 0, 0, 0, 0, "default", 8, "rgb", -1, "false", "default", "[0] Integrated Camera", 0, -1, &(pMainWindow->hv_AcqHandle) ); GrabImageStart(pMainWindow->hv_AcqHandle, -1); ClearWindow(pMainWindow->m_HWindowID); GrabImage(&(pMainWindow->ho_Image), pMainWindow->hv_AcqHandle); GetImagePointer1((pMainWindow->ho_Image),NULL,NULL,&(pMainWindow->m_ImageWidth),&(pMainWindow->m_ImageHeight) ); SetPart(pMainWindow->m_HWindowID,0,0,pMainWindow->m_ImageHeight-1,pMainWindow->m_ImageWidth-1); m_bShowFlag=TRUE;//设置运行状态 m_bRun=TRUE; while (m_bShowFlag){ if(m_bRun){ GrabImageAsync(&(pMainWindow->ho_Image), pMainWindow->hv_AcqHandle, -1); DispObj(pMainWindow->ho_Image, pMainWindow->m_HWindowID); Sleep(50); } } ClearWindow(pMainWindow->m_HWindowID); CloseFramegrabber(pMainWindow->hv_AcqHandle); CloseWindow(pMainWindow->m_HWindowID); ExitThread(0); }
while循环以前的代码与上一篇相似,循环中当m_bRun为TRUE时执行获取与显示图像的语句,所以当全局变量m_bRun被置为FALSE时显示会中止,实现了前述的功能(注意,此时线程并不退出)。
四、中止按钮的响应函数
只须要一句话就够了。由于m_bRun被声明为volatile变量,在子线程外部能够更改它,修改成FALSE以后子线程中实时显示的语句就没法执行,表现出来就是图像静止,再也不更新。
m_bRun=FALSE;
在这个程序中,子线程一直没有退出,即m_bShowFlag没有被置为FLASE。
以前我试过在中止按钮里把m_bShowFlag置为FALSE,即让线程退出,而后再次点击开始按钮时从新启动线程,可是在关闭窗口时会出现下面的错误。
触发了一个断点。其缘由多是堆被损坏。缘由也多是用户在HalconCamera.exe具备焦点时按下了F12。
这个错误多是退出线程时没有把空间释放干净所致,在屡次的开启与关闭子线程(即屡次点击开始和中止按钮)后,就会出现问题。
因此只能改成如今的线程不退出方案,让子线程一直执行,经过修改其中的标志位来启动和中止显示。