기술 MFC 쓰레드 활용 +OpenCV 화면 표출
MFC 쓰레드 활용
- 헤더파일 설정
CWinThread* m_pThread[3];
void DisplayImage_BitBit(Mat Displayimage, int index);
static UINT RunMainPCThread(LPVOID pParam);
- CWinThread* m_pThread[3]; 쓰레드 활용하기 위해 선언
- DisplayImage_BitBit(Mat, index) 크게 활용한 것은 화면 표시해주는 함수
- static UINT RunMainPCThread(LPVOID pParam); 쓰레드 돌아갈 함수 Static으로 선언해야 클래스내에서 사용이 가능하다.
typedef struct
{
int Cam_index;
HANDLE param;
}ThreadParameter;
추가로 쓰레드를 인자로 사용하기 위해서 구조체를 선언했느데 define.h 폴더에 추가해 주면 되나.
- 쓰레드 생성
{ //1. 쓰레드에 인자 없이 사용하고 싶은 경우 m_pThread[0] = AfxBeginThread(RunMainPCThread, this); //쓰레드 생성 //2. 쓰레드에 인자 추가하고 싶은 경우 ThreadParameter* Thread_Cam_1 = new ThreadParameter; Thread_Cam_1->param = this; Thread_Cam_1->Cam_index = 1; m_pThread[0] = AfxBeginThread(RunMainPCThread, (LPVOID)Thread_Cam_1); }
쓰레드 변수에 쓰레드 함수를 할당해준다. 이떄 this는 다이어 로그 쓰레드를 의미한다
#쓰레드 함수는
UINT EnLargeMent_IMG_DLG::RunMainPCThread(LPVOID pParam)
{
// 1번 인자없이 가져온경우 바로 적용하면되고
EnLargeMent_IMG_DLG* pDlg = (EnLargeMent_IMG_DLG*)pParam;
// 2번 인자가 있는경우 구조체에서 인자를 가져와서 활용해야한다.
ThreadParameter* item = (ThreadParameter*)param;
EnLargeMent_IMG_DLG* pDlg = (EnLargeMent_IMG_DLG*)item->param;
Mat Video_frame;
VideoCapture* capture = new VideoCapture(0);
capture->set(CAP_PROP_FRAME_WIDTH, 320);
capture->set(CAP_PROP_FRAME_HEIGHT, 240);
if (!capture->isOpened())
{
return 0;
}
while (pDlg->m_isWorkingThread[0])
{
capture->read(Video_frame);
pDlg->DisplayImage_BitBit(Video_frame, MainLive_1);
}
return 0;
}
다이얼로그 파일은 인자로 받아서 활용이 가능하다 문제는 이것은 쓰레드인데 종료하고 끄지 않으면 에러가 발생한다. m_isWorkingThread 변수를 통해 False만들어준다 (4번 쓰레드 종료 참조)
- 화면 표출
void EnLargeMent_IMG_DLG::DisplayImage_BitBit(Mat Displayimage, int index) { Mat mat_frame = Displayimage.clone(); int bpp = 8 * mat_frame.elemSize(); assert((bpp == 8 || bpp == 24 || bpp == 32)); int padding = 0; //32 bit image is always DWORD aligned because each pixel requires 4 bytes if (bpp < 32) padding = 4 - (mat_frame.cols % 4); if (padding == 4) padding = 0; int border = 0; //32 bit image is always DWORD aligned because each pixel requires 4 bytes if (bpp < 32) { border = 4 - (mat_frame.cols % 4); } Mat mat_temp; if (border > 0 || mat_frame.isContinuous() == false) { // Adding needed columns on the right (max 3 px) cv::copyMakeBorder(mat_frame, mat_temp, 0, 0, 0, border, cv::BORDER_CONSTANT, 0); } else { mat_temp = mat_frame; } RECT r; if (index == PC_ENLARGE_ORI) { m_PC_Ori.GetClientRect(&r); } else if (index == PC_ENLARGE_RESULTORI) { m_PC_ResultOri.GetClientRect(&r); } else if (index == PC_ENLARGE_RESULTMOD) { m_PC_ResultModify.GetClientRect(&r); } else { m_PC_Ori.GetClientRect(&r); } cv::Size winSize(r.right, r.bottom); cimage_mfc.Create(winSize.width, winSize.height, 24); BITMAPINFO* bitInfo = (BITMAPINFO*)malloc(sizeof(BITMAPINFO) + 256 * sizeof(RGBQUAD)); bitInfo->bmiHeader.biBitCount = bpp; bitInfo->bmiHeader.biWidth = mat_temp.cols; bitInfo->bmiHeader.biHeight = -mat_temp.rows; bitInfo->bmiHeader.biPlanes = 1; bitInfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); bitInfo->bmiHeader.biCompression = BI_RGB; bitInfo->bmiHeader.biClrImportant = 0; bitInfo->bmiHeader.biClrUsed = 0; bitInfo->bmiHeader.biSizeImage = 0; bitInfo->bmiHeader.biXPelsPerMeter = 0; bitInfo->bmiHeader.biYPelsPerMeter = 0; //그레이스케일 인경우 팔레트가 필요 if (bpp == 8) { RGBQUAD* palette = bitInfo->bmiColors; for (int i = 0; i < 256; i++) { palette[i].rgbBlue = palette[i].rgbGreen = palette[i].rgbRed = (BYTE)i; palette[i].rgbReserved = 0; } } // Image is bigger or smaller than into destination rectangle // we use stretch in full rect if (mat_temp.cols == winSize.width && mat_temp.rows == winSize.height) { // source and destination have same size // transfer memory block // NOTE: the padding border will be shown here. Anyway it will be max 3px width SetDIBitsToDevice(cimage_mfc.GetDC(), //destination rectangle 0, 0, winSize.width, winSize.height, 0, 0, 0, mat_temp.rows, mat_temp.data, bitInfo, DIB_RGB_COLORS); } else { // destination rectangle int destx = 0, desty = 0; int destw = winSize.width; int desth = winSize.height; // rectangle defined on source bitmap // using imgWidth instead of mat_temp.cols will ignore the padding border int imgx = 0, imgy = 0; int imgWidth = mat_temp.cols - border; int imgHeight = mat_temp.rows; StretchDIBits(cimage_mfc.GetDC(), destx, desty, destw, desth, imgx, imgy, imgWidth, imgHeight, mat_temp.data, bitInfo, DIB_RGB_COLORS, SRCCOPY); } HDC dc; if (index == PC_ENLARGE_ORI) { dc = ::GetDC(m_PC_Ori.m_hWnd); cimage_mfc.BitBlt(dc, 0, 0); ::ReleaseDC(m_PC_Ori.m_hWnd, dc); } else if (index == PC_ENLARGE_RESULTORI) { dc = ::GetDC(m_PC_ResultOri.m_hWnd); cimage_mfc.BitBlt(dc, 0, 0); ::ReleaseDC(m_PC_ResultOri.m_hWnd, dc); } else if (index == PC_ENLARGE_RESULTMOD) { dc = ::GetDC(m_PC_ResultModify.m_hWnd); cimage_mfc.BitBlt(dc, 0, 0); ::ReleaseDC(m_PC_ResultModify.m_hWnd, dc); } else { dc = ::GetDC(m_PC_Ori.m_hWnd); cimage_mfc.BitBlt(dc, 0, 0); ::ReleaseDC(m_PC_Ori.m_hWnd, dc); } cimage_mfc.ReleaseDC(); cimage_mfc.Destroy(); }
OpenCV Map 이미지를 호출할때 MFC Picture Contorl에 넣을시 4의 배수의 크기(width,height)를 가져야한다. 이 떄 m_PC_Ori 와 같은 변수들은 mfc 에서 Picture Contorl을 변수로 만들어서 사용하면된다.
- 쓰레드 종료
void EnLargeMent_IMG_DLG::OnClose() { // TODO: 여기에 메시지 처리기 코드를 추가 및/또는 기본값을 호출합니다. m_isWorkingThread[0] = false; capture.release(); CDialogEx::OnClose(); }
다이어로그 종료시 쓰레드 끄고 나가는 습관을 가지자
-
추가 유의사항 쓰레드에서 OpenCV imshow를 활용하고 싶으면 ```cpp while (pDlg->m_isWorkingThread[1]) { item->capture.read(Video_frame);
imshow("test",Video_frame); waitKey(10); // 쓰레드가 잠시 대기상태 // Sleep(10); // 사용시 쓰레드 자체가 동작을 잠자기 때문에 표시 안되다/ }
```
출처
- 개발프로젝트- 식산 볼타입
- https://m.blog.naver.com/mincheol9166/220718941148
댓글남기기