3 분 소요

MFC 쓰레드 활용

  1. 헤더파일 설정
	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. 쓰레드 생성
    {
      //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번 쓰레드 종료 참조)

  1. 화면 표출
    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을 변수로 만들어서 사용하면된다.

  1. 쓰레드 종료
    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); // 사용시 쓰레드 자체가 동작을 잠자기 때문에 표시 안되다/   }
    

```

출처

  1. 개발프로젝트- 식산 볼타입
  2. https://m.blog.naver.com/mincheol9166/220718941148

댓글남기기