http://www.imaso.co.kr/?doc=bbs/gnuboard.php&bo_table=article&wr_id=43180

 

 

GPGPU를 이용한 레이트레이싱

월간 마이크로소프트웨어 2013년 7월호 / Smart & Contents

GPGPU(General-purpose GPU)는 GPU의 강력한 성능을 일반적인 컴퓨팅 분야에 쓰도록 지원하는 것이다. GPGPU 활용을 위해서는 여러 가지 방법이 있는데, 현재는 OpenCL이나 CUDA를 이용하는 것이 보편화된 방법이다. 이 글에서는 CUDA를 이용해서 레이트레이싱(raytracing)을 하는 과정의 기초 부분을 살펴보기로 한다.

김혁 hybrid@nexon.co.kr|서강대학교 대학원에서 컴퓨터 그래픽스를 전공하면서 레이트레이싱을 사용한 렌더링 기술을 연구했다. 현재는 넥슨 데브캣 스튜디오에서 신규 게임 프로젝트 K를 개발 중에 있다.

게임에서 사용되는 렌더링 방식은 래스터라이제이션(rasterization)이라고 한다. 각각의 삼각형을 스크린에 투영해 렌더링될 화면의 픽셀 색상을 결정하는 이 방식과는 달리, 레이트레이싱에서는 픽셀(pixel) 기준으로 픽셀에 칠해질 색상을 결정하는 방식으로서 계산하는 방향이 다르다.


< 그림 1> 레이트레이싱

레이트레이싱의 과정을 가장 간단하게 요약하면 다음과 같다.

1) 카메라 정보로부터 스크린의 각 픽셀에 해당하는 광선(ray) 생성
2) 각 광선 기준으로 카메라와 가장 가까운 삼각형을 찾음
3) 그 삼각형과 광선이 교차(hit)하는 지점을 중심으로 셰이딩(shading) 계산을 수행

이 과정에서 속도를 빠르게 하기 위한 알고리즘을 제대로 도입하지 않으면 제 아무리 강력한 GPU 병렬 처리의 힘을 빌린다 하더라도 매우 많은 시간이 걸린다. 빠른 처리를 위해서는 단순히 강력한 하드웨어뿐만이 아니라 적절한 알고리즘과 하드웨어에 대한 이해가 필수적이다.

레이트레이싱을 위한 가장 간단한 구현 방식은 픽셀에 해당하는 광선을 현재 렌더링할 장면(scene)에 있는 모든 삼각형과 교차 검사를 하는 것이다. 이는 굉장히 많은 시간이 걸리기 때문에 일반적으로는 특수한 공간 구조를 만들어 삼각형을 배치한다. 여기서는 BSP트리(Binary Space Partitioning Tree)의 일종인 KD트리(KD-Tree)를 기준으로 설명한다. 최근에는 BVH (Bounding Volume Hierarchy) 방식도 많이 쓰이는 추세다. BVH는 KD트리보다는 동적 장면(dynamic scene)에 적합한 특징을 가지고 있는데, 근본적인 것은 KD트리를 기반으로 하기 때문에 KD트리를 알면 쉽게 이해하고 구현할 수 있다.


< 그림 2> 일반적인 BSP와 KD트리

KD트리
만약 KD트리와 같은 공간가속구조(spatial acceleration structure)를 전혀 사용하지 않는다면 하나의 광선에 대해 (1) 모든 삼각형과 일일이 교차 계산을 하거나 (2) 삼각형을 광선 기준으로 정렬 후 가까운 곳부터 교차 계산을 해야 한다. 공간가속구조는 삼각형을 엄밀하게 정렬하기보다는 공간을 나눠 가까운 곳부터 먼저 계산할 수 있도록 해주는 장치다.


< 그림 3> 광선이 1, 2, 3번의 리프 노드(공간)를 순차적으로 탐색

