같은 컨트롤의 개수가 많을 때 같은 이벤트 처리기를 여러개 만들 경우, 보기도 안좋고 번거로운 작업이 필요합니다.

이벤트 처리기를 한 데 묶어 쓰는 방법입니다.




우선 테스트를 위해 버튼을 네개 만들어 두었습니다. 각각의 ID는 IDC_BUTTON1~IDC_BUTTON4까지입니다.




대화상자 cpp파일 내에 처리 내용이 담긴 함수를 만들어 줍니다. 인자는 UINT 형 변수이고 반환형은 없어야 합니다.

인자로 ID가 넘어와 이걸 통해 컨트롤 간의 구분이 가능합니다. 함수의 원형은 헤더 파일에 선언합니다.




메시지 맵에 빨간 박스와 같이 적습니다. ON_COMMAND_RANGE 매크로인데, 첫번째 인수는 시작 ID, 두번째 인수는 끝 ID, 세번째 인수는 반환형이 void이고 인자가 UINT인 함수의 주소를 넘겨줍니다.

그러면 IDC_BUTTON1~IDC_BUTTON4까지의 컨트롤에 이벤트가 발생할 경우 해당 함수가 호출됩니다.




잘 작동하는 것을 볼 수 있습니다.

 설치할 적에는 창이 잘 떴는데, 제거를 하기 위해 인스톨 파일을 다시 실행하니

완전 투명하게 뜨거나 오른쪽 상단 구석에 검은 사각형만 떠서 골머리를 앓았습니다.

닷넷을 다시 깔아봐도 안되고, 시스템 복원을 해봐도 안되고, 무엇이 문제였나 했는데

설마하고 그래픽 카드 드라이버 설치하니 해결 -_-;;; 나의 오전을 돌려줘..

에디트박스의 글자 크기를 변경하고 싶을 때는 CFont를 이용하면 된다.

우선 CFont의 인스턴스를 전역에 선언해둔 후(지역에 선언하면 이후 커서만 커지고 글자 크기는 그대로가 된다)

컨트롤을 GetDlgItem으로 가져온 후, SetFont로 앞에서의 인스턴스의 주소를 넘겨주면 된다.


CFont g_editFont;

g_editFont.CreatePointFont( 200, TEXT( "굴림" ) );

GetDlgItem( IDC_EDIT_NUM )->SetFont( &g_editFont );



<정상적으로 된 경우>


<폰트 인스턴스를 지역에 선언한 경우>


rescan.zip


32비트 환경에서 동작

자세한 사항은 사용법 참고

1. VS2010과 VS2012가 함께 설치되어 있는 경우 등등

닷넷 프레임워크 충돌로 인해 발생. VS2010 서비스 팩 1을 설치한다

링크 : http://www.microsoft.com/ko-kr/download/details.aspx?id=23691


2. 그 외의 경우

프로젝트 설정에서 매니페스트 포함을 '아니요'로 바꾼다

#include <afxwin.h>

// MFC를 사용해서 윈도우 프로그램을 작성할 때는 반드시 <afxwin.h> 헤더를 포함시킨다.


#include "Cmmsdk.h"

#include "CmmsdkDef.h"


/*

class CMyWindow : public CWnd

// CWnd의 파생 클래스를 생성.

// PostNcDestroy라는 가상 함수를 재정의. 윈도우가 파괴될 때 MFC 프레임워크에 의해 호출된다.

{

public:

virtual void PostNcDestroy();

};


void CMyWindow::PostNcDestroy()

{

delete this; // 창을 닫을 시 자신의 메모리를 삭제

}

*/


class CMainFrame : public CFrameWnd

// MFC로 윈도우 창을 만들기 위해서는 CFrameWnd에서 파생된 클래스를 만들어야 한다.

{

};


class CMFCTest : public CWinApp

// MFC는 CWinApp라는 클래스를 제공하는데, MFC를 사용하는 모든 윈도우 프로그램은

// CWinApp 클래스의 파생클래스를 반드시 1개 만들어야 한다.

{

public:

virtual BOOL InitInstance();

virtual BOOL ExitInstance();

};


BOOL CMFCTest::InitInstance()

// CWinApp 클래스는 InitInstance() 라는 가상 함수를 제공하며,

