2014. 10. 31. 12:48

Volume Rendering

요즘 지도교수님이 볼륨렌더링에 꽂히셨는지 관련 공부를 많이 시키시네요. 그냥 이런식으로 끄적이다 보면 내 머리 안에서도 정리가 되는 경우가 많기 때문에, 포스팅 하고자 합니다. 혹시 지나가시다가 이상한 정보를 발견하면 꼭 피드백 부탁드립니다. (_ _) 그리고 그래픽스에 무지한 분들도 그냥 가볍게 읽을 수 있도록 전문용어를 왠간히 풀어서 써보고자 합니다. 


볼륨 렌더링이란 말 그대로 3차원 정보의 텍스쳐를 활용하여 3차원의 꽉 찬 객체를 컴퓨터 화면 상에 그리는 일이다. Geometry 정보를 이용하여 형태를 잡고 그 위에 색을 입히거나 이미지를 입히는 것으로 생각할 수 있지만, 이는 과학이나 의학 영역의 데이터 시각화를 위해서는 부족하며, 볼륨 렌더링이라는 용어로 불리지 않는다.

* 그래픽스에서의 Geometry란 지형정보를 말하는데, 고체의 물체의 표면을 구성하기 위한 지리적 정보쯤으로 해석할 수 있겠다. 쉽게 말하면 보통 그래픽스에서는 xyz 축으로 하는 3차원 가상세계에서 객체를 표현하기 위한 좌표 집합 쯤 되겠다.

위키피디아에서는 이렇게 설명한다. 

In scientific visualization and computer graphics, volume rendering is a set of techniques used to display a 2D projection of a 3D discretely sampled data set, typically a 3D scalar field.

과학 영역의 시각화나 컴퓨터 그래픽스 분야에서, 볼륨 렌더링은 개별적으로 샘플링한 3차원 데이터를 2차원으로 프로젝션(화면 투영)하여 보여주기 위한 기술의 집합이다. 

즉, 우리가 먼지를 그리려면? 연기를 그리려면? 복잡하게는 사람 뇌와 근육 피부조직을 컴퓨터에서 시각화하고 싶다고 한다면, 단지 점과 선으로 이루어진 표현으로는 부족하다. 2차원 이미지가 픽셀로 표현되듯이, 3차원의 객체도 박셀(Voxel. 2차원의 픽셀과 같은 개념)로 표현해야 한다. (박셀의 상상은, 교수님 표현을 빌리자면, 마인크래프트를 상상하면 된다. 단지 3차원을 구성하는 큐브가 매우 작아서 한점이 되었다고 생각하면 되겠다.)

볼륨 렌더링을 위한 방법은 크게 Texture slicing 방법과 Ray casting 으로 볼 수 있다. 

(물론 큐브형태의 Solid Texture를 만들고 이를 조각하는 개념으로 Geometry 정보를 입혀 그려내는 방법도 있지만, 게임등에서 사용하면 몰라도 의학 데이터를 그려낼 수는 없다. 이 글에서는 생략.)

일단, 3D 볼륨 데이터가 어떻게 주어지는지를 먼저 보면, 한 축을 기준으로 단층을 여러장 촬영하여 한대 묶어서 표현한다. 쉽게 CT 촬영을 생각할 수 있는데, 아래 이미지를 보면 쉽게 감이 올 것이다.

(출처 - 위키피디아 'X-ray computed tomography')

머리 끝에서 코 중앙부까지 내려가며 CT 촬영 한 결과다. 이 2D 이미지들을 쭉 나열하면 아래 이미지처럼 그럴싸한 3차원 데이터가 탄생한다. (참고로 위에 부합하는 볼륨 이미지가 없어서 ㅎㅎ 축을 바꿔 촬영한 CT 단면으로 생각해주시기 바랍니다.)

(출처 - Purdue Univ. CGT521 클래스 강의 자료 중 일부 발췌 입니다. ㅎ)

다시 돌아가서, 볼륨 렌더링의 첫번째 방법인 Texture slicing 방법을 들여다보면, 그냥 이런 원리로, 슬라이스 된 2차원 이미지를 쭉쭉 그려주자는 것에 있다. (검은 영역은 투명처리 하고 나머지 밝은 영역에 적당히 알파값을 입힘.) slicing 방법도 몇가지로 나뉘는데 크게 axis-aligned 와 view-aligned 로 나뉜다. axis-aligned 방법은 이 주어진 데이터를 x, y, z 축으로 슬라이스하여 볼륨을 그리자는 것이다. 

헌데 문제가 있다. 만약 뷰의 방향을, 위 그림의 사람 얼굴과 정면으로 마주보도록 바꾼다면..? 뷰 방향은 2차원 이미지 평면의 접선이 되버려서 제대로 이미지가 그려지지 않는다. 그래서 나온 방법이 View-aligned slice 이다. 즉, 항상 볼륨 데이터를 2차원 이미지로 자를때에, x, y, z 축을 기준으로 하는게 아니라, 뷰 방향과 수직이 되는 평면으로 슬라이스를 하자는 것이다. 이것은 뷰 방향을 바꿀때마다 슬라이스를 추가적으로 재계산하여야 한다. 

그러나 이 방법은 이제 고전적인 방법으로 보인다. 예를들어 CT 사진의 검은 영역, 즉 공기 영역까지도 매번 계산량에 포함되는데다가 이런식으로 뷰 방향을 항상 고려해야 하고, 그러다보면 최적화 이슈등으로 복잡하고 어려운 기술들이 덧붙여진다. 

Ray casting 은 이러한 문제를 깔끔히 해결해주고도 높은 퀄리티의 렌더링을 얻을 수 있다. Ray casting은, 화면이라는 2차 평면의 픽셀 하나하로 광선을 하나하나 쏜다고 가정하고, 그 광선상에 위치되는 모든 박셀의 색상을 합하여 해당 픽셀을 그리겠다는 것이다. 이렇게 하면 화면의 픽셀 수만큼만 연산하면 되고, 뷰 방향에 상관 없이 single pass 만으로 볼륨을 그려낼 수 있다. 게다가 이러한 점때문에 Fragment Shader를 연산에 사용할 수 있다! (slicing 방법은 사용할 수 없다. 모든 슬라이스 정보를 Fragment shader 가 알아야 한다는 건데, 이 슬라이스 정보를 넘길 방법이 없다. 아마 추측컨데, ray casting 이 훨씬 아름다워 보임에도 그때당시 슬라이싱 방법을 사용했던 것은, Programmable Shader 유무 여부가 아니었을까 조심스레 추측해본다. Shader 가 없었다면 레이 캐스팅 방법은 상당히 헤비했을테니까.)

(출처 - a very simple volume rendering implementation with 3D textures 논문.)

더 자세한 Ray casting 에 대한 설명은 위키피디아에서 'Volume ray casting'을 찾아보면 잘 설명되어 있으므로 참고하기 바란다. 

잠깐 다음의 설명하고자 하는 Transfer function을 위해서 간단한 Ray casting 단계를 인용하고 넘어가겠다. 

  • 1단계 - Ray casting. 광선 던지기. ㅎㅎ 뷰방향과 일치하는 방향으로 광선을 모든 픽셀에서 쏜다.
  • 2단계 - Sampling. 광선과 부피의 교차지점은 선으로 볼 수 있다. 선 상의 모든 점을 더한다는건 불가능하므로 적절히 간격을 가지고 최종 색상을 결정할 데이터 점들을 선택한다. 이를 샘플링이라 한다. 
  • 3단계 - Shading. 쉐이딩. 빛 등을 고려하여 샘플링한 데이터 점의 색상을 결정한다. 
  • 4단계 - Compositing. 이렇게 결정된 광선 위의 모든 샘플링 데이터 색상을 더한다. 


(출처 - 위키피디아 Volume ray casting)

참고로 a very simple volume rendering implementation with 3D textures 논문을 보면, Ray casting with texture slices 라는 개념이 설명된다. 이 부분에서 혼동이 올 수 있는데, 해당 논문에서는 샘플링 기법으로 3D 텍스쳐를 슬라이스 해서 광선과 슬라이스 2차 평면의 교차점을 샘플링하겠다... 이 개념인듯 하다. 하지만 굳이 슬라이스를 접목할 필요 없이, Shader 내에서 샘플링 간격을 지정해주고 샘플링 하면 되므로... 텍스쳐 슬라이싱이 필요하다고 생각되지는 않는다.

여하튼, Volume ray casting 에서 1단계와 2단계는 대충 알았고, 3단계.. 색상을 어떻게 결정할지가 그 다음 이슈이다. CT촬영 결과에서 봤다시피, 우리는 신체 부위별 색상을 결정할 수가 없다. 단지 CT 에서는 X 선의 전파량에 따라 조직이 있는 부분은 희게, 조직이 없는부분은 검게 표현할 뿐이다. 즉, 조직의 밀도가 높으면 매우 하얗고 밀도가 낮을수록 점점 어둡게 표현된다고 볼수 있다. 그래서 뼈는 대부분 강렬하게 하얗다. 

뭐... 그냥 회색으로 다 표현해 버릴 수도 있다. 하지만 예쁘지 않다. ㅎㅎ -_- 그것보다... 볼륨 렌더링이 절대적으로 필요한 의학 영역의 경우, 조직과 조직간 구분이 쉽게 되어야만 한다. 의학 영역에서 볼륨 렌더링을 하는 이유는 조직을 검사하기 위한 것이기 때문에. 

따라서 볼륨에 색상을 입혀야 한다. 하지만 어떻게...? 그래서 소개한다. Transfer function!

Transfer function은 포토샵 같은 프로그램에서도 쉽게 볼수 있다. 대부분 포토샵으로 특정 색을 더 튀고 강렬하게 하거나 칼라 사진을 흑백으로 바꾸거나 한 경험이 있을것이다. 이것이 모두 Transfer function을 이용하여 색상의 변화에 규칙성을 부여하는 것이다. 

이전에 이미지의 히스토그램 개념을 먼저 알고 넘어가야 한다. 이미지의 히스토그램이란, 색상의 집약도(Intensity. 강렬도라 해야하나.. 그냥 앞으로 Intensity로 쓰겠습니다.)를 x축으로 하고 색상의 갯수를 y축으로 하는 히스토그램이다. (참고로 실제 연산시 x 축은 보통 0~1로 노멀라이즈 됨) 뭔 말이고 하면, 일단 쉽게 2차원 Greyscale이미지를 예로 들자면, 흑백이미지니까, 모든 픽셀은 0~255로 하는 회색으로 표현 가능하다. 이때, 각 색상별 갯수를 나타내는 것이다. 예를 들어 64x64 이미지가 그냥 전부 검정색이면, 히스토그램은 0에 몰빵된 그래프를 보여줄 것이다. 

그렇다면 똑같은 계산으로 3차원 박셀 데이터를 히스토그램화 할 수 있다. CT 촬영을 예로 들면, 그렇다면 Intensity 값 0근처는 공기부로 표현할 수 있고, Intensity 값이 크면서도 갯수가 많은 데이터는 뻐 등으로 유추할 수 있다. 따라서 x 축을 이러한 느낌적인 느낌으로 ㅎㅎ 구간을 나누어서, 주어진 RGB 컬러로 환산하는 함수가 바로 Transfer function 이다. 여기서 이 '느낌적인 느낌' 이게 상당히 어렵다. 자동으로는 구분이 어려워서 쉽게는 그냥 사람이 눈으로 보면서 이쯤 뼈겠구나.. 하면 거기에 하얀색 할당하고, 이쯤 뇌겠구나.. 하면 거기에 노란색 할당하는 식이다. 더 어려운건, 뇌와 비슷한 밀도를 가진 다른 조직이 있다고 하면, 그 다른 조직이 뇌로 취급되어 색상이 입혀 질 수 있다는 것이다. 물론 모든 조직의 밀도는 조금씩 다 다르므로.. 매우 섬세히 작업한다면 구분할 수 있지만, 이 섬세한 영역을 아직은 컴퓨터가 자동으로는 해주지 못하고 있다. 

일단은 간단히 그냥 0~10은 빨강, 11~20은 주황, 21~30은 초록 이런식으로 고정해서 색을 입히기도 한다. Transfer function을 자동으로 생성한다면 얼마나 좋을까. 지금은 대부분 사람이 수동으로 고쳐서 생성하는 것만큼 정확하지가 않다. 아무튼 그래도 대충이라도 자동화 하기위한 노력들이 계속되고 있다. 관련 분야가 궁금하다면 Histogram equalization과 Histogram specification을 키워드로 구글링 하라.

참고로! 히스토그램을 이용하는 방법 말고도 볼륨에 색상을 입히기 위한 다른 방법들도 당연히 있지만, 나의 연구 분야는 현재까지는 Histogram specification 으로 한정되어 있으므로... ㅎㅎㅎ (히스토그램 스페시피케이션을 위한 이용한 볼륨 렌더링에 대해 좋은 아이디어가 있다면 공유 바랍니다... 냅다 논문 내 버리게. -_-)

아래의 블로그는 Volume rendering 부터 Transfer function, Shading 까지 모든것을 구현할 수 있게 아주 친절히 설명하고 있다. (감사해요.. ㅠ 덕분에 파이널 프로젝트를 시작할 엄두는 낼 수 (?) 있게 됬습니다... 하하..) 아주 좋은 포스팅이니 구현을 원하는 분들은 꼭 참고하기 바란다.

http://graphicsrunner.blogspot.com/search/label/Volume%20Ray-Casting




여기까지 부족한 글 읽어주셔서 감사합니다. 볼륨 렌더링에 대한 영문 논문이나 블로그는 많은데 한글 자료는 잘 보이지 않는 것 같아서, 저도 공부 겸사 작성해 보았네요. 잘못된 부분이 있을 수 있으므로 적극적인 피드백 부탁드립니다. (과연 내 블로그에 오는 이가 있긴 할까만은... ㅎㅎ) 그나저나... 무지하게 바쁜데 지금 블로그 포스팅 글 작성하다가 재미들려서 완전 지금 반나절 다 까묵었네요.. 아오... ㅠ ㅠ ㅠ