<그림 3>과 같이 광선은 1, 2, 3번의 리프 노드(공간)를 순차적으로 탐색하게 된다. 1번을 탐색한 후 삼각형이 없기 때문에 넘어가고, 2번 노드에서는 두 삼각형을 모두 교차 계산해 그림의 왼쪽 삼각형이 더 가까움을 알 수 있고, 이미 교차를 했기 때문에 3번의 노드는 탐색할 필요가 없다. <그림 3>에서처럼 공간이 이미 나눠져 있으면 광선 기준으로 가까운 곳부터 탐색해 빠르게 교차 계산을 수행할 수 있다.

이때 KD트리를 위한 레이트레이싱 알고리즘은 이미 많이 최적화돼 왔고, 많은 참고자료들이 존재한다. 그 내용들은 다른 자료들을 참고하길 바라면서 CUDA를 이용한 레이트레이싱에 집중해 설명하기로 한다.

CUDA의 이용
지면 관계상 여기서 제시되는 소스 코드나 레이트레이싱 구현에 관한 내용은 생략한다. 마찬가지로 CUDA에 관한 기본적인 내용 또한 가능한 간단하게 설명한다. CUDA를 이용하면 GPU를 이용해서 많은 연산을 병렬로 처리할 수 있다. 전부는 아니더라도 상당히 많은 부분의 C/C++ 코드를 사용할 수 있긴 하지만, GPU의 처리 구조와 하드웨어 구조를 이해해야만 GPU의 성능을 제대로 끌어낼 수 있다(상황에 따라서는 잘못 구현할 경우, 충분히 GPU에 적합한 응용 분야라 하더라도 멀티스레딩을 이용한 CPU 계산보다 느릴 수도 있다).

최근의 하드웨어는 많이 나아진 편이지만, GPU 연산을 위한 처리상의 제약사항은 제법 많은 편이다. 레지스터 사용 개수, 메모리 액세스, 한번에 병렬 처리하는 양(당연한 말이지만, GPU에서는 모든 GPU 내의 스레드가 동시에 수행되지 않는다)을 프로그래머가 고려해야 하고, 이런 이슈들로 인해 많은 성능 차이가 날 수 있다. 여기서 몇 가지 이슈를 다뤄본다.

먼저 각 스레드에는 독립적인 광선 하나씩이 배분된다. 교차하는 삼각형 중 가장 가까운 삼각형을 찾아야 하는데, 앞서 설명했듯이 트리를 탐색하고 리프 노드에 도달할 경우 리프 노드에 있는 삼각형들과 교차 검사를 한 후 교차하는 삼각형들이 없을 경우 다른 노드를 탐색하게 된다. 이때 다른 노드를 찾아가는 과정에 스택을 사용하게 된다. 이 스택의 액세스 빈도는 상당히 높다.

struct StackInfo;
StackInfo SharedStack[STACK_SIZE];

CUDA 코드 내에서 이 스택을 위와 같이 로컬 변수와 같이 선언할 경우 일반적으로 글로벌 메모리에 할당되게 된다. CUDA에서 글로벌 메모리는 굉장히 느려서 잦은 읽기/쓰기 처리에 적합하지 않다.

__shared__ StackInfo SharedStack[STACK_SIZE];

만약 CUDA 코드 내에서 위와 같이 선언해주면 이 메모리는 쉐어드 메모리(shared memory)에 잡히게 된다.


< 그림 4> 스레드와 불럭, 그리고 메모리

CUDA는 여러 개의 스레드가 하나의 블럭을 형성하는데, 이 블럭에는 각각 일정 양의 쉐어드 메모리를 할당할 수 있다. 블럭은 프로그래머가 지정한 개수만큼 여러 개의 스레드로 구성된다. 즉, 여러 스레드가 모여서 1개의 블럭을 형성하고, 같은 블럭 내의 여러 스레드는 쉐어드 메모리를 공유해서 사용한다(<그림 4>를 보면 알 수 있듯이 두 스레드는 각각 자신만의 레지스터와 로컬 메모리를 가지고 있고 쉐어드 메모리를 공유해서 사용한다). 

공용 메모리는 무척 빠르지만 용량이 매우 제한돼 있고, 더욱이 공용 메모리를 많이 사용할수록 L1 캐시 메모리의 사용이 제한된다. 실제 우리가 캐시 메모리를 제어하진 않지만, 로컬 메모리나 글로벌 메모리를 액세스할 때는 캐시 메모리를 통해 접근하게 되는데, 공용 메모리를 사용할 경우 그만큼 L1 캐시 메모리의 사용량이 줄어든다. 반면 글로벌 메모리는 많은 공간을 가지고 있지만, 상대적으로 속도가 많이 느리기 때문에 프로그래머는 최대한 글로벌 메모리의 액세스를 줄여야 한다는 것을 염두에 둬야 한다.

CUDA 아키텍처에서는 이처럼 여러 메모리가 각각의 장단점을 가지고 있다. 또한 GPU 하드웨어에 따라 성능이나 제약에도 차이가 있기 때문에 사용하는 하드웨어의 특성에 대한 이해도 필요하다. 

다시 레이트레이싱의 스택으로 돌아와서 스택을 살펴보자. 사실 레이트레이싱에서 스택은 제법 많은 메모리를 필요로 한다. 렌더링할 장면에 따라 다르지만 스택이 수백 개가 필요한 경우도 많다.


< 그림 5> 자동차의 앞부분을 간략하게 렌더링한 장면(스택 크기를 200개로 잡아서 스택이 부족한 부분은 검은색으로 깨져서 표현됨)

앞서 용량 제약을 말했다시피 일반적인 방법으로는 이렇게 큰 스택을 쉐어드 메모리에 넣을 수 없다. 그리고 용량의 제약이 없는 글로벌 메모리나 로컬 메모리에 할당하기에는 너무 잦은 액세스 때문에 성능 저하에 원인이 될 수 있다. 이러한 제약 속(그리고 딱히 이런 제약과는 무관하게)에서 여러 연구가 진행됐는데, 여기서는 그 중 숏스택(short stack) 방법을 소개하겠다.

__shared__ StackInfo SharedStack[SHORT_STACK_SIZE*BLOCK _SIZE_X*BLOCK_SIZE_Y];
const int stackIndex = SHORT_STACK_SIZE*(tx+ty* BLOCK_ SIZE_X);
StackInfo *Stack = & SharedStack [stackIndex];

이 코드에서 BLOCK_SIZE_X, Y는 CUDA의 블럭 내의 스레드 개수를 2차원 배열로 지정한 형태다. 다음과 같이 CUDA의 커널(ShootRayWithShortStack 함수)을 실행할 때 지정해준다.

dim3 dimGrid( RayCount/BLOCK_SIZE_X/BLOCK_SIZE_Y, 1, 1 );
dim3 dimBlock( BLOCK_SIZE_X, BLOCK_SIZE_Y, 1 );
ShootRayWithShortStack<<<dimGrid, dimBlock>>>(…);

이 과정에서 stackIndex에 해당하는 부분이 각 스레드 자신에게 할당 받은 스택의 시작 주소가 된다. 따라서 &SharedStack [stackIndex]로 스레드 자신의 스택을 사용한다.

단, 여기서는 이 SHORT_STACK의 크기를 크게 잡지 않는다. 1~5개 정도로 매우 작은 양을 할당한다. 그러면 하나의 블럭은 SHORT_STACK*BLOCK_SIZE_X* BLOCK_SIZE_Y만큼의 스택을 쉐어드 메모리에 가지고 있게 되고, 각 스레드는 SHORT _STACK만큼의 자신만의 스택을 얻게 된다. 이런 작은 크기의 스택에서는 금방 스택 오버플로우가 발생하게 된다. 이때의 처리는 간단하게(원형 큐의 방식과 마찬가지로) 가장 아래에 있는 오래된 스택 정보를 삭제하고 새 스택 정보를 위에 넣는다.


< 그림 6> 숏스택에서 오버플로우 발생 시 가장 오래된 스택 정보를 삭제

오버플로우가 발생하지 않는 환경에서는 이 스택을 평소처럼 쓰면 된다. 하지만 작은 스택 크기 때문에 스택은 금방 비게 된다. 스택이 비어 있어서 더 이상 POP을 해야 할 수 없는 상황에서는 리스타트 알고리즘을 사용한다.

리스타트 알고리즘은 사실 매우 간단한데, 부족한 스택 정보를 어떻게 하기보다는 이미 계산한 부분에 해당하는 광선을 잘라내고 트리 탐색을 처음부터 다시 시작하는 것이다.


< 그림 7> 리스타트 알고리즘의 예(이미 탐색한 부분의 광선을 자르고 트리를 처음부터 다시 탐색한다. 이 경우 스택이 필요하지 않음)

<그림 7>에서 윗 그림은 현재 광선이 1에 해당하는 부분을 탐색 완료한 시점이다. 이때 원래는 상단의 인터널 노드에 해당하는 정보를 스택으로부터 얻어 와야 하는데, 스택의 정보가 없기 때문에 <그림 7>의 아래 그림처럼 광선의 꼬리 부분을 잘라낸다. 그리고 트리를 처음부터 다시 탐색하면 1번에 해당하는 리프 노드는 다시 탐색하지 않게 된다.

이 방법을 쓰면 당연히 노드를 중복해서 탐색하게 되는데, 현실적으로 봤을 때 예상외로 중복해서 탐색하게 되는 노드의 비율이 그렇게 많지 않다(<표 1> 및 참고자료 6번 참조).


< 표 1> Daniel R. Horn 등이 실험한 중복 노드의 탐색 비율(Robot 장면)

이처럼 숏스택 방식은 빠른 액세스가 가능한 적은 개수의 스택을 유지하면서도 리스타트 알고리즘을 통해 렌더링에 문제가 없게 하는 특징이 있다. 아무리 수백 개 크기의 스택이 필요하다고 하더라도 그 중에 실제로 잦은 액세스를 하는 것들은 적기 때문에 이러한 방법을 통해 속도 향상을 얻을 수 있다.

그 외의 성능 이슈들
실제로 고려해야 하지만 자세히 설명하지 못한 성능 이슈들은 많이 있다. 삼각형과 광선의 교차 계산은 제법 많은 연산을 필요로 한다. 이 과정을 최소화하기 위해서 삼각형을 미리 Barycentric 좌표계로 변환시켜서 실제 연산 시에 빠르게 수행하도록 하는 방법이 있다. 각 노드에 들어가는 정보도 8바이트로 압축할 수 있다. 

또한 사실 최근 GPU 하드웨어의 속도가 매우 향상됐기 때문에, 어느 정도 레이트레이서의 성능을 올려놓으면, 성능의 병목 지점은 레이트레이싱 처리가 아닌 CPU 메모리(host memory)와 GPU 메모리(device memory) 간의 데이터 전송이 된다. 이를 고려해 최대한 메모리 전송이 일어나지 않게 전반적인 레이트레이서 설계를 하는 것도 중요하다.

지금까지 KD트리가 만들어진 것으로 가정하고 설명했지만, 사실 KD트리는 어떻게 만드느냐에 따라서도 트리를 만드는 속도와 렌더링 속도가 영향을 미친다. 그리고 상대적으로 KD트리는 렌더링 속도가 빠르지만, 새로 트리를 만드는 것은 느린 편이다. 이에 최근에는 KD트리와 상당 부분 흡사한 BVH(Bounding Volume Hierarchy) 방법도 사용된다. BVH는 렌더링 속도는 KD트리보다 느리지만, 트리 업데이트 기능이 있어서(이는 KD트리에서는 불가능) 어느 정도의 동적인 장면에서도 사용할 수 있다.

레이트레이싱을 넘어서
기본적인 레이트레이싱을 수행하는 레이트레이서가 빠르게 구현되면, 여기서 다양한 기법들로 발전시킬 수 있다. 여기서는 광선 기준으로 가장 가까이 교차하는 삼각형을 얻는 것을 설명했는데, 이렇게 교차된 삼각형을 기준으로는 셰이딩, 라이팅을 해야 사실적인 렌더링 결과를 얻을 수 있다. 이 과정에는 기본적으로 다음과 같은 광선의 종류들이 존재한다.

- 주 광선(primary)
- 2차 광선(secondary ray)
- 그림자 광선(shadow ray)

주 광선은 카메라로부터 나오는 광선이다. 2차 광선은 반사각/입사각을 기준으로 해서 반사(혹은 굴절)되는 광선이다. 이 광선을 통해 거울과 같은 물체를 사실적으로 렌더링할 수 있다. 그림자 광선은 조명 방향으로 광선을 쏘아 조명과 셰이딩할 지점 사이에 가리는 물체가 없는지를 검사하는 것이다. 물체가 가린다면 이 부분은 그림자가 지는 부분이 된다. 이런 특징을 본다면 픽셀에 해당하는 주 광선 1개에 대해 여러 개의 2차 광선(보통 2~3개 정도의 단계를 밟는다)을 쏘고, 조명 개수만큼의 그림자 광선을 쏘기 때문에 레이트레이싱에 필요한 교차 검사는 엄청나게 많아진다.

뿐만 아니다. 포톤 매핑(Photon Mapping)을 사용하게 되면 픽셀과 상관없는 조명 기준의 많은 광선(포톤의 경로)을 사용하게 되고, 패스트레이싱(Pathtracing, 경로추적법) 등의 방법을 사용하게 되면 한 방향으로의 반사가 아니라 주변광을 모아야 하기 때문에 훨씬 많은 광선이 필요하게 된다. 이런 상황일수록 레이트레이서의 작은 성능 차이는 큰 결과의 차이를 가져온다.

결론
이 글은 레이트레이싱과 CUDA 모두 익숙하지 않은 독자에게는 혼란스러운 내용이 될 수 있지만, CUDA 사용에 있어서 메모리 사용의 제약이나 제약된 구조가 큰 걸림돌이므로 이를 위해 상황에 맞는 알고리즘들이 연구되고 개발된다는 점은 이해하면 좋겠다. CUDA는 매우 강력하게 병렬 처리를 도와주지만, 하드웨어와 구조에 대한 이해, 그리고 올바른 알고리즘의 도입 없이는 그 강력한 성능을 제대로 활용하지 못하거나 오히려 CPU보다 안 좋은 결과를 낼 수 있음을 기억하자.


참고자료
1. Ingo Wald, Realtime Ray Tracing and Interactive Global Illumination (PhD thesis), Saarland University, 2004
2. Daniel Reiter Horn et al., Interactive k-D Tree GPU Raytracing, Proceedings of the 2007 symposium on Interactive 3D graphics and games, 2007
3. Tim Foley and Jeremy Sugerman, KD-Tree Acceleration Structures for a GPU Raytracer, Proceedings of the ACM SIGGRAPH/EUROGRAPHICS conference on Graphics hardware, 2005
4. Michael Hapala et al., Efficient Stack-less BVH Traversal for Ray Tracing, Proceedings 27th Spring Conference of Computer Graphics (SCCG) 2011, 2011
5. Philip Dutre et al., Advanced Global Illumination, 2/E, ISBN-13: 978-1568813073, 2006
6. Daniel Reiter Horn et al., Kd-Tree Acceleration Structures for a GPU Raytracer, http://graphics.stanford.edu/papers/i3dkdtree/i3dkdtreeonline.ppt



threadingbuildingblocks(TBB dubug .dll .lib) 가 필요한 경우
아래에서 최신 win용 파일을 다운 받는다.
http://threadingbuildingblocks.org/ver.php?fid=174

해당 파일을 압축을 풀고
\tbb40_233oss\bin\ia32\vc10  에 있는   (32bit , visual studio 10 의 경우)
\tbb40_233oss\bin\intel64\vc10 에 있는 (64bit , visual studio 10 의 경우)

tbb_debug.dll
tbb.dll
두 파일을
해당 프로젝트의 폴더(debug, release 폴더가 있는 폴더)
또는 system32에 복사해주면 된다.

참고 출처 : moorim21 님의 블로그
http://blog.naver.com/PostView.nhn?blogId=moorim21&logNo=80139961251


opencv_calib3d231d.lib
opencv_contrib231d.lib
opencv_core231d.lib
opencv_features2d231d.lib
opencv_flann231d.lib
opencv_gpu231d.lib
opencv_haartraining_engined.lib
opencv_highgui231d.lib
opencv_imgproc231d.lib
opencv_legacy231d.lib
opencv_ml231d.lib
opencv_objdetect231d.lib
opencv_ts231d.lib
opencv_video231d.lib


------------------------
opencv_calib3d231d.lib
opencv_contrib231d.lib
opencv_core231d.lib
opencv_features2d231d.lib
opencv_flann231d.lib
opencv_gpu231d.lib
opencv_haartraining_engined.lib
opencv_highgui231d.lib
opencv_imgproc231d.lib
opencv_legacy231d.lib
opencv_ml231d.lib
opencv_objdetect231d.lib
opencv_ts231d.lib
opencv_video231d.lib

출처 : http://martinblog.tistory.com/822


1. Static Control(Picture Control) 생성


2. 멤버변수 추가

3. 헤더파일 수정(이미지 변수 선언. 이때 화면에 뿌려줄 이미지를 전역으로 설정 한다.)

4. 열기 버튼 내용 수정 및 윈도우 종료시 이미지 Release

5. Static Control에 이미지 출력
- IplImage형의 이미지를 CvvImage형으로 복사한 뒤 그 핸들을 이용하여 Static Control에 이미지 출력

6. 테스트 결과
----------------------------------------------
출처 : http://www.t9t9.com/37
----------------------------------------------

OpenCV 를 이용한 템플릿 매칭(Template Matching) 하기
대한민국 최고 영상처리 커뮤니티 OpenCV Korea - http://opencv.co.kr

OpenCV 를 이용하여 간단한 템플릿 매칭(Template Matching)을 해보도록 하겠습니다. 먼저 2가지 준비물을 챙겨오시기 바랍니다.

A. 소스 이미지 : 어지럽혀진 책상과 같은 장면을 디카로 찍습니다.
B. 찾고자 하는 물체 이미지 : 사진에서 찾고자 하는 물체를 포토샵으로 따로 저장합니다.

템플릿 매칭의 세계로 입문할 준비가 되셨나요? 미처 준비물을 준비하지 못하신 분들을 위해 아래에 보시면 소스들을 준비해 두었습니다. 필요하신 분께선 다운받아 마음껏 사용하세요. 템플릿 매칭은 간단하게 딱 2단계로 설명을 하겠습니다.

1단계 : 유사성(Similarity) 찾기
이제부터 우리는 템플릿 매칭을 수행하기위해 OpenCV 함수중
cvMatchTemplate()
이란 함수를 이용하여 A 와 B 의 상관계수 맵(Coefficient Map)을 구할 것입니다. 이 함수의 Key point 는 A 이미지 전체를 탐색하면서 B 이미지와 각각 얼마만큼 유사성(Similarity)이 있는지를 구하는 것 입니다. 유사성을 구하는 방법에는 유클리디안(Euclidean)법 등을 이용( R(x,y)=sumx',y'[T(x',y')-I(x+x',y+y')]2 ) 합니다. OpenCV Document 를 보면 공식이 각각의 방법에 대해 자세히 나와있습니다.

2단계 : 최대값(Maximum value) 찾기
1단계에서 구한 상관계수 맵에서 최대의 유사성을 갖는 값(Maximum value)을 찾아 사각형으로 표시하면 가장 유사한 물체를 찾게 된 것입니다. OpenCV 에서는 최대최소값을 찾는 것 조차도 간단하게 cvMinMaxLoc() 함수를 제공하여 찾을 수 있도록 해주었습니다. 가끔은 이런 함수들이 있다는 것을 인지해야 하고 사용법일 익히는게 더 어렵고 그냥 코딩하는게 더 쉬울때도 있습니다. 하지만 이런 함수들은 나중에 프로젝트 개발시 시간을 단축시켜 주는 뛰오난 효과가 있지요.

다음은 소스 코드 입니다.

void main()
{
double min, max;
CvPoint left_top;

IplImage *A = cvLoadImage("My_Desk.jpg", -1); // 책상(A)을 먼저 읽고
IplImage *B = cvLoadImage("Stapler.jpg", -1); // 스테이플러(B)를 읽는다.
IplImage* C = cvCreateImage( cvSize( A->width - B->width+1, A->height - B->height+1 ), IPL_DEPTH_32F, 1 );
// 상관계수를 구할 이미지(C)

cvMatchTemplate(A, B, C, CV_TM_CCOEFF_NORMED); // 상관계수를 구하여 C 에 그린다.
cvMinMaxLoc(C, &min, &max, NULL, &left_top); // 상관계수가 최대값을 값는 위치 찾기
cvRectangle(A, left_top, cvPoint(left_top.x + B->width, left_top.y + B->height), CV_RGB(255,0,0));
// 찾은 물체에 사격형을 그린다.

Show("T9-result",A); // 결과 보기
Show("T9-sample",B); // 스테이플러(B) 보기
Show("C",C);
// 상관계수 이미지 보기
cvWaitKey(0);

// 모든 이미지 릴리즈
cvReleaseImage(&A);
cvReleaseImage(&B);
cvReleaseImage(&C);

// 모든 윈도우 제거
cvDestroyAllWindows();
}



다음은 준비물로 필요한 샘플 이미지와 결과물 입니다.

- A. 나의 책상 이미지

- B. 찾고자 하는 스테이플러 이미지

- C. 상관계수가 그려진 이미지

- 결과 이미지, 찾은 부분을 빨간 사각형으로 표시


- 소스 코드 및 데모 프로그램 다운받기(마우스 우클릭, 다른이름으로 저장)


 

출처: 정보디스플레이공학
저자: 강정원
출판사: 청문각

p.267  (6.2 영상처리)

(2) 필터를 사용하는 영상처리 (image processing using filter)

 픽셀의 점 처리 방식은 단순히 점 대 점 처리 방식으로 영상 내의 세부사항 등을 변경할 수는 없다. 이것은 점 처리가 단일 입력 픽셀을 대응되는 출력 픽셀에 맵핑시키기만 하기 때문이다. 점 처리는 픽셀에 있어서 이웃하는 입력 픽셀들을 고려하지 않는다. 영상내의 이웃한 픽셀들은 처리영역 안에서의 밝기 경향에 대한 중요한 정보를 제공한다. 필터링은 영상의 한 화소를 처리하기 위해 주변의 화소들과 반응하여 변환함으로써 영상이 가지는 여러 가지 특성 등을 개선하는 것을 가능하게 해준다. 필터는 임의의 목적에 따라 다양한 형태로 존재하는데, 필터(filter)를 마스크(mask), 커널(kernel), 템플릿(template), 윈도(window) 등으로 부르기도 한다.
 그림 6.4의 (a)는 크기가 3X3인 마스크이며, (b)는 필터에 의해 변환될 원본영상의 화소값들이다. 원본화도(x,y)에 대한 그림 6.4의 필터를 적용한 개선 화소는 다음과 같은 컨볼루션(convolution) 연산을 통하여 구할 수 있다.
 
 

필터에 의해 변환된 화소는 원본 영상과 같은 공간적 위치에 재배열되며, 이렇게 한화소에서 마스크를 옮겨가며 모든 화소에 필터를 적용하면, 필터가 적용된 영상을 얻을 수 있다.

matlab 에서 gray image 는  class 가(즉, 변수종류가) unit8 인 변수에 저장된다.

intArray = uint8(array)

ex)

sample =uint8([  41   42   68  126
                        56   87  110  125
                       101  123  113  117
                        88  102   74  102]);
imshow(sample);

3차원 배열의 크기를 가지는 RGB (color) image 를
2차원 배열의 크기를 가지는 gray image 로 변환한다.

ex)

peppers_rgb=imread(peppers.png');    % peppers_rgb -> 384x512x3 크기를 가지는 3차원 배열
peppers_gray=rgb2gray(peppers_rgb);    % peppers_gray -> 384x512    크기를 가지는 2차원 배열
figure
subplot(2,1,1); imshow(peppers_rgb);
subplot(2,1,2); imshow(peppers_gray);

+ Recent posts