// 사용자는 반드시 이 가상함수를 재정의해서 응용 프로그램의 초기화 코드를 만든다.

// 초기화에 성공하면 TRUE를, 실패하면 FALSE를 리턴한다.

// 함수를 재정의 할 때는 CWinApp::InitInstance()를 먼저 한번 호출해야 한다.(언어 관련 리소스 초기화)

{

CWinApp::InitInstance();


//==================================================

// CWnd 윈도우 1

//==================================================

CMainFrame* pFrame = new CMainFrame;

// 사용자가 만든 클래스에 대해서 new를 사용해 객체를 생성한다.

// MFC가 종료될 때 new를 이용해 할당받은 메모리는 자동으로 소거되므로 delete해주지 않아도 된다.


m_pMainWnd = pFrame;

// CWinApp의 멤버 변수인 m_pMainWnd에 반드시 사용자가 만든 윈도우 객체의 주소를 넣어야 한다.

// 윈도우 생성 이후 MFC가 해당 포인터 변수를 이용해 몇 가지 작업을 자동으로 수행한다.\


pFrame->Create( 0, "MFC 테스트!!" );

// 실제 윈도우를 생성하는 함수. 첫 번째 인자에는 윈도우 클래스 이름이 들어가며,

// 0이 전달되면 MFC가 내부적으로 윈도우 클래스를 등록해서 사용하게 된다.


pFrame->ShowWindow( SW_SHOW );

// 윈도우를 보여주기 위해 꼭 사용해야 하는 함수.

// SW_SHOW 부분은 바꾸어서 창의 크기를 조절하는 등의 변경이 가능


pFrame->UpdateWindow();

// 생략해도 되나, 이 함수를 호출해 주면 조금 빠르게 동작하는 것 처럼 보일 수 있다.


/*

//==================================================

// CWnd 윈도우 2

//==================================================

CString classname = AfxRegisterWndClass(

CS_HREDRAW | CS_VREDRAW,

LoadStandardCursor( IDC_ARROW ),

( HBRUSH )::GetStockObject( WHITE_BRUSH ), // WinAPI 함수이므로 앞에 스코프 연산자(::)를 붙인다.

LoadStandardIcon( IDI_APPLICATION ) );

// CFrameWnd를 사용해서 윈도우를 만들 경우 사용자가 직접 윈도우 클래스를 만들지 않아도 MFC가 내부적으로 등록

// CWnd클래스를 사용해서 윈도우를 만들 경우 반드시 윈도우 클래스를 먼저 등록.

// 윈도우 클래스 스타일, 커서, 배경 브러시, 아이콘


CMyWindow * pWnd = new CMyWindow;

pWnd->CreateEx( 0, classname, "CWnd로 만든 윈도우", WS_OVERLAPPEDWINDOW, CRect( 0, 0, 300, 300 ), 0, 0 );

// 확장 윈도우 스타일, 윈도우 클래스 이름(윈도우2), 타이틀 문자열,

// 윈도우 스타일, 윈도우 위치 및 크기, 부모 윈도우 포인터, ID


pWnd->ShowWindow( SW_SHOW );

pWnd->UpdateWindow();

// 화면상에 출력

*/


if( cmmLoadDll() == TRUE ) // DLL로딩 함수 실행

{

AfxMessageBox( "DLL 로딩에 성공하였습니다!!" );

}

else

{

AfxMessageBox( "DLL 로딩에 실패하였습니다!!" );

AfxGetMainWnd()->SendMessage( WM_CLOSE ); // 종료 메시지 보냄

}


return TRUE;

}


BOOL CMFCTest::ExitInstance()

{

cmmUnloadDll();

return CWinApp::ExitInstance();

}


CMFCTest test;

// 사용자가 만든 파생클래스에 대해서 객체를 전역적으로 한 개만 생성해야 한다.



/*

MFC는 내부적으로 WinMain() 함수를 제공한다.

*/

