영상 이미지 처리를 위한 여러 라이브러리를 찾아보다
일을 하다가 곧 들어갈 플젝에 영상 처리가 필요해서 python 기반의 라이브러리를 알아보고 있다.
VTK, ITK(simple ITK), Mayavi, Pyvista 등등 여러 라이브러리가 있지만 역시 근본을 접해봐야 아종들도 다루거나 이해하기 쉬울테니 러닝커브가 높은 편에 속하는 VTK를 먼저 손대었다.(그러지 말았어야 했나 싶기도)
VTK가 뭔데?
원래는 cpp 로 만들어진 이미지 처리 ? 시각화 라이브러리라고 알고 있다. js에선 OpenGL WebGL이 있듯 그런 느낌이 아닐까 싶다.
애초에 이름부터 Visualization Tool Kit 이다. https://vtk.org/
소올직히 공식 문서가 엄청 내용이 많지만... 친절한 편이라고 생각되지 않는다. (내가 멍청한거면 어쩔 수 없지만)
정말 봐도봐도 잘 모르겠어서 서칭도 해보고 옛날 영상들도 이것저것 다 끌어와서 닥치는대로 보고 있다. 그래서 조금은 이해가 가는듯... 아닌듯 (?) 하다 ㅎㅎ..
https://youtu.be/UIgaLDgb2fY?si=_sXXQyoa-H-U8Pvd
https://youtu.be/qFfwS9qMwpM?si=rzY095SS_Y7tRJXB
설치
pip install vtk
또는 가상환경을 쓰고 있다면,
conda install -c conda-forge vtk
설치야 어렵지 않다. 사용법이 어려운거지...
크게 VTK 의 구조에 대해 설명하고자 한다. 모든 볼륨 모델에 해당하는 사항은 아닐 수 있지만 큰 맥락상 보자면 이런 순서로 이해하면 접근이 쉬웠다. 나는 이걸 몰라서 돌아돌아 헤매고 직접 이거저거 해보고 알게되었다.
VTK 의 기본 구조
일단 VTK를 사용하기로 마음 먹었다면 이 사이트와 친해지자.
https://examples.vtk.org/site/
나는 python을 사용하고 있으므로 Python 으로 들어간다. 엄청나게 많은 목록들이 나를 기다리고 있을 것이다 ^^
그중에서 Geometric Object 부분의 Cylinder를 보겠다.
def create_cylinder():
colors = vtk.vtkNamedColors()
bkg = map(lambda x: x / 255.0, [26, 51, 102, 255])
colors.SetColor("BkgColor", *bkg)
cylinder = vtk.vtkCylinderSource()
cylinder.SetResolution(8)
# Mapper 는 만들어준 모델을 커스텀할 수 있는 역할
cylinderMapper = vtk.vtkPolyDataMapper()
cylinderMapper.SetInputConnection(cylinder.GetOutputPort())
# Actor 는 Mapper와 Property를 모델에 적용시켜줌
cylinderActor = vtk.vtkActor()
cylinderActor.SetMapper(cylinderMapper)
cylinderActor.GetProperty().SetColor(colors.GetColor3d("White")) # 색상 지정
cylinderActor.RotateX(30.0) # 회전
cylinderActor.RotateY(-45.0)
# Rnederer 는 Actor 를 렌더링 해주는 역할
ren = vtk.vtkRenderer()
# RenderWindow 는 렌더링 해줄 윈도우창 설정
renWin = vtk.vtkRenderWindow()
renWin.AddRenderer(ren)
# RenderWindowInteractor 는 카메라 등 설정
iren = vtk.vtkRenderWindowInteractor()
iren.SetRenderWindow(renWin)
# Actor 를 Renderer 에 설정, RenderWindow 로 윈도우 설정
ren.AddActor(cylinderActor)
ren.SetBackground(colors.GetColor3d("BkgColor"))
renWin.SetSize(300, 300)
renWin.SetWindowName('CylinderExample')
# RenderWindowInteractor 를 초기화
iren.Initialize()
# Renderer 카메라 설정 후 RenderWindow 로 렌더 처리
ren.ResetCamera()
ren.GetActiveCamera().Zoom(1.5)
renWin.Render()
# 설정해준 값들을 기반으로 RenderWindowInteractor의 이벤트 루프 시작
iren.Start()
다른 도형들에서도 공통적으로 보이는 부분이
- Source or PolyData ... 등 데이터 모델류
- Mapper
- Property
- Actor (Volume) 모델 객체 자체라고 이해하면 쉽다
- Renderer 렌더링 엔진
- RenderWindow
- RenderWindowInterator
요렇게 나눌 수 있더라. 이름은 물론 조금씩 상이하고 설정 방법도 다 제각기이다. 이걸 외운다는 것은 말도 안되고 우리는 개발자니까 이 프로세스를 받아들여서 적시적재에 사용할 줄 아는 응용력을 기르면 되는 것이다.
도형이나 모델을 그려줄 데이터를 준비해두고 -> 그 데이터를 Mapper에 Set이든 Add -> Property 에선 불투명도나 색상 등 설정 -> Mapper 와 Property 를 Actor 에 Set -> Renderer 엔진에 Actor를 넣고 -> Renderer 엔진을 보여줄 윈도우가 RenderWindow -> RenderWindowInterator 는 윈도우의 이벤트를 받아들이고 응답해줌
나는 위와 같은 프로세스를 여러 예제를 직접 다뤄보며 이해했다.
이해에 있어 이 분의 깃헙 예제로도 많은 이해가 될 수 있었다.
https://github.com/djeada/VTK-Examples/tree/main/src
특히 위의 말한 프로세스를 클래스화 하여
# https://github.com/djeada/VTK-Examples/blob/main/src/simple_pipeline.py
import vtk
class VisualisationPipeline:
def __init__(self, mappers, point_size=1, edges_visible=False):
self.mappers = mappers
self.point_size = point_size
self.edges_visible = edges_visible
def create_actors(self):
actors = []
for mapper in self.mappers:
actor = vtk.vtkActor()
actor.SetMapper(mapper)
actor.GetProperty().SetPointSize(self.point_size)
if self.edges_visible:
actor.GetProperty().SetEdgeVisibility(1)
actor.GetProperty().SetOpacity(0.5)
actor.GetProperty().BackfaceCullingOn()
actors.append(actor)
return actors
def create_renderer(self, actors):
renderer = vtk.vtkRenderer()
for actor in actors:
renderer.AddActor(actor)
renderer.SetBackground(0, 0, 0)
return renderer
def create_window(self, renderer):
window = vtk.vtkRenderWindow()
window.AddRenderer(renderer)
window.SetSize(800, 600)
return window
def create_interactor(self, window):
interactor = vtk.vtkRenderWindowInteractor()
interactor.SetRenderWindow(window)
interactor.Initialize()
return interactor
def run(self):
actors = self.create_actors()
renderer = self.create_renderer(actors)
window = self.create_window(renderer)
interactor = self.create_interactor(window)
interactor.Start()
위의 코드를 직접 쳐보며 정리했더니 한번에 이해가 팟.. 되었다.
아직 시작 단계지만 찬찬히 정리해보려고 한다. 처음 공부하는 이 글을 읽는 분들에게 조금이라도 도움이 되었길... 너무 설명을 못해서 안되었어도 어쩔 수 없겠지만ㅎ
근데 원래 공부는 직접 부딪히며 깨져봐야 느는거 아닌가 싶다.
'Ect. > Library' 카테고리의 다른 글
Swagger UI 세팅 (Feat. Nodejs) (1) | 2024.02.10 |
---|---|
VTK 의 작동 Flow (0) | 2023.12.12 |
pynetdicom 의 storescu에 대해 (1) | 2023.10.08 |
pynetdicom 을 사용하여 DICOM Networking 시도 (1) | 2023.10.07 |
react-babylonjs 라이브러리 사용해보기 (1) | 2023.10.03 |