๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ

๐Ÿ–ฑ๏ธ ๊ธฐ์ˆ  ๊ฒ€ํ† 

[React] ์›น ์˜๋ฃŒ ์˜์ƒ ๋ทฐ์–ด์˜ ์ƒˆ ํ‘œ์ค€ Cornerstone3D ์™„์ „ ๋ถ„์„

Cornerstone3D ์™„์ „ ๋ถ„์„
Deep Dive

์›น ์˜๋ฃŒ ์˜์ƒ ๋ทฐ์–ด์˜ ์ƒˆ ํ‘œ์ค€
Cornerstone3D ์™„์ „ ๋ถ„์„

์•„ํ‚คํ…์ฒ˜, ๋ Œ๋”๋ง ํŒŒ์ดํ”„๋ผ์ธ, ํŒจํ‚ค์ง€ ๊ตฌ์กฐ, ์žฅ๋‹จ์ ๊นŒ์ง€ โ€” ์ง์ ‘ VTK.js๋กœ ๋ทฐ์–ด๋ฅผ ๋งŒ๋“  ์‹œ๊ฐ์œผ๋กœ ๋ถ„์„ํ•ฉ๋‹ˆ๋‹ค.

WebGL ยท GPU ๋ Œ๋”๋ง DICOM ยท NIfTI MPR ยท MIP ยท Volume OHIF ํ†ตํ•ฉ ์˜คํ”ˆ์†Œ์Šค

Cornerstone3D๋ž€?

Cornerstone3D๋Š” OHIF(Open Health Imaging Foundation)๊ฐ€ ์ฃผ๋„ํ•˜์—ฌ ๊ฐœ๋ฐœํ•œ ์›น ๊ธฐ๋ฐ˜ ์˜๋ฃŒ ์˜์ƒ ๋ Œ๋”๋ง ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ž…๋‹ˆ๋‹ค. 2022๋…„ 4์›”์— ๊ณต๊ฐœ๋œ ์ดํ›„ ๊ธฐ์กด Cornerstone(Legacy)์˜ ํ•œ๊ณ„๋ฅผ ๊ทน๋ณตํ•˜๊ณ , vtk.js๋ฅผ ๋ Œ๋”๋ง ๋ฐฑ๋ณธ์œผ๋กœ ์ฑ„ํƒํ•˜์—ฌ GPU ๊ฐ€์† ๊ธฐ๋ฐ˜์˜ ๊ณ ์„ฑ๋Šฅ ์˜๋ฃŒ ์˜์ƒ ๋ทฐ์ž‰์„ ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค.

๋‹จ์ˆœํ•œ ๋ Œ๋”๋Ÿฌ๊ฐ€ ์•„๋‹Œ ์™„์„ฑํ˜• ํ”„๋ ˆ์ž„์›Œํฌ๋กœ, DICOM ์ด๋ฏธ์ง€ ๋กœ๋”ฉ๋ถ€ํ„ฐ MPR, MIP, Volume Rendering, ์„ธ๊ทธ๋ฉ˜ํ…Œ์ด์…˜, ์ธก์ • ๋„๊ตฌ, ๋ทฐํฌํŠธ ๋™๊ธฐํ™”๊นŒ์ง€ ์˜๋ฃŒ ์˜์ƒ ๋ทฐ์–ด์— ํ•„์š”ํ•œ ๊ฑฐ์˜ ๋ชจ๋“  ๊ธฐ๋Šฅ์„ ๋‚ด์žฅํ•ฉ๋‹ˆ๋‹ค.

2022
์ตœ์ดˆ ๊ณต๊ฐœ (v1.0)
8+
๊ณต์‹ ํŒจํ‚ค์ง€
vtk.js
๋ Œ๋”๋ง ๋ฐฑ๋ณธ
MIT
๋ผ์ด์„ ์Šค

ํŒจํ‚ค์ง€ ๊ตฌ์กฐ

Cornerstone3D๋Š” ๊ด€์‹ฌ์‚ฌ์— ๋”ฐ๋ผ ๋ช…ํ™•ํžˆ ๋ถ„๋ฆฌ๋œ ๋ฉ€ํ‹ฐ ํŒจํ‚ค์ง€ ๊ตฌ์กฐ๋ฅผ ์ฑ„ํƒํ•ฉ๋‹ˆ๋‹ค. ๊ฐ ํŒจํ‚ค์ง€๋Š” ๋…๋ฆฝ์ ์œผ๋กœ ์„ค์น˜ยท์‚ฌ์šฉ ๊ฐ€๋Šฅํ•˜๋ฉฐ, ํ•„์š”ํ•œ ๊ฒƒ๋งŒ ๊ณจ๋ผ ์“ธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

CORE @cornerstonejs/core RenderingEngine ยท Viewport ยท Cache TOOLS @cornerstonejs/tools ์ธก์ • ยท ์„ธ๊ทธ๋ฉ˜ํ…Œ์ด์…˜ ยท ๋™๊ธฐํ™” DICOM IMAGE LOADER @cornerstonejs/dicom-image-loader WADO-RS/URI ยท Web Worker ๋””์ฝ”๋”ฉ NIFTI VOLUME LOADER @cornerstonejs/nifti-volume-loader NIfTI ๋ณผ๋ฅจ ์ŠคํŠธ๋ฆฌ๋ฐ ๋กœ๋“œ ADAPTERS @cornerstonejs/adapters DICOM SR ยท SEG ๋ณ€ํ™˜ POLYMORPHIC SEG @cornerstonejs/polymorphic-seg Labelmap โ†” Contour โ†” Surface AI @cornerstonejs/ai SAM2 ยท ์Šค๋งˆํŠธ ์„ธ๊ทธ๋ฉ˜ํ…Œ์ด์…˜ vtk.js WebGL ๋ Œ๋”๋ง ๋ฐฑ๋ณธ
โš™๏ธ
@cornerstonejs/core
๋ Œ๋”๋ง ์—”์ง„, ๋ทฐํฌํŠธ, ์ด๋ฏธ์ง€ ์บ์‹œ, ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ํ”„๋กœ๋ฐ”์ด๋”์˜ ํ•ต์‹ฌ ๋ ˆ์ด์–ด.
๐Ÿ”ง
@cornerstonejs/tools
์ธก์ • ๋„๊ตฌ(Length, ROI), ์„ธ๊ทธ๋ฉ˜ํ…Œ์ด์…˜ ํŽธ์ง‘(Brush, Scissors), ํฌ๋กœ์Šคํ—ค์–ด, ๋™๊ธฐํ™”.
๐Ÿ“‚
@cornerstonejs/dicom-image-loader
DICOMweb(WADO-RS/URI) ๋กœ๋”ฉ + Web Worker ๊ธฐ๋ฐ˜ ์˜คํ”„๋ฉ”์ธ์Šค๋ ˆ๋“œ ๋””์ฝ”๋”ฉ.
๐Ÿง 
@cornerstonejs/nifti-volume-loader
NIfTI ๋ณผ๋ฅจ์„ ์ŠคํŠธ๋ฆฌ๋ฐ ๋ฐฉ์‹์œผ๋กœ ์ฒญํฌ ๋‹จ์œ„ ๋กœ๋“œ. ๋Œ€์šฉ๋Ÿ‰ ํŒŒ์ผ ์ตœ์ ํ™”.
๐Ÿ”„
@cornerstonejs/adapters
DICOM SR ยท SEG ํŒŒ์ผ์„ Cornerstone ์–ด๋…ธํ…Œ์ด์…˜ ํฌ๋งท์œผ๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ์–ด๋Œ‘ํ„ฐ.
๐Ÿค–
@cornerstonejs/ai
SAM2(Segment Anything Model 2) ๊ธฐ๋ฐ˜ AI ์Šค๋งˆํŠธ ์„ธ๊ทธ๋ฉ˜ํ…Œ์ด์…˜ ์ง€์›.

๋ Œ๋”๋ง ์—”์ง„ โ€” ํ•ต์‹ฌ ํ˜์‹ 

Cornerstone3D์˜ ๊ฐ€์žฅ ํฐ ๊ธฐ์ˆ ์  ๋„์•ฝ์€ ์˜คํ”„์Šคํฌ๋ฆฐ ๊ณต์œ  WebGL ์ปจํ…์ŠคํŠธ์ž…๋‹ˆ๋‹ค. ๊ธฐ์กด ๋ฐฉ์‹(๋ทฐํฌํŠธ๋ณ„ ๋…๋ฆฝ WebGL ์ปจํ…์ŠคํŠธ)์˜ ์„ฑ๋Šฅ ํ•œ๊ณ„๋ฅผ ๊ทผ๋ณธ์ ์œผ๋กœ ํ•ด๊ฒฐํ•ฉ๋‹ˆ๋‹ค.

Legacy Cornerstone

๋ทฐํฌํŠธ๋ณ„ ๋…๋ฆฝ ์ปจํ…์ŠคํŠธ

  • ๋ทฐํฌํŠธ๋งˆ๋‹ค ๋ณ„๋„ WebGL ์ปจํ…์ŠคํŠธ ์ƒ์„ฑ
  • GPU ๋ฉ”๋ชจ๋ฆฌ ์ค‘๋ณต ์ ์œ  (๋ณผ๋ฅจ N๊ฐœ ๋ณต์‚ฌ)
  • ๋ธŒ๋ผ์šฐ์ € ์ปจํ…์ŠคํŠธ ์ˆ˜ ์ œํ•œ์— ์ทจ์•ฝ
  • PET/CT Fusion ๊ฐ™์€ ๋ณต์žก ๋ ˆ์ด์•„์›ƒ ๋ถˆ๊ฐ€
  • ๋ทฐํฌํŠธ ์ฆ๊ฐ€ ์‹œ ์„ฑ๋Šฅ ๊ธ‰๊ฒฉํžˆ ์ €ํ•˜
Cornerstone3D โ€” ํ˜„์žฌ ๊ธฐ๋ณธ๊ฐ’

๋‹จ์ผ ์˜คํ”„์Šคํฌ๋ฆฐ ๊ณต์œ  ์ปจํ…์ŠคํŠธ

  • ํ•˜๋‚˜์˜ ์˜คํ”„์Šคํฌ๋ฆฐ ์บ”๋ฒ„์Šค๋กœ ์ „์ฒด ๋ทฐํฌํŠธ ๊ตฌ๋™
  • Shared Volume Mapper โ€” ํ…์Šค์ฒ˜ GPU์— 1๋ฒˆ๋งŒ ์—…๋กœ๋“œ
  • ๋ทฐํฌํŠธ ์ˆ˜์— ๋ฌด๊ด€ํ•˜๊ฒŒ ์ปจํ…์ŠคํŠธ 1๊ฐœ ์œ ์ง€
  • 10๊ฐœ ๋ทฐํฌํŠธ๋„ ๋ณผ๋ฅจ 2๊ฐœ(CT+PET)๋งŒ GPU์— ์˜ฌ๋ฆผ
  • ์˜คํ”„์Šคํฌ๋ฆฐ โ†’ ์˜จ์Šคํฌ๋ฆฐ ํ”ฝ์…€ ๋ณต์‚ฌ๋กœ ํ‘œ์‹œ
๐Ÿ’ก PET-CT Fusion 3ร—3 ๋ ˆ์ด์•„์›ƒ(CT 3๊ฐœ + PET 3๊ฐœ + Fusion 3๊ฐœ = 9 ๋ทฐํฌํŠธ)์—์„œ๋„ GPU์—๋Š” CT, PET ๋ณผ๋ฅจ ํ…์Šค์ฒ˜ ๋‹จ 2๊ฐœ๋งŒ ์˜ฌ๋ผ๊ฐ‘๋‹ˆ๋‹ค. Fusion ๋ทฐํฌํŠธ๋Š” ์ด๋ฏธ ์—…๋กœ๋“œ๋œ ํ…์Šค์ฒ˜๋ฅผ ์žฌ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
OFFSCREEN CANVAS โ€” ๋‹จ์ผ WebGL ์ปจํ…์ŠคํŠธ CT Axial camera A ยท VOI range CT Sagittal camera B ยท VOI range PET Axial camera A ยท SUV range Fusion Axial CT + PET blend ยทยทยท CT Volume Mapper GPU ํ…์Šค์ฒ˜ 1ํšŒ ์—…๋กœ๋“œ CT Axial ยท Sagittal ยท Fusion ๊ณต์œ  PET Volume Mapper GPU ํ…์Šค์ฒ˜ 1ํšŒ ์—…๋กœ๋“œ PET Axial ยท Coronal ยท Fusion ๊ณต์œ 

๋ฐ์ดํ„ฐ ๋กœ๋”ฉ ํŒŒ์ดํ”„๋ผ์ธ

DICOMweb ์„œ๋ฒ„์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ถˆ๋Ÿฌ์™€ ๋ทฐํฌํŠธ์— ๋ Œ๋”๋งํ•˜๊ธฐ๊นŒ์ง€์˜ ํ๋ฆ„์ž…๋‹ˆ๋‹ค.

1
Image ID ๋“ฑ๋ก ยท MetaData ์กฐํšŒ
wadors:https://server/studies/.../frames/1 ํ˜•ํƒœ์˜ Image ID๋กœ ์ด๋ฏธ์ง€๋ฅผ ์ฐธ์กฐํ•ฉ๋‹ˆ๋‹ค. MetaData Provider์—์„œ PixelSpacing, ImagePosition ๋“ฑ์„ ์กฐํšŒํ•ฉ๋‹ˆ๋‹ค.
2
Image Loader โ†’ Web Worker ๋””์ฝ”๋”ฉ
WADO-RS ์š”์ฒญ โ†’ DICOM ํŒŒ์ผ ์ˆ˜์‹  โ†’ Web Worker ๋‚ด์—์„œ ์˜คํ”„๋ฉ”์ธ์Šค๋ ˆ๋“œ๋กœ JPEG 2000 / HTJ2K ๋“ฑ ํ”ฝ์…€ ๋ฐ์ดํ„ฐ๋ฅผ ๋””์ฝ”๋”ฉํ•ฉ๋‹ˆ๋‹ค.
3
Image Cache ์ €์žฅ
๋””์ฝ”๋”ฉ๋œ ์ด๋ฏธ์ง€ ๊ฐ์ฒด๋ฅผ ์ธ๋ฉ”๋ชจ๋ฆฌ ์บ์‹œ์— ์ €์žฅ. ๊ฐ™์€ ์ด๋ฏธ์ง€ ์žฌ์š”์ฒญ ์‹œ ์บ์‹œ์—์„œ ์ฆ‰์‹œ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
4
Volume ๊ตฌ์„ฑ (Volume Viewport)
์‹œ๋ฆฌ์ฆˆ ์ „์ฒด ์Šฌ๋ผ์ด์Šค๋ฅผ ์กฐํ•ฉํ•ด 3D Volume ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. Streaming Volume Loader: ํ˜„์žฌ ์Šฌ๋ผ์ด์Šค๋ถ€ํ„ฐ ์šฐ์„  ๋กœ๋“œํ•˜์—ฌ ์ ์ง„์ ์œผ๋กœ ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค.
5
GPU ํ…์Šค์ฒ˜ ์—…๋กœ๋“œ (Shared Mapper)
๋ณผ๋ฅจ ๋ฐ์ดํ„ฐ๋ฅผ GPU ํ…์Šค์ฒ˜๋กœ ์—…๋กœ๋“œํ•ฉ๋‹ˆ๋‹ค. ๋™์ผ ๋ณผ๋ฅจ์„ ์—ฌ๋Ÿฌ ๋ทฐํฌํŠธ๊ฐ€ ์ฐธ์กฐํ•˜๋”๋ผ๋„ ํ…์Šค์ฒ˜๋Š” 1ํšŒ๋งŒ ์—…๋กœ๋“œ๋ฉ๋‹ˆ๋‹ค.
6
์˜คํ”„์Šคํฌ๋ฆฐ ๋ Œ๋”๋ง โ†’ ํ™”๋ฉด ํ‘œ์‹œ
vtk.js WebGL ํŒŒ์ดํ”„๋ผ์ธ์œผ๋กœ ์˜คํ”„์Šคํฌ๋ฆฐ ์บ”๋ฒ„์Šค์— ๋ Œ๋”๋ง ํ›„, ๊ฐ ๋ทฐํฌํŠธ ์˜์—ญ์˜ ํ”ฝ์…€์„ ์˜จ์Šคํฌ๋ฆฐ ์บ”๋ฒ„์Šค๋กœ ๋ณต์‚ฌํ•ฉ๋‹ˆ๋‹ค.

์ฃผ์š” ๊ธฐ๋Šฅ ๋Œ€์กฐํ‘œ

Cornerstone3D์˜ ๋‚ด์žฅ ๊ธฐ๋Šฅ๊ณผ ์ปค์Šคํ…€ VTK.js ๊ตฌํ˜„ ๋ฐฉ์‹๊ณผ์˜ ์ฐจ์ด๋ฅผ ๋น„๊ตํ•ฉ๋‹ˆ๋‹ค.

๊ธฐ๋ŠฅCornerstone3D์ปค์Šคํ…€ VTK.js ๊ตฌํ˜„๋‚œ์ด๋„ ์ฐจ์ด
MPR (Axial/Sagittal/Coronal)๋‚ด์žฅ ์ž๋™ ๊ตฌ์„ฑvtkImageMapper + ์ขŒํ‘œ๊ณ„ ์ง์ ‘ ์„ค์ •์‰ฌ์›€
MIP / Average Projection๋‚ด์žฅ BlendMode ํ† ๊ธ€์ง์ ‘ ๊ตฌํ˜„ ํ•„์š”์‰ฌ์›€
Volume Rendering๋‚ด์žฅ Transfer Function ํฌํ•จvtkVolumeMapper ์ˆ˜๋™ ์„ค์ •๋ณดํ†ต
์„ธ๊ทธ๋ฉ˜ํ…Œ์ด์…˜ ์˜ค๋ฒ„๋ ˆ์ด๋‚ด์žฅ Labelmap ยท Contour ยท SurfaceRGBA ๋ณ€ํ™˜ ํ›„ vtkImageSlice์‰ฌ์›€
์„ธ๊ทธ๋ฉ˜ํ…Œ์ด์…˜ ํŽธ์ง‘๋‚ด์žฅ Brush/Scissors/Threshold๋ณ„๋„ ๊ตฌํ˜„ ํ•„์š” (๋งค์šฐ ๋ณต์žก)์–ด๋ ค์›€
์ธก์ • ๋„๊ตฌ (Length, ROI)๋‚ด์žฅ SVG ์–ด๋…ธํ…Œ์ด์…˜ + ๋ฌผ๋ฆฌ ๊ณต๊ฐ„SVG ์˜ค๋ฒ„๋ ˆ์ด + ์ขŒํ‘œ ๋ณ€ํ™˜ ์ง์ ‘ ๊ตฌํ˜„๋ณดํ†ต
ํฌ๋กœ์Šคํ—ค์–ด ๋™๊ธฐํ™”๋‚ด์žฅ CrosshairsTool์ง์ ‘ ์ขŒํ‘œ ๋ณ€ํ™˜ + ๋ Œ๋”๋Ÿฌ ์—ฐ๋™๋ณดํ†ต
๋ทฐํฌํŠธ ๋™๊ธฐํ™”๋‚ด์žฅ Synchronizer API์ง์ ‘ ์ด๋ฒคํŠธ ์—ฐ๊ฒฐ๋ณดํ†ต
DICOM ๋กœ๋”ฉ๋‚ด์žฅ DICOMweb ์™„์ „ ์ง€์›๋ณ„๋„ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ํ•„์š”์‰ฌ์›€
์ปค์Šคํ…€ ๋ Œ๋”๋ง ํŒŒ์ดํ”„๋ผ์ธ์ œํ•œ์  ์ถ”์ƒํ™” ๋ ˆ์ด์–ด ๋‚ด๋ถ€VTK API ์ง์ ‘ ์ œ์–ด ๊ฐ€๋Šฅ์–ด๋ ค์›€
๋น„ํ‘œ์ค€ ๋ฐ์ดํ„ฐ ํฌ๋งท๋ถ€๋ถ„ ์ง€์› ํ”Œ๋Ÿฌ๊ทธ์ธ ํ˜•ํƒœ์ž์œ ๋กญ๊ฒŒ ๊ตฌํ˜„ ๊ฐ€๋Šฅโ€”

์žฅ์ ๊ณผ ๋‹จ์ 

โœ… ์žฅ์ 
  • ๋น ๋ฅธ ๊ฐœ๋ฐœ ์†๋„
    MPR, ์„ธ๊ทธ๋ฉ˜ํ…Œ์ด์…˜, ์ธก์ • ๋„๊ตฌ๊ฐ€ ๋ชจ๋‘ ๋‚ด์žฅ๋˜์–ด ์ˆ˜๊ฐœ์›”์น˜ ์ž‘์—…์ด ์„ค์น˜ ํ•œ ๋ฒˆ์œผ๋กœ ํ•ด๊ฒฐ๋ฉ๋‹ˆ๋‹ค.
  • ๊ณต์œ  GPU ํ…์Šค์ฒ˜
    Shared Volume Mapper๋กœ ๋ณต์žกํ•œ PET-CT Fusion ๋ ˆ์ด์•„์›ƒ๋„ ์ตœ์†Œ ๋ฉ”๋ชจ๋ฆฌ๋กœ ๊ตฌ๋™๋ฉ๋‹ˆ๋‹ค.
  • DICOM ํ‘œ์ค€ ์™„์ „ ์ง€์›
    DICOMweb, WADO-RS/URI, DICOM SR ์–ด๋Œ‘ํ„ฐ๊นŒ์ง€ ์˜๋ฃŒ ํ‘œ์ค€ ์Šคํƒ์ด ๊ธฐ๋ณธ ํƒ‘์žฌ๋ฉ๋‹ˆ๋‹ค.
  • ์ŠคํŠธ๋ฆฌ๋ฐ ๋ณผ๋ฅจ ๋กœ๋”
    NIfTI/DICOM ๋ณผ๋ฅจ์„ ์Šฌ๋ผ์ด์Šค ๋‹จ์œ„๋กœ ์ ์ง„์ ์œผ๋กœ ๋กœ๋“œํ•˜์—ฌ ์ดˆ๊ธฐ ํ‘œ์‹œ ์‹œ๊ฐ„์„ ๋‹จ์ถ•ํ•ฉ๋‹ˆ๋‹ค.
  • AI ์„ธ๊ทธ๋ฉ˜ํ…Œ์ด์…˜ ๋‚ด์žฅ
    SAM2 ๊ธฐ๋ฐ˜ ์Šค๋งˆํŠธ ์„ธ๊ทธ๋ฉ˜ํ…Œ์ด์…˜์ด ๊ณต์‹ ํŒจํ‚ค์ง€๋กœ ์ œ๊ณต๋ฉ๋‹ˆ๋‹ค.
  • OHIF Viewer ํ†ตํ•ฉ
    ์—”ํ„ฐํ”„๋ผ์ด์ฆˆ๊ธ‰ ๋ ˆํผ๋Ÿฐ์Šค ๋ทฐ์–ด(OHIF)์™€ ๊ณต์‹ ํ†ตํ•ฉ CI/CD๊ฐ€ ์šด์˜๋ฉ๋‹ˆ๋‹ค.
  • SVG ์–ด๋…ธํ…Œ์ด์…˜
    ๋ชจ๋“  ์ธก์ • ๋„๊ตฌ๊ฐ€ SVG๋กœ ๋ Œ๋”๋ง๋˜์–ด ์–ด๋–ค ํ•ด์ƒ๋„์—์„œ๋„ ์„ ๋ช…ํ•˜๊ฒŒ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค.
โš ๏ธ ๋‹จ์ 
  • ์ปค์Šคํ„ฐ๋งˆ์ด์ง• ํ•œ๊ณ„
    ์ถ”์ƒํ™” ๋ ˆ์ด์–ด๊ฐ€ ๋‘๊บผ์›Œ ๋ Œ๋”๋ง ํŒŒ์ดํ”„๋ผ์ธ์„ ์„ธ๋ฐ€ํ•˜๊ฒŒ ์ œ์–ดํ•˜๊ธฐ ์–ด๋ ต์Šต๋‹ˆ๋‹ค.
  • ๋ฒˆ๋“ค ํฌ๊ธฐ
    vtk.js๋ฅผ ๋‚ด๋ถ€์ ์œผ๋กœ ํฌํ•จํ•˜๋ฏ€๋กœ ๋ฒˆ๋“ค์ด ํฝ๋‹ˆ๋‹ค. Tree-shaking ํšจ๊ณผ๋„ ์ œํ•œ์ ์ž…๋‹ˆ๋‹ค.
  • ๊ฐ€ํŒŒ๋ฅธ ํ•™์Šต ๊ณก์„ 
    RenderingEngine, Viewport, ToolGroup, Synchronizer ๋“ฑ ๊ณ ์œ  ๊ฐœ๋…์„ ์Šต๋“ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  • DICOM ์ค‘์‹ฌ ์„ค๊ณ„
    NIfTI ๋“ฑ ๋น„ํ‘œ์ค€ ํฌ๋งท ์ง€์›์€ ๋ณ„๋„ ํŒจํ‚ค์ง€๋กœ ์ œ๊ณต๋˜๋ฉฐ, REST API ๊ธฐ๋ฐ˜ ์ปค์Šคํ…€ ์—ฐ๋™์— ์ถ”๊ฐ€ ์ž‘์—…์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.
  • API ๋ณ€๊ฒฝ ์žฆ์Œ
    v1 โ†’ v2 ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜์—์„œ ๋Œ€๊ทœ๋ชจ API ๊ฐœํŽธ์ด ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. Breaking Change ๋นˆ๋„๊ฐ€ ๋†’์Šต๋‹ˆ๋‹ค.
  • ๋””๋ฒ„๊น… ๋‚œ์ด๋„
    vtk.js โ†’ Cornerstone โ†’ ๋„๊ตฌ ๋ ˆ์ด์–ด์˜ 3์ค‘ ์ถ”์ƒํ™”๋กœ ๋ฒ„๊ทธ ์›์ธ ์ถ”์ ์ด ๋ณต์žกํ•ฉ๋‹ˆ๋‹ค.

์–ธ์ œ Cornerstone3D๋ฅผ, ์–ธ์ œ ์ปค์Šคํ…€ VTK.js๋ฅผ?

โš–๏ธ ์ •๋‹ต์€ ์—†์Šต๋‹ˆ๋‹ค. ํ”„๋กœ์ ํŠธ์˜ ์š”๊ตฌ์‚ฌํ•ญ, ํŒ€ ๊ทœ๋ชจ, ์ผ์ •, ๋ฐ์ดํ„ฐ ํฌ๋งท์— ๋”ฐ๋ผ ์ตœ์  ์„ ํƒ์ด ๋‹ฌ๋ผ์ง‘๋‹ˆ๋‹ค.
์‹œ๋‚˜๋ฆฌ์˜คCornerstone3D์ปค์Šคํ…€ VTK.js
DICOM / DICOMweb ๊ธฐ๋ฐ˜ ํ‘œ์ค€ ์›Œํฌํ”Œ๋กœ๊ฐ•๋ ฅ ์ถ”์ฒœ๋น„ํšจ์œจ
๋น ๋ฅธ MVP, ์†Œ๊ทœ๋ชจ ํŒ€์ ํ•ฉ๋ฆฌ์†Œ์Šค ๊ณผ๋‹ค
OHIF Viewer ๊ธฐ๋ฐ˜ ํ™•์žฅํ•„์ˆ˜๋ถˆ๊ฐ€
REST API ๊ธฐ๋ฐ˜ ๋น„ํ‘œ์ค€ ๋ฐ์ดํ„ฐ (NIfTI, .nii.gz)๊ฐ€๋Šฅ (ํ”Œ๋Ÿฌ๊ทธ์ธ)์ž์œ ๋กญ๊ฒŒ ๊ตฌํ˜„
๋…์ž์ ์ธ ๋ทฐ ํƒ€์ž… / ๋ Œ๋”๋ง ๋กœ์ง ํ•„์š”์ œํ•œ์ ํ•ฉ
Canvas ๊ธฐ๋ฐ˜ MIP / ์ปค์Šคํ…€ ์˜ค๋ฒ„๋ ˆ์ด์ง€์› ์•ˆ ๋จ์ž์œ ๋กญ๊ฒŒ ๊ตฌํ˜„
์„ธ๊ทธ๋ฉ˜ํ…Œ์ด์…˜ ํŽธ์ง‘ ๋„๊ตฌ (Brush, Scissors)๋‚ด์žฅ๊ตฌํ˜„ ๋‚œ์ด๋„ ๋†’์Œ
๋ฒˆ๋“ค ํฌ๊ธฐ ์ตœ์†Œํ™”๋ถˆ๋ฆฌ์œ ๋ฆฌ

์ดˆ๊ธฐํ™” ํ๋ฆ„ โ€” ์ฝ”๋“œ๋กœ ๋ณด๊ธฐ

// 1. ํŒจํ‚ค์ง€ ์„ค์น˜
// pnpm add @cornerstonejs/core @cornerstonejs/tools @cornerstonejs/dicom-image-loader

import * as cornerstone from '@cornerstonejs/core';
import * as cornerstoneTools from '@cornerstonejs/tools';
import dicomImageLoader from '@cornerstonejs/dicom-image-loader';

// 2. ์ดˆ๊ธฐํ™”
await cornerstone.init();
cornerstoneTools.init();
dicomImageLoader.init();

// 3. RenderingEngine ์ƒ์„ฑ (๋‹จ์ผ WebGL ์ปจํ…์ŠคํŠธ)
const engine = new cornerstone.RenderingEngine('myEngine');

// 4. ๋ทฐํฌํŠธ ๊ตฌ์„ฑ (MPR 3๋ถ„ํ• )
engine.setViewports([
  { viewportId: 'axial',    type: 'ORTHOGRAPHIC', element: divAxial,
    defaultOptions: { orientation: 'AXIAL' } },
  { viewportId: 'sagittal', type: 'ORTHOGRAPHIC', element: divSagittal,
    defaultOptions: { orientation: 'SAGITTAL' } },
  { viewportId: 'coronal',  type: 'ORTHOGRAPHIC', element: divCoronal,
    defaultOptions: { orientation: 'CORONAL' } },
]);

// 5. ๋ณผ๋ฅจ ๋กœ๋“œ (์ŠคํŠธ๋ฆฌ๋ฐ)
const volume = await cornerstone.volumeLoader.createAndCacheVolume(
  volumeId, { imageIds }
);
await volume.load();   // ์ ์ง„์  ๋กœ๋“œ ์‹œ์ž‘

// 6. ๋„๊ตฌ ๊ทธ๋ฃน ์„ค์ •
const toolGroup = cornerstoneTools.ToolGroupManager.createToolGroup('tg');
toolGroup.addTool(cornerstoneTools.LengthTool.toolName);
toolGroup.addViewport('axial', 'myEngine');
toolGroup.setToolActive(cornerstoneTools.LengthTool.toolName);

๊ฒฐ๋ก  โ€” ์–ด๋–ค ์„ ํƒ์ด ๋งž์„๊นŒ?

Cornerstone3D๋Š” ํ‘œ์ค€ DICOM ์›Œํฌํ”Œ๋กœ๋ฅผ ๋น ๋ฅด๊ฒŒ ๊ตฌ์ถ•ํ•ด์•ผ ํ•˜๋Š” ํŒ€์—๊ฒŒ๋Š” ํ˜„์กดํ•˜๋Š” ๊ฐ€์žฅ ๊ฐ•๋ ฅํ•œ ์„ ํƒ์ง€์ž…๋‹ˆ๋‹ค. ๊ณต์œ  GPU ํ…์Šค์ฒ˜, ์ŠคํŠธ๋ฆฌ๋ฐ ๋ณผ๋ฅจ ๋กœ๋”, ๋‚ด์žฅ ์„ธ๊ทธ๋ฉ˜ํ…Œ์ด์…˜ ํŽธ์ง‘ ๋„๊ตฌ๋Š” ์ง์ ‘ ๊ตฌํ˜„ํ•˜๋ ค๋ฉด ์ˆ˜๊ฐœ์›”์ด ๊ฑธ๋ฆฌ๋Š” ๊ธฐ๋Šฅ๋“ค์ž…๋‹ˆ๋‹ค.

๋ฐ˜๋ฉด ๋น„ํ‘œ์ค€ ๋ฐ์ดํ„ฐ ํฌ๋งท(NIfTI REST API), ๋…์ž์  ๋ทฐ ํƒ€์ž…, Canvas ๊ธฐ๋ฐ˜ ์ปค์Šคํ…€ ๋ Œ๋”๋ง์ด ํ•ต์‹ฌ์ธ ํ”„๋กœ์ ํŠธ๋ผ๋ฉด ์ปค์Šคํ…€ VTK.js ๊ตฌํ˜„์ด ๋” ์ ํ•ฉํ•ฉ๋‹ˆ๋‹ค. Cornerstone3D์˜ ์ถ”์ƒํ™” ๋ ˆ์ด์–ด๋Š” ์ด๋Ÿฐ ๊ฒฝ์šฐ ์˜คํžˆ๋ ค ๊ฑธ๋ฆผ๋Œ์ด ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๐Ÿงญ ๋‘ ์ ‘๊ทผ์„ ํ˜ผ์šฉํ•˜๋Š” ์ „๋žต๋„ ์žˆ์Šต๋‹ˆ๋‹ค. DICOM ํ‘œ์ค€ ๋ทฐ๋Š” Cornerstone3D๋กœ, ์ปค์Šคํ…€ NIfTI/MIP ๋ทฐ๋Š” ์ง์ ‘ ๊ตฌํ˜„ํ•œ VTK.js ํŒŒ์ดํ”„๋ผ์ธ์œผ๋กœ ๋ถ„๋ฆฌํ•˜๋Š” ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค.

์ฐธ๊ณ  ์ž๋ฃŒ: cornerstonejs.org ยท GitHub ยท OHIF Viewer

2026 ยท NIfTI Viewer ํ”„๋กœ์ ํŠธ โ€” Cornerstone3D ๊ตฌ์กฐ ๋ถ„์„