개요

 링크드 리스트나, 동적할당, 비트맵 등등을 한번에 마구잡이로 사용하다보면 가끔 자원 해제를 깜빡하여 미아가 되어 메모리 안을 떠도는 경우가 있습니다. 이를 해결해주는 것 중 Visual Leak Detector for Visual C++(이하 VLD)라는 유용한 라이브러리가 있습니다. 단순하게 프로젝트에 라이브러리를 추가해 주고, 헤더를 인클루드하면 디버깅 모드에서 메모리누수가 날 때 Output창에 출력해주는 것을 토대로 소스를 고쳐나가면 됩니다.



설치방법


공식사이트(https://vld.codeplex.com/) 에서 Downloads메뉴에 들어간 후, 원하는 버전을 다운받습니다.

파일을 다 다운받았다면 적당히 설치해 줍니다.



설치가 끝나면, VS에서 원하는 프로젝트를 열고, Project메뉴를 열고 Properties를 엽니다.



메뉴를 열고, VC++ Directories를 클릭합니다.



Include Directories에 VLD의 include폴더를 추가합니다.



Library Directories에 VLD의 lib폴더에서, 자신의 컴퓨터에 맞는 폴더를 선택하여 추가합니다.



사용하기


소스의 #include 부분의 최하단에 vld.h를 인클루드합니다.



디버그를 하면 누수가 생길 때 마다 위와 같이 Output 창에 누수 위치와 상세 정보가 출력됩니다.



누수가 없으면 이렇게 출력됩니다.

이 엄청난 누수를 어떻게 해결할까 힘들었는데, 해결하고 아무 누수가 없다는 글을 보니 기분이 상쾌해지네요.:)



+.덧붙임


릴리즈 모드와 디버그 모드를 구분해 사용하려면, VLD를 인클루드해주는 부분을 이렇게 수정합니다.

개요

 VFW는 마이크로소프트에서 개발한 라이브러리로, Video For Windows의 줄임말입니다. 이번 실험(?)의 목표는 VFW 함수를 이용하여 간단하게 웹캠의 영상을 추출해 화면에 보여주는 것입니다.


준비물

개발 환경 : VS2010

개발 언어 : C+Win32 API



코딩하기

1. VFW 라이브러리 추가하기

 라이브러리를 추가하는 방법은 두가지입니다. 하나는 소스 상단에 #pragma comment( lib, "vfw32.lib" )를 추가해 주는 것이고, 다른 하나는 프로젝트 링커 옵션에 추가하는 것입니다. VS2010에서 pragma를 쓰지 않고 라이브러리를 추가하는 법은 아래와 같습니다. 영문판 VS2010이기 때문에 한글판과 이름 등에 차이가 있을 수 있습니다. 굳이 vfw가 아니더라도, 모든 라이브러리가 같은 방법을 사용하니 참고하시기 바랍니다.




우선 vfw.h를 인클루드합니다.




프로젝트폴더 위에서 오른쪽 마우스를 클릭해 메뉴를 연 뒤, Properties 메뉴를 엽니다.




Linker->Input을 선택합니다.




Additional Dependencies의 라이브러리 목록을 클릭하면 ▼버튼이 생기는데, 이를 클릭합니다.




vfw32.lib를 추가합니다.




빌드에 성공했습니다.



2. WM_CREATE에서 설정하기



우선 캡쳐 윈도우 핸들을 생성하여 저장합니다.




웹캠과 연결하는 함수를 호출합니다.




이미지를 갱신하는 속도를 지정합니다.




비디오 포맷에 대한 정보를 biVFW 변수에 저장합니다.




직접 설정하면 번거롭기 때문에 기존 값에서 필요한 값만 수정합니다.




수정된 값을 반영합니다.




미리보기를 할 수 있게 해줍니다.




화면이 출력됩니다.

(실행시 Debug모드로 실행하면 에러가 뜨므로, Start without debugging(Ctrl+F5)으로 실행해 주어야 합니다.)


+. Win7에서는 정상작동을 하지 않는 것 같음...



소스 및 실행파일


main.c


VFWExample.exe


개요

 비트맵이란 래스터 그래픽스라고도 하며, 각 점 하나하나의 정보를 갖고 있습니다. 제일 기본적인 이미지 형식으로 모든 운영체제에서 사용할 수 있습니다.[각주:1] 이번에는 Win32 API의 구조체를 이용해 비트맵 파일의 구성을 알아내고 화면에 출력하는 것이 주된 목표입니다. 우선 비트맵 구조체들을 이용해 비트맵의 내용을 분석하는 것으로 시작합니다.



