이번에는 자주 사용하는 기능인 C&P(Copy & Paste)를 구현해 보도록 하겠습니다.
C&P를 구현하기 위해서는 이미지를 DIB(장치 독립 비트맵;Device Independent Bitmap)으로 변환하여 클립보드 영역에 넘겨주거나, 클립보드 영역에서 데이터를 받아오면 됩니다.
클립보드 영역으로 데이터를 넘겨주기 위하여 CopyClipBoard맴버함수, CopyToHandle 맴버 함수를 추가하여, ID_EDIT_COPY이벤트 핸들러에 등록합니다.
먼저 CopyClipBoard 맴버 함수를 Doc Class에 작성하겠습니다.
반환 형식 : void
함수 이름 : CopyClipBoard
매개 변수 형식 : IplImage *
매개 변수 이름 : m_pCopyImage
다음과 같이 코딩합니다.
HANDLE hDIB = CopyToHandle(m_pCopyImage); if(::OpenClipboard(AfxGetMainWnd()->GetSafeHwnd())){ if(::EmptyClipboard()){ if(NULL == ::SetClipboardData(CF_DIB, hDIB)){ AfxMessageBox(TEXT("클립보드에 영상을 저장하는 도중 오류가 발생했습니다.")); } } } ::CloseClipBoard();
이번에는 아까와 같은 방법으로 CopyToHandle 맴버 함수를 작성합니다.
반환 형식 : HANDLE
함수 이름 : CopyToHandle
매개 변수 형식 : IplImage *
매개 변수 이름 : image
다음과 같이 코딩합니다. (출처 : 오픈소스 OpenCV를 이용한 컴퓨터 비전 실무 프로그래밍 p237)
int nChannels = image->nChannels; int bpp = 8*nChannels; IplImage *flip_image = cvCloneImage( image ); cvFlip( image, flip_image, 0 ); int bmpDataSize = flip_image->imageSize; // 비트맵 헤더 구성 BITMAPINFOHEADER bmpHeader; bmpHeader.biSize = sizeof(BITMAPINFOHEADER); // 이 구조체의 크기 bmpHeader.biHeight = image->height; // 영상의 높이 bmpHeader.biWidth = image->width; // 영상의 너비 bmpHeader.biPlanes = 1; // 비트 플레인 수 (항상 1임) bmpHeader.biBitCount = bpp; // 한 화소당 비트 개수 bmpHeader.biCompression = BI_RGB; // BI_RGB : 압축하지 않음 bmpHeader.biSizeImage = bmpDataSize; // 영상의 크기 if(8 == bpp) bmpHeader.biClrUsed = bmpHeader.biClrImportant = 256; else if(24 == bpp) bmpHeader.biClrUsed = bmpHeader.biClrImportant = 0; int bmpAllSize = sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD)*256 + bmpDataSize*sizeof(char); HGLOBAL hDIB = (HGLOBAL)::GlobalAlloc(GMEM_MOVEABLE| GMEM_ZEROINIT, bmpAllSize ); if(NULL == hDIB) return NULL; LPSTR pDIB = (LPSTR)::GlobalLock((HGLOBAL)hDIB); memcpy( pDIB, &bmpHeader, sizeof(BITMAPINFOHEADER) ); if(8 == bpp){ RGBQUAD palette[256]; memcpy( pDIB+sizeof(BITMAPINFOHEADER), palette, sizeof(RGBQUAD)*256 ); memcpy( pDIB+sizeof(BITMAPINFOHEADER)+sizeof(RGBQUAD)*256, flip_image->imageData, bmpDataSize ); } else if(24 == bpp){ memcpy( pDIB+sizeof(BITMAPINFOHEADER), flip_image->imageData, bmpDataSize ); } ::GlobalUnlock((HGLOBAL)hDIB); cvReleaseImage( &flip_image ); return hDIB;
이제 ID_EDIT_COPY에 대한 이벤트 핸들러만 수정해 주면 끝입니다.
이벤트 핸들러를 수정하기 위해 다음과 같이 합니다.
void CMFC_OpenCVView::OnEditCopy() { // TODO: 여기에 명령 처리기 코드를 추가합니다. CMFC_OpenCVDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); pDoc->CopyClipBoard( pDoc->m_CvvImage.GetImage() ); }
프로그램에서 Copy하여 그림판 같은 곳에 Paste했을 때 정상적으로 동작한다면 성공.
이번에는 Paste기능을 구현해 보도록 하겠습니다. Paste는 Copy의 역순입니다.
CreateFromHandle맴버 함수를 작성하고, ID_EDIT_PASTE에 이벤트 핸들러를 등록하고, 마지막으로 클립보드 영역에 DIB 데이터가 존재할 경우에만 ID_EDIT_PASTE가 동작하도록 하기 위해서 OnUpdateEditPaste 맴버 함수를 작성하겠습니다. 이번에는 모든 함수가 App Class에서 작성될 것이고, 코드만 첨부합니다.
void CMFC_OpenCVApp::OnEditPaste() { // TODO: 여기에 명령 처리기 코드를 추가합니다. POSITION pos = GetFirstDocTemplatePosition(); CDocTemplate *pTemplate = GetNextDocTemplate(pos); CMFC_OpenCVDoc *pDoc = (CMFC_OpenCVDoc* )pTemplate->OpenDocumentFile(NULL); if( pDoc ){ HANDLE hDIB = NULL; if (::OpenClipboard(AfxGetMainWnd()->GetSafeHwnd())) hDIB = ::GetClipboardData(CF_DIB); if( hDIB ){ IplImage *image = CreateFromHandle( hDIB ); pDoc->m_CvvImage.CopyOf( image, image->nChannels*8 ); POSITION pos = pDoc->GetFirstViewPosition(); CMFC_OpenCVView *pView = (CMFC_OpenCVView *)pDoc->GetNextView(pos); CSize sizeTotal = CSize(pDoc->m_CvvImage.Width(), pDoc->m_CvvImage.Height()); pView->SetScrollSizes(MM_TEXT, sizeTotal); pView->ResizeParentToFit(FALSE); } ::CloseClipboard(); } }
IplImage * CMFC_OpenCVApp::CreateFromHandle(HANDLE hDIB) { LPSTR pDIB = (LPSTR)::GlobalLock((HGLOBAL)hDIB); BITMAPINFOHEADER bmpHeader; memcpy( &bmpHeader, pDIB, sizeof(BITMAPINFOHEADER) ); int height = bmpHeader.biHeight; int width = bmpHeader.biWidth; int nChannels = bmpHeader.biBitCount/8; int bmpDataSize = bmpHeader.biSizeImage; char *imageData = (char *)calloc( bmpDataSize, sizeof(char) ); if(8 == bmpHeader.biBitCount) memcpy( imageData, pDIB+sizeof(BITMAPINFOHEADER)+sizeof(RGBQUAD)*256, bmpDataSize ); else if(24 == bmpHeader.biBitCount) memcpy( imageData, pDIB+sizeof(BITMAPINFOHEADER), bmpDataSize ); IplImage *flip_image = cvCreateImage(cvSize(width,height), IPL_DEPTH_8U, nChannels); memcpy( flip_image->imageData, imageData, bmpDataSize ); IplImage *recover_image = cvCloneImage( flip_image ); cvFlip( flip_image, recover_image, 0 ); ::GlobalUnlock((HGLOBAL)hDIB); free(imageData); cvReleaseImage( &flip_image ); return recover_image; }
void CMFC_OpenCVApp::OnUpdateEditPaste(CCmdUI *pCmdUI) { // TODO: 여기에 명령 업데이트 UI 처리기 코드를 추가합니다. if( !IsClipboardFormatAvailable(CF_DIB) ) pCmdUI->Enable( FALSE ); }
App Class의 헤더 파일에 #include <cv.h>를 추가해 줘야 합니다.
cfile25.uf.19311C444D273F25031758.pdfcfile7.uf.1227C1444D273F261C4D3D.zip