03장 영상처리를 위한 Visual C++ 디지털 영상 파일 포맷 MFC AppWizard[exe]를 이용한 MFC 프로젝트 작성 MFC를 이용한 영상처리 입.출력 프로그램 작성 MFC를 이용한 영상 축소 MFC를 이용한 영상 확대 MFC를 이용한 양자화 영상처리 한빛미디어(주)
학습목표 3장. 영상처리를 위한 Visual C++ 영상처리에 사용되는 RAW 파일 포맷을 이해한다. MFC AppWizard[exe]를 이용한 영상처리 프로그램 기법을 익힌다. MFC의 기본 구조를 이해한다.
영상 파일 포맷의 종류: BMP, JPEG, RAW, GIF, PSD, TIFF 등 BMP, JPEG 파일 포맷 Section 01 디지털 영상 파일 포맷 영상 파일 포맷의 종류: BMP, JPEG, RAW, GIF, PSD, TIFF 등 BMP, JPEG 파일 포맷 영상의 색상 정보, 해상도 등을 알 수 있는 정보가 헤더(Header)에 포함되어 있음 → 추가 작업이 필요해 프로그램이 더 복잡해짐. RAW 파일 포맷 헤더 정보 없이 완전한 데이터만으로 구성 → 복잡한 헤더 정보를 해석할 필요가 없어 영상처리가 복잡하지 않음. 헤더 정보가 없어 영상의 색상 정보나 해상도 정보를 사용자가 미리 알아야 하는 단점이 있음.
Raw와 BMP 파일 구조
[실습하기 3-1] MFC AppWizard[exe]를 이용한 MFC프로젝트 작성
Section 03 MFC를 이용한 영상처리 입출력 프로그램 작성 디지털 영상을 처리하려면 디지털 영상을 Visual C++로 입력받은 뒤 일정한 루틴 따라 처리한 후 그 결과를 다시 디지털 영상 파일로 저장하는 과정이 필요. RAW 포맷의 디지털 영상에는 0~255로 구성된 8비트 그레이 레벨이 있어 C 언어의 unsigned char(0~255, 8비트)로 저장 가능 디지털 영상처리의 입력이나 출력 값은 모두 정수형이지만, 다양한 처리과정에서 실수 값이 나올 수도 있음. 이 때는 입력 영상의 데이터 값을 실수형(double)로 전환하여 영상을 처리해야 데이터가 손실되지 않음. OnOpenDocument 함수를 이용한 영상 파일 입력 영상 데이터를 파일에서 읽어 오려면 OnOpenDocument 함수를 재정의해야 함. OnOpenDocument 함수는 [파일]-[열기] 메뉴를 클릭하면 파일을 입력받을 수 있는 대화상자를 실행시키는 역할 수행
[실습하기 3-2] OnOpenDocument 함수를 이용한 파일 입력 프로그램 CImageProcessingDoc클래스에 OnOpenDocument 함수 추가 [프로젝트]-[클래스 마법사]에서
[실습하기 3-2] OnOpenDocument 함수를 이용한 파일 입력 프로그램 → CImageProcessingDoc클래스에 가상함수 OnOpenDocument 선택하고 [함수추가] 버튼 클릭
[실습하기 3-2] OnOpenDocument 함수를 이용한 파일 입력 프로그램
[실습하기 3-2] OnOpenDocument 함수를 이용한 파일 입력 프로그램 CImageProcessingDoc클래스에 변수추가 영상을 입력 받으려면 영상 데이터를 저장하는 변수(영상의 가로축, 세로축, 전체크기)를 지정해야 함. 클래스뷰 창으로 이동해서 CImageProcessingDoc 바로가기 메뉴에서 변수추가 선택
[실습하기 3-2] OnOpenDocument 함수를 이용한 파일 입력 프로그램 → 변수추가마법사 창에서 아래와 같이 변수 추가 (4개 변수이므로 4번 반복)
[실습하기 3-2] OnOpenDocument 함수를 이용한 파일 입력 프로그램 ImageProcessingDoc.cpp의 OnOpenDocument로 이동하여 함수 재정의 BOOL CImageProcessingDoc::OnOpenDocument(LPCTSTR lpszPathName) { if (!CDocument::OnOpenDocument(lpszPathName)) return FALSE; CFile File; // 파일 객체 선언 File.Open(lpszPathName, CFile::modeRead | CFile::typeBinary); // 파일 열기 대화상자에서 선택한 파일을 지정하고 읽기 모드 선택 // 이 책에서는 영상의 크기 256*256, 512*512, 640*480만을 사용한다. if(File.GetLength() == 256*256){ // RAW 파일의 크기 결정 m_height = 256; m_width = 256; } else if(File.GetLength() == 512*512){ // RAW 파일의 크기 결정 m_height = 512; m_width = 512; else if(File.GetLength() == 640*480){ // RAW 파일의 크기 결정 m_height = 480; m_width = 640; else{ AfxMessageBox("Not Support Image Size"); // 해당 크기가 없는 경우 return 0; m_size = m_width * m_height; // 영상의 크기 계산 m_InputImage = new unsigned char [m_size]; // 입력 영상의 크기에 맞는 메모리 할당 for(int i = 0 ; i<m_size ; i++) m_InputImage[i] = 255; // 초기화 File.Read(m_InputImage, m_size); // 입력 영상 파일 읽기 File.Close(); // 파일 닫기 return TRUE;
[실습하기 3-2] OnOpenDocument 함수를 이용한 파일 입력 프로그램 유니코드 or 멀티바이트 문자집합인지 아래와 같이 확인 후 , 유니코드일 경우 _T(문자열)로 변환시킴 AfxMessageBox(_T("Not Support Image Size"))
[실습하기 3-2] OnOpenDocument 함수를 이용한 파일 입력 프로그램 CImageProcessingView 클래스로 이동하여 OnDraw 함수를 재정의하여 영상 데이터를 출력 void CImageProcessingView::OnDraw(CDC* pDC) { CImageProcessingDoc* pDoc = GetDocument(); // 도큐먼트 클래스 참조 ASSERT_VALID(pDoc); int i, j; unsigned char R, G, B; for(i=0 ; i<pDoc->m_height ; i++){ for(j=0 ; j<pDoc->m_width ; j++){ R = G = B = pDoc->m_InputImage[i*pDoc->m_width+j]; pDC->SetPixel(j+5, i+5, RGB(R, G, B)); }
Section 03 MFC를 이용한 영상처리 입출력 프로그램 작성 OnSaveDocument 함수를 이용한 파일 출력 Visual C++에서 처리된 영상 데이터는 1차원이나 2차원 형태의 배열 데이터로 존재함. 이 배열 데이터를 확장자가 raw인 파일로 출력하려면 프로그램이 필요한데, 이것을 지원하는 함수가 바로 OnSaveDocument
[실습하기 3-3] OnSaveDocument 함수를 이용한 파일 출력 프로그램 CImageProcessingDoc클래스에 OnSaveDocument 함수 추가 클래스마법사창에서 아래와 같이 지정하여 함수 추가 버튼 클릭
[실습하기 3-3] OnSaveDocument 함수를 이용한 파일 출력 프로그램 BOOL CImageProcessingDoc::OnSaveDocument(LPCTSTR lpszPathName) { CFile File; // 파일 객체 선언 CFileDialog SaveDlg(FALSE, "raw", NULL, OFN_HIDEREADONLY); // raw 파일을 다른 이름으로 저장하기를 위한 대화상자 객체 선언 if(SaveDlg.DoModal() == IDOK){ // DoModal 멤버 함수에서 저장하기 수행 File.Open(SaveDlg.GetPathName(), CFile::modeCreate | CFile::modeWrite); // 파일 열기 File.Write(m_InputImage, m_size); // 파일 쓰기 File.Close(); // 파일 닫기 } return TRUE; CFileDialog SaveDlg(FALSE, _T("raw"), NULL, OFN_HIDEREADONLY); 유니코드일 경우 _T(문자열)로 변환시킴
[실습하기 3-3] OnSaveDocument 함수를 이용한 파일 출력 프로그램 [열기]버튼으로 이미지 열기 → [저장] 버튼을 클릭 → [다른 이름으로 저장] 대화상자에서 저장할 파일 이름을 입력해 저장
Section 04 MFC를 이용한 영상 축소 다운 샘플링(Down Sampling) 디지털 영상을 축소하는 가장 간단한 방법 다운 샘플링은 원 영상의 값을 일정한 좌표 단위로 버리는 것. 디지털 영상은 2차원이므로 수평축 샘플링과 수직축 샘플링이 모두 되어야 함.
[실습하기 3-4] 영상 축소 프로그램 메인프레임 메뉴 추가 Workspace 창에서 [리소스뷰] 탭 클릭 → [Menu]-[IDR_ImageProcessingTYPE] 더블 클릭하여 프레임메뉴에 <영상처리>와 하위메뉴에 <DownSampling> 직접입력 → <DownSampling>의 바로가기에서 ID속성을 아래와 같이 입력
[실습하기 3-4] 영상 축소 프로그램 CImageProcessingView클래스에 처리기 추가 [MFC ClassWizard] 대화상자에서 Class name을 CImageProcessingView, Object Ids 항목을 ID_DOWN_SAMPLING, Message 항목을 COMMAND로 선택 → <처리기추가>버튼 클릭 →멤버함수 추가
[실습하기 3-4] 영상 축소 프로그램 샘플링 비율 입력받기 위한 대화상자 추가 [리소스뷰] 창에서 [Dialog] 폴더 선택 → 바로가기 메뉴 [Insert Dialog] 클릭 → 새 [Dialog] 대화상자가 추가됨
[실습하기 3-4] 영상 축소 프로그램 → Controls 도구 상자를 이용하여 대화상자 편집(항목 2개 추가) → 대화상자에 삽입된 각 항목의 속성(Properties)을 다음과 같이 설정
[실습하기 3-4] 영상 축소 프로그램 대화상자 클래스 추가, 멤버변수 추가 대화상자 더블클릭 → [MFC 클래스추가마법사] 대화상자에서 새로 추가된 대화상자에 대한 클래스 추가 (이름 앞에는 반드시 ‘C’를 붙여주어야 함)→ 클래스의 이름을 등록하면 파일 이름은 자동으로 생성됨.
[실습하기 3-4] 영상 축소 프로그램 → [MFC ClassWizard] 대화상자의 클래스이름항목에서 CDownSampleDlg 클래스를 선택한 뒤 [멤버변수] 탭 클릭 → 컨트롤ID에서 ID_EDIT1항목을 지정한 뒤 [변수추가] 버튼 클릭 → [멤버변수추가] 대화상자에서 다음과 같이 지정하고 [OK] 버튼 클릭
[실습하기 3-4] 영상 축소 프로그램 #include "stdafx.h" #include "ImageProcessing.h" 메인 프레임에서 [영상처리]-[DownSample] 메뉴를 클릭했을 때 실제로 다운 샘플링이 발생하도록 프로그램을 작성. 파일 입·출력 때처럼 Doc 클래스에서 실제로 프로그램을 작성하고, View 클래스가 작성된 프로그램을 호출하여 화면에 출력할 수 있도록 만듦 추가된 대화상자를 Doc 클래스에서 사용하려면 다음과 같은 선언 부분이 필요함. ImageProcessingDoc.cpp 파일의 윗부분에 DownSampleDlg.h 코드 추가 #include "stdafx.h" #include "ImageProcessing.h" #include "ImageProcessingDoc.h" #include "DownSampleDlg.h" // 대화상자 사용을 위한 헤더 선언
[실습하기 3-4] 영상 축소 프로그램 → CImageProcessingDoc 클래스에 바로가기 메뉴 [추가]-[함수추가]를 클릭 → 멤버함수추가마법사에서 OnDownSampling 함수 추가
[실습하기 3-4] 영상 축소 프로그램 → CimageProcessingDoc 클래스항목에서 바로가기 메뉴 [추가]-[변수추가]를 클릭 → 처리 결과 저장 변수, 결과 영상의 가로축 크기, 세로축 크기 지정 변수 등의 사용할 변수 추가 (4번 반복)
[실습하기 3-4] 영상 축소 프로그램 → 다음과 같이 OnDownSampling 함수 작성 void CImageProcessingDoc::OnDownSampling() { int i, j; CDownSampleDlg dlg; if(dlg.DoModal() == IDOK) // 대화상자의 활성화 여부 m_Re_height = m_height / dlg.m_DownSampleRate; // 축소 영상의 세로 길이를 계산 m_Re_width = m_width / dlg.m_DownSampleRate; // 축소 영상의 가로 길이를 계산 m_Re_size = m_Re_height * m_Re_width; // 축소 영상의 크기를 계산 m_OutputImage = new unsigned char [m_Re_size]; // 축소 영상을 위한 메모리 할당 for(i=0 ; i<m_Re_height ; i++){ for(j=0 ; j<m_Re_width ; j++){ m_OutputImage[i*m_Re_width + j] = m_InputImage[(i*dlg.m_DownSampleRate*m_width)+dlg.m_DownSampleRate*j]; // 축소 영상을 생성 }
[실습하기 3-4] 영상 축소 프로그램 → 다음과 같이 View 클래스의 OnDownSampling 함수 작성 void CImageProcessingView::OnDownSampling() { // TODO: Add your command handler code here CImageProcessingDoc* pDoc = GetDocument(); // Doc 클래스 참조 ASSERT_VALID(pDoc); pDoc->OnDownSampling(); // Doc 클래스에 OnDownSampling 함수 호출 Invalidate(TRUE); // 화면 갱신 }
[실습하기 3-4] 영상 축소 프로그램 → 처리된 결과를 화면에 표시하기 위해 OnDraw 함수를 다음과 같이 재정의 void CImageProcessingView::OnDraw(CDC* pDC) { CImageProcessingDoc* pDoc = GetDocument(); // Doc 클래스 참조 ASSERT_VALID(pDoc); // TODO: add draw code for native data here int i, j; unsigned char R, G, B; // 입력 영상 출력 for(i = 0 ; i<pDoc->m_height ; i++){ for(j = 0 ; j<pDoc->m_width ; j++){ R = pDoc->m_InputImage[i*pDoc->m_width+j]; G = B = R; pDC->SetPixel(j+5, i+5, RGB(R, G, B)); } // 축소된 영상 출력 for(i = 0 ; i<pDoc->m_Re_height ; i++){ for(j = 0 ; j<pDoc->m_Re_width ; j++){ R = pDoc->m_OutputImage[i*pDoc->m_Re_width+j]; pDC->SetPixel(j+pDoc->m_width+10, i+5, RGB(R, G, B));
[실습하기 3-4] 영상 축소 프로그램 → 실행 결과 영상. 결과 영상을 m_OutputImage 배열에 입력, 결과 영상의 가로축 크기와 세로축 크기를 m_Re_width, m_Re_height 로 저장, OnDraw 함수에서는 처리된 결과를 화면 오른쪽에 출력