2015년 10월 19일 월요일

Computer Vision] Harris Corner Detector

 영상처리에서 Corner를 찾는 일은 매우 중요하다. 사진이 변형 되어졌을 때 변하지 않는 corner를 찾는 것이 사진을 비교하거나 처리함에 있어 간단하고 편리하기 때문이다. Edge같은 경우는 사진이 회전하였을때 어떤 Edge이 같은지를 판단하기가 힘든데 corner같은 경우는 쉽다.

 해리스코너의 원리는 사진에서 어느 한 지점의 윈도우에서 세로와 가로 대각선으로 움직였을 때의 변화량 값을 측정하여 그곳이 코너인지 아닌지를 판단하는 것이다.  만약 픽셀의 위치가 Edge였을 경우를 생각해보자 아래의 그림의 경우 y축으로 위아래로 한픽셀씩 움직이면 변화량의 차이는 거의 없을 것이다. 하지만 x축으로 좌우로 움직이면 변화량은 있을 것이다. 윈도우를 상하 좌우 대각으로 움직였을 때 한쪽 방향이 변화량의 거의 없다면 그곳은 Edge로 판단한다. flat 즉, 평평한 곳일 경우는 어느 방향으로 해도 변화량의 차이가 없을 것이다. 마지막으로 corner를 생각해보면 상하 좌우 대각어느 방향으로 움직여도 변화량이 강하게 보이기 때문에 그곳을 corner로 판단할 수 있다.
 자세한 수학적인 이론과 방법은 생략하기로 하고 실제로 matlab에서 Harris Corner를 찾는 방법을 설명하겠다.(모르는 것을 설명하면 더 큰 혼란을 줄 수 있을것 같다.) 지금 설명하는 방법은 완벽한 정답이 아니니 경계하고 전체적인 틀로써 봐주면 좋을 것 같다.
 먼저 제일 먼저 해야 할 것은 x축 gradient와 y축 gradient이다. Canny Edge에서 했던 것과 동일하게 각각 5*1 행렬과 1*5행렬을 만들어 이미지 전체를 convolution하여 얻은 값이다. 이렇게 하면 각각 sample image와 같은 크기의 x축 gradient 행렬과 y축 gradient행렬이 만들어 질 것이다. 각 gradient값은 x축과 y축으로 이동하였을 때의 차이 값을 나타내는데 +일수도 있고 -일 수도 있다.  x축은 x축끼리 y축은 y축 끼리 제곱한 것을 각각 Ix2와 Iy2로 표현하겠다. 그리고 x축과 y축 을 서로 곱한것은 Ixy로 한다. (여기서 x축과 y축은 각 gradient를 말하는 것이다) Ix2과 Iy2그리고 Ixy에 가우시안 필터를 적용한 것을 다시 Sx2와 Sy2그리고 Sxy로 하겠다. corner를 찾는 것에 있어서 이 Sx2, Sy2 그리고 Sxy를 구하는 것이 중요하다. 각 행렬이 의미하는 것은  Sx2와 Sy2는 말그대로 각 x축과 y축에 대한 gradient 값이라면 Sxy는 대각성분으로 볼 수 있다. 이 성분을 가지고 corner를 찾는다. 아래의 그림을 보면서 설명을 이어 나가겠다.
그림을 보면서 설명을 이어나가도록 하겠다. 먼저 식에 대한 설명을 하자면 H(x,y)란 2x2행렬이 보일 것이다. corner를 구하는 것에 있어서 determine이란 개념을 사용하는데 determine이란 간단하게 설명하자면 A = {a b; c d}라는 2x2의 행렬이 있을 때 ad-bc이다.
(역행렬구하는 공식과 사용하는데 자세한 설명은 찾아보면 쉽게 찾아 낼 수 있을 것이다.)
k는 경험적으로 0.04-0.06을 택하고 있다. 그리고 Trace(H)란 대각의 합으로 위의 A행렬에서 a+d를 말하는 것이다. 이 식에서 중요한 최종 목표는 각 행열에 해당하는 R값을 구하는 것이다.  우리는 원하는 각 픽셀의 Sx2, Sy2, Sxy값을 모두 가지고 있다. 잊 대입하여 그 픽셀에 해당하는 R값을 구할 수 있을 것이다.  그렇게 구한 R행렬에서 주변 값들중에 최대값인 것만을 남기고 그 값을 기준으로 몇 %까지의 수를 화면에 표시할 것인지를 정하여야 한다. 이렇게 정한 기준에 따라 코너의 개수가 달라질 수 있다.

 이제 Sx2와 Sy2 그리고 Sxy에 대한 이야기를 좀더 해보겠다. 각 x와 y의 gradient라 하였는데 머리속으로 상상하면서 따라와보자. gradient의 값이 있을때는 언제일까. gradient의 값이 없을 때는 언제일까. 가로로된 직선위에서의 x축 gradient는 어떻게 될까. 만약 가로로된 직선이 있다면 x축 gradient는 0에 가까울 것이다. 반대로 세로로 된 직선 위에 픽셀의 y축 gradient는 0에 가까울 것이다. det(H)를 생각해보자 Sx2*Sy2-Sxy^2 이다. 여기서 Sx나 Sy 둘중 하나의 값이 0에 가깝다면 Det값은 어떻게 정해질까. 그렇게 된다면 R값을 정함에 있어서 -값이 나올 것이다. 반대로 corner를 생각해보자. x축 gradient와 y축 gradient가 높을 것으로 예상되지 않는지. 

 지금 여기서 내가 설명한 것은 정말 수학적인 지식이 필요하지 않은 Harris Corner Detector를 의식의 흐름처럼 그냥 간단하게 설명한 것이다. 흔히 말하는 '가라'식의 표현이라서 이 글을 읽고 전체적인 흐름만 이해한다고 생각하면 된다. 정말 수학적으로 어떤식으로 인해 유도되는것은 중요하지만 처음부터 그런것을 맞닥뜨리게 되면 혼란스러울 것이다. 전체적인 흐름을 알고 수학적인 식이나 원리를 보게 된다면 이해하기가 더 편할수도 있을 것이다. 마지막으로 한가지 말하고 싶은 것은 이것이 정답이 아닐 수도 있다는 것이다. 더 다양한 시점에서 공부하길 바란다.




harrisco('lena_N.jpg', 0.008);

function [ output_args ] = harrisco( img_name, num )
%HARRISCO Summary of this function goes here
%   Detailed explanation goes here
gau = fspecial('gaussian', [7 7], 2);
fx = [-2 -1 0 1 2];
fy = [-2; -1; 0; 1; 2];
img = imread(img_name);
%img = rgb2gray(img);

[h w] = size(img);

lx = filter2(fx, img);
ly = filter2(fy, img);
lx2 = lx.*lx;
ly2 = ly.*ly;
lxy = lx.*ly;

Sx2 = filter2(gau, lx2);
Sy2 = filter2(gau, ly2);
Sxy = filter2(gau, lxy);
H = [Sx2 Sxy; Sxy Sy2];

for i=1:h
    for j=1:w
        R(i,j) = (Sx2(i,j)*Sy2(i,j)-Sxy(i,j)*Sxy(i,j)) - 0.04*((Sx2(i,j)+Sy2(i,j))^2);
    end
end


m = max(max(R));

for i=2:h-1
    for j=2:w-1
        if(m * num < R(i,j))
        
            if R(i-1,j) > R(i,j) || R(i+1,j) > R(i,j) || R(i,j-1) > R(i,j) || R(i,j+1) > R(i,j) || R(i+1,j+1) > R(i,j) || R(i-1,j+1) > R(i,j) || R(i+1,j-1) > R(i,j) || R(i-1,j-1) > R(i,j)
                result(i,j) = 0;
            else
                result(i,j) = 1;
            end         
        else
           reslut(i,j) = 0;
        end
        
    end