비트맵 파일의 구조[각주:2]

 제일 우선적으로 파일 시그니쳐인 BM이 앞에 옵니다. 매직 넘버라고도 하며, 이를 통해 이 파일이 bmp파일인지 아닌지를 알 수 있습니다. 그다음에는 파일의 크기와, 예약된 공간 두 개, 각 픽셀의 RGB값이 저장된 위치의 오프셋을 알 수 있는 값이 저장되어있습니다. 이를 구조체로 쉽게 접근할 수 있도록 한 것이 BITMAPFILEHEADER 입니다.

 BITMAPHEADER의 바로 뒤에 오는 것이 DIB 헤더입니다. BITMAPINFO 구조체를 이용하면 되며 이곳에는 이미지의 크기(파일크기가 아닌), 폭, 높이, 압축 형식, 색 등을 알 수 있습니다.

 BITMAPFILEHEADER에서 RGB값이 저장된 오프셋을 원래 위치에서 더하면 RGB값들이 나오는데, 3바이트가 한 픽셀의 색을 갖고 있고, BGR순으로 들어 있습니다. 만약 가로 300px, 세로 300px의 비트맵 이미지가 있다면 300*300 = 90000, 90000*3 = 270000Bytes의 RGB 정보가 저장되어 있겠네요.


<비트맵의 상세한 구조 그림[각주:3]>



코딩하기

 여기서 쓸 비트맵 구조체는 BITMAPFILEHEADER, BITMAPINFO의 두가지가 있으며, 이 두 구조체는 Windows.h를 인클루드하여 사용할 수 있습니다. 이 프로그램은 화면에 비트맵 이미지를 출력해야 하므로 기본적인 WinAPI 창 프로그램 소스가 필요합니다. 프로그램의 기본 흐름은 아래와 같습니다.


1. BITMAPFILEHEADER와 BITMAPINFO 각각의 포인터를 만들어 준다(인스턴스가 아닌 포인터이다)

2. 파일을 연다

3. 파일을 버퍼에 읽어들인다(BITMAPFILEHEADER와 BITMAPINFO의 크기를 합한 정도가 적당하다)

4. 앞에서 만들어 둔 BITMAPFILEHEADER의 포인터가 버퍼의 시작지점을 가르키고, BITMAPINFO는 버퍼의 시작지점에서 sizeof( BITMAPFILEHEADER )만큼 떨어진 곳을 가르키도록 한다

5. 파일의 커서를 BITMAPFILEHEADER의 멤버 변수인 bfOffBits의 값만큼 이동시킨다.

6. BITMAPINFO의 bmiHeader.biSizeImage만큼 malloc을 이용해 공간을 확보한다

7. 확보한 공간에 파일을 읽어들여 RGB정보를 메모리 내부에 저장한다.

8. WM_PAINT문에서 반복문과 SetPixel함수등의 함수를 이용해 화면에 출력한다. (여기서는 SetPixel사용)

-> 값은 unsigned char *형 변수를 선언하여 위치를 이동하며 찍는 것이 좋으며, 해당 변수의 이름을 x라고 했을 때 RGB( x+2, x+1, x )식으로 색을 지정해야 한다.

9. 확보된 공간을 해제시켜준다.


전체적인 절차는 이와 같으며, 자세한 정보는 각주의 msdn문서를 참고하여 작성할 수 있습니다.

이 때, 화면에 찍힌 사진은 아래와 같을 것입니다.(출력이 이상하게 되거나 터진다면 이미지를 300*300으로 하시기 바랍니다)


<원본 이미지>


<출력된 이미지>


왜 이렇게 나오는 것일까!? 이유는 비트맵이 거꾸로 저장되어 있기 때문입니다. 만약 출력을 아래와 같은 방법으로 했다면


ucpData = ucBitmap;

for( y=0; y>uiYSize; y++ )
{
  for( x=0; x<uiXSize; x++ )
  {
    SetPixel( hDC, x, y, RGB( *(ucpData+2), *(ucpData+1), *(ucpData) ) );
    ucpData = ucpData + 3;
  }
}


for( y=uiYSize; y>0; y-- )


로 고쳐보기 바랍니다.. 아래부터 출력이 되어 출력이 정상적으로 될 것입니다.


 그런데... 이 상태에서 가로의 길이를 299나 298, 297 등으로 지정하면 미친듯이 깨지거나 대각선으로 쪼개져 나오는 등의 이상 현상을 발견할 수 있습니다. 이는 컴퓨터가 최적화를 위해 4바이트 단위로 데이터를 저장하여 생기는 문제인데요, 실제로는 존재하지 않는 픽셀이지만 남는 공간을 00으로 채워넣어 비트가 밀려나버려 이상한 출력이 되는 것입니다.(RGB값은 3바이트로 저장되고 3바이트가 실제로 하나의 픽셀므로, 1바이트만 미끌어져도 대형 참사가 일어납니다.)

 이것은 가로 길이에서 4를 나눈 나머지 값을 구하여 x의 반복문이 끝난 직후에 커서를 저 만큼 더 이동해주면 됩니다.


ucpData = ucBitmap;

for( y=uiYSize; y>0; y-- )
{
  for( x=0; x<uiXSize; x++ )
  {
    SetPixel( hDC, x, y, RGB( *(ucpData+2), *(ucpData+1), *(ucpData) ) );
    ucpData = ucpData + 3;
  }
  ucpData = ucpData + ( uiXSize%4 );//이동해주었다!!
}



이미지 조작하기

 저장된 RGB값을 조금씩조금씩 조작하여 독특하게 띄울 수 있습니다. 비트맵도 영상의 하나이니, 영상처리의 기초가 될 수 있겠습니다. 자주 보는 이미지 조작 중 '흑백'과 '역상' 등등을 들 수 있는데요, 이를 한번 직접 구현하여 봅시다.


흑백



 우선 흑백은 절대 검정~흰색 사이의 색 외에 다른 색이 쓰이지 않습니다. 그러므로 원래 갖고 있는 RGB값의 어두운 정도를 구해야 하는데요, 제일 쉽게 생각해낸 방법은 '평균내기'입니다. ( R의 값+B의 값+G의 값 ) / 3으로 구해진 값을 R,G,B부분에 넣어서 출력하는 것입니다.


빨강 색조



 아까의 흑백을 조금 더 응용하여 빨간 색조로 출력하도록 바꾸어 보았습니다. 단순히 평균을 내어 R부분에만 값을 넣고 나머지 부분에는 0을 넣었습니다. 하지만 색이 다소 어두워져 보정을 해주어야 하는 것 같습니다.


특정 색 강조



 흑백을 또 조금조금 꼬아서.... 노란색만 추출(?)해본 것입니다. 어정쩡하게 추출한 것이라 군데군데 오류가 보이네요.. R+G는 노랑색이다보니 이 두 값의 평균을 내어 100이상이면 노란색으로 간주, 출력하도록 하고 나머지는 흑백처리를 하도록 하였습니다. 원본과 비교해 보면 푸른 계열인 멜빵은 회색계열로, 노란색인 살색은 그대로 나오는 것을 볼 수 있습니다.


역상



 재미들려서 막 하게 되는군요. 하지만 사실 제일 처음 시도한 건 역상이었습니다.ㅋㅋ;;

역상은 모든 색을 반전시켜 출력하는 것으로, 간단히 1을 비트연산 XOR하거나 틸트시켜주면 됩니다.

깜찍한 캐릭터들이 좀 무서워졌네요; 이로써 모든 정리를 끝마칩니다.



※이번에 적은 내용은 순수 제 주관으로 작성한 내용이기 때문에 실제 영상처리와 틀린 점이 있을 수 있습니다만, 연습삼아 작성하게 되었습니다.



참고자료

  1. http://ko.wikipedia.org/wiki/%EB%B9%84%ED%8A%B8%EB%A7%B5 [본문으로]
  2. http://msdn.microsoft.com/en-us/library/windows/desktop/dd183392(v=vs.85).aspx [본문으로]
  3. http://www.yaldex.com/games-programming/0672323699_ch07lev1sec7.html [본문으로]


PE101-v1KO.pdf


+ Recent posts