OpenCV與MFC實戰之影象處理 樣本採集小工具製作 c++MFC課程設計
原文作者:aircraft
原文連結:https://www.cnblogs.com/DOMLX/p/12111102.html
入門不久的人可以通過opencv實戰來鍛鍊一下學習opencv的成果,百度雲連結:
連結:https://pan.baidu.com/s/1jGOD97Zx96ZDAvlkQtaPYQ
提取碼:afip
執行環境VS2017,需要配置庫為:opencv
題目:樣本採集小工具
需求:
用MFC和opencv完成樣本採集小工具。
介面功能
1、選中原圖片集的目錄。
2、選擇當前是正樣本還是負樣本?並選中其目錄。
3、通過上一張下一張更換原圖片集的圖片顯示。
滑鼠點選圖片顯示區域功能
1、左擊圖片選中,以滑鼠點選處為中心,寬W*高H的區域。
2、滑鼠滾輪上滾擴大選中區域。
3、滑鼠滾輪下滾縮小選中區域。
4、右擊儲存選中區域的圖片在正樣本或負樣本的目錄下,取決於當前選中正樣本還是負樣本。
完成介面如圖:
第一步:把MFC介面的那些控制元件都拖動好並且繫結好opencv圖形框
在MFC的初始化函式中新增我們的繫結程式碼:
BOOL CpicroiDlg::OnInitDialog() { CDialogEx::OnInitDialog(); // 設定此對話方塊的圖示。 當應用程式主視窗不是對話方塊時,框架將自動 // 執行此操作 SetIcon(m_hIcon, TRUE); // 設定大圖示 SetIcon(m_hIcon, FALSE); // 設定小圖示 //InitializeSkin(("Minimized.ssk"));//初始化 // TODO: 在此新增額外的初始化程式碼 namedWindow("ImageShow", CV_WINDOW_KEEPRATIO); // 用OpenCV建立一個視窗 CRect cWindowRect; m_PictureControl.GetClientRect(&cWindowRect); // 獲取控制元件視窗大小 //int nWindowWidth = cWindowRect.Width(); //int nWindowHeight = cWindowRect.Height(); //resizeWindow("ImageShow", 200, 100); HWND hPictureWindow = (HWND)cvGetWindowHandle("ImageShow"); // 獲取OpenCV視窗的控制代碼 HWND hParentWindow = ::GetParent(hPictureWindow); ::SetParent(hPictureWindow, GetDlgItem(IDC_PIC)->m_hWnd); // 關聯OpenCV視窗和MFC的控制元件視窗 ::ShowWindow(hParentWindow, SW_HIDE); GetDlgItem(IDC_PIC)->ShowWindow(0); // 開始不顯示圖片控制元件 setMouseCallback("ImageShow", onMouse, 0); pcom.InsertString(0, "正樣本"); pcom.InsertString(1, "負樣本"); pcom.SetCurSel(0); return TRUE; // 除非將焦點設定到控制元件,否則返回 TRUE }
第二步:選中原圖片集的目錄
這樣接下來操作的圖片都從這個目錄順序讀取,並且切換上下張
新增好對話方塊類之後:
雙擊這個控制元件,進入相應的編輯函式內部編寫事件處理程式碼:
void CpicroiDlg::OnBnClickedMainFilePath() { // TODO: 在此新增控制元件通知處理程式代 CFileDialog dlg(TRUE, NULL, NULL, OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, _T("image files (*.jpeg; *.jpg; *.bmp;*.png) All Files (*.*) |*.*||")); CString m_strPath; CString m_folderpath; if (IDOK == dlg.DoModal()) { m_strPath = dlg.GetPathName(); m_folderpath = dlg.GetFolderPath(); } string strName = CT2A(m_strPath.GetString()); // CString和string之間的型別轉換 string strPathNames = CT2A(m_folderpath.GetString()); sourceImage = imread(strName); nWindowWidth = sourceImage.cols; nWindowHeight = sourceImage.rows; resizeWindow("ImageShow", nWindowWidth, nWindowHeight); if (strName == "")return; tempImage = sourceImage.clone(); imshow("ImageShow", sourceImage); GetDlgItem(IDC_PIC)->ShowWindow(1); SetDlgItemText(IDC_MAIN_FILE, m_folderpath); UpdateWindow(); getFiles1(strPathNames, files); filesLen = files.size()-1; for (auto path : files) { index++; if (path == strName)break; } }
同樣的也要選擇儲存的檔案路徑:
void CpicroiDlg::OnBnClickedMainFilePath2() { // TODO: 在此新增控制元件通知處理程式程式碼 if (m_dlgMainFile.DoModal() != IDOK) return; CString cstrFile; //檔案全名 string strFile; cstrFile = m_dlgMainFile.GetFolderPath(); strFile = CT2A(cstrFile.GetString()); SetDlgItemText(IDC_MAIN_FILE2, cstrFile); UpdateWindow(); getFiles1(strFile, filesPath2); picIndex = filesPath2.size(); GetDlgItem(IDC_MAIN_FILE2)->GetWindowTextA(cs_pcomValue); s_pcomComValue = CT2A(cs_pcomValue.GetString()); }
void CpicroiDlg::OnBnClickedMainFilePath3() { // TODO: 在此新增控制元件通知處理程式程式碼 if (m_dlgMainFile.DoModal() != IDOK) return; CString strFile; //檔案全名 strFile = m_dlgMainFile.GetFolderPath() + "\\"; SetDlgItemText(IDC_MAIN_FILE3, strFile); UpdateWindow(); }
這樣只要選擇好了正負樣本點選右鍵就可以自動儲存到相應的選擇好的目錄了
第三步:滑鼠控制影象的裁剪和選定
這裡我們要實現的是滑鼠點擊出現一個矩形框,然後用滑鼠的滾輪去滾動,讓矩形框圍繞中心點改變大小,然後點選右鍵則儲存圖形
void onMouse(int event, int x, int y, int flag, void*) { //CDC *pDC = GetDC(); //CString str; str.Format(TEXT("%d,%d"), x, y); //pDC->FillSolidRect(0, 0, 100, 100, GetSysColor(COLOR_WINDOW)); //pDC->TextOut(1, 0, str); switch (event) { case CV_EVENT_LBUTTONDOWN://左鍵按下 flag = true; mousFlag = 1; moux = x; mouy = y; if ((x - width / 2) < 0)Lu.x = 0; else Lu.x = x - width / 2; if ((y - height / 2) < 0)Lu.y = 0; else Lu.y = y - height / 2; if ((x + width / 2) > nWindowWidth - 1)Rd.x = nWindowWidth - 1; else Rd.x = x + width / 2; if ((y + height / 2) > nWindowHeight - 1 )Rd.y = nWindowHeight - 1; else Rd.y = y + height / 2; lastImage = tempImage.clone(); rectangle(tempImage, Lu, Rd, Scalar(0, 255, 0), 1, 0, 0); imshow("ImageShow", tempImage); break; case CV_EVENT_RBUTTONDOWN://右鍵按下 { Rect rect(Lu.x, Lu.y, Rd.x-Lu.x, Rd.y-Lu.y); g_rect = rect; } if (s_pcomComValue == "") { MessageBox(AfxGetMainWnd()->m_hWnd, "存放目錄未填寫","警告", MB_OK); break; } s_save = s_pcomComValue +"\\"+ to_string(picIndex) + ".jpg"; ROI = sourceImage(g_rect); imwrite(s_save, ROI); picIndex++; break; case CV_EVENT_MOUSEWHEEL: int mousWhellFlag; int value; mousWhellFlag = 1; value = getMouseWheelDelta(flag); if (value > 0) { Lu.x = Lu.x > 0 ? Lu.x -= step : Lu.x; Lu.y = Lu.y > 0 ? Lu.y -= step : Lu.y; Rd.x = Rd.x < (nWindowWidth - 1) ? Rd.x += step : (nWindowWidth - 1); Rd.y = Rd.y < (nWindowHeight - 1) ? Rd.y += step : (nWindowHeight - 1); } else { Lu.x = Lu.x < x ? Lu.x += step : x; Lu.y = Lu.y < y ? Lu.y += step : y; Rd.x = Rd.x > x ? Rd.x -= step : x; Rd.y = Rd.y > y ? Rd.y -= step : y; } { Rect rect(Lu.x, Lu.y, Rd.x - Lu.x, Rd.y - Lu.y); g_rect = rect; } tempImage = lastImage.clone(); rectangle(tempImage, Lu, Rd, Scalar(0, 255, 0), 1, 0, 0); imshow("ImageShow", tempImage); break; default: break; } }
因為我們裁剪已經畫矩形框都不能在原圖上畫,所以我們複製一份影象顯示,所有的操作都是在複製 的臨時影象上操作的,然後在根據緩衝重新整理,將影象替換就行了
最後說一句,這個是直接顯示原圖的,如果原圖過大隻能看到部分,這時候在程式碼裡面加個判斷,然後用opencv的影象歸一化的函式去改變一下影象大小即可。
因為不想寫的很詳細,(別問為什麼,問就是因為最近很懶!!!),所以我給出了專案原始碼的百度雲在文章的開頭
若有興趣交流分享技術,可關注本人公眾號,裡面會不定期的分享各種程式設計教程,和共享原始碼,諸如研究分享關於c/c++,python,前端,後端,opencv,halcon,opengl,機器學習深度學習之類有關於基礎程式設計,影象處理和機器視覺開發的知識