end

[posc, posr] = find(result == 1);

imshow(img)
hold on;
plot(posr,posc, 'r+');
end



2015년 10월 7일 수요일

matlab] Canny Edge Detection Method

 컴퓨터 비전에 사용되는 Canny Edge에 설명해 보겠다. Canny Edge란 사진에서 물체와 물체의 경계가 되는 선을 찾는 것이다. 막상 생각해보면 어렵겠지만 한번 이해 한다면 그렇게 어렵게 느껴지지 않을 것이다. (흑백 사진을 기준으로 한다.)

 첫번째로 사진의 x축 gradient(경사도, 가중치)와 y축 gradient를 구한다. 구하는 방법은 gradient를 구할 필터를 하나 만들고 그것을 한픽셀 한픽셀씩 convolution하면 되는 것이다. 다시 말해 필터를 만들어 반복문을 통하여 한픽셀 한픽셀씩 픽셀의 gradient를 구하면 되는 것이다. 예를 들어 설명하자면 [-2 -1 0 1 2] 라는 1*5행렬의 필터를 만든다. 그리고 어떤 x,y의 픽셀에 저 필터를 대입하면 x,y의 값에는 0이 곱해지고 주위의 한픽셀씩에는 -1과 1이 2픽셀 옆에는 -2와 2의 값이 곱해진다음 0을 곱한 x,y를 포함한 5개의 숫자를 더한 값이 새로운 배열의 값이 된다. 이 방식을 이용해 원래 사진과 같은 크기의 배열을 만들어 낼 수 있다. y축은 저것을 5*1배열의 필터를 만들어 반복하면 된다. (저 필터의 크기나 값은 이 방식을 이해한다면 원하는 대로 바꿀 수 있다.)

 처음에는 이게 어떤 의미인지 헷갈릴 수 있다. 만약 사진의 평평한 부분 흰도화지에 저 필터를 적용 한다고 생각해보자. 자신의 좌우나 세로의 주변 값은 모두 동일할 것이다. 그럼 동일한 값에 -2와 -1을 곱하고 1과 2를 곱해서 다 더한 값은 0이나 0에 가까운 숫자가 나올 것이다. 필터를 사용하다가 흰색부분을 지나서 검은색지역으로 넘어 간다고 생각해보자. 그러면 그 경계가 되는 부분에서  [흰색] [검출 대상 픽셀] [검정] 처럼 흰색과 검정 중간 경계에서 저 gradient 필터를 적용한다면 [-2*255] [-1*255] [0] [1*0] [2*0]라는 값을 얻을 수있다. 저것을 다 더한다면 -765라는 큰 값을 얻을 수 있게 된다. (gradient에서 +와 -는 중요하지 않다. 나중에 검출할 때 부호에 상관없이 값의 크기만 얻기 위해서 한번 제곱을 해 준다.)
그리고 다시 검은색 지점을 지나칠때는 흰색과 동일하게 0에 가까운 값만을 얻을 것이다.

그럼 이제 전체적으로 gradient를 구한 값을 생각해보자. 밝기나 색이 바뀌는 부분(우리가 생각하는 Edge)에서는 +나 -로 큰 값이 저장 되어있고 나머지는 0에 가까운 수가 되어 있을 것이다. 이것은 x축필터로 한번 y축 필터로 한번씩 해서 x의 gradient와 y의 gradient를 구할 수 있다. 여기까지 이해가 안된다면 과정을 다시 생각해 보는 것도 좋은 것이다. 지금의 gradient의 개념이 앞으로 나올 개념들의 기초가 될 것이다.

위에서 구한 x축과 y축의 gradient 값을 각각 제곱해서 더 해준 값에 루트를 씌운다. 그렇게 해서 전체적인 gradient값을 구한 후 또 하나 필요한 건 각 픽셀의 기울기이다. 각 픽셀에 gradient가 존재 한다면 필요한 것이 기울기이다. 값이 존재한다면 선이 존재한다는 말이 되는 것이고 그렇다면 세로로 이어진 선인지 가로로 이어진 선인지 대각선인지를 판단할 수 있어야 한다. x축 gradient는 가로축방향만 가지는 값이고 y축 gradient값은 세로축 방향만을 가지는 값이다.  기울기를 구하는 것은 atan(y/x)이다. 이것으로 구한 값을 이용해 방향성을 알 수 있다.

-0.4142< atan(y/x) <= 0.4142 가로방향
0.4142< atan(y/x) < 2.4142 2시와 7시방향을 가로지르는 직선 방향의 대각
|atan(y/x)| >= 2.4142 는 세로방향
-2.4142 < atan(y/x) <= -0.4142 10시와 5시 방향의 대각

이것으로 각 픽셀의 방향성을 알 수 있다. 이제 각 픽셀의 방향성을 검토하면서 그 방향성 둘중 한군데에서 자기 자신보다 큰 값을 가지는 픽셀이 있다면 자기 자신을 0으로 만든다.
이렇게 전체적으로 반복해 나간다면 남아 있는것은 그 방향에서 제일 큰 값을 가지는 픽셀만 남아있을 수 있게 되는 것이다.

2015년 10월 5일 월요일

matlab] 매트랩 함수 사용 정리



ans = imread('img.jpg');
이미지를 읽어 들어와 그 픽셀 값을 변수에 저장한다 컬러 이미지의 경우 3차원 배열 R,G,B
흑백이미지의 경우 x,y값만 가지는 2차원 배열이 생성된다.

imshow(ans);
figure, imshow(ans);
배열 변수의 값을 출력하여 사용자에게 보여준다. 그래프나 사진작업을 이용할때 이용하면 좋다. figure를 사용하면 사진을 여러개 띄울 수 있다.

A .* B  A ./ B ....
A배열과 B배열의 각각의 요소끼리 곱한다. 매트랩에서 기본적으로 행렬연산을 제공하지만 이건 단순 곱을 할 수 있다. 같은 방법으로 나누기도 가능하다

ans = sum(A);
A의 모든 변수를 더해준다.  2차원 배열의 경우 각 행을 첫번째 열에 다 더하기 때문에 ans = sum(sum(A))를 하면 모든 값을 더한 수 하나만 나온다. 


ans = fspecial('gaussian', [7 7], 2);
매트랩에서 제공하는 필터를 만들어서 제공해준다. gaussian말고 다른 함수가 있지만 잘 모르겠다.

ans = filter2(filter, img);
 반복문을사용할 필요 없이 filter를 img에 적용 시킬 수 있다. filter의 사이즈는 사용자가 자유롭게 설정할 수 있다.

What is Gaussian Filter, Mean Filter and Median Filter?

Gaussian FilterMean Filter 그리고 Median Filter는 모두 사진의 노이즈를 제거해서 좀 더 매끄러운 사진을 만들 수 있게 해준다. 각각은 필터안에 들어가는 값과 방식의 차이가 있지만 방법은 동일하다고 볼 수 있다. 먼저 제일 간단한 개념을 보면 사진의 각 픽셀의 값을 재정의 하게 되는데 재정의의 필요한 픽셀은 주변픽셀의 값에 영향을 받아서 정한 다는 것이다. Mean Filter에서 3x3filter size의 경우 자신을 제외한 자기를 둘러싸고 있는 8개의 픽셀과 자기 자신을 포함한 총 9개의 픽셀을 다 더한 후 9로 나누어 준 평균으로 재정의 함으로써 전체적으로 부드러운 이미지를 얻어 낼 수 있다. 방식을 유지한 채 한 픽셀 한 픽셀을 이동하여 전체적으로 필터로 픽셀 값을 얻어낸다. Median Filter의 경우는 중간 값을 이용하여 값을 재정의한다. 자신과 주변에 둘러싼 픽셀을 정렬하여 가장 중간에 오는 값으로 픽셀 값을 정한다. Gaussian Filter는 필터에 가우시안 분포를 이용하여 필터의 값을 정하게 된다. 정하는 식은 필터의 정 중앙은 좌표 0,0으로 취급한다. 아래의 식을 사용하여 필터의 값을 정한 후 그 필터를 한픽셀 한픽셀씩 지나가면서 값을 재정의 한다.
시그마 필터의 사이즈와 시그마 값을 입력받아서 실행하게 되어진다. 시그마가 높을수록 사진이 전체적으로 좀더 뭉개진다.
filter size의 크기는 변할 수 있으며 정사각형의 홀수개의 크기를 가진다.
 

What are the spherical aberration, chromatic aberration and vignetting?

물체나 장면이 보인다는 것은 반사된 빛이 렌즈를 통하여 한점에 상이 맺혀야 제대로 보일 수 있다. spherical aberration이란 구면 수차라고 말할 수 있다. 구면 수차는 반사된 평행한 광선이 렌즈를 통하여 한점에 맺혀야 하는데 그렇지 않고 맺히는 위치가 앞뒤에서 어긋나 버리는 현상을 말한다. 렌즈의 주변부와 중심부가 굴절율이 다르기 때문에 일어난다. 이 구면 수차를 줄이는 방법은 비구면 렌즈의 사용과 조리개를 중심한 좌우 대칭 렌즈의 구조, 조리개를 좁혀서 활영 한다면 나아 질 수 있다. chromaic aberration는 색수차라고 불리어 진다.파장에 따른 굴절률의 차이에 의해 생기는 수차이다. 긴 파장의 빛일수록 렌즈를 통과한 뒤에 다른 빛보다 초점이 렌즈에서 먼 쪽으로 맺히기 때문에 일어나는 현상으로, 광학기기에 사용하는 렌즈를 만들 때에는 이를 보정하기 위해 여러 개의 렌즈를 결합한다. vignetting이란 사진의 외곽이나 모서리가 어둡게 나오는 현상이다. 렌즈 주변부의 광량 저하로 촬영된 사진의 외곽 부분이 어두워지는 현상이며, 촬영시 최대한 조리개를 줄이게 되면 비네팅 현상을 줄일 수 있다. 조리개를 줄여 줌으로써 주변부와 중심부의 광량을 어느정도 맞출 수 있기 때문이다.

Please explain perspective, weak-perspective and orthographic projections.

사람의 눈은 사물이나 장면을 볼 때, 가까이 있는 것은 크게 보이고 멀이 있는 것은 작게 보인다. 우리가 입체적인 사물을 볼 때 우리가 보는 방향에 따라 모양이 달라 보인다. 입체적인 정육면체를 생각해 보자. 정면에서 봤을때는 정사각형이지만 옆에서 보거나 위에서 볼 때는 정육면체의 각이 직각을 이루지 않고 평행사변형이나 사다리꼴과 같은 모습을 볼 수 있게 된다. 이것은 우리가 입체적인 사물이나 장면을 보았을때 이러한 현상이 나타나게 되는데 이것의 원인은 가까이 있는 것은 크게 보이고 멀리 있는 것은 작게 보이는 원근감 때문에 생기는 것이다. 이 때 우리가 생각해야 하는 것은 어떤 규칙에 의해서 그렇게 보이는 지 알아야 하는 것이다. 사물이나 장면은 우리의 눈높이에 따라 직선을 그으면 소실점이라는 선이 생긴다. 혹시 전철이 다니는 선로 위에 서본 적이 있다면 평행한 두 선로를 따라 시선을 움직이다 보면 저 멀리 두 선로가 만나는 듯이 보인 적이 있을 것이다. 원근에 따라 두 선로가 한점에 모이게 되는 현상이 일어나는데 이것을 vanishing point(소실점) 이라고 한다.
projection이란 이러한 3차원의 object를 화면에 표현하기 위해 2D평면으로 투영하는 것을 projection이라고 하는데 perspective projection이란 이러한 원근의 원리를 이용하여 가까운 것을 크게 멀리있는 것을 작게 그리고 vanishing point를 고려해서 projection을 하는 것을 말한다. 반대로 orthographic projection은 원근에서 중요한 역할을 하는 vanishing point를 고려하지 않고 투영하는 방식이다. 입체적인 사물을 보았을 때 그대로의 모습을 projection 하는 것이다. vanishing point를 고려하지 않아 그로 인해 생기는 왜곡된 모습이 아닌 원래 모습 그대로를 알 수 있다. weak-perspective projectionorthographic projectionsperspective projection의 중간 이라고 볼 수 있다. 원리는 orthographic projection의 원리를 사용하지만 vanishing point를 구현 하기도 하는데 이는 정확한 값에 의한 것이 아니라 근사치를 이용해 구현 되어 진다.

2015년 9월 20일 일요일

gaussian filter

이번에 구현한 것은 가우시안 필터이다. 사실 이게 맞는지도 잘 모르겠다. 일단 원하던 결과

는 나온다. 가우시안에 대해서 검색하면 요상한 그래프를 자주 볼 수 있을 것이다. 뭐 표준

편차에 어쩌구 저쩌구 하는데 나는 이에 대해서 이번에 처음듣게 되어서 생소하였다.  간단

하게 말하자면 가운데의 비중이 제일크고 주변으로 갈수록 비중이 작아 진다는 건데 시그

마 값에 의해서 주변 값들의 비중이 결정 되어진다 시그마가 높으면 주변 값의 비중이 높아

진다. 그래서 주어진 식에 대입해서 그렇게 해서 반복문으로 아래를 만들었다. 다중포문을

사용할때는 주어진 값이 변경된다면 변수를 이용해서 하는 편이 더 깔끔하고 좋았다는 걸

느꼈다.


function [ output_args ] = gaussian( img_name, f_size, sigma )
%GAUSSIAN Summary of this function goes here
%   Detailed explanation goes here


       t_img = imread(img_name);
     
       filter = zeros(f_size);

       [h w] = size(t_img);
     
       result = zeros(h, w, 'uint8');
     
       f_size_buf = (f_size-1)/2;
       gauss_buf = 1/(2*pi*(sigma^2));
     
     
       for i=-f_size_buf:f_size_buf
           for j=-f_size_buf:f_size_buf
               filter(i+f_size_buf+1,j+f_size_buf+1) = gauss_buf * exp(-1*(i^2+j^2)/(2*(sigma^2)));
           end
       end
     
             
     
     
     
     
     
     
     
     
     
       i= 0;
       j= 0;
       k= 0;
       l= 0;
     
       for i=f_size_buf+1:h-f_size_buf
           for j=f_size_buf+1:w-f_size_buf
               temp = 0;
               for k= -f_size_buf:f_size_buf
                   for l= -f_size_buf:f_size_buf
                       temp = temp + t_img(i+k,j+l)*filter(k+f_size_buf+1,l+f_size_buf+1);
                   end
               end
               result(i-1,j-1) = ceil(temp);
           end
       end
     
       figure, imshow(t_img);
       figure, imshow(result);


end

2015년 9월 18일 금요일

matlab. mean filter

매트랩 mean filter이다. 단순히 하나의 픽셀을 둘러싸고있는 정사각형의 배열(둘러싸고 있

는 픽셀 3*3이라면 2,2의 중앙의 값을 결정 하기 위한)에서 모든 수를 더하고 그 픽셀의

개수만큼 나눠주어서 평균값을 계산하여 픽셀의 수치를 계산하는 것이다.




function [ output_args ] = mean( img_name, f_size )
%MEAN Summary of this function goes here
%   Detailed explanation goes here

       t_img = imread(img_name);
     
       filter = zeros(f_size);
       filter(1:f_size,1:f_size) = 1/(f_size*f_size);

       [h w] = size(t_img);
     
       result = zeros(h, w, 'uint8');
     
       i= 0;
       j= 0;
       k= 0;
       l= 0;
     
       for i=2:h-1
           for j=2:w-1
               temp = 0;
               for k= -1:1
                   for l= -1:1
                       temp = temp + t_img(i+k,j+l)*filter(k+2,l+2);
                   end
               end
               result(i-1,j-1) = ceil(temp);
           end
       end
     
       figure, imshow(t_img);
       figure, imshow(result);
     
end

matlab. redian 필터 적용.

이번에는 처음 접해보는 매트랩이란 프로그램으로 이미지 수정 작업을 하였다.

우리가 흔히 볼 수 있는 어플의 기능중 하나인 필터의 개념을 직접 구현해 보는 것이였는데

방법은 정사각형의 픽셀을 정렬하여 가장 가운데 있는 중간 값을 새로운 배열의 넣고

사진의 한픽셀 한픽셀 이동하면서 이 짓거리를 반복하는 것이다. 그러면 사진이 전체적으

로 부드러워 지면서 노이즈(잡티)가 제거 되는 것이다. 이번 과제를 하면서 고역이었던 것이

머리로는 방법을 알겠는데 매트랩으로 구현하는 것이 나에겐 너무 어려웠다. 일단 반복문을

너무 오랜만에 써보는 것도 있지만 매트랩과 c언어와의 차이점 때문에 고생을 좀 많이 했

다. 반복문 사용방법이라든지 달랐으니까... 힘들었던 부분중 하나가 ceil이란 함수 인데

이것은 수를 반올림 해주는 것이다. 매트랩의 default형은 double 인 것 같은데 막상

double 형태의 배열을 imshow() 를 이용하여 출력할려면 잘 안되는 것이다. 그래서

zeros(h, w, 'uint8')로 설정한 다음에 ceil을 이용하여 넣을때 반올림을 해줘서 넣으니까 된

것 같다. 아직도 왜 이해가 잘 안간다.  필터 사이즈는 그냥 3으로 해서 했다 일단 쉽게 설정

해놓고 이제 필터값 변경에 따른 반복문 변수를 바꿔주는 일만 남았지...







function [] = median( img_name, f_size )
%MEDIAN Summary of this function goes here
%   Detailed explanation goes here

       t_img = imread(img_name);
     
       filter = zeros(f_size);
       filter(1:f_size,1:f_size) = 1/(f_size*f_size);

       [h w] = size(t_img);
     
       result = zeros(h, w, 'uint8');
     
       i= 0;
       j= 0;
       k= 0;
       l= 0;
       n= 1;
     
     
       for i=2:h-1
           for j=2:w-1
                temp = zeros(9,1);
                n = 1;
               for k= -1:1
                   for l= -1:1
                       temp(n) = ceil(t_img(i+k,j+l));
                       n = n +1;
                   end
               end


               for sort_i = 1:8
                   minindex = sort_i;
                   for sort_j= minindex+1:9
                      if(temp(minindex) > temp(sort_j))
                          minindex = sort_j;
                      end
                      if(minindex ~= sort_j)
                          sort_buf = temp(minindex);
                          temp(minindex) = temp(sort_j);
                          temp(sort_j) = sort_buf;
                      end
                   end
               end                  

               result(i-1,j-1) = temp(5);
           end
       end
       figure, imshow(t_img);
       figure, imshow(result);
     
end

2015년 5월 10일 일요일

SDL 화면 크기 얻어 오는 함수

SDL_GetWindowSize(SDL_Window*, *int, *int);

이번 대학교 기말과제를 하면서 애를 먹은 녀석이다. 전체화면으로 게임을 구상하려는데 그 중앙지점을 찾고 싶은데 아무리 찾아도 화면 전체의 크기를 얻어오는 함수가 보이질 앉는 것이다. 그러다 찾은놈이 이녀석이다. 이 함수는 SDL_Window로 열려 있는 창의 크기를 얻어오는 함수이다. 그래서 다양한 컴퓨터에서의 크기의 비율로 게임을 만들 수 있다.

2015년 4월 29일 수요일

SDL 회전 대칭

SDL_RenderCopyEx(SDL_Renderer*, SDL_Texture*, SDL_Rect*, double angle, SDL_Point*, SDL_RendererFlip) // angle은 회전 각도 시계방향으로 회전해서 출력 SDL_RendererFlip은 어떻게 뒤집는지 SDL_FLIP_NONE은 뒤집지 않는 것, SDL_FLIP_HORIZEONTAL, SDL_FLIP_VERTICAL은 각각 횡방향, 종방향 뒤집는것

InitializeWindow

bool InitializeWindow(const char* title, int xpos, int ypos, int width, int height, int fullscreen)
{
int flags = 0;
if(fullscreen)
{
flags = SDL_WINDOW_FULLSCREEN;
}

// attempt to initialize SDL
if(SDL_Init(SDL_INIT_EVERYTHING) == 0)
{
std::cout << "SDL init success\n";
// init the window
g_window = SDL_CreateWindow(title, xpos, ypos,
width, height, flags); // 여기에 flags 대신 flags|SDL_WINDOW_RESIZABLE 을 넣으면 창 크기 조절가능

if(g_window != 0) // window init success
{
std::cout << "window creation success\n";
g_renderer = SDL_CreateRenderer(g_window, -1, 0);
if(g_renderer != 0) // renderer init success
{
std::cout << "renderer creation success\n";
SDL_SetRenderDrawColor(g_renderer,
255,255,255,255);
}
else
{
std::cout << "renderer init fail\n";
return false; // renderer init fail
}
}
else
{
std::cout << "window init fail\n";
return false; // window init fail
}
}
else
{
std::cout << "SDL init fail\n";
return false; // SDL init fail
}
std::cout << "init success\n";
g_flag_running = true; // everything inited successfully, start the main loop

return true;

}

SDL 기본 그리기


Intro::Intro()
{


SDL_Surface* temp_surface = IMG_Load("Resources/intro.png"); // 이미지를 가져와 메인메모리(CPU)에 할당한다.
texture_ = SDL_CreateTextureFromSurface(g_renderer, temp_surface); // CPU에 있는 이미지를 그래픽메모리에 할당
SDL_FreeSurface(temp_surface); // 메인메모리(CPU)에 있는 데이터를 삭제한다.

SDL_QueryTexture(texture_, NULL, NULL, &source_rectangle_.w, &source_rectangle_.h); // 텍스트 정보를 가져온다.

destination_rectangle_.x = source_rectangle_.x = 0; // source_rectangle은 원래 이미지의 시작이미지 destination은 가져올 이미지의 시작이미지
destination_rectangle_.y = source_rectangle_.y = 0;
destination_rectangle_.w = source_rectangle_.w; // 이말은 사진의 원래 크기만큼 생성한다는 말
destination_rectangle_.h = source_rectangle_.h;
}
void Intro::Render()
{
SDL_SetRenderDrawColor(g_renderer, 255,255,255,255); // 배경의 색을 지정해주는 함수
SDL_RenderClear(g_renderer); // clear the renderer to the draw color

    SDL_RenderClear(g_renderer); // clear the renderer to the draw color
SDL_RenderCopy(g_renderer, texture_, &source_rectangle_, &destination_rectangle_); // 그림을 그리기 위한 함수 범위를 전체로 하고싶다면 포인터대신 NULL

SDL_RenderPresent(g_renderer); // draw to the screen // 위에 가져온 그림을 화면에 출력
}

2015년 4월 21일 화요일

[대학교 알고리즘] HeapSort

void MakeHeap(int A[ ], int Root, int LastNode) {
/* 입력 : A[Root:LastNode] - 히프로 변환할 배열. 입력 당시에
  A[Root+1:LastNode] 는 이미 히프 구조이다.
출력 : A[Root:LastNode] - 최대값 히프 */
int Parent, LeftSon, RightSon, Son, RootValue;
Parent = Root;
RootValue = A[Root];
LeftSon = 2 * Parent + 1;
RightSon = LeftSon + 1;
while (LeftSon <= LastNode) {
if (RightSon <= LastNode && A[LeftSon] < A[RightSon])
Son = RightSon;
else Son = LeftSon;
if (RootValue < A[Son]) {
A[Parent] = A[Son];
Parent = Son;
LeftSon = Parent * 2 + 1;
RightSon = LeftSon + 1;
}
else break;
}
A[Parent] = RootValue;
}

void HeapSort(int A[ ], int n) {
/* 입력: A[0:n-1] - 정렬할 배열, n - 정렬할 원소의 개수.
출력: A[0:n-1] - 정렬된 배열. */
int i;
for (i=n/2; i>0; i--)
MakeHeap(A, i-1, n-1);
for (i=n-1; i>0; i--) {
Swap(&A[0], &A[i]);
MakeHeap(A, 0, i-1);
}
}

[대학교 알고리즘] MergeSort

void Merge(int A[ ], int Low, int Mid, int High) {
/* 입력: A[Low:Mid], A[Mid+1:High] - 정렬된 두 배열.
출력: A[Low:High] - A[Low:Mid]와 A[Mid+1:high]를 합병하여 정렬된 배열 */
int B[NUM_OR_KEYS]; // 퀵정렬에는 없지만 합병정렬에만 있는 합병을 하기 위한 배열.
int i, LeftPtr, RightPtr, BufPtr;
LeftPtr = Low; // 왼쪽
RightPtr = Mid +1; // 반을 나누기 위해 가운데
BufPtr = Low; // 이것도 왼쪽
while (LeftPtr <= Mid && RightPtr <= High) //
if (A[LeftPtr] < A[RightPtr])
B[BufPtr++] = A[LeftPtr++];
else B[BufPtr++] = A[RightPtr++];
if (LeftPtr > Mid)
for (i=RightPtr; i<=High; i++)
B[BufPtr++] = A[i];

else
for (i=LeftPtr; i<=Mid; i++)
B[BufPtr++] = A[i];
for (i=Low; i<=High; i++)
A[i] = B[i];
}

void MergeSort(int A[ ], int Low, int High) {
/* 입력: A[Low:High] - 정렬하려는 배열. Low, High - 정렬할 원소가 있는 곳을
나타내는 최소, 최대 인덱스.
출력: A[Low:High] - 정렬된 배열. */
int Mid;
if (Low < High) {
Mid = (Low + High) / 2;
MergeSort(A[ ], Low, Mid);
MergeSort(A[ ], Mid+1, High);
Merge(A[ ], Low, Mid, High);
}
}

[대학교 알고리즘] QuickSort

int Partition (int A[ ], int Left, int Right) {
/* 입력 : A[Left:Right] - 분할하려는 부분배열, A[Left] - 분할 원소,
  A[Right] - 더미 키.
출력 : A[Left:Right], Right - 분할 원소를 제자리에 위치시키고,
그 인덱스를 변수 Right에 담아서 복귀한다. */
int PartElem, Value;
PartElem = Left; // 분할 배열의 첫 번째 인덱스 복사.
Value = A[PartElem]; // 기준이 되는 인덱스의 값을 복사해서 비교값으로 사용한다 기준이지
do {
do { } while (A[++Left] < Value); // 첫값은 기준값이기 때문에 ++사용
do { } while (A[--Right] > Value); // 함수 입력받을때 +1로 원래 배열의 수보다 많이받아서 이것도 통일성을 위해 --사용
if (Left < Right) Swap(&A[Left], &A[Right]); // 위에서 두개 비교한 인덱스 값이 오른쪽이 더 크다면
else break; // 오른쪽에 작은값이 있고 왼쪽에 큰값이 있다는 것이다.
} while (1); // 둘이 같을수는 없고 Right이 더 작다면 정렬이 끝난 상태로 반복문 탈출
A[PartElem] = A[Right]; // 정렬이 끝난 그 기준값과 가운데 있는 값을 교환한다. 원래 Right값은 당연히 기준값보다 작다.
A[Right] = Value;
return Right; // 분할할때 중앙인덱스를 반환.

}
void QuickSort (int A[ ], int Left, int Right) {
/* 입력 : A[Left:Right+1] - 정렬할 배열, A[Right+1]은 배열 내의 최대값보다 큰 값.
   Left, Right - 배열 내의 정렬할 원소의 위치를 알리는 최소, 최대 인덱스.
출력 : A[Left:Right] - 정렬된 배열. A[Right+1]은 더미 키가 있는 곳.*/
int Middle;
if (Right > Left) { // Right가 Left보다 더 크다는건 정렬이 안끝났다는 말이다.
Middle = Partition(A[ ], Left, Right+1); // 반의 인덱스를 리턴받아서 새로운 Right값으로 사용. '
QuickSort(A[ ], Left, Middle-1); // 기준이 되는 인덱스는 최선의 값이기 때문에 제외하고 정렬
QuickSort(A[ ], Middle+1, Right); // 위와 마찬가지
} // 계속 반의 반의 반 이런식으로 하나가 남는다면 Right 와 Left가 같아진다. 그럼 반복문 종료 재귀함수 생각하면 편함.

}

[대학교 알고리즘] ShellSort

void ShellSort(int A[ ], int n) {
/* 입력 : A[0:n-1] - 정렬할 원소가 있는 배열, n - 정렬할 원소의 개수.
출력 : A[0:n-1] - 정렬된 배열.*/
int h, i, j, Value;
h = 1; // 간격을 설정해줄 변수
do h = 3 * h + 1; while (h < n); // 일단 크기를 늘린다.
do {
h = h / 3; // h는 이미 n보다 큰 수 이기때문에 한번 나누어준다.
for (i=h, i<n; i++) { // i = h 부터 시작하지만 큰 반복문에서 h값은 점점 줄어들기 때문에 전체적으로 반복하게 된다.
Value = A[i]; // 현재의 기준값을 저장
j = i; // 인덱스 일치
while (A[j-h] > Value) { // 처음엔 A[0] 인덱스와 비교 그후에 1씩 증가하는것을 볼 수 있다.
A[j] = A[j-h]; // 삽입정렬과 방법은 같다. 거리가 다르다.
j -= h; // i 값이 h값보다 커지게 되는 순간에는 자신의 아래쪽 간격으로 비교를 할 수도 있다.
if (j <= h-1) break;
}
A[j] = Value; // 만약 비교하여 자리가 바뀌었다면 그자리에 넣어준다.
}
} while (h > 1); // h간격이 1일때까지 실행을 마친후에 종료된다.
}

[대학교 알고리즘] InsertionSort

void InsertionSort(int A[ ], int n) {
/* 입력: A[0:n] - 정렬할 원소가 있는 배열, n - 정렬할 원소의 개수.
 A[0]는 A[1:n]의 어느 값보다도 더 작은 더미 키이다.
출력: A[0;n] - A[0]은 더미 키이고, A[1:n]은 정렬된 배열임. */
int i, j, Value;
for (i=2; i<=n; i++) { // 인덱스가 하나씩 늘어나지만 자신의 왼쪽의 숫자와 비교
Value = A[i]; // 비교대상의 제일 오른쪽 값을 넣어준다.
j = i; // 인덱스 일치
while (A[j-1] > Value) { // 자신의 왼쪽 값과만 비교를 실행
A[j] = A[j-1]; j--; // 왼쪽이 크다면 기존값은 저장이 되어있으므로 하나씩 밀어준다.
}
A[j] = Value; // 마지막에 자리에 비교대상을 넣어준다.
}

}

[대학교 알고리즘] BubbleSort

void BubbleSort(int A[ ], int n) {
/* 입력 : A[0:n-1] - 정렬할 원소가 있는 배열, n - 정렬할 원소의 개수.
출력 : A[0:n-1] - 정렬된 배열.*/
int i, Sorted;
Sorted = FALSE; // 이미 정렬된 함수일 경우 반복문을 빠져나간다.
while (!Sorted) { // 이미 정렬되어있을경우 Sorted 값이 TURE에서 바뀌지 않는다.
Sorted = TRUE;
for (i=1; i<n; i++) // 처음부터 마지막 까지 반복한다. 매번 n번 반복한다.
if (A[i-1] > A[i]) { // 뒤에있는 값이 크다면 자리를 바꿔주고 Sorted 값을 바꿔주어 반복이 지속되게 해준다.
Swap(&A[i-1], &A[i]); // 처음에는 마지막에 제일 큰수가 다음실행엔 두번째로 큰수가 마지막-1에 저장. 반복을 많이 실행.
Sorted = FALSE;
}
}
}

[대학교 알고리즘] SelectionSort

void SelectionSort (int A[], int n) {
/* 입력 : A[0:n-1] - 정렬할 원소가 있는 배열, n - 정렬할 원소의 개수.
출력 : A[0:n-1] - 정렬된 배열.*/
int i, j, MinIndex;
for (i=0; i<n-1; i++){ // 맨 처음부터 마지막 원소 전까지 반복
MinIndex = i; // 현재의 값을 기준으로
for (j = MinIndex+1; j<n; j++) // 기준 +1에서부터 마지막 까지 반복
if (A[MinIndex] > A[j]) // 반복문을 돌면서 제일 작은 값을 찾는다.
MinIndex = j;
if (MinIndex != i)
Swap(&A[i], &A[MinIndex]); // 제일작은 값을 기준값과 바꾸어주면서 제일 작은 값이 왼쪽에 위치한다.
}
}

2015년 4월 8일 수요일

[대학교 과제] 미디어처리 알고리즘, postfix / stack / parse tree / queue

주어진 postfix 표기의 식을 입력해서 스택을 사용해서 주어진 식을 연산하는 알고리즘을완성하시오.
 
#include <iostream>
using namespace std;
 
class Stack // 스택을 사용하기 위한 클래스다.
{
public:
Stack() // 메인함수에서 선언과 동시에 실행 되게 한다.
{
stack_top = -1; // 스택을 사용하기 위해 스택번호의 초기화
i = 0; // 입력받은 데이터 배열에서 사용하기 위한 인덱스 초기화
cout << "테스트 데이터를 입력하세요. [숫자와 연산자 중간에는 띄어쓰기를 해야 한다.]" << endl << "테스트 데이터 : "; // 입력받을 데이터 출력
cin.getline(test_data, 100, '\n'); // getline 함수를 사용해 띄어쓰기까지 입력받아서 test_data에 넣는다.
}
void Start() // 입력받은 데이터로 실행하는 함수
{
buf_ptr = buf; // buf_ptrbuf의 시작주소로 초기화
while(test_data[i] != NULL) // test_data의 모든 단어를 읽은 후 반복문 종료
{
if(test_data[i] == ' ') // 띄어쓰기일때 실행
{
if(buf_ptr != buf) // 버퍼에 자료가 들어가 있다면 실행
Push(); // Push()함수를 이용해 test_datastack_arr에 넣는다.
while(buf_ptr != buf){ // buf의 데이터와 buf_ptr의 시작주소를 초기화
(*buf_ptr) = NULL;
buf_ptr--;
}
}
else if(test_data[i] < 48) // 유니코드 48아래인 특수기호가 test_data에 들어있을때 실행
Push(test_data[i]); // Push함수를 이용해 연산을 처리한다.
else
{
*buf_ptr = test_data[i]; // test_data의 숫자를 buf_ptr을 이용해 buf에 하나씩 넣는다.
buf_ptr++; // buf_ptr가 가리키는 주소 하나 증가
}
i++; // test_data의 인덱스 하나 증가
 
}
cout << "입력된 값에 대한 결과 값이 나왔습니다." << endl; // 모든 과정이 나온후 출력된다.
}
void Push()
{
stack_arr[++stack_top] = atoi(buf); // buf에 문자로 저장되어있는 숫자를 숫자로 바꾸어 인덱스 하나 증가하여 stack_arr에 넣는다..
}
void Push(char a)
{
if(a == '*') // 입력받은 문자중 '*'라면 스택에서 숫자 두개를 곱한후 두개중 첫번째 숫자 위치에 수를 저장하고 인덱스 하나 감소
stack_arr[stack_top-1] *= stack_arr[stack_top--];
else if(a == '+') // 입력받은 입력받은 문자중 '+'라면 스택에서 숫자 두개를 더한후 두개중 첫번째 숫자 위치에 수를 저장하고 인덱스 하나 감소
stack_arr[stack_top-1] += stack_arr[stack_top--];
}
void Print_Stack(void)
{
cout << "계산된 결과는 : " << *stack_arr << endl; // 계산된 결과를 출력하는 코드다.
}
private:
char test_data[100], buf[10]; // 클래스에서 사용 될 변수의 선언
char *buf_ptr;
int stack_arr[100];
int stack_top, i;
};
 
int main()
{
Stack s; // 선언과 동시에 사용자에게 입력을 받는다.
s.Start(); // 입력받은 문자를 이용해 연산
s.Print_Stack(); // 연산후 출력
}
 
그림입니다.

원본 그림의 이름: practice 1.JPG

원본 그림의 크기: 가로 674pixel, 세로 438pixel
 
스택을 사용해서 postfix 표기의 식을 입력받아 연산한다. 모든 데이터를 char형으로 입력받아 그것을 buf배열과 atoi함수를 이용해 int 형 배열에 스택으로 넣어놓고 연산기호를 입력 받았을 때 스택에 있는 수를 계산하고 다시 집어넣는 방식으로 구현 하였다.
 
 
2. 포인터를 사용하여 데이터구조 스택(STACK2)을 구현하고, 다른 연산( -./ )도 포함된 다음의 data를 처리하는 알고리즘으로 수정하여 실습하시오.
 
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
using namespace std;
 
class Stack // 스택을 사용하기 위한 클래스다.
{
public:
Stack() // 메인함수에서 선언과 동시에 실행 되게 한다.
{
cout << "테스트 데이터를 입력하세요. [숫자와 연산자 중간에는 띄어쓰기를 해야 한다.]" << endl << "테스트 데이터 : "; // 입력받을 데이터 출력
cin.getline(test_data, 100, '\n'); // getline 함수를 사용해 띄어쓰기까지 입력받아서 test_data에 넣는다.
}
void Start() // 입력받은 데이터로 실행하는 함수
{
buf_ptr = buf; // buf_ptrbuf의 시작주소로 초기화
stack_ptr = stack_arr; // 스택을 사용하기 위해 변수 대신에 포인터를 사용한다.
stack_ptr--; // 변수로 사용 할 때와 동일한 방식으로 사용하기 위해 주소값을 -1해준다.
i = 0; // test_data의 인덱스 초기화
while(test_data[i] != NULL) // test_data의 모든 단어를 읽은 후 반복문 종료
{
if(test_data[i] == ' ') // 띄어쓰기일때 실행
{
if(buf_ptr != buf) // 버퍼에 자료가 들어가 있다면 실행
Push(); // Push()함수를 이용해 test_datastack_arr에 넣는다.
while(buf_ptr != buf){ // buf의 데이터와 buf_ptr의 시작주소를 초기화
(*buf_ptr) = NULL;
buf_ptr--;
}
}
else if(test_data[i] < 48) // 유니코드 48아래인 특수기호가 test_data에 들어있을때 실행
Push(test_data[i]); // Push함수를 이용해 연산을 처리한다.
else
{
*buf_ptr = test_data[i]; // test_data의 숫자를 buf_ptr을 이용해 buf에 하나씩 넣는다.
buf_ptr++; // buf_ptr가 가리키는 주소 하나 증가
}
i++; // test_data의 인덱스 하나 증가
 
}
cout << "입력된 값에 대한 결과 값이 나왔습니다." << endl; // 모든 과정이 나온후 출력된다.
}
void Push()
{
*++stack_ptr = atoi(buf); // buf에 문자로 저장되어있는 숫자를 숫자로 바꾸어 인덱스 하나 증가하여 stack_arr에 넣는다..
}
void Push(char a)
{
if(a == '*') // 입력받은 문자중 '*'라면 스택에서 숫자 두개를 곱한후 두개중 첫번째 숫자 위치에 수를 저장하고 인덱스 하나 감소
*(--stack_ptr) *= *(stack_ptr+1);
else if(a == '+') // 입력받은 문자중 '+'라면 스택에서 숫자 두개를 더한후 두개중 첫번째 숫자 위치에 수를 저장하고 인덱스 하나 감소
*(--stack_ptr) += *(stack_ptr+1);
else if(a == '/') // 입력받은 문자중 '/'라면 스택에서 숫자 두개를 나눈후 두개중 첫번째 숫자 위치에 수를 저장하고 인덱스 하나 감소
*(--stack_ptr) /= *(stack_ptr+1);
else if(a == '-') // 입력받은 문자중 '-'라면 스택에서 숫자 두개를 뺀후 두개중 첫번째 숫자 위치에 수를 저장하고 인덱스 하나 감소
*(--stack_ptr) -= *(stack_ptr+1);
}
void Print_Stack(void)
{
cout << "계산된 결과는 : " << *stack_ptr << endl; // 계산된 결과를 출력하는 코드다.
}
private:
char test_data[100], buf[10]; // 클래스에서 사용 될 변수의 선언
char *buf_ptr;
int stack_arr[100];
int i;
int *stack_ptr;
};
 
int main()
{
Stack s; // 선언과 동시에 사용자에게 입력을 받는다.
s.Start(); // 입력받은 문자를 이용해 연산
s.Print_Stack(); // 연산후 출력
}
그림입니다.

원본 그림의 이름: practice 2.JPG

원본 그림의 크기: 가로 674pixel, 세로 440pixel
 
위에 주어진 실습1과 동일한 방식에서 인덱스를 변수로 사용하지 않고, 포인터를 이용해 가리키게 하였다. 추가로 /연산도 추가하였으며 이 또한 포인터를 이용하여 처리하였다.
 
3. 다음 주어진 postfix 표기의 수식을 입력하여 스택을 사용해서 이진 parse 트리로 표현하고, 이 트리를 postorder 트래버설한 결과를 출력하시오.
 
#include <iostream>
using namespace std;
 
typedef struct treeNode{ //tree를 사용하기위해 구조체 노드를 사용하였다.
char data; // 노드에 들어갈 값
struct treeNode *left; // 트리의 왼쪽을 가르키기 위한 값
struct treeNode *right; // 트리의 오른쪽을 가르키기 위한 값
} treeNode;
 
class Stack // 스택을 사용하기 위한 클래스다.
{
public:
Stack() // 메인함수에서 선언과 동시에 실행 되게 한다.
{
stack_top = -1; // 스택을 사용하기 위해 스택번호의 초기화
i = 0; // 입력받은 데이터 배열에서 사용하기 위한 인덱스 초기화
cout << "테스트 데이터를 입력하세요. [각 문자 중간에는 띄어쓰기를 해야 한다.]" << endl << "테스트 데이터 : "; // 입력받을 데이터 출력
cin.getline(test_data, 100, '\n'); // getline 함수를 사용해 띄어쓰기까지 입력받아서 test_data에 넣는다.
}
void Start() // 입력받은 데이터로 실행하는 함수
{
while(test_data[i] != NULL) // test_data의 모든 단어를 읽은 후 반복문 종료
{
if(test_data[i] == ' '){} // test_data의 값이 ' '일 때 아무런 처리도 하지않고 넘어간다.
else if(test_data[i] < 48) // 유니코드 48아래인 특수기호가 test_data에 들어있을때 실행
push(test_data[i]);
else // testdata에 문자가 들어 있을 때 그 문자를 push를 이용해 스택에 넣는다.
push();
i++; // test_data의 인덱스 1 증가.
 
}
cout << "입력된 값에 대한 결과 값이 나왔습니다." << endl << "postorder 트래버설한 결과 : "; // 모든 과정이 나온후 출력된다.
postorder(Node_ptr[0]); // postorder함수를 사용해 만들어진 트리를 postdorder트래버설한 결과를 출력한다.
cout << endl;
}
void push() // 문자를 입력받을 경우 트리의 최하위를 차지하게 되므로 바로 노드를 만들어준다.
{
Node_ptr[++stack_top] = new treeNode; // 노드를 만든다.
Node_ptr[stack_top]->data = test_data[i]; // 데이터에 testdata로 부터 입력받은 값을 넣는다.
Node_ptr[stack_top]->left = NULL; // 최하위 노드이므로 NULL 저장.
Node_ptr[stack_top]->right = NULL; // 최하위 노드이므로 NULL 저장.
}
void push(char a) // 문자가 아닌 특수기호를 입력받을 경우 이미 만들어진 노드를 이용해 트리 구조를 만든다.
{
Node_ptr[++stack_top] = new treeNode; // 노드를 생성한다.
Node_ptr[stack_top]->data = test_data[i]; // testdata로부터 입력 받은 값을 넣는다.
Node_ptr[stack_top]->right = Node_ptr[stack_top-1]; // 스택에 들어있는 상위 2개중 먼저 들어있는 노드를 가리킨다.
Node_ptr[stack_top]->left = Node_ptr[stack_top-2]; // 스택에 들어있는 상위 2개중 나중에 들어있는 노드를 가리킨다.
Node_ptr[stack_top-2] = Node_ptr[stack_top]; // 새로만든 인덱스를 사용한 2개의 노드중 앞에 노드위치에 넣는다.
stack_top -= 2; // stack_top 값도 -2해준다.
}
void postorder(treeNode* root) // postorder 트래버설을 실행하는 함수
{
if(root) {
postorder(root->left); // 먼저 왼쪽노드를 방문하고 두번째 노드를 방문하고 자신의 값을 출력한다.
postorder(root->right); // 왼쪽 노드와 오른쪽 노드가 없다면 최하위 노드이므로 바로 출력한다.
cout << root->data << ' ';
}
}
 
 
private: // class에서 사용할 변수 선언
char test_data[100];
int stack_top, i;
struct treeNode *Node_ptr[100];
};
 
 
void main()
{
Stack s; // 선언과 동시에 사용자로 부터 입력받는다.
s.Start(); // Start함수를 이용해 입력받은 데이터 처리
}
그림입니다.

원본 그림의 이름: prectice 3.JPG

원본 그림의 크기: 가로 672pixel, 세로 436pixel
사용자로부터 문자를 입력받아 그것을 트리로 구현하였다. 문자는 자식노드가 없는 하위 노드이고 연산자를 만났을땐 연산자가 상위노드가 되고 문자가 그 연산자의 하위노드가 되는 형식을 취한다. 기본적인 트리구조와 스택구조를 사용하여 스택구조를 이용하여 문자를 받는 동시에 트리를 완성할 수 있도록 처리하였다.
 
4. 앞 문제에서 만들어진 트리에 대해서 큐를 정의하고, 이것을 사용하여 level order로 트래버설한 결과를 출력하는 프로그램을 완성하고 실습하시오.
 
#include <iostream>
using namespace std;
 
typedef struct treeNode{ //tree를 사용하기위해 구조체 노드를 사용하였다.
char data; // 노드에 들어갈 값
struct treeNode *left; // 트리의 왼쪽을 가르키기 위한 값
struct treeNode *right; // 트리의 오른쪽을 가르키기 위한 값
} treeNode;
 
class Stack // 스택을 사용하기 위한 클래스다.
{
public:
Stack() // 메인함수에서 선언과 동시에 실행 되게 한다.
{
init_queue(); // 큐를 사용하기 위해 큐를 초기화 하는 함수
init_stack(); // 스택을 사용하기 위해 스택을 초기화 하는 함수
cout << "테스트 데이터를 입력하세요. [각 문자 중간에는 띄어쓰기를 해야 한다.]" << endl << "테스트 데이터 : "; // 입력받을 데이터 출력
cin.getline(test_data, 100, '\n'); // getline 함수를 사용해 띄어쓰기까지 입력받아서 test_data에 넣는다.
}
void Start() // 입력받은 데이터로 실행하는 함수
{
while(test_data[i] != NULL) // test_data의 모든 단어를 읽은 후 반복문 종료
{
if(test_data[i] == ' '){} // test_data의 값이 ' '일 때 아무런 처리도 하지않고 넘어간다.
else if(test_data[i] < 48) // 유니코드 48아래인 특수기호가 test_data에 들어있을때 실행
push(test_data[i]);
else // testdata에 문자가 들어 있을 때 그 문자를 push를 이용해 스택에 넣는다.
push();
i++; // test_data의 인덱스 1 증가.
 
}
cout << "입력된 값에 대한 결과 값이 나왔습니다." << endl; // 모든 과정이 나온후 출력된다.
cout << "postorder : ";
postorder(node_ptr[0]); // postorder함수를 사용해 만들어진 트리를 postdorder트래버설한 결과를 출력한다.
cout << endl;
cout << "levelorder : ";
levelorder(node_ptr[0]); // postorder방식으로 만들어진 함수를 사용해 levelorder트래버설한 결과를 출력한다.
}
void push() // 문자를 입력받을 경우 트리의 최하위를 차지하게 되므로 바로 노드를 만들어준다.
{
node_ptr[++stack_top] = new treeNode; // 노드를 만든다.
node_ptr[stack_top]->data = test_data[i]; // 데이터에 testdata로 부터 입력받은 값을 넣는다.
node_ptr[stack_top]->left = NULL; // 최하위 노드이므로 NULL 저장.
node_ptr[stack_top]->right = NULL; // 최하위 노드이므로 NULL 저장.
}
void push(char a) // 문자가 아닌 특수기호를 입력받을 경우 이미 만들어진 노드를 이용해 트리 구조를 만든다.
{
node_ptr[++stack_top] = new treeNode; // 노드를 생성한다.
node_ptr[stack_top]->data = test_data[i]; // testdata로부터 입력 받은 값을 넣는다.
node_ptr[stack_top]->right = node_ptr[stack_top-1]; // 스택에 들어있는 상위 2개중 먼저 들어있는 노드를 가리킨다.
node_ptr[stack_top]->left = node_ptr[stack_top-2]; // 스택에 들어있는 상위 2개중 나중에 들어있는 노드를 가리킨다.
node_ptr[stack_top-2] = node_ptr[stack_top]; // 새로만든 인덱스를 사용한 2개의 노드중 앞에 노드위치에 넣는다.
stack_top -= 2; // stack_top 값도 -2해준다.
}
void postorder(treeNode* root) // postorder 트래버설을 실행하는 함수
{
if(root) {
postorder(root->left); // 먼저 왼쪽노드를 방문하고 두번째 노드를 방문하고 자신의 값을 출력한다.
postorder(root->right); // 왼쪽 노드와 오른쪽 노드가 없다면 최하위 노드이므로 바로 출력한다.
cout << root->data << ' ';
}
}
void init_stack() // 스택에 사용할 변수를 초기화 하는 함수
{
stack_top = -1;
i = 0;
}
void init_queue() // 큐에서 사용할 변수를 초기화 하는 함수
{
front = rear = 0;
}
void put_queue(treeNode *p) // 큐에 노드를 저장하고 rear1증가한다.
{
queue_ptr[rear++] = p;
}
treeNode *get_queue() // 현재위치의 큐를 반납하고 front1증가한다.
{
return queue_ptr[front++];
}
void levelorder(treeNode* Q)
{
put_queue(Q); // 시작할때 트리의 최상위 노드를 큐에 넣어준다.
while(notsame()){ // notsame()는 큐의 시작위치와 마지막위치가 같은경우 0을 반납한다.
Q = get_queue(); // get_queue함수로부터 반납받은 노드를 Q에 넣어준다
cout << Q->data << ' '; // 그 노드의 값을 출력한다.
if(Q->left != NULL) // 그리고 왼쪽노드의 자식이있으면 큐에 넣어주고 오른쪽도 자식이 있으면 큐에 넣어준다.
put_queue(Q->left); // 큐에 레벨 별로 저장하고 다음 반복문에서 그 레벨의 값을 출력하고 자식이 있다면 넣어주는
if(Q->right != NULL) // 방식을 반복한다.
put_queue(Q->right);
}
cout << endl;
}
int notsame() // 큐의 시작 위치와 마지막위치가 같으면 0을 반납하여 while을 종료하게 된다.
{
if(queue_ptr[front] == queue_ptr[rear])
return 0;
else
return 1;
}
private: // class에서 사용할 변수 선언
char test_data[100];
int stack_top, i;
struct treeNode *node_ptr[100];
struct treeNode *queue_ptr[100];
int front, rear;
};
 
 
 
void main()
{
Stack s;
s.Start();
}
그림입니다.

원본 그림의 이름: practive 4.JPG

원본 그림의 크기: 가로 671pixel, 세로 436pixel
위에 3번 문제에서 완성한 트리를 이용하고 큐를 정의하여 levelorder 트래버설한 결과를 출력한 것이다. 자신의 레벨에서 출력을 한 후 자식 노드를 큐에 넣어두고 다음 반복문에서 실행하게 함으로써 레벨 별로 출력이 가능하도록 했다.