MFC에서 VFW(Video For Window)를 사용하여 웹캠의 영상을 가져온 후, 그레이스케일 영상으로 변환하여
PIcture Control에 원본과 그레이스케일 영상을 보여주도록 만들어 보았습니다.
ps. 프로젝트가 32비트인 경우에만 정상적으로 동작하는 듯합니다. 64비트로 변경시 capDriverConnect 함수에서 에러가 나네요.
2020.11.20 다시 테스트해보니 64비트 에서도 정상적으로 웸캠 영상을 가져옵니다. 하지만 일부 웹캠에서 사용하는 픽셀 포맷과 안맞아서 검은 화면이 나오는 듯합니다.
OpenCV를 사용하여 얻은 영상을 Picture Control에 보여주는 방식도 있습니다. 다음 포스트를 참고하세요.
OpenCV와 MFC 연동해서 웹캠 영상을 출력하기
https://webnautes.tistory.com/2039
이미지를 OpenCV의 포맷으로 변환하는 것은 나중에 해볼까 합니다.
1. 우선 Visual Studio에서 MFC 프로젝트를 만듭니다. 포스트에서는 Visual Studio 2019를 사용하였습니다.
MFC 항목이 보이지 않는다면 다음 포스트를 참고하여 추가 패키지를 설치하세요.
C/C++, Win32 API, MFC 개발을 위해 Visual Studio Community 2019 설치하는 방법
https://webnautes.tistory.com/1328
응용 프로그램 종류로 “대화 상자 기반”을 선택하고 SDL은 체크 해제합니다.
추가로 바꿀 옵션은 없습니다. 마침 버튼을 클릭합니다.
2. 왼쪽에 보이는 프로젝트 창에서 리소스 뷰를 선택합니다
보이지 않는다면 메뉴에서 보기 > 리소스 뷰를 선택하세요.
Dialog 항목에서 다이얼로그를 선택합니다. IDD_ABOUTBOX 아래에 있는 것을 선택하면 됩니다.
3. 마우스로 모서리를 클릭후 드래그하여 다이얼로그의 크기를 적절히 조정한 후..
비주얼 스튜디오 오른쪽에 보이는 도구 상자를 클릭하여 Picture Control을 드래그하여 두 개 가져옵니다.
Picture Control의 크기를 적절히 조정한 후, 스크린샷처럼 나란히 배치합니다.
각각의 Picture Control을 선택한 후, 마우스 우클릭하여 보이는 메뉴에서 속성을 선택합니다.
각각 ID를 ICD_PICTURE1, IDC_PICTURE2로 변경합니다.
굳이 안해줘도 되지만 해주는 편이 좋을 듯합니다.
각각의 Picture Control을 선택한 후, 마우스 우클릭하여 보이는 메뉴에서 변수 추가를 선택합니다.
각각 변수 이름을 m_picture1, m_picture2로 추가한 후, 마침 버튼을 클릭하면 됩니다.
4. 프로젝트 창에서 솔루션 탐색기를 선택한 후,
소스 파일 항목에서 Dlg.cpp로 끝나는 파일을 선택합니다.
본 포스트에서는 webcam_exampleDlg.cpp입니다.
5. 소스 파일 상단에 다음 코드를 추가합니다.
#ifdef _DEBUG #define new DEBUG_NEW #endif #include <vfw.h> #pragma comment (lib,"vfw32.lib") HWND hWindow; int iBitmapFlag = 0; CBitmap capture_map; unsigned char *arrImage1; unsigned char *arrImage2; |
6. OnInitDialog 함수에 다음 코드를 추가합니다
// TODO: 여기에 추가 초기화 작업을 추가합니다. hWindow = capCreateCaptureWindow(NULL, WS_CHILD | WS_VISIBLE, 0, 0, 640, 480, m_hWnd, 1); bool ret = capDriverConnect(hWindow, 0); if (ret == false) { AfxMessageBox(L"Webcam not found", MB_ICONERROR); return false; } // picture control에 영상이 보이도록 합니다. CRect wndRC; m_picture1.GetClientRect(wndRC); m_picture1.MapWindowPoints(this, wndRC); wndRC.DeflateRect(1, 1, 1, 1); ::SetWindowPos(hWindow, NULL, wndRC.left, wndRC.top, wndRC.Width(), wndRC.Height(), SWP_NOZORDER); ::ShowWindow(hWindow, SW_SHOW); // 일정 주기로 웹캠으로부터 영상을 가져오기 위해 타이머를 사용합니다. SetTimer(1, 30, 0); return TRUE; // 포커스를 컨트롤에 설정하지 않으면 TRUE를 반환합니다. |
7. 메뉴에서 프로젝트 > 클래스 마법사를 선택합니다.
클래스 이름이 Dlg로 끝나는 클래스인지 확인한 후, 왼쪽에 보이는 탭에서 메시지를 선택합니다.
메시지 목록에서 WM_TIMER를 선택 한 후, 오른쪽에 보이는 처리 추가 버튼을 클립합니다.
기존 처리기에 OnTimer 함수가 추가된 것을 볼 수 있습니다.
8. 메시지 항목에서 WM_DESTROY를 선택하고 처리기 추가 버튼을 클릭합니다.
OnDestroy 함수가 추가된 것을 볼 수 있습니다. 확인 버튼을 클릭합니다.
9. OnTimer 함수에 다음 코드를 추가합니다.
// TODO: 여기에 메시지 처리기 코드를 추가 및/또는 기본값을 호출합니다. if (nIDEvent == 1) { capGrabFrame(hWindow); } RECT rect; m_picture1.GetClientRect(&rect); int window_width = rect.right; int window_height = rect.bottom; int image_width = window_width; int image_height = window_height; int b = 4; arrImage1 = new unsigned char[b * image_width * image_height]; arrImage2 = new unsigned char[b * image_width * image_height]; BITMAPINFO bmpinfo; bmpinfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); bmpinfo.bmiHeader.biWidth = image_width; bmpinfo.bmiHeader.biHeight = -image_height; bmpinfo.bmiHeader.biPlanes = 1; bmpinfo.bmiHeader.biBitCount = 32; bmpinfo.bmiHeader.biCompression = BI_RGB; bmpinfo.bmiHeader.biSizeImage = 0; bmpinfo.bmiHeader.biXPelsPerMeter = 0; bmpinfo.bmiHeader.biYPelsPerMeter = 0; bmpinfo.bmiHeader.biClrUsed = 0; bmpinfo.bmiHeader.biClrImportant = 0; // 웹캠 영상을 배열에 저장합니다. CDC dc; HDC hdc = ::GetDC(m_picture1.m_hWnd); dc.Attach(hdc); CDC capture_dc; if (iBitmapFlag == 0) { capture_map.CreateCompatibleBitmap(&dc, image_width, image_height); iBitmapFlag = 1; } capture_dc.CreateCompatibleDC(&dc); capture_dc.SelectObject(&capture_map); capture_dc.BitBlt(0, 0, image_width, image_height, &dc, 0, 0, SRCCOPY); LPBYTE lpBits = arrImage1; int err; int ret = GetDIBits(capture_dc, capture_map, 0, image_height, lpBits, &bmpinfo, DIB_RGB_COLORS); if (ret == 0) err = GetLastError(); capture_dc.DeleteDC(); ReleaseDC(&dc); // 그레이스케일 영상 or 컬러 영상 보여줄지 여부 int iFlagGray = 1; for (int y = 0; y < image_height; y++) { for (int x = 0; x < image_width; x++) { int index1 = ((y * image_width) + x); index1 *= 4; if (iFlagGray == 1) //grayscale { // 그레이스케일로 변환합니다. // 편의상 그레이스케일 이미지도 32비트 영상입니다. int grayblue = arrImage1[index1] * 0.11; // 11% int graygreen = arrImage1[index1 + 1] * 0.59; // 59% int grayred = arrImage1[index1 + 2] * 0.30; // 30% int grayall = grayblue + graygreen + grayred; arrImage2[index1] = grayall; // blue arrImage2[index1 +1] = grayall; // green arrImage2[index1 +2] = grayall; // red arrImage2[index1 +3] = 0; // this byte is not used } else //color { arrImage2[index1] = arrImage1[index1]; //blue arrImage2[index1 +1] = arrImage1[index1 + 1]; //green arrImage2[index1 +2] = arrImage1[index1 + 2]; //red arrImage2[index1 +3] = 0; // this byte is not used } } } int bpp = 32; CImage cimage_mfc; cimage_mfc.Create(window_width, window_height, 32); BITMAPINFO *bitInfo = (BITMAPINFO*)malloc(sizeof(BITMAPINFO) + 256 * sizeof(RGBQUAD)); bitInfo->bmiHeader.biBitCount = bpp; bitInfo->bmiHeader.biWidth = window_width; bitInfo->bmiHeader.biHeight = -window_height; 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) // 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; } }*/ // 이미지 크기와 영상을 보여줄 영역 크기가 같은 경우와 다른 경우를 다르게 처리 if (image_width == window_width && image_height == window_height) { SetDIBitsToDevice(cimage_mfc.GetDC(), 0, 0, window_width, window_height, 0, 0, 0, image_height, arrImage2, bitInfo, DIB_RGB_COLORS); } else { int destx = 0, desty = 0; int destw = window_width; int desth = window_height; int imgx = 0, imgy = 0; int imgWidth = image_width; int imgHeight = image_height; StretchDIBits(cimage_mfc.GetDC(), destx, desty, destw, desth, imgx, imgy, imgWidth, imgHeight, arrImage2, bitInfo, DIB_RGB_COLORS, SRCCOPY); } // picture control에 이미지를 보여줍니다. HDC dc2 = ::GetDC(m_picture2.m_hWnd); cimage_mfc.BitBlt(dc2, 0, 0); ::ReleaseDC(m_picture2.m_hWnd, dc2); cimage_mfc.ReleaseDC(); cimage_mfc.Destroy(); delete arrImage1; delete arrImage2; CDialogEx::OnTimer(nIDEvent); |
10. OnDestroy 함수에 다음 코드를 추가합니다.
void CwebcamexampleDlg::OnDestroy() { CDialogEx::OnDestroy(); // TODO: 여기에 메시지 처리기 코드를 추가합니다. capCaptureStop(hWindow); capDriverDisconnect(hWindow); } |
참고한 곳
http://www.orgler.it/webcam.htm
'개발 환경 > Windows 설치 및 활용' 카테고리의 다른 글
Visual Studio 2022에서 WinAPI 프로젝트 생성하는 방법 (0) | 2023.10.20 |
---|---|
C/C++, Win32 API, MFC 개발을 위해 Visual Studio Community 2022 설치하는 방법 (0) | 2023.10.18 |
윈도우에서 tee 사용하여 화면출력과 파일 저장을 동시에 하기 (0) | 2023.10.12 |
윈도우에서 안드로이드 태블릿을 세컨드 모니터로 사용하기 (0) | 2023.10.12 |
구글 로그인 안되고 무한 로딩시 해결방법 (0) | 2023.10.12 |
시간날때마다 틈틈이 이것저것 해보며 블로그에 글을 남깁니다.
블로그의 문서는 종종 최신 버전으로 업데이트됩니다.
여유 시간이 날때 진행하는 거라 언제 진행될지는 알 수 없습니다.
영화,책, 생각등을 올리는 블로그도 운영하고 있습니다.
https://freewriting2024.tistory.com
제가 쓴 책도 한번 검토해보세요 ^^
그렇게 천천히 걸으면서도 그렇게 빨리 앞으로 나갈 수 있다는 건.
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!