CAFE

영상처리및제어

샘플-opencv를 이용한 usb 웹캠 영상 캡쳐하기

작성자한창호|작성시간16.01.11|조회수11,546 목록 댓글 0

OpenCV를 MFC 화면에 띄우기 위한 방법을 2가지로 보여준다.

  1) OpenCV 3.1.0  에서 BitmapInfo를 이용한 MFC로 그리기

  2) OpenCV 2.4.11 에서 CvvImage를 이용한 MFC로 그리기

 

 

1. Web캠 영상을 OpenCv를 이용해 MFC 화면에 출력하기  (BitmapInfo)

     (참고: OpenCV-MFC에 출력 Cvvimage 대체)

 

 

* Visual Studio 2022 + OpenCV 4.9.0 사용 샘플

 

 

 

 * Mat 이미지를 MFC 화면에 출력하는 예제

단계설명
프로젝트 생성대화상자로 프로젝트 생성한다.
프로젝트명:  ocvCam

OpenCV 설정하기: https://cafe.daum.net/smhan/cczU/61

리소스뷰에서 그림을 출력할 화면 추가하기m_View는 비트맵 영상을 출력할 부분으로 리소스에서 Picture Control로 선택하여 추가한 다음 변수추가는 CStatic으로 설정하여 추가하면 된다.

제어변수추가

이미지 화면         IDC_VIEW   컨트롤   m_View    CStatic
카메라 시작 버튼  IDC_CAM_START  
카메라 멈춤 버튼  IDC_CAM_STOP
이미지 읽기 버튼  IDC_IMG_LOAD
이미지 저장 버튼  IDC_IMG_SAVE
xxxDlg.h#include "opencv2/opencv.hpp"
using namespace cv;

public: 
  Mat m_Image;
  VideoCapture m_Capture;

  BOOL m_bThreadFlag
= FALSE;

  void FillBitmapInfo(BITMAPINFO* bmi, int width, int height, int bpp, int origin);
  void DisplayImage(CDC* pDC, CRect rect, Mat& srcimg);

**Dlg.cpp

필요한 함수 추가
void CocvCamDlg::FillBitmapInfo(BITMAPINFO* bmi, int width, int height, int bpp, int origin)
{
 assert(bmi&&width >= 0 && height >= 0 && (bpp == 8 || bpp == 24 || bpp == 32));
 BITMAPINFOHEADER *bmih = &(bmi->bmiHeader);
 memset(bmih, 0, sizeof(*bmih));
 bmih->biSize = sizeof(BITMAPINFOHEADER);
 bmih->biWidth = width;
 bmih->biHeight = origin ? abs(height) : -abs(height);
 bmih->biPlanes = 1;
 bmih->biBitCount = (unsigned short)bpp;
 bmih->biCompression = BI_RGB;
 if (bpp == 8) {
  RGBQUAD *palette = bmi->bmiColors;
  for (int i = 0; i<256; i++)  {
   palette[i].rgbBlue = palette[i].rgbGreen = palette[i].rgbRed = (BYTE)i;
   palette[i].rgbReserved = 0;
  }
 }
}

void CocvCamDlg::DisplayImage( CDC* pDC, CRect rect, Mat& srcimg )
{
 Mat img;
 int step = ((int)(rect.Width() / 4)) * 4; // 4byte 단위조정해야 영상이 기울어지지 않는다.
 
 if (srcimg.empty()) return;
 resize( srcimg, img, Size( step, rect.Height() ) );
 uchar buffer[sizeof( BITMAPINFOHEADER ) * 1024];
 BITMAPINFO* bmi = (BITMAPINFO*)buffer;

 int bmp_w = img.cols;
 int bmp_h = img.rows;
 int depth = img.depth();
 int channels = img.channels();
 int bpp = 8*channels;

 FillBitmapInfo( bmi, bmp_w, bmp_h, bpp, 0 );

 int from_x = MIN( 0, bmp_w - 1 );
 int from_y = MIN( 0, bmp_h - 1 );
 int sw = MAX( MIN( bmp_w - from_x, rect.Width() ), 0 );
 int sh = MAX( MIN( bmp_h - from_y, rect.Height() ), 0 );

 SetDIBitsToDevice( pDC->m_hDC, rect.left, rect.top, sw, sh, from_x, from_y, 0, sh, img.data + from_y*img.step, bmi, 0 );
 img.release();
OnInitDialog() 함수

카메라 설정
BOOL CocvCamDlg::OnInitDialog()
{
  // TODO: 여기에 추가 초기화 작업을 추가합니다.

  m_Capture.open(0);
   // 카메라 연결
  if (!m_Capture.isOpened()) AfxMessageBox(_T("There is no camera captured!"));
}
OnPaint() 함수

이미지 출력
void CocvCamDlg::OnPaint()
{
  if (IsIconic())
  {
    ...
  else
  {

    // Mat 이미지인 m_Image를 화면 m_View에 출력해보자.
    if (m_Image.data != NULL) {
      CRect rect;
      CDC* pDC = m_View.GetDC();           // 출력한 부분의 DC 얻기
      m_View.GetClientRect(rect);               // 출력할 영역 얻기
      DisplayImage(pDC, rect, m_Image);  // 카메라에서 읽어들인 영상을 화면에 그리기
      ReleaseDC(pDC);
    }

   CDialogEx::OnPaint();
  }
}
불러오기void CocvCamDlg::OnBnClickedLoadBmp()
{
 // TODO: 여기에 컨트롤 알림 처리기 코드를 추가합니다.

 wchar_t szFilter[] = _T("Image (*.BMP) | *.BMP;*.GIF;*.JPG | All Files(*.*)|*.*||");
 CFileDialog dlg(TRUE, _T("bmp"), _T("test"), OFN_HIDEREADONLY, szFilter);

 if (dlg.DoModal() == IDOK) {   
  CT2CA ansiStr(dlg.GetPathName().GetBuffer(0));  // Unicode T to Ascii
  std::string filenameansiStr );

  m_Image = imread(filename,1); // 1 for color
  Invalidate(FALSE);
 }
}
저장하기void CcvCam2013Dlg::OnBnClickedSaveBmp()
{
  // TODO: 여기에 컨트롤 알림 처리기 코드를 추가합니다.

  wchar_t szFilter[] = _T("Image (*.BMP) | *.BMP;*.GIF;*.JPG | All Files(*.*)|*.*||");
  CFileDialog dlg(FALSE, _T("bmp"), _T("test"), OFN_HIDEREADONLY, szFilter);

  if (dlg.DoModal() == IDOK) {
    // flip(m_Image, m_Image, 0); // vertical
    std::string filename(CT2CA(dlg.GetPathName()));  // 유니코드변환
    //CStringA filename(dlg.GetPathName().GetBuffer(0));
    imwrite(filename, m_Image);
  }
}
카메라 시작

- 쓰레드 함수 처리
// Cam 영상 출력 쓰레드
UINT ThreadImageCaptureFunc(LPVOID param)
{
  CocvCamDlg *pDlg = ( CocvCamDlg *) param;
  while (pDlg->m_bThreadFlag) {  
    pDlg->m_Capture >> pDlg->m_Image;  // 영상 획득
    pDlg->Invalidate(FALSE);                     // OnPaint()에서 이미지 출력
 }
 return 0;


// 시작 버튼
void CocvCamDlg::OnBnClickedCamStart()
{
  // TODO: 여기에 컨트롤 알림 처리기 코드를 추가합니다.

  m_bThreadFlag = TRUE;

  CWinThread *pThread = ::AfxBeginThread(ThreadImageCaptureFunc, this);
}
카메라 멈춤// 멈춤 버튼
void CocvCamDlg::OnBnClickedCamStop()
{
  // TODO: 여기에 컨트롤 알림 처리기 코드를 추가합니다.

   m_bThreadFlag = FALSE;
}

 


2. CvvImage를 이용하여 OpenCV의 IplImage를 MFC로 그리기
 

 

다음과 같은 영상 캡쳐 프로그램을 작성해 보자.  오른쪽 하단에 조그마한 화면에 이진영상처리한 결과를 보여준다. 이 부분을 통해 어떻게 데이터를 처리하는지 공부할 수 있다.

샘플소스:   (Timer 또는 Thread 사용 OpenCV 2.4.11, x86) - cvvimage 사용

 

1) OpenCV 2.4.11을 설치한다.  (설치참고: https://cafe.daum.net/smhan/cczU/61 )

 

  내컴퓨터 - 시스템 속성 - 고급시스템 설정 - 고급 -환경변수 - 시스템 변수에' 탭에서 Path를 수정한다. (재부팅 필요)

  PATH      ... ;C:\opencv\build\x86\vc12\bin   (vc2013)
  CLASSPATH  ... ;C:\opencv\build\x86\vc12\bin   (vc2013)

  전처리기 정의에 추가한다.  _CRT_SECURE_NO_WARNINGS

  C++ -> General -> Additional Include Directories에 추가한다.  C:\opencv\build\include

  Linker-> General -> Additional Library Directories에 추가한다. C:\opencv\build\x86\vc12\lib

  Linker -> Input -> Additional Dependencies 에 추가한다.

 

2) 파일 추가  

 

3) 다이얼로그 리소스에서 버튼과 CStatic을 추가한다.

IDC_VIEW                   // static  캡쳐 영상 띄울 window
IDC_VIEW_BIN            //  static  이진 영상처리한 결과
IDC_CAM_START        // button  캡쳐 시작
IDC_CAM_STOP          // button  캡쳐 멈춤
IDC_BMP_LOAD          // button  bmp 파일 일기
IDC_BMP_SAVE          // button  bmp 파일 쓰기

 

4) ...Dlg.h에 다음을 추가한다.

#include <opencv/cv.h>
#include <opencv/highgui.h>
#include "CvvImage.h"

// 클래스 내에 다음을 추가한다.
IplImage* m_Image;
CvCapture* m_Capture;
CvvImage m_cImage; 

 

6) ...Dlg.cpp에 다음을 추가한다.

// Timer 대신에 쓰레드를 사용해 보자. 먼저 전역변수와 함수를 다음과 같이 선언한다.

// 카메라 영상 캡처 시작 버튼을 누르면 다음과 같이 쓰레드를 실행한다.

BOOL m_bThreadFlag; 
UINT ThreadImageCaptureFunc(LPVOID param);


IplImage* m_Image;
CvCapture* m_Capture;


BOOL CcvCam2013Dlg::OnInitDialog()
{
 ...
 m_Capture = cvCreateCameraCapture(0);
 if (!m_Capture)  AfxMessageBox(_T("There is no camera captured!"));
}

void CcvCam2013Dlg::OnPaint()
{
  ...
  if (m_Image != NULL) {
   // ---------------------------------------------------------------
   // 카메라에서 읽어들인 영상을 화면에 그리기
   // ---------------------------------------------------------------
   CRect rect;
   CDC* pDC;
   pDC = m_View.GetDC();
   m_View.GetClientRect(rect);
   m_cImage.CopyOf(m_Image);
   m_cImage.DrawToHDC(pDC->m_hDC, rect);
   ReleaseDC(pDC);

   // ---------------------------------------------------------------
   // 다음 소스를 통해 영상 데이터를 다루는 법을 익힌다.
   // ---------------------------------------------------------------
   // 칼라영상을 이진화 한다.
   pDC = m_BinView.GetDC();
   m_BinView.GetClientRect(rect);

   for (int y = 0; y < m_Image->height; y++) {
    for (int x = 0; x < m_Image->widthStep; x++) {
     if ((unsigned char) m_Image->imageData[y*m_Image->widthStep + x] > 100)
      m_Image->imageData[y*m_Image->widthStep + x] = 255;
     else
      m_Image->imageData[y*m_Image->widthStep + x] = 0;
    }
   }
   // ---------------------------------------------------------------
   m_cImage.CopyOf(m_Image);
   m_cImage.DrawToHDC(pDC->m_hDC, rect);
   ReleaseDC(pDC);
  }
}

void CcvCam2013Dlg::OnBnClickedCamStart()
{

 SetTimer(1, 30, NULL);
}

void CcvCam2013Dlg::OnBnClickedCamStop()
{

 KillTimer(1);
}

void CcvCam2013Dlg::OnDestroy()
{
 CDialogEx::OnDestroy();


 KillTimer(1);
 if (m_Capture) cvReleaseCapture(&m_Capture);
}

void CcvCam2013Dlg::OnTimer(UINT_PTR nIDEvent)
{

 // 영상 획득
 m_Image = cvQueryFrame(m_Capture);
 Invalidate(FALSE);
 CDialogEx::OnTimer(nIDEvent);
}

void CcvCam2013Dlg::OnBnClickedLoadBmp()
{

 wchar_t szFilter[] = _T("Image (*.BMP) | *.BMP;*.GIF;*.JPG | All Files(*.*)|*.*||");
 CFileDialog dlg(TRUE, _T("bmp"), _T("test"), OFN_HIDEREADONLY, szFilter);
 if (dlg.DoModal() == IDOK)
 {
  CStringA filename(dlg.GetPathName().GetBuffer(0));
  m_Image = cvLoadImage(filename,1); // 1 for color
  Invalidate();
 }
}

void CcvCam2013Dlg::OnBnClickedSaveBmp()
{

 wchar_t szFilter[] = _T("Image (*.BMP) | *.BMP;*.GIF;*.JPG | All Files(*.*)|*.*||");
 CFileDialog dlg(FALSE, _T("bmp"), _T("test"), OFN_HIDEREADONLY, szFilter);
 if (dlg.DoModal() == IDOK)
 {
  cvFlip(m_Image, m_Image, 0); // vertical
  CStringA filename(dlg.GetPathName().GetBuffer(0));
  cvSaveImage(filename, m_Image);
 }


// 카메라 영상 캡처 시작 버튼을 누르면 다음과 같이 쓰레드를 실행한다.
void CcvCam2013Dlg::OnBnClickedCamStart()
{

 m_bThreadFlag = TRUE;
 CWinThread *pThread = ::AfxBeginThread(ThreadImageCaptureFunc, this);
}

// 멈춤 버튼을 누르면 다음과 같이 쓰레드 플래그를 false로 만들어 종료시킨다.
void CcvCam2013Dlg::OnBnClickedCamStop()
{

 m_bThreadFlag = FALSE;
 }

// 쓰레드 함수는 다음과같이 구성한다.
UINT ThreadImageCaptureFunc(LPVOID param)
{
 CcvCam2013Dlg *pDlg = (CcvCam2013Dlg *)param;
 while (m_bThreadFlag)
 {
  m_Image = cvQueryFrame(m_Capture);  // 영상 획득
  pDlg->Invalidate(FALSE);
 }
 return 0;
}

 

 

 

 

다음검색
현재 게시글 추가 기능 열기

댓글

댓글 리스트
맨위로

카페 검색

카페 검색어 입력폼