在(二)中,我們已經得到了相機的畸變參數,利用這參數糾正圖像原理十分簡單,不再贅述。本文主要介紹如何實現批量的畸變糾正,同時實時顯示處理進度,程序運行界面如圖1所示
圖 1. 圖像畸變糾正程序運行界面
在ImageRectifier中,我們需要輸入畸變參數和相機參數,然後把同一相機拍攝的多幅圖像一次性輸入,即可批量進行處理。程序附帶了處理進度條,運行情況一目瞭然。
本程序的實現,最關鍵的是多線程的運用。我們可以在前端實時觀看程序處理進度,同時程序後臺不斷地處理圖像。前端的處理進度顯示和後臺的處理結果通過消息傳遞關聯起來。下面我們首先簡要介紹工作線程的原理,然後介紹本程序具體實現思路。
一、工作線程原理介紹
參考《Visual C++面向對象編程教程(第二版)》(清華大學出版社)
二、利用工作線程實現進度顯示和後臺處理的關聯
我們的思路是這樣的,用戶啓動任務後,我們在主程序中啓動一個線程函數,並把圖像處理的任務交給這個線程函數,由它在後臺執行,執行過程中不斷髮送消息給前端的主線程,前端的主線程一旦接收到消息則更新當前執行進度條。
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中,我們就可以把消息發送給這個指針,最終讓對話框本身接收並處理消息。