上一篇實現了在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。
這個錯誤多是退出線程時沒有把空間釋放乾淨所致,在屢次的開啓與關閉子線程(即屢次點擊開始和中止按鈕)後,就會出現問題。
因此只能改成如今的線程不退出方案,讓子線程一直執行,經過修改其中的標誌位來啓動和中止顯示。