影像批量畸變糾正--雙像三維建模小軟件開發實例(三)

在(二)中,我們已經得到了相機的畸變參數,利用這參數糾正圖像原理十分簡單,不再贅述。本文主要介紹如何實現批量的畸變糾正,同時實時顯示處理進度,程序運行界面如圖1所示


圖 1. 圖像畸變糾正程序運行界面

在ImageRectifier中,我們需要輸入畸變參數和相機參數,然後把同一相機拍攝的多幅圖像一次性輸入,即可批量進行處理。程序附帶了處理進度條,運行情況一目瞭然。

本程序的實現,最關鍵的是多線程的運用。我們可以在前端實時觀看程序處理進度,同時程序後臺不斷地處理圖像。前端的處理進度顯示和後臺的處理結果通過消息傳遞關聯起來。下面我們首先簡要介紹工作線程的原理,然後介紹本程序具體實現思路。

一、工作線程原理介紹

 參考《Visual C++面向對象編程教程(第二版)》(清華大學出版社)

進程(process)是應用程序的一個運行實例,即一個運行中的應用程序,線程(thread)是隸屬於進程的獨立執行體,一個進程有且只有一個主線程,而同時可以擁有其他多個線程。線程分爲工作線程(worker thread)和用戶界面線程(user interface thread)兩種,前者沒有窗口,沒有消息泵,不需要處理消息,後者有窗口,有消息泵可以處理界面消息。一般地,利用AfxBeginThread(ThreadProc,this)函數創建一個工作線程,ThreadProc是線程入口函數,this是傳入線程的參數。在一個程序的主函數中利用AfxBeginThread函數啓動線程後,線程會獨立執行,當然,AfxBeginThread有其他一些缺省參數可以控制線程函數的執行時機,優先級等,在此我們不做研究。
 

二、利用工作線程實現進度顯示和後臺處理的關聯

我們的思路是這樣的,用戶啓動任務後,我們在主程序中啓動一個線程函數,並把圖像處理的任務交給這個線程函數,由它在後臺執行,執行過程中不斷髮送消息給前端的主線程,前端的主線程一旦接收到消息則更新當前執行進度條。

1、消息和消息響應函數的關聯(黑體顯示):

BEGIN_MESSAGE_MAP(CImageRectifierDlg, CDialog)
//{{AFX_MSG_MAP(CImageRectifierDlg)
ON_MESSAGE(WM_USERMSG,OnMyMessage)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

2、WM_USERMSG的定義

#define WM_USERMSG WM_USER+100

這樣做的目的是爲了避免和其他消息數值衝突

3、OnMyMessage()是對話框類CImageRectifierDlg的成員函數,在該函數中完成進度條的更新

LRESULT CImageRectifierDlg::OnMyMessage(WPARAM wParam, LPARAM lParam)
{
	CString str;
	str.Format("%d",lParam);
	str += "%";
	this->m_path_imgfile.SetItemText(wParam,1,str);
	this->m_path_imgfile.Update(wParam);
	return 1;
}
4、後臺的線程函數處理任務和發送消息的方法

UINT MyWorkThread(LPVOID pParam)
{
    CImageRectifierDlg* dlg = (CImageRectifierDlg*) pParam;
    CvMat *intrinsic_matrix = cvCreateMat(3,3,CV_32FC1);
	CvMat *distortion_coeffs = cvCreateMat(4,1,CV_32FC1);
	CV_MAT_ELEM(*intrinsic_matrix,float,0,0) = dlg->f1;
	CV_MAT_ELEM(*intrinsic_matrix,float,0,1) = 0;
	CV_MAT_ELEM(*intrinsic_matrix,float,0,2) = dlg->x0;
	CV_MAT_ELEM(*intrinsic_matrix,float,1,0) = 0;
	CV_MAT_ELEM(*intrinsic_matrix,float,1,1) = dlg->f2;
	CV_MAT_ELEM(*intrinsic_matrix,float,1,2) = dlg->y0;
	CV_MAT_ELEM(*intrinsic_matrix,float,2,0) = 0;
	CV_MAT_ELEM(*intrinsic_matrix,float,2,1) = 0;
	CV_MAT_ELEM(*intrinsic_matrix,float,2,2) = 1;

	CV_MAT_ELEM(*distortion_coeffs,float,0,0) = dlg->k1;
	CV_MAT_ELEM(*distortion_coeffs,float,1,0) = dlg->k2;
	CV_MAT_ELEM(*distortion_coeffs,float,2,0) = dlg->p1;
	CV_MAT_ELEM(*distortion_coeffs,float,3,0) = dlg->p2;

	IplImage *image = cvLoadImage(dlg->filepaths.at(0));
	IplImage *mapx = cvCreateImage(cvGetSize(image),IPL_DEPTH_32F,1);
	IplImage *mapy = cvCreateImage(cvGetSize(image),IPL_DEPTH_32F,1);
	cvInitUndistortMap(intrinsic_matrix,distortion_coeffs,mapx,mapy);
	int i=0;
	CString str;
	for (i=0;i<dlg->filepaths.size();i++)
	{
        PostMessage(dlg->m_hWnd,WM_USERMSG,i,1);
		str = dlg->filepaths.at(i);
		image = cvLoadImage(str);
		IplImage *t = cvCloneImage(image);
		cvRemap(t,image,mapx,mapy);
		cvSaveImage(str+"Rectify.tif",image);
		cvReleaseImage(&t);
        PostMessage(dlg->m_hWnd,WM_USERMSG,i,100);
	}
    PostMessage(dlg->m_hWnd,WM_USERMSG,0,100);
	return 1;
}
稍微解釋一下發送消息的函數PostMessage的參數,第一個參數是接收消息的窗口對象,這裏就是對話框本身,第二個參數是消息標識,第三和第四個參數是傳給消息響應函數(在這裏即爲OnMyMessage)的參數。

另外非常重要的一點,我們的線程函數是全局函數,而消息響應函數卻是對話框類CImageRectifier的成員函數,他們如何關聯的呢?是通過線程函數的參數LPVOID型的pParam來實現的。具體地,我們在CImageRectifier::OnOK()中啓動線程時是這樣啓動的:thread = AfxBeginThread(MyWorkThread,(LPVOID)this),這就把對話框對象本身的指針作爲參數傳給了線程函數MyWorkThread,於是在MyWorkThread中,我們就可以把消息發送給這個指針,最終讓對話框本身接收並處理消息。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章