제 7 장 동영상 처리
학습목표 AVI 파일에 대해 설명할 수 있다 VFW 라이브러리를 활용할 수 있다 동영상과 PC 카메라 영상에 대한 영상처리 프로그램을 작성할 수 있다 2018-11-19 영상처리
AVI 파일 디스플레이 AVI(Audio Video Interface) 스트림(stream) 이라는 단위로 구성 AVI파일은 오디오 스트림, 비디오 스트림, MIDI 스트림, 텍스트 스트림 AVI파일은 하나 이상의 스트림을 포함해야 함 동일한 스트림을 여러 개 포함할 수도 있음 비디오 스트림 대부분 압축되어 있음 Cinepak, Indeo 등 여러 가지 형식 2018-11-19 영상처리
AVI 파일 디스플레이 VFW(Video For Windows) 비디오 처리를 위한 라이브러리 AVI 파일을 읽어서 각 frame을 추출할 수 있는 함수 제공 2018-11-19 영상처리
AVI 파일 디스플레이 LPBITMAPINFOHEADER pbmih; PAVIFILE pavi; PAVISTREAM pstm; PGETFRAME pfrm; AVIFILEINFO fi; AVISTREAMINFO si; int stm; int frame; int x, y; unsigned char *image; AVIFileInit(); AVIFileOpen(&pavi, AVIFileName, OF_READ | OF_SHARE_DENY_NONE,NULL); AVIFileInfo(pavi,&fi,sizeof(AVIFILEINFO)); 2018-11-19 영상처리
AVI 파일 디스플레이 AVIStreamGetFrameClose(pfrm); for (stm = 0;stm < fi.dwStreams; stm++) { AVIFileGetStream(pavi, &pstm, 0, stm); AVIStreamInfo(pstm, &si, sizeof(si)); if (si.fccType == streamtypeVIDEO) { pfrm = AVIStreamGetFrameOpen(pstm,NULL); for (frame = 0; frame < si.dwLength; frame++) { pbmih = (LPBITMAPINFOHEADER) AVIStreamGetFrame(pfrm,frame); if (!pbmih) contunue; image = (unsigned char *) ((LPSTR)pbmih + (WORD)pbmih->biSize); // 프레임의 비트맵 데이터 사용 (일차원 배열 형태로 저장비트맵 // 데이터를 image가 가리킴) } AVIStreamGetFrameClose(pfrm); AVIStreamRelease(pstm); AVIFileRelease(pavi); AVIFileExit(); 2018-11-19 영상처리
실습
AVI 파일 출력 프로그램 메뉴막대에 동영상 처리 메뉴 추가 [동영상 처리] 메뉴에 부메뉴 추가 이름 : 동영상 처리 ID : ID_AVI_VIEW 2018-11-19 영상처리
AVI 파일 출력 프로그램 CImageProView 클래스에 OnAviView() 함수를 추가 void CImageProView::OnAviView() { CFile file; CFileDialog dlg(TRUE); if(dlg.DoModal()==IDOK) { strcpy(AVIFileName, dlg.GetPathName()); viewMode = AVI_FILE; } Invalidate(FALSE); 2018-11-19 영상처리
AVI 파일 출력 프로그램 ImageProView.cpp 파일의 앞부분에 AVI_FILE 상수에 대한 정의를 추가 // 추가한 후 #define TWO_IMAGES 1 #define THREE_IMAGES 2 #define TWO_IMAGES_SCALED 4 #define MORPHING 8 #define AVI_FILE 16 2018-11-19 영상처리
AVI 파일 출력 프로그램 CImageProView 클래스에 AVIFileName 변수를 추가 class CImageProView : public CScrollView { protected: // create from serialization only CImageProView(); DECLARE_DYNCREATE(CImageProView) // Attributes public: CImageProDoc* GetDocument(); int viewMode; char AVIFileName[256]; // 추가된 부분 ... } 2018-11-19 영상처리
AVI 파일 출력 프로그램 OnDraw() 함수를 수정 void CImageProView::OnDraw(CDC* pDC) { CImageProDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); if (viewMode == AVI_FILE) { loadAVIFile(pDC); return; } if (pDoc->inputImg == NULL) return; ... 2018-11-19 영상처리
AVI 파일 출력 프로그램 loadAVIFile() 함수를 CImageProView 클래스에 추가한다. 반환 형식 : void 매개변수 형식 : CDC * 매개변수 이름 : pDC 2018-11-19 영상처리
AVI 파일 출력 프로그램 loadAVIFile() 함수 편집 void CImageProView::loadAVIFile(CDC * pDC) { LPBITMAPINFOHEADER pbmih; PAVIFILE pavi; PAVISTREAM pstm; PGETFRAME pfrm; AVIFILEINFO fi; AVISTREAMINFO si; int stm; int frame; int x, y; unsigned char *image; 2018-11-19 영상처리
AVI 파일 출력 프로그램 AVIFileInit(); AVIFileOpen(&pavi, AVIFileName, OF_READ | OF_SHARE_DENY_NONE, NULL); AVIFileInfo(pavi,&fi,sizeof(AVIFILEINFO)); for (stm=0;stm < fi.dwStreams;stm++) { AVIFileGetStream(pavi,&pstm,0,stm); AVIStreamInfo(pstm, &si, sizeof(si)); if (si.fccType == streamtypeVIDEO) { pfrm = AVIStreamGetFrameOpen(pstm,NULL); for (frame = 0; frame < si.dwLength; frame++) { pbmih = (LPBITMAPINFOHEADER) AVIStreamGetFrame(pfrm,frame); if (!pbmih) contunue; image = (unsigned char *) ((LPSTR)pbmih + (WORD)pbmih->biSize); 2018-11-19 영상처리
AVI 파일 출력 프로그램 for (y = 0; y < fi.dwHeight; y++) for (x = 0; x < fi.dwWidth; x++) pDC->SetPixel(x, fi.dwHeight-y-1, RGB(image[(y*fi.dwWidth+x)*3+2], image[(y*fi.dwWidth+x)*3+1], image[(y*fi.dwWidth+x)*3])); } AVIStreamGetFrameClose(pfrm); AVIStreamRelease(pstm); AVIFileRelease(pavi); AVIFileExit(); 2018-11-19 영상처리
AVI 파일 출력 프로그램 CImageProView.h 파일 앞부분에 VFW 라이브러리에 대한 헤더 파일에 대한 include 문을 추가 // ImageProView.cpp : implementation of the CImageProView class // #include "stdafx.h" #include "ImagePro.h" #include "ImageProDoc.h" #include "ImageProView.h" #include <vfw.h> // 추가된 부분 2018-11-19 영상처리
AVI 파일 출력 프로그램 VFW 라이브러리를 링크하도록 설정 솔루션 탐색기 뷰를 선택 ImagePro를 선택하고 마우스 오른쪽 버튼을 클릭 팝업메뉴에서 [속성] 메뉴 항목을 선택 2018-11-19 영상처리
AVI 파일 출력 프로그램 속성 창에서 [구성속성]=>[링커]=>[입력] 항목을 선택 [추가 종속성] 입력 상자를 선택하고 vfw32.lib를 입력 2018-11-19 영상처리
AVI 파일 출력 프로그램 프로그램을 컴파일하고 실행 [동영상처리] 메뉴에서 [AVI 파일 출력] 항목선택 2018-11-19 영상처리
비디오 캡쳐 함수 VFW 라이브러리의 비디오 캡쳐 함수 capGetDriverDescription() capCreateCaptureWindow() capDriverConnect() capPreviewRate() capPreview() capSetVideoFormat() capDriverDisconnect() capSetCallbackOnFrame() 2018-11-19 영상처리
비디오 캡쳐 함수 capGetDriverDescription() 함수 BOOL capGetDriverDescription(index, name, name_size, version, version_size); 입력 index : 드라이버 번호 (0 – 9) 출력 name : 캡쳐드라이버의 이름 version : 캡쳐드라이버의 버전 2018-11-19 영상처리
비디오 캡쳐 함수 capCreateCaptureWindow() 함수 HWND capCreateCaptureWindow(name, style, x, y, width, height, hWnd, id); 입력 name : 윈도우의 이름 style : 윈도우의 스타일 (예 : WS_CHILD, WS_VISIBLE) x,y : 캡쳐 윈도우의 좌측 상단의 좌표를 지정 width와 height : 캡쳐 윈도우의 크기 hWnd : 부모 윈도우의 핸들(handle) 값 id : 윈도우의 식별 번호 결과 윈도우가 정상적으로 생성되면 캡쳐 윈도우의 핸들 그렇지 않으면 NULL 값이 반환 2018-11-19 영상처리
비디오 캡쳐 함수 capDriverConnect() capDriverConnect(hWnd, index); 캡쳐 윈도우를 캡쳐 드라이버에 연결 입력 hWnd : 캡쳐 윈도우의 핸들 index : 캡쳐 드라이버의 번호 출력 TRUE : 캡쳐 장치가 정상적으로 작동하여 연결이 성공 FALSE : 실패 2018-11-19 영상처리
비디오 캡쳐 함수 capPreviewRate() capPreviewRate(hWnd, rate); 카메라에서 입력된 비디오를 파일에 저장하는 것이 아니라 화면에 보여줌 입력 hWnd : 캡쳐 윈도우의 핸들 값 rate : 밀리초(ms) 단위의 시간 rate 값을 66으로 설정하면 0.066초마다 새로운 비디오 프레임을 캡쳐해서 디스플레이 1초에 15개의 비디오 프레임이 디스플레이 2018-11-19 영상처리
비디오 캡쳐 함수 capPreview() capPreviewRate(hWnd, flag); 미리 보기 기능을 켜거나 끔 입력 flag : TRUE 또는 FALSE TRUE : 미리 보기 모드를 켬 FALSE : 미리 보기 모드를 끔 2018-11-19 영상처리
비디오 캡쳐 함수 capSetVideoFormat() capSetVideoFormat(hWnd, videoFormat, videoFormat_size); 캡쳐된 비디오 데이터 형식을 설정 입력 hWnd : 캡쳐 윈도우의 핸들 값 videoFormat 설정하고자 하는 비디오 데이터 형식 각 프레임에 대한 비트맵 형식을 BITMAPINFO 구조로 기술 2018-11-19 영상처리
비디오 캡쳐 함수 typedef struct tagBITMAPINFO { BITMAPINFOHEADER bmiHeader; RGBQUAD bmiColors[1]; } BITMAPINFO; typedef struct tagBITMAPINFOHEADER { DWORD biSize; // BITMAPINFOHEADER 구조체의 크기 LONG biWidth; // 영상의 가로 크기 LONG biHeight; // 영상의 세로 크기 (양수: 좌측하단, 음수:좌측상단이 원점) WORD biPlanes; // 목표 장치의 플레인 수(1로 설정해야함) WORD biBitCount; // 각 픽셀의 비트수 DWORD biCompression; // 압축 방법(BI_RGB 또는 0: 무압축 비트맵) DWORD biSizeImage; // 비트맵 영상 크기 (바이트단위) LONG biXPelsPerMeter; // 수평 해상도(미터당 픽셀 수) LONG biYPelsPerMeter; // 수직 해상도(미터당 픽셀 수) DWORD biClrUsed; // 사용된 컬러의 수 DWORD biClrImportant; // 비트맵 디스플레이에 사용되는 컬러 수 } BITMAPINFOHEADER; 2018-11-19 영상처리
비디오 캡쳐 함수 BITMAPINFOHEADER 구조체에서 biSize, biWidth, biHeight, biPlanes, biBitCount 값을 설정하고 나머지 값들은 0으로 설정하면 된다. BITMAPINFO bmi; memset( &bmi.bmiHeader, 0, sizeof(bmi.bmiHeader)); // 전체 값을 0으로 설정 bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader); bmi.bmiHeader.biWidth = 320; bmi.bmiHeader.biHeight = 240; bmi.bmiHeader.biPlanes = 1; bmi.bmiHeader.biBitCount = 24; 2018-11-19 영상처리
비디오 캡쳐 함수 capDriverDisconnect() capDriverDisconnect(hWnd); capDriverConnect() 함수에 의하여 연결한 캡쳐 윈도우와 캡쳐 장치를 분리 2018-11-19 영상처리
비디오 캡쳐 함수 capSetCallbackOnFrame() BOOL capSetCallbackOnFrame(hWnd, func); 캡쳐 장치로부터 비디오 프레임이 캡쳐되었을 때에 이를 화면에 보여주기 위해서 호출되는 callback 함수를 설정 입력 hWnd : 캡쳐 윈도우의 핸들 값 func : 호출될 함수 이름 2018-11-19 영상처리
실습
카메라 영상 출력 프로그램 [동영상 처리] 메뉴 아래에 카메라 영상 출력을 위한 부메뉴를 추가 이름 : 카메라 영상 출력 ID : ID_CAMERA_VIEW 2018-11-19 영상처리
카메라 영상 출력 프로그램 CImageProView 클래스에 OnCameraView() 함수를 추가 void CImageProView::OnCameraView() { char DeviceName[80]; char DeviceVersion[80]; HWND hWndC = 0; HWND parent = m_hWnd; // 현재의 윈도우의 핸들 for( int wIndex = 0; wIndex < 10; wIndex++ ) { if( capGetDriverDescription( wIndex, DeviceName, sizeof (DeviceName), DeviceVersion, sizeof (DeviceVersion))) hWndC = capCreateCaptureWindow ( "My Own Capture Window", WS_CHILD | WS_VISIBLE , 0, 0, 320, 240, parent, 0); 2018-11-19 영상처리
카메라 영상 출력 프로그램 if( capDriverConnect (hWndC, wIndex)) { BITMAPINFO bmi; capPreviewRate(hWndC, 66); // rate, in milliseconds capPreview(hWndC, TRUE); // 미리 보기 기능을 켬 memset( &bmi.bmiHeader, 0, sizeof(bmi.bmiHeader)); bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader); bmi.bmiHeader.biBitCount = 24; bmi.bmiHeader.biWidth = 320; bmi.bmiHeader.biHeight = 240; bmi.bmiHeader.biPlanes = 1; if (capSetVideoFormat( hWndC, &bmi, sizeof(bmi))) { capSetCallbackOnFrame( hWndC, FrameCallbackProc); viewMode = CAMERA; imageProView_obj = this; return; } else capDriverDisconnect( hWndC ); ::DestroyWindow(hWndC); } } } 2018-11-19 영상처리
카메라 영상 출력 프로그램 ImageProView.cpp 파일의 앞부분에 카메라 영상 출력에 대한 정의를 추가 // 추가한 후 #define TWO_IMAGES 1 #define THREE_IMAGES 2 #define TWO_IMAGES_SCALED 4 #define MORPHING 8 #define AVI_FILE 16 #define CAMERA 32 2018-11-19 영상처리
카메라 영상 출력 프로그램 FrameCallbackProc() 함수를 정의 CImageProView * imageProView_obj; void FrameCallbackProc( HWND hWnd, VIDEOHDR* hdr ) { if(hWnd && hdr && hdr->lpData) if( imageProView_obj ) imageProView_obj->OnFrame((unsigned char *) hdr->lpData); } 2018-11-19 영상처리
카메라 영상 출력 프로그램 CImageProView 클래스에 OnFrame() 함수를 추가 반환 형식: void 매개변수 형식 : unsigned char * 매개변수 이름 : data 2018-11-19 영상처리
카메라 영상 출력 프로그램 OnFrame() 함수를 다음과 같이 편집 void CImageProView::OnFrame(unsigned char *data) { Invalidate(FALSE); } 2018-11-19 영상처리
카메라 영상 출력 프로그램 OnDraw() 함수의 첫 부분을 수정 void CImageProView::OnDraw(CDC* pDC) { CImageProDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); if (viewMode == AVI_FILE) { loadAVIFile(pDC); return; } else if (viewMode == CAMERA) { ... 2018-11-19 영상처리
카메라 영상 출력 프로그램 프로그램을 컴파일하고 수행 [동영상 처리] 메뉴에서 [카메라 영상 출력] 항목을 선택 2018-11-19 영상처리
동영상에 대한 선명화 연산 [동영상 처리] 메뉴에 선명화 연산에 대한 부메뉴를 추가 이름 : 선명화(카메라) ID : ID_VIDEO_SHARPENING 2018-11-19 영상처리
동영상에 대한 선명화 연산 CImageProView 클래스에 OnVideoSharpening() 함수를 추가 void CImageProView::OnVideoSharpening() { operation = SHARPENING; } 2018-11-19 영상처리
동영상에 대한 선명화 연산 변수 operation을 CImageProView 클래스 정의에 추가 class CImageProView : public CScrollView { protected: // create from serialization only CImageProView(); DECLARE_DYNCREATE(CImageProView) // Attributes public: CImageProDoc* GetDocument(); int viewMode; char AVIFileName[256]; int operation; // 추가된 부분 … } 2018-11-19 영상처리
동영상에 대한 선명화 연산 CImageProView() 함수에서 변수 operation의 값을 초기화 CImageProView::CImageProView() { operation = NO_OP; } 2018-11-19 영상처리
동영상에 대한 선명화 연산 변수 operation에 사용할 상수를 CImageProView.cpp 파일 첫 부분에 정의 #define TWO_IMAGES 1 #define THREE_IMAGES 2 #define TWO_IMAGES_SCALED 4 #define MORPHING 8 #define AVI_FILE 16 #define CAMERA 32 #define NO_OP 0 // 추가된 부분 #define SHARPENING 1 // 추가된 부분 2018-11-19 영상처리
동영상에 대한 선명화 연산 OnFrame() 함수를 다음과 같이 수정 void CImageProView::OnFrame(unsigned char *data) { if (operation == NO_OP) { Invalidate(FALSE); } else if (operation == SHARPENING) CImageProDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); int x, y, i; pDoc->imageHeight = 240; pDoc->imageWidth = 320; pDoc->depth = 3; 2018-11-19 영상처리
동영상에 대한 선명화 연산 // 기억장소 할당 pDoc->inputImg = (unsigned char **) malloc(pDoc->imageHeight * sizeof(unsigned char *)); pDoc->resultImg = (unsigned char **) malloc(pDoc->imageHeight * for (i = 0; i < pDoc->imageHeight; i++) { pDoc->inputImg[i] = (unsigned char *) malloc(pDoc->imageWidth * pDoc->depth); pDoc->resultImg[i] = (unsigned char *) malloc(pDoc->imageWidth * } 2018-11-19 영상처리
동영상에 대한 선명화 연산 // 입력 영상 복사 for (y = 0; y < pDoc->imageHeight; y++) for (x = 0; x < pDoc->imageWidth * pDoc->depth; x++) pDoc->inputImg[y][x] = data[y*pDoc->imageWidth*3+x]; // 영상 처리 수행 pDoc->RegionSharpening(); // 결과 영상 복사 data[y*pDoc->imageWidth*3+x] = pDoc->resultImg[y][x]; Invalidate(FALSE); } 2018-11-19 영상처리
동영상에 대한 선명화 연산 [동영상 처리] 메뉴에서 [카메라 영상 출력] 부메뉴를 선택 그 다음에 [선명화(카메라)] 부메뉴를 선택 2018-11-19 영상처리
동영상에 대한 차영상 출력 [동영상 처리] 메뉴에 차영상 출력을 위한 부메뉴를 추가 이름 : 차영상 출력(카메라) ID : ID_VIDEO_SUBTRACT 2018-11-19 영상처리
동영상에 대한 차영상 출력 CImageProView 클래스에 OnVideoSubtract() 함수를 추가 void CImageProView::OnVideoSubtract() { operation = SUBTRACT; } 2018-11-19 영상처리
동영상에 대한 차영상 출력 CImageProView.cpp 파일 첫 부분에 SUBTRACT 상수를 정의 #define TWO_IMAGES 1 #define THREE_IMAGES 2 #define TWO_IMAGES_SCALED 4 #define MORPHING 8 #define AVI_FILE 16 #define CAMERA 32 #define NO_OP 0 #define SHARPENING 1 #define SUBTRACT 2 2018-11-19 영상처리
동영상에 대한 차영상 출력 OnFrame() 함수를 다음과 같이 수정 void CImageProView::OnFrame(unsigned char *data) { if (operation == NO_OP) { Invalidate(FALSE); } else if (operation == SHARPENING) CImageProDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); int x, y, i; pDoc->imageHeight = 240; pDoc->imageWidth = 320; pDoc->depth = 3; 2018-11-19 영상처리
동영상에 대한 차영상 출력 // 기억장소 할당 pDoc->inputImg = (unsigned char **) malloc(pDoc->imageHeight * sizeof(unsigned char *)); pDoc->resultImg = (unsigned char **) malloc(pDoc->imageHeight * for (i = 0; i < pDoc->imageHeight; i++) { pDoc->inputImg[i] = (unsigned char *) malloc(pDoc->imageWidth * pDoc->depth); pDoc->resultImg[i] = (unsigned char *) malloc(pDoc->imageWidth * } 2018-11-19 영상처리
동영상에 대한 차영상 출력 // 입력 영상 복사 for (y = 0; y < pDoc->imageHeight; y++) for (x = 0; x < pDoc->imageWidth * pDoc->depth; x++) pDoc->inputImg[y][x] = data[y*pDoc->imageWidth*3+x]; // 영상 처리 수행 pDoc->RegionSharpening(); // 결과 영상 복사 data[y*pDoc->imageWidth*3+x] = pDoc->resultImg[y][x]; Invalidate(FALSE); } 2018-11-19 영상처리
동영상에 대한 차영상 출력 else if (operation == SUBTRACT) { static unsigned char *image1=NULL; static unsigned char *image2=NULL; int i, length = 320 * 240 * 3; if (image1 == NULL) { image1 = (unsigned char *) malloc(length); for (i = 0; i < length; i++) image1[i] = data[i]; } else if (image2 == NULL) { image2 = (unsigned char *) malloc(length); for (i = 0; i < length; i++) image2[i] = data[i]; else { for (i = 0; i < length; i++) image1[i] = image2[i]; 2018-11-19 영상처리
동영상에 대한 차영상 출력 if (image1 && image2) { for (int i = 0; i < 320 * 240; i++) { if (abs((image1[i*3]+image1[i*3+1]+image1[i*3+2]) / 3 - (image2[i*3]+image2[i*3+1]+image2[i*3+2]) / 3) < 20) { data[i*3] = (char ) 0; data[i*3+1] =(char ) 0; data[i*3+2] = (char )0; } else { data[i*3] = (char )255; data[i*3+1] = (char )255; data[i*3+2] = (char )255; Invalidate(FALSE); }} 2018-11-19 영상처리
동영상에 대한 차영상 출력 프로그램을 컴파일하고 실행 카메라 앞에서 움직이면 움직인 부분이 하얀색으로 표시됨 2018-11-19 영상처리