이 예제는 임의의 정수(0~999)를 N개 생성하여 정렬하는 예제이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#include <string>
#include <vector>
#include <algorithm>
 
using namespace std;
 
// ...
 
static const int ITER_NUM = 2000;
 
void CStlTestView::OnStlVector()
{
        int i, rn, cnt;
        CString str, tmp;
        vector<int> numbers;
        vector<int>::iterator iter;
 
        // 랜덤 발생 초기화
        srand((unsigned int)time(0));
 
        // 입력
        for( i = 0 ; i < ITER_NUM ; i++ )
        {
                rn = rand() % 1000;
                numbers.push_back(rn);
        }
 
        // 상위 20개 출력
        cnt = 1;
        str = "After random number generation...nn";
        for( iter = numbers.begin() ; iter != numbers.begin()+20 ; iter++ )
        {
                tmp.Format("%2d: %dn", cnt++, *iter);
                str += tmp;
        }
         
        AfxMessageBox(str);
 
        // 전체 정렬
        sort(numbers.begin(), numbers.end());
 
        // 상위 20개 출력
        cnt = 1;
        str = "After sorting the random numbers...nn";
        for( iter = numbers.begin() ; iter != numbers.begin()+20 ; iter++ )
        {
                tmp.Format("%2d: %dn", cnt++, *iter);
                str += tmp;
        }
 
        AfxMessageBox(str);
}

실행 결과의 예는 아래와 같다.


Posted by kkokkal
:

Visual Studio .NET 을 사용하여 프로그램을 만들어보면, 전체 프로그램의 전체 프레임은 윈도우 테마를 따르지만, 내부 컨트롤들은 여전히 구식 스타일을 그대로 사용한다. 간단한 작업만으로 모든 컨트롤들에게 윈도우 비주얼 스타일을 적용할 수 있기때문에, 5분만 투자하여 멋진 프로그램을 만들어보자.

지금부터 설명할 내용들은 이미 codeproject 또는 MSDN 사이트에서 이미 찾을 수 있는 내용들이지만, 아무래도 한글이 이해하는데 빠르지 않을까 싶다. 보다 많은 정보를 원한다면 아래 링크를 따라가보기 바란다.

http://www.codeproject.com/cpp/xpstylemfc.asp
http://www.codeproject.com/cpp/AddXpStyle.asp
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnwxp/html/xptheming.asp

이제부터 하나하나 설명하도록 하겠다. 여기서 설명은 다이얼로그 기반의 프로그램을 실제로 하나 만들어보면서 진행하도록 하겠다.

우선 XPStyle 이라는 제목의 프로젝트를 하나 생성하자. 당연히 'MFC 응용프로그램' 항목을 선택하고, MFC 응용 프로그램 마법사에서는 응용 프로그램의 종류를 '대화 상자 기반'을 선택하도록 하자. 아무래도 컨트롤 올리기가 편하니까... 그리고, 중요한 것은 아래 그림과 같은 고급 기능 탭이다. 그림을 보자.


오른쪽 항목에서 맨 아래쪽 '공용 컨트롤 매니패스트' 가 선택이 되어있어야 한다. 기본적으로 선택이 되어있으므로 따로 건들이지만 않으면 된다. 이제 [마침] 버튼을 클릭하여 프로젝트를 생성한다.

아마 빈 대화상자를 편집할 수 있는 화면이 나타날 것이다. 왼쪽의 리소스 뷰의 [XPStyle.rc] 에서 마우스 오른쪽 버튼을 클릭하여 [리소스추가...] 를 선택하거나 또는 상단의 메뉴에서 [프로젝트]->[리소스추가...] 를 선택하여 다음과 같은 창이 나오게 하자.


오른쪽 버튼들 중에서 세번째 [사용자지정...] 버튼을 눌러서, 새 사용자 지정 리소스에 숫자 24를 입력하고 [확인] 버튼을 누른다.


그러면 위와 같이 RT_MANIFEST 라는 항목을 새로 편집하는 창이 나타나게 된다. 여기에 XML 문장을 입력해야한다. XML 문장을 완전히 새로 쓰는 것은 아니고, 솔루션 탐색기에서 XPStyle.manifest 파일에 적혀있는 내용을 그대로 복사해서 옮기면 된다. 아래 그림을 보자.


위 그림에서 오른편의 XML 문장을 전체 선택하여 copy 해서 아까 리소스 편집기로 다시 가서 paste 한다. 결과 그림은 다음과 같다.


마지막으로, 새로 추가하였던 리소스의 아이디(ID)를 IDR_RT_MANIFEST1 에서 숫자 1로 변경한다.


이로써 모든 작업이 끝났다. 이제 프로그램을 빌드하고 실행해보면 버튼과 같은 컨트롤에도 windows visual style 이 적용된 것을 확인할 수 있을 것이다.

아래 샘플 프로그램의 프로젝트 파일을 다운받아서 테스트해보기 바란다.



Posted by kkokkal
:

GVF 코드는 http://iacl.ece.jhu.edu/projects/gvf/ 에서 matlab 으로 구현된 것을 구할 수 있다. 이를 C/C++ 형태로 변환하기 위해서는 matlab 코드를 분석할 수 있어야 하는데, 개인적으로 분석한 결과를 제시하려고 한다.

실제 gvf.m 코드는 매우 간략하다. 아래 코드는 코드 설명을 위해 여백을 추가하였을 뿐, 웹에서 다운받은 코드와 완전히 동일하다.


위의 코드에서 실제 iteration이 일어나는 부분의 코드에 대한 설명은 따로 하려고 한다. C. Xu의 논문을 보게 되면, Section III의 C. Numerical Implementation 에서 자세한 구현 방법을 설명해놓고 있다. 자세히 읽어보면 아마 무슨 내용인지는 알 수 있지 않을까.. 흠. Xu의 논문은 위 사이트에서 다운받을 수 있다. (C. Xu and J.L. Prince, "Snakes, Shapes, and Gradient Vector Flow", IEEE Transactions on Image Processing, 359-369, March, 1998 ) 식 (16)이 matlab 코드로 변환되는 과정을 살펴보도록 하자.


Posted by kkokkal
:
Matlab 의 gradient 함수와 del2 함수에 대한 분석 및 C 코드 변환은 GVF 의 Matlab 코드를 분석하는 과정에서 이루어졌다. GVF란 Gradient Vector Flow 의 약자로써, http://iacl.ece.jhu.edu/projects/gvf/ 에서 자세한 정보를 얻을 수 있다.

---------------------------------------------------------------------------------------

Matlab의 del2 는 Laplacian 연산을 의미한다.

>> help del2
DEL2 Discrete Laplacian.
L = DEL2(U), when U is a matrix, is a discrete approximation of
0.25*del^2 u = (d^2u/dx^2 + d^2/dy^2)/4. The matrix L is the same
size as U, with each element equal to the difference between an
element of U and the average of its four neighbors.

L = DEL2(U), when U is an N-D array, returns an approximation of
(del^2 u)/2/n, where n is ndims(u).

아래 코드 분석을 보자. 몇몇 설명은 아래 gradient 관련 글을 참고하기 바란다.



이 코드를 C/C++로 변환하면 다음과 같이 하면 될 듯 싶다. ^^;


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
void IEGvf::GvfLaplacian(IEFloatImage& img, IEFloatImage& lap)
{
    register int i, j;
 
    int w = img.GetWidth();
    int h = img.GetHeight();
 
    lap.SetPixels(0);
 
    float** pimg = img.GetPixels2D();
    float** plap = lap.GetPixels2D();
 
    // x 방향
    for( j = 0 ; j < h ; j++ )
    {
        plap[j][0]   = pimg[j][1]*2   - pimg[j][2];
        plap[j][w-1] = pimg[j][w-2]*2 - pimg[j][w-3];
    }
 
    for( j = 0 ; j < h   ; j++ )
    for( i = 1 ; i < w-1 ; i++ )
    {
        plap[j][i] = ((pimg[j][i+1] - pimg[j][i]) - (pimg[j][i] - pimg[j][i-1]))/2;
    }
 
    // y 방향
    for( i = 0 ; i < w ; i++ )
    {
        plap[0][i]   += pimg[1][i]*2   - pimg[2][i];
        plap[h-1][i] += pimg[h-2][i]*2 - pimg[h-3][i];
    }
 
    for( j = 1 ; j < h-1 ; j++ )
    for( i = 0 ; i < w   ; i++ )
    {
        plap[j][i] += ((pimg[j+1][i] - pimg[j][i]) - (pimg[j][i] - pimg[j-1][i]))/2;
    }
 
    for( j = 0 ; j < h ; j++ )
    for( i = 0 ; i < w ; i++ )
    {
        plap[j][i] /= 2;
    }
}


Posted by kkokkal
:
Matlab 의 gradient 함수와 del2 함수에 대한 분석 및 C 코드 변환은 GVF 의 Matlab 코드를 분석하는 과정에서 이루어졌다. GVF란 Gradient Vector Flow 의 약자로써, http://iacl.ece.jhu.edu/projects/gvf/ 에서 자세한 정보를 얻을 수 있다.

---------------------------------------------------------------------------------------

Matlab 에서 'help gradient' 라고 치면 다음과 같이 출력된다.

>> help gradient
GRADIENT Approximate gradient.
    [FX,FY] = GRADIENT(F) returns the numerical gradient of the
    matrix F. FX corresponds to dF/dx, the differences in the
    x (column) direction. FY corresponds to dF/dy, the differences
    in the y (row) direction. The spacing between points in each
    direction is assumed to be one. When F is a vector, DF = GRADIENT(F)
    is the 1-D gradient.
    (이하 생략)

뭐... 대충 읽어보면 입력 F를 받아서 그것의 x 방향, y 방향으로의 미분값을 각각 구하여 FX와 FY에 저장한다는 내용이다. 아래 Matlab 코드를 보자.


위의 코드를 C/C++ 로 변환하는 것은 쉽다. 위의 빨간색 글씨를 쓰는, 그런 코드 이해가 어려운 거시지 이해만 했다면 C 코드 쓰는 것이야 어려울 턱이 있나.. ^^;

코드는 다음과 같다. 대충 보고 이해하기를...

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
void Gradient(IEFloatImage& img, IEFloatImage& fx, IEFloatImage& fy)
{
        register int i, j;
 
        int w = img.GetWidth();
        int h = img.GetHeight();
 
        fx.CreateImage(w, h);
        fy.CreateImage(w, h);
 
        float** pimg = img.GetPixels2D();
        float** pfx  = fx.GetPixels2D();
        float** pfy  = fy.GetPixels2D();
 
        // x 방향 그래디언트 fx 계산
        for( j = 0 ; j < h ; j++ )
        {
                pfx[j][0]   = pimg[j][1]   - pimg[j][0];
                pfx[j][w-1] = pimg[j][w-1] - pimg[j][w-2];
        }
 
        for( j = 0 ; j < h   ; j++ )
        for( i = 1 ; i < w-1 ; i++ )
        {
                pfx[j][i] = (pimg[j][i+1] - pimg[j][i-1])/2.f;
        }
 
        // y 방향 그래디언트 fy 계산
        for( i = 0 ; i < w ; i++ )
        {
                pfy[0][i]   = pimg[1][i]   - pimg[0][i];
                pfy[h-1][i] = pimg[h-1][i] - pimg[h-2][i];
        }
 
        for( j = 1 ; j < h-1 ; j++ )
        for( i = 0 ; i < w   ; i++ )
        {
                pfy[j][i] = (pimg[j+1][i] - pimg[j-1][i])/2.f;
        }
}
Posted by kkokkal
:


Images.zip


영상처리를 공부하다보면 자주 보게 되는 이미지 모음입니다.
출처는 아래와 같구요...

http://www.dip.ee.uct.ac.za/imageproc/

참고로, 폴더에 USC 라 함은 아래 사이트를 참고하세요.
이곳에 가면 더 많은 표준 영상을 구할 수 있습니다.

http://sipi.usc.edu/database/



Posted by kkokkal
:


RGB로 표현되는 트루칼라를 그레이스케일로 변환하는 식은 다음과 같다.

L = 0.299R + 0.587G + 0.114B

CxImage 라이브러리( http://www.xdp.it/cximage.htm )에는 위의 변환식을 define 문과 shift 연산자를 사용하여 속도 향상을 꾀하였다는 특색이 있다. CxImage 소스에 적혀있는 변환 코드는 다음과 같다.

// color to grey mapping <H. Muelner> <jurgene>
// Same as #define RGB2GRAY(r, g, b) (((b)*114 + (g)*587 + (r)*299)/1000)
#define RGB2GRAY(r, g, b) (((b)*117 + (g)*601 + (r)*306) >> 10)

이와는 달리, 매우 간단하게 grayscale 형태로 바꾸는 다음과 같은 식도 존재한다.

grayscale = (R + G + B) / 3

그러나, 이 방법은 말 그대로 매우 단순하게 grayscale 과 비슷한 값으로 바꾸는 것이고, 정확한 변환식은 아닌 것으로 생각된다.

(2005년 12월 28일 추가)

Matlab에서도 rgb2gray() 라는 함수가 있는데 이 함수에서는 다음과 같은 행렬을 이용하여 grayscale 로 변환을 한다.

T = inv([1.0 0.956 0.621; 1.0 -0.272 -0.647; 1.0 -1.106 1.703]);

이 행렬 T의 첫 번째 행이 위에서 제시한 변환식과 일치한다.

>> T

T =

     0.2989 0.5870 0.1140
     0.5959 -0.2744 -0.3216
     0.2115 -0.5229 0.3114


Posted by kkokkal
:
영상 처리 프로그래밍에 있어서 가장 많이 접하는 타입이 바로 2차원 배열이다. C/C++ 에서는 모든 자료형에 대하여 다차원 배열을 지원하고 있다. 그러나, 기본적인 2차원 배열을 사용하려면 그 크기를 미리 지정해야한다는 단점이 있다. 그러나, 다양한 크기의 영상을 배열에 넣기 위해서는 배열의 크기가 프로그램 동작 시간에 결정되게 마련이고, 그 때문에 new 연산자를 이용한 동적 배열 할당이 필요하다.

가장 일반적인 형태의 2차원 배열 동적 할당 방법은 다음과 같다.


위의 코드를 그림으로 설명하면 다음과 같다.


위의 코드에서 주의해야 할 점은 할당한 메모리 공간을 해제할 때, 그림상 하늘색으로 표현된 공간을 모두 해제해주어야 한다는 점이다. 즉, 가로로 그려진 하늘색 메모리 공간 w개와 세로로 그려진 메모리 공간 1개를 delete [] <변수이름> 형태로 삭제해주어야 한다는 점이다.

이제 이것과 동일한 역할을 하는, 새로운 2차원 배열 동적 할당 방법을 알아보자. 코드는 다음과 같다.


마찬가지로 이 코드에 대한 그림 설명을 보면 이해가 더 쉽게 되리라 믿는다.


이 새로운 방법에서는 new 연산을 2번만 수행한다는 장점이 있다. 그러므로, for 문을 돌면서 메모리를 할당하거나 해제하는 작업이 없다. pixels[1]~pixels[h-1] 까지는 모두 새로운 메모리를 할당하여 그 위치를 가리키는 것이 아니라, pixels[0] 이 할당한 큰 메모리 공간의 중간을 각각 가리키는 방식을 취한다.

이러한 Alloc2 방법은 배열의 크기가 아주 크지 않을 때에는 Alloc1 보다 어느정도의 속도 향상의 성능을 보여준다. 예를 들어, 256x256 크기의 배열을 10번 할당&해제할 때의 속도를 VC++ 6.0의 Profile 기능을 이용하여 그 시간을 체크해보았다. 실험에 사용한 컴퓨터 환경은 Pentium IV 2.8GHz(3.3GHz로 overclocking), 1GByte RAM, Windows XP sp2 이다.

Func Func+Child Hit
Time % Time % Count Function
---------------------------------------------------------
20.624 58.9 20.624 58.9 10 Alloc1(void) (dyna2d.obj)
14.391 41.1 14.391 41.1 10 Alloc2(void) (dyna2d.obj)
0.007 0.0 35.023 100.0 1 _main (dyna2d.obj)

그러나, 배열의 크기를 키우면 그다지 속도 차이가 나지 않음을 발견하였다. 1024x1024 배열을 10번 할당하고 해제할 때의 속도를 보자.

Func Func+Child Hit
Time % Time % Count Function
---------------------------------------------------------
297.449 54.1 297.449 54.1 10 Alloc1(void) (dyna2d.obj)
252.632 45.9 252.632 45.9 10 Alloc2(void) (dyna2d.obj)
0.007 0.0 550.087 100.0 1 _main (dyna2d.obj)

사실, 2003년도에 이 Alloc2 방법이 Alloc1 방법보다 매우 빠르다고 생각했었는데, 이번 실험에서 이렇게 결과가 나온 것은 상당히 당혹스러웠다. 그러나, 이번 실험의 하나하나를 다 따져보았으나 잘못된 부분은 발견할 수 없었다. 결국, 속도상에서의 merit 는 그다지 없는 것이 아닌가 하는 결론을 내려야만 했다.

첨부파일은 VC++ 6.0 에서 profile 을 보기 위해 만든 프로젝트와 VC .NET에서 GetTickCount 함수를 이용하여 배열을 할당 & 해제하는데 걸리는 시간을 재는 윈도우용 프로그램의 프로젝트 압축파일이다. 참고하시길...


ps. VC .NET 으로 만든 프로그램으로 256x256 배열을 1000번 할당 & 해제할 경우, 은근히 속도 차이를 나타내는 것을 발견하였다. 흠... 작은 배열일 때는 과연 Alloc2 가 유리한 것인가? ㅡㅡ;

012345
Posted by kkokkal
:


view 클래스에서 WM_ERASEBKGND 메시지 핸들러를 추가하여 다음과 같이 코드를 입력한다.

1
2
3
4
5
6
7
8
BOOL CImageShopView::OnEraseBkgnd(CDC* pDC)
{
    CBrush br;
    br.CreateHatchBrush(HS_DIAGCROSS, RGB(200, 200, 200));
    FillOutsideRect(pDC, &br);
 
    return TRUE; // Erased
}


한가지 주의할 것은, 함수 반환값을 항상 TRUE로 지정해주어야 한다.

참고로, FillOutsideRect 함수에 대한 설명은 다음과 같다.

Call FillOutsideRect to fill the area of the view that appears outside of the scrolling area. Use FillOutsideRect in your scroll view’s OnEraseBkgnd handler function to prevent excessive background repainting.




Posted by kkokkal
:
예를 들어, 다이얼로그 박스가 떠있고, 내부적으로 연산량이 많은 작업을 하고 있다고 치자.
다이얼로그에서 cancle 버튼을 클릭하면 내부적인 연산을 취소하고 싶다.
그러나, 일반적으로 이 경우, 윈도우즈는 버튼이 클릭되었다는 메시지 처리를 바로 수행하지
못하고, 단지 메시지 큐에만 넣게된다. 바로 single-thread를 사용하기 때문이다.

이러한 문제를 해결하기 위해서는 multi-thread 를 사용하는 것이 가장 적절한 해법이겠으나,
다소 복잡할 수 있다.

여기서는 single thread를 사용하면서, 주기적으로 메시지 큐를 검사하는 방법을 설명한다.

다음 함수를 보자.

void ProcessMessages()
{
CWinApp* pApp = AfxGetApp();
MSG msg;

while( PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE) )
pApp->PumpMessage();
}

만약 프로그램 내부에서 복잡한 연산을 수행하는 루틴에 위의 함수를 주기적으로 호출한다면,
multi-thread 를 쓰는 경우보다는 약간 어색할 수 있지만, 그래도 윈도우 메시지를 처리할 수 있다.

Posted by kkokkal
: