15 장프로젝트 _ 웨이브렛변환 웨이브렛변환의개념 웨이브렛변환을위한 MFC 설정하기 파일입. 출력설정하기 웨이브렛변환을위한대화상자설정하기 순방향웨이브렛변환구현하기 역방향웨이브렛변환구현하기 한빛미디어 ( 주 )
15 장. 프로젝트 _ 웨이브렛변환 학습목표 웨이브렛변환의개념을소개한다. 순방향웨이브렛변환프로그램을실습한다. 역방향웨이브렛변환프로그램을실습함다. 2
Section 01 웨이브렛변환의개념 웨이브렛은기본함수로 sine, cosine 함수외에웨이브렛모함수를사용하는데, 각주파수영역에따라변화하는다양한기저함수를생성하여사용함 시간 - 주파수에국부적인성질이있음. 푸리에변환은시간과주파수정보를동시에파악할수없지만웨이브렛에서는이둘을동시에파악가능 3
필터뱅크를이용한이산웨이브렛변환수행 이산웨이브렛변환은저주파통과필터와고주파통과필터로구성된필터뱅크로수행됨. 이때, 사용되는필터는특수하게설계된것으로, 직교특성, 선형특성, 고주파와저주파부분을정확하게분할하는특성이있음. 4
필터뱅크를이용한이산웨이브렛변환수행 ( 계속 ) 필터뱅크의동작을이용한웨이브렛변환과역변환의수행과정을나타낸것. 필터링은컨벌루션으로수행됨. 웨이브렛변환과정에서는각각필터링한뒤데이터의크기를절반으로줄이는다운샘플링이수행되고, 웨이브렛역변환과정에서는각필터링한뒤데이터의크기를원상태로보상해주려고두배로증가시키는업샘플링이수행됨. 5
필터뱅크를이용한 2 차원이산웨이브렛변환 웨이브렛을영상에적용하려면 2 차원처리를수행해야함. 분리성을만족함. 먼저세로방향으로웨이브렛을수행한뒤가로방향으로웨이브렛을수행하면 2 차원웨이브렛변환이가능 6
Section 02 웨이브렛변환을위한 MFC 설정하기 ➊ Visual C++ 프로그램을실행 [File]-[New] 메뉴클릭 7
Section 02 웨이브렛변환을위한 MFC 설정하기 ( 계속 ) ➋ [New] 대화상자의 [Projects] 탭에서 MFC AppWizard [exe] 항목을선택 Project name 란에다음과같이입력 8
Section 02 웨이브렛변환을위한 MFC 설정하기 ( 계속 ) ➌ [MFC AppWizard] 대화상자에서 MDI 기반에서작성하기위해 MDI(multiple documents) 항목을선택 [Finish] 버튼클릭해기본환경설정완료 9
Section 03 파일입 출력설정하기 ➊ Visual C++ 프로그램에서 [View]-[ClassWizard] 메뉴클릭 ➋ [MFC ClassWizard] 대화상자의 Class name 항목에서 CWaveletTransformDoc를선택 Message 항목에서 OnOpenDocument 더블클릭 CWaveletTransformDoc에 OnOpenDocument 함수추가 10
Section 03 파일입 출력설정하기 ( 계속 ) ➌ 함수에서사용할입력영상버퍼변수, 출력영상버퍼변수, 영상의크기와관련된변수추가 11
Section 03 파일입 출력설정하기 ( 계속 ) ➍ BOOL CWaveletTransformDoc::OnOpenDocument(LPCTSTR lpszpathname) 에다음프로그램추가 (1) BOOL CWaveletTransformDoc::OnOpenDocument(LPCTSTR lpszpathname) { if(!cdocument::onopendocument(lpszpathname)) return FALSE; CFile File; File.Open(lpszPathName, CFile::modeRead CFile::typeBinary); 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{ AfxMessageBox("Not Support Image Size"); return 0; 12
Section 03 파일입 출력설정하기 ( 계속 ) ➍ BOOL CWaveletTransformDoc::OnOpenDocument(LPCTSTR lpszpathname) 에다음프로그램추가 (2) m_size = m_height * m_width; m_inputimage = new unsigned char [m_height * m_width]; // 입력영상저장공간 m_outputimage = new unsigned char [m_height * m_width]; // 출력영상저장공간 for(int i = 0 ; i<m_height * m_width ; i++){ m_inputimage[i] = 255; // 초기화 m_outputimage[i] = 255; // 초기화 File.Read(m_InputImage, m_height * m_width); // 파일읽기 File.Close(); return TRUE; 13
Section 03 파일입 출력설정하기 ( 계속 ) ➎ 입력된영상을확인하기위해 CWaveletTransformView::OnDraw(CDC* pdc) 에다음프로그램추가 void CWaveletTransformView::OnDraw(CDC* pdc) { CWaveletTransformDoc* pdoc = GetDocument(); ASSERT_VALID(pDoc); // TODO: add draw code for native data here int i, j; int 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]; B = G = R; pdc->setpixel(j+5, i+5, RGB(R, G, B)); // 입력영상을화면에출력 for(i=0 ; i<pdoc->m_height ; i++){ for(j=0 ; j<pdoc->m_width ; j++){ R = pdoc->m_outputimage[i*pdoc->m_width + j]; B = G = R; pdc->setpixel(j+pdoc->m_width+10, i+5, RGB(R, G, B)); // 복원된영상을화면에출력 14
Section 04 웨이브렛변환을위한대화상자설정하기 ➊ ResourceView 창에서 [WaveletTransform resources]-[dialog] 폴더에서마우스오른쪽버튼을클릭 바로가기메뉴 [Insert Dialog] 클릭 ➋ 추가된대화상자를오른쪽과같이편집 15
Section 04 웨이브렛변환을위한대화상자설정하기 ( 계속 ) ➌ 대화상자속성을다음과같이정의 16
Section 04 웨이브렛변환을위한대화상자설정하기 ( 계속 ) ➍ 대화상자위에서마우스오른쪽버튼을눌러 [ClassWizard] 메뉴를클릭 [ClassWizard] 대화상자에서 [Add Class] 버튼클릭 [Adding a Class] 대화상자에서 Create a new class 항목선택 17
Section 04 웨이브렛변환을위한대화상자설정하기 ( 계속 ) ➎ 다음과같이대화상자를클래스에등록 18
Section 04 웨이브렛변환을위한대화상자설정하기 ( 계속 ) ➏ [Class Wizard] 대화상자에서 [Add Variable] 버튼클릭 [Add Member Variable] 대화상자에서 CWaveletTransforDlg 대화상자를선택 변수추가 19
Section 05 순방향웨이브렛변환구현하기 ➊ ResourceView 창에서 [Menu]-[IDR_IMAGETYPE] 더블클릭해메뉴추가 ➋ 메뉴를추가할부분을더블클릭 [Menu Item Properties] 대화상자에서다음과같이캡션추가 20
➌ 프로그램을실행하는실제메뉴를추가하려고 ➋ 처럼메뉴가들어갈위치를더블클릭 [Menu Item Properties] 대화상자에서다음과같이속성지정 ➍ 해당메뉴위에서마우스오른쪽버튼을눌러 바로가기메뉴 [ClassWizard] 클릭 21
➎ [MFC Class Wizard] 대화상자의 [Message Maps] 탭에서 Object IDs 항목과 Messages 항목을다음과같이지정하고 [Add Function] 버튼클릭 [Add Member Function] 대화상자에서 [OK] 버튼을눌러함수를추가 [Edit Code] 버튼을눌러함수로이동 22
➏ CWaveletTransformView::OnWaveletTransform에다음과같이프로그램을작성. CWaveletTransformView는 CWaveletTransformDoc를호출하는역할을하고, 웨이브렛변환은 CWaveletTransformDoc에서일어나게됨 void CWaveletTransformView::OnWaveletTransform() { CWaveletTransformDoc* pdoc = GetDocument(); ASSERT_VALID(pDoc); pdoc->onwavelettransform(); // Doc 클래스에서생성해야할함수이름 Invalidate(TRUE); 23
➐ ResoureViess 창의 CWaveletTransformDoc 폴더위에서마우스오른쪽버튼클릭후바로가기메뉴 [Add Member Function] 클릭 [Add Member Function] 대화상자에서 OnWaveletTransform 함수추가 24
➑ CWaveletTransformDoc::OnWaveletTransform 에서는입력된영상과미리만든대화상자를이용하여웨이브렛변환실행. Wavelet Transform 대화상자를호출할수있도록 [File View]-[Header File]-[WaveletTransformDoc.h] 파일을클릭하여다음부분추가 // avelettransformdoc.h : interface of the CWaveletTransformDoc class ////////////////////////////////////////////////////////////// #if!defined(afx_wavelettransformdoc_h 4A38BAEB_ E3CB_44C9_B2CB_057CD4D9FF0A INCLUDED_) #define AFX_WAVELETTRANSFORMDOC_H 4A38BAEB_E3CB_44C9_ B2CB_057CD4D9FF0A INCLUDED_ #if _MSC_VER > 1000 #pragma once #endif // _MSC_VER > 1000 #include "WaveletTransformDlg.h" // 대화상자헤더선언 class CWaveletTransformDoc : public CDocument { protected: // create from serialization only CWaveletTransformDoc(); DECLARE_DYNCREATE(CWaveletTransformDoc) // Attributes public: // Operations public: CWaveletTransformDlg *pdlg; 25
➒ CWaveletTransformDoc 클래스의생성자와소멸자를다음과같이작성 CWaveletTransformDoc::CWaveletTransformDoc() { // TODO: add one-time construction code here pdlg = new CWaveletTransformDlg(this); CWaveletTransformDoc::~CWaveletTransformDoc() { delete pdlg; 26
➓ 생성된함수 CWaveletTransformDoc::OnWaveletTransform 을다음과같이작성여기서 WaveletTransform 대화상자를호출하게됨. void CWaveletTransformDoc::OnWaveletTransform() { if(pdlg->getsafehwnd() == NULL) pdlg->create(idd_dialog1); // 모달리스대화상자이용 pdlg->showwindow(sw_show); 27
11 CWaveletTransformDlg 클래스로이동. 대화상자를이용하여변환에사용되는변수를입력받게됨. [File View]-[Header Files]-[WaveletTransformDlg.h] 파일로이동하여다음프로그램추가 ////////////////////////////////////////////////////////// // CWaveletTransformDlg dialog class CWaveletTransformDoc; class CWaveletTransformDlg : public CDialog { // Construction public: CWaveletTransformDlg(CWnd* pparent = NULL); // standard constructor CWaveletTransformDlg(CWaveletTransformDoc *pdoc, CWnd* pparent = NULL); : // Implementation protected: CWaveletTransformDoc *m_pdoc; 28
12 [File View]-[Source Files]-[WaveletTransformDlg.cpp] 파일로이동하여다음과같이프로그램수정 #include "WaveletTransformDoc.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = FILE ; #endif //////////////////////////////////////////////////////////// // CWaveletTransformDlg dialog CWaveletTransformDlg::CWaveletTransformDlg(CWnd* pparent /*=NULL*/) : CDialog(CWaveletTransformDlg::IDD, pparent) { m_pdoc = NULL; CWaveletTransformDlg::CWaveletTransformDlg(CWaveletTransform Doc *pdoc, CWnd* pparent /*=NULL*/) : CDialog(CWaveletTransformDlg::IDD, pparent) { m_pdoc = pdoc; // Doc 클래스참조 m_level = 1; // Wavelet 분해레벨초기화 m_error = 0.0f; // MSE 초기화 m_snr = 0.0f; // SNR 초기화 m_filtercheck = 0; // Filter Check 초기화 29
13 Section 05 순방향웨이브렛변환구현하기 ( 계속 ) [MFC Class Wizard] 대화상자의 Wavelet Transform 대화상자에서 [ 변환 ] 버튼클릭 웨이브렛변환이일어나도록다음과같이함수추가 30
void CWaveletTransformDlg::OnButtonUpdate() { // TODO: Add your control notification handler code here UpdateData(TRUE); m_pdoc->m_level = m_level; // 대화상자에서입력받은레벨값을 Doc 클래스에전달 m_pdoc->onwaveletencode(); // 웨이브렛변환 UpdateData(FALSE); // 결과업데이트 31
14 Section 05 순방향웨이브렛변환구현하기 대화상자의변환버튼이클릭되면변환이실행되고실행코드는 CWaveletTransformDoc 클래스에서있게됨. CWaveletTransformDoc 클래스로이동하여 OnWaveletEncode 함수추가 32
15 수학함수를사용하기위해 CWaveletTransforDoc.cpp 의위쪽에다음과같이선언 // WaveletTransformDoc.cpp:implementation of the CWaveletTransformDoc class // #include "stdafx.h" #include "WaveletTransform.h" #include "WaveletTransformDoc.h" #include "math.h" // 수학함수를위한헤더선언 33
16 Section 05 순방향웨이브렛변환구현하기 ( 계속 ) OnWaveletEncode 함수에서사용되는전역변수를선언하기위해 [WaveletTransform Classes]-[CWaveletTransformDoc] 폴더위에서마우스오른쪽버튼클릭 [Add Member Variable] 클릭 [Add Member Variable] 대화상자를이용해변수추가 34
17 Section 05 순방향웨이브렛변환구현하기 ( 계속 ) CWaveletTransformDoc 폴더위에서마우스오른쪽버튼클릭 [Add Member Function] 클릭 [Add Member Function] 대화상자에서 OnWaveletEncode 함수에서사용되는함수를선언하고작성 35
1 OnDownSampling 함수추가하기 double* CWaveletTransformDoc::OnDownSampling(double *m_target, int size) { int i; double* m_temp; m_temp = new double [size / 2]; for(i=0 ; i<size/2 ; i++) m_temp[i] = m_target[2*i]; // 다운샘플링처리 return m_temp; 36
2 OnConvolution 함수추가하기 (1) double* CWaveletTransformDoc::OnConvolution(double *m_target, double *m_filter, int size, int mode) { // Circular Convolution 을위한함수 int i, j; double *m_temp, *m_tempconv; double m_sum = 0.0; m_temp = new double [size + m_filtertap - 1]; m_tempconv = new double [size]; // Convolution 결과출력배열 switch(mode){ case 1 : // Circular Convolution 을위한초기화 for(i=0 ; i<size ; i++) m_temp[i] = m_target[i]; for(i=0 ; i<m_filtertap-1 ; i++) m_temp[size + i] = m_target[i]; break; 37
2 OnConvolution 함수추가하기 (2) case 2 : for(i=0 ; i<m_filtertap-1 ; i++) m_temp[i] = m_target[size - m_filtertap + i + 1]; for(i=m_filtertap-1 ; i<size + m_filtertap-1 ; i++) m_temp[i] = m_target[i - m_filtertap + 1]; break; for(i=0 ; i<size ; i++){ for(j=0 ; j<m_filtertap ; j++){ m_sum += (m_temp[j+i] * m_filter[m_filtertap-j-1]); // Convolution 연산 m_tempconv[i] = m_sum; m_sum = 0.0; return m_tempconv; // 연산결과를반환 38
3 OnFilterTapGen 함수추가하기 void CWaveletTransformDoc::OnFilterTapGen() { // Filter Tap 선택 switch(pdlg->m_filtercheck) { case 0 : m_filtertap = 2; break; case 1 : m_filtertap = 4; break; case 2 : m_filtertap = 6; break; case 3 : m_filtertap = 8; break; default : AfxMessageBox("Wrong Filter Tap"); return; 39
4 OnFilterGen 함수추가하기 (1) void CWaveletTransformDoc::OnFilterGen(double *m_h0, Double *m_h1, double *m_g0, double *m_g1) { // 필터계수값 int i; switch(m_filtertap) { case 2 : m_h0[0] = 0.70710678118655; m_h0[1] = 0.70710678118655; break; case 4 : m_h0[0] = -0.12940952255092; m_h0[1] = 0.22414386804186; m_h0[2] = 0.83651630373747; m_h0[3] = 0.48296291314469; break; case 6 : m_h0[0] = 0.03522629188210; m_h0[1] = -0.08544127388224; m_h0[2] = -0.13501102001039; m_h0[3] = 0.45987750211933; m_h0[4] = 0.80689150931334; m_h0[5] = 0.33267055295096; break; 40
4 OnFilterGen 함수추가하기 (2) case 8 : m_h0[0] = -0.01059740178500; m_h0[1] = 0.03288301166698; m_h0[2] = 0.03084138183599; m_h0[3] = -0.18703481171888; m_h0[4] = -0.02798376941698; m_h0[5] = 0.63088076792959; m_h0[6] = 0.71484657055254; m_h0[7] = 0.23037781330886; break; default : AfxMessageBox("Wrong Filter"); return; // H0 필터계수를이용해, H1, G0, G1 필터계수생성 for(i=0 ; i<m_filtertap ; i++) m_h1[i] = pow(-1,i+1) * m_h0[m_filtertap - i - 1]; for(i=0 ; i<m_filtertap ; i++) m_g0[i] = m_h0[m_filtertap - i - 1]; for(i=0 ; i<m_filtertap ; i++) m_g1[i] = pow(-1, i) * m_h0[i]; 41
5 OnMem2DAllocUnsigned 함수추가하기 unsigned char** CWaveletTransformDoc::OnMem2DAllocUnsigned (int height, int width) { // unsigned char 형태의 2차원배열할당 int i, j; unsigned char** temp; temp = new unsigned char *[height]; for(i=0 ; i<height ; i++) // 2 차원배열할당 temp[i] = new unsigned char [width]; for(i=0 ; i<height ; i++){ for(j=0 ; j<width ; j++){ temp[i][j] = 0; // 2차원배열초기화 return temp; 42
6 OnMem2DAlloc double 함수추가하기 double** CWaveletTransformDoc::OnMem2DAlloc double(int height, int width) { // double 형태의 2차원배열할당 int i, j; double** temp; temp = new double *[height]; for(i=0 ; i<height ; i++) temp[i] = new double [width]; for(i=0 ; i<height ; i++){ for(j=0 ; j<width ; j++){ temp[i][j] = 0; return temp; 43
7 OnScale 함수추가하기 (1) double** CWaveletTransformDoc::OnScale(double **m_target, int height, int width) { // 정규화함수 : 필터링된값을 0~255 사이의값으로정규화 int i, j; double min, max; double **temp; temp = OnMem2DAlloc double(height, width); min = max = m_target[0][0]; for(i=0 ; i<height ; i++){ for(j=0 ; j<width ; j++){ if(m_target[i][j] <= min){ min = m_target[i][j]; // 최소값 if(m_target[i][j] >= max){ max = m_target[i][j]; // 최대값 44
7 OnScale 함수추가하기 (2) max = max - min; for(i=0 ; i<height ; i++){ for(j=0 ; j<width ; j++){ temp[i][j] = (m_target[i][j] - min) * (255. / max); // 정규화처리 return temp; 45
18 OnWaveletEncode 함수작성 (1) void CWaveletTransformDoc::OnWaveletEncode() { // Wavelet encode 함수 if(m_level <= 0 (pow(2, m_level+3) > (double) m_width) (pow(2, m_level+3) > (double) m_height)){ AfxMessageBox("Not support decomposition level"); return; // 최대분해레벨이 512*512이면 6레벨로제한 int i, j, k, width, height; double *m_conv1, *m_conv2, *m_conv3, *m_conv4; // Convolution을위한버퍼 double *m_down1, *m_down2, *m_down3, *m_down4; // 다운샘플링을위한버퍼 double *m_hor, *m_ver1, *m_ver2; double **m_l, **m_h, **m_ll, **m_lh, **m_hl, **m_hh, **m_sll, **m_slh, **m_shl, **m_shh; m_tempinput = OnMem2DAlloc double(m_height, m_width); m_tempoutput = OnMem2DAlloc double(m_height, m_width); m_arrangeimage = OnMem2DAllocUnsigned(m_Height, m_width); for(i=0 ; i< m_height ; i++){ for(j=0 ; j< m_width ; j++){ m_tempinput[i][j] = (double) m_inputimage[i* m_width + j]; // 1차원입력을 2차원배열로변환 46
18 OnWaveletEncode 함수작성 (2) OnFilterTapGen(); // 필터 tap 생성 m_filterh0 = new double [m_filtertap]; // 필터계수를위한배열 m_filterh1 = new double [m_filtertap]; // 필터계수를위한배열 m_filterg0 = new double [m_filtertap]; // 필터계수를위한배열 m_filterg1 = new double [m_filtertap]; // 필터계수를위한배열 OnFilterGen(m_FilterH0, m_filterh1, m_filterg0, m_filterg1); // 필터계수생성 width= m_width; height= m_height; for(k=0 ; k<m_level ; k++){ m_l = OnMem2DAlloc double(height, width/2); // m_h = OnMem2DAlloc double(height, width/2); // m_ll = OnMem2DAlloc double(height/2, width/2); // LL 저장을위한배열 m_lh = OnMem2DAlloc double(height/2, width/2); // LH 저장을위한배열 m_hl = OnMem2DAlloc double(height/2, width/2); // HL 저장을위한배열 m_hh = OnMem2DAlloc double(height/2, width/2); // HH 저장을위한배열 m_hor = new double [width]; // 횡입력을위한배열 47
18 OnWaveletEncode 함수작성 (3) for(i=0 ; i<height ; i++){ for(j=0 ; j<width ; j++){ m_hor[j] = m_tempinput[i][j]; // 입력배열을 1차원배열에할당 m_conv1 = OnConvolution(m_Hor, m_filterh0, width, 1); // Convolution 처리 m_conv2 = OnConvolution(m_Hor, m_filterh1, width, 1); // Convolution 처리 m_down1 = OnDownSampling(m_Conv1, width); // 다운샘플링 m_down2 = OnDownSampling(m_Conv2, width); // 다운샘플링 for(j=0 ; j<width/2 ; j++){// 다운샘플링결과를저장 m_l[i][j] = m_down1[j]; m_h[i][j] = m_down2[j]; m_ver1 = new double[height]; m_ver2 = new double[height]; 48
18 OnWaveletEncode 함수작성 (4) for(i=0 ; i<width/2 ; i++){ for(j=0 ; j<height ; j++){ m_ver1[j] = m_l[j][i]; // 열방향으로 1차원배열에할당 m_ver2[j] = m_h[j][i]; m_conv1 = OnConvolution(m_Ver1, m_filterh0, height, 1); // Convolution 처리 m_conv2 = OnConvolution(m_Ver1, m_filterh1, height, 1); m_conv3 = OnConvolution(m_Ver2, m_filterh0, height, 1); m_conv4 = OnConvolution(m_Ver2, m_filterh1, height, 1); m_down1 = OnDownSampling(m_Conv1, height); // 다운샘플링 m_down2 = OnDownSampling(m_Conv2, height); m_down3 = OnDownSampling(m_Conv3, height); m_down4 = OnDownSampling(m_Conv4, height); for(j=0 ; j<height/2 ; j++){ m_ll[j][i] = m_down1[j]; // 결과저장 m_lh[j][i] = m_down2[j]; m_hl[j][i] = m_down3[j]; m_hh[j][i] = m_down4[j]; 49
18 OnWaveletEncode 함수작성 (6) m_sll = OnScale(m_LL, height/2, width/2); // 처리결과를정규화 m_slh = OnScale(m_LH, height/2, width/2); m_shl = OnScale(m_HL, height/2, width/2); m_shh = OnScale(m_HH, height/2, width/2); for(i=0 ; i<height/2 ; i++){ for(j=0 ; j<width/2 ; j++){ m_tempoutput[i][j] = m_ll[i][j]; m_tempoutput[i][j+(width/2)] = m_hl[i][j]; m_tempoutput[i+(height/2)][j] = m_lh[i][j]; m_tempoutput[i+(height/2)][j+(width/2)] = m_hh[i][j]; // 처리결과를정렬 m_arrangeimage[i][j] = (unsigned char)m_sll[i][j]; m_arrangeimage[i][j+(width/2)] = (unsigned char)m_shl[i][j]; m_arrangeimage[i+(height/2)][j] = (unsigned char)m_slh[i][j]; m_arrangeimage[i+(height/2)][j+(width/2)] = (unsigned char)m_shh[i][j]; // 정규화과정을거친정렬영상 width = width / 2; // 분해를계속하기위해영상의가로축크기를반으로줄임 height = height / 2; // 분해를계속하기위해영상의세로축크기를반으로줄임 50
18 OnWaveletEncode 함수작성 (7) m_tempinput = OnMem2DAlloc double(height, width); for(i=0 ; i<height ; i++){ for(j=0 ; j<width ; j++){ m_tempinput[i][j] = m_ll[i][j]; // LL 값을새로운입력으로할당 delete [] m_conv1, m_conv2, m_conv3, m_conv4; delete [] m_down1, m_down2, m_down3, m_down4; delete [] m_hor, m_ver1, m_ver2; for(i=0 ; i<height ; i++){ // 메모리해제 delete[] m_ll[i]; delete[] m_lh[i]; delete[] m_hl[i]; delete[] m_hh[i]; delete[] m_sll[i]; delete[] m_slh[i]; delete[] m_shl[i]; delete[] m_shh[i]; delete[] m_l[i]; delete[] m_h[i]; delete m_l, m_h, m_ll, m_lh, m_hl, m_hh, m_sll, m_slh, m_shl, m_shh; UpdateAllViews(NULL); 51
정렬영상출력 ➊ [Resource View] 창의 [WaveletTransform resources-dialog] 폴더에서마우스오른쪽버튼클릭 [Insert Dialog] 클릭해대화상자하나삽입 ( [OK], [Cancel] 버튼은삭제 ) [Dialog Properties] 대화상자에서캡션 Arrange Image 로설정 52
➋ 대화상자를클래스에추가 ➌ [MFC ClassWizard] 대화상자의 [Message Maps] 탭에서 Messages 항목의 WM_INITDIALOG 를클릭하여 OnInitDialog 함수추가. 대화상자를초기화하는역할을함. 53
➍ Messages 항목의 WM_PAINT 지를클릭하여 OnPain 함수추가. 이곳은대화상자에그림을그리는역할수행 54
➎ CWaveletTransformDoc 에서 CArrangeDlg 클래스에출력할영상과크기를저장하는변수선언 55
➏ 생성된함수에다음프로그램추가 1 OnInitDialog 함수추가하기 BOOL CArrangeDlg::OnInitDialog() { CDialog::OnInitDialog(); CRect rect, rectc; GetWindowRect(&rect); GetClientRect(&rectC); int cx, cy; CSize sizeimg; sizeimg.cx = Width; sizeimg.cy = Height; cx = sizeimg.cx + rect.width() - rectc.width() + 4; // 정렬영상출력을위한대화상자크기조절 cy = sizeimg.cy + rect.height() - rectc.height() + 4; SetWindowPos(this, 0, 0, cx, cy, SWP_NOZORDER); return TRUE; // return TRUE unless you set the focus to a control // EXCEPTION: OCX Property Pages should return FALSE 56
2 OnPaint 함수추가하기 void CArrangeDlg::OnPaint() { CPaintDC dc(this); // device context for painting // TODO: Add your message handler code here // Do not call CDialog::OnPaint() for painting messages int i, j; unsigned char R, G, B; for(i=0 ; i<height ; i++){ for(j=0 ; j<width ; j++){ R = m_tempimage[i][j]; G = B = R; dc.setpixel(j, i, RGB(R, G, B)); // 정규화된정렬영상을화면에출력 57
➐ 정렬된영상은 Wavelet Transform(IDD_DIALOG1) 대화상자에서 [Arrange Image] 버튼을클릭하면영상이출력되게함. [MFC ClassWizard] 대화상자에서 [Arrange Image] 버튼에함수추가 58
➑ ArrangeDlg 를사용하기위해 CWaveletTransformDlg.cpp 파일위쪽에 ArrangeDlg.h 선언 #include "stdafx.h" #include "WaveletTransform.h" #include "WaveletTransformDlg.h" #include "WaveletTransformDoc.h" #include "ArrangeDlg.h" 59
➒ OnButtonArrange 함수추가 void CWaveletTransformDlg::OnButtonArrange() { // 버튼을누르면정렬영상출력 CArrangeDlg dlg; dlg.width = m_pdoc->m_width; dlg.height = m_pdoc->m_height; dlg.m_tempimage = m_pdoc->m_arrangeimage; UpdateData(TRUE); dlg.domodal(); // 정렬영상을위한대화상자출력 60
➓ Wavelet Transform(IDD_DIALOG1) 대화상자의 [ 종료 ] 버튼에변환을종료하는프로그램추가 61
11 OnButtonEnd 함수추가 void CWaveletTransformDlg::OnButtonEnd() { CDialog::OnOK(); 62
Section 06 역방향웨이브렛변환구현하기 ➊ 역방향웨이브렛변환은순방향변환이일어난뒤변환된값을사용하여역방향변환을하게됨. 따라서순방향에서사용되었던함수와대화상자가재사용됨. ➋ CWaveletTransformDlg::OnButtonArrange 함수에다음프로그램추가 void CWaveletTransformDlg::OnButtonUpdate() { // TODO: Add your control notification handler code here UpdateData(TRUE); m_pdoc->m_level = m_level; m_pdoc->onwaveletencode(); m_pdoc->onwaveletdecode(); // 웨이브렛역변환 m_pdoc->onsnr(); // 신호대잡음비 UpdateData(FALSE); 63
Section 06 역방향웨이브렛변환구현하기 ( 계속 ) ➌ 순방향웨이브렛변환처럼 CWaveletTransformDoc 클래스에 OnWaveletDecode 함수를추가하여역방향웨이브렛변환을수행할수있도록한. 역방향웨이브렛변환하는데필요한함수는 CWaveletTransformDoc 클래스에추가 64
Section 06 역방향웨이브렛변환구현하기 ( 계속 ) 1 OnUpSampling 함수추가하기 double* CWaveletTransformDoc::OnUpSampling(double *m_target, int size) { // 업샘플링을위한함수 int i; double* m_temp; m_temp = new double[size * 2]; for(i=0 ; i<size*2 ; i++) m_temp[i] = 0.0; // 초기화 for(i=0 ; i<size ; i++) m_temp[2*i] = m_target[i]; // 업샘플링처리 return m_temp; 65
Section 06 역방향웨이브렛변환구현하기 ( 계속 ) 2 OnSNR 함수추가하기 void CWaveletTransformDoc::OnSNR() { // 신호대잡음비를위한함수 double OrgSum, ErrSum, MeanErr, MeanOrg; int i; OrgSum = 0.0; ErrSum = 0.0; // calculate mean squared error for(i=0 ; i<m_size ; i++){ // 오류의에너지계산 ErrSum += ((double) m_inputimage[i] - m_recon[i]) * ((double) m_inputimage[i] - m_recon[i]); MeanErr = ErrSum / m_size; // 오류에너지평균 for(i=0 ; i<m_size ; i++){ // 신호의에너지계산 OrgSum += ((double) m_inputimage[i]) * ((double) m_inputimage[i]); MeanOrg = OrgSum / m_size; // 신호에너지평균 pdlg->m_error = (float)meanerr; // 오류출력 pdlg->m_snr = (float)(10 * (double)log10(meanorg / MeanErr)); // 신호대잡음비계산 66
Section 06 역방향웨이브렛변환구현하기 ( 계속 ) ➍ OnWaveletDecode 함수작성 (1) void CWaveletTransformDoc::OnWaveletDecode() { int i, j, k; int width, height; double *templl, *templh, *temphl, *temphh, *templ, *temph; double **L, **H; double *Up1, *Up2, *Up3, *Up4; double *Conv1, *Conv2, *Conv3, *Conv4; double **R; width= m_width / (int)(pow(2, m_level)); height= m_height / (int)(pow(2, m_level)); m_recon = new double [m_width * m_height]; for(k=m_level ; k>0 ; k--){ if(width > m_width height >m_height){ // 분해종료 return; templl = new double [height]; templh = new double [height]; temphl = new double [height]; temphh = new double [height]; 67
Section 06 역방향웨이브렛변환구현하기 ( 계속 ) ➍ OnWaveletDecode 함수작성 (2) L = OnMem2DAlloc H = OnMem2DAlloc templ = new double [width]; temph = new double [width]; double(height*2, width); double(height*2, width); R = OnMem2DAlloc double(height*2, width*2); for(i=0 ; i<width ; i++){ for(j=0 ; j<height ; j++){ // 정렬영상에서처리하려는열을분리 templl[j] = m_tempoutput[j][i]; templh[j] = m_tempoutput[j + height][i]; temphl[j] = m_tempoutput[j][i + width]; temphh[j] = m_tempoutput[j + height][i + width]; Up1 = OnUpSampling(tempLL, height); // 업샘플링 Up2 = OnUpSampling(tempLH, height); Up3 = OnUpSampling(tempHL, height); Up4 = OnUpSampling(tempHH, height); Conv1 = OnConvolution(Up1, m_filterg0, height*2, 2); // 컨벌루션연산 Conv2 = OnConvolution(Up2, m_filterg1, height*2, 2); Conv3 = OnConvolution(Up3, m_filterg0, height*2, 2); Conv4 = OnConvolution(Up4, m_filterg1, height*2, 2); 68
Section 06 역방향웨이브렛변환구현하기 ( 계속 ) ➍ OnWaveletDecode 함수작성 (3) for(j=0 ; j<height*2 ; j++){ L[j][i] = Conv1[j] + Conv2[j]; H[j][i] = Conv3[j] + Conv4[j]; for(i=0 ; i<height*2 ; i++){ for(j=0 ; j<width ; j++){ templ[j] = L[i][j]; // 횡데이터분리 temph[j] = H[i][j]; Up1 = OnUpSampling(tempL, width); // 업샘플링 Up2 = OnUpSampling(tempH, width); Conv1 = OnConvolution(Up1, m_filterg0, width*2, 2); // 컨벌루션연산 Conv2 = OnConvolution(Up2, m_filterg1, width*2, 2); for(j=0 ; j<width*2 ; j++){ R[i][j] = Conv1[j] + Conv2[j]; for(i=0 ; i<height*2 ; i++){ for(j=0 ; j<width*2 ; j++){ m_tempoutput[i][j] = R[i][j]; // 복원데이터를다시정렬 69
Section 06 역방향웨이브렛변환구현하기 ( 계속 ) ➍ OnWaveletDecode 함수작성 (4) height = height * 2; // 영상의크기를두배확장 width = width * 2; for(i=0 ; i< m_height ; i++){ for(j=0 ; j< m_width ; j++){ m_recon[i* m_width + j] = R[i][j]; m_outputimage[i* m_width + j] = (unsigned char)r[i][j]; // 최종복원된결과를출력 UpdateAllViews(NULL); delete [] templl, templh, temphl, temphh, templ, temph; // 메모리해제 delete [] Up1, Up2, Up3, Up4; delete [] Conv1, Conv2, Conv3, Conv4; for(i=0 ; i< m_height ; i++){ // 메모리해제 delete[] L[i]; delete[] H[i]; delete[] R[i]; delete L, H, R; 70
➎ 결과영상 Section 06 역방향웨이브렛변환구현하기 ( 계속 ) 71
Thank you