Cornerstone3D์ ์ต์ ํ์
์์ ๋ก๋ ๊ธฐ์ ์์ ๋ถ์
Web Worker ๋์ฝ๋ฉ, ์คํธ๋ฆฌ๋ฐ ๋ณผ๋ฅจ, HTJ2K Progressive Loading, VoxelManager๊น์ง โ ๋ด๋ถ ๋์ ์๋ฆฌ๋ฅผ ํํค์นฉ๋๋ค.
์ ์๋ฃ ์์ ๋ก๋ฉ์ ํน๋ณํ ์ด๋ ต๋?
์ผ๋ฐ ์น ์ด๋ฏธ์ง์ ๋ฌ๋ฆฌ ์๋ฃ ์์ ๋ฐ์ดํฐ๋ ๊ทน๋จ์ ์ธ ์ ์ฝ ์กฐ๊ฑด์ ๊ฐ์ง๋๋ค. 512ร512 ํฝ์ CT ์ฌ๋ผ์ด์ค 300์ฅ์ ์์ถ ํด์ ์ ์ฝ 150MB์ด๋ฉฐ, PET-CT Fusion์ฒ๋ผ ์ฌ๋ฌ ์๋ฆฌ์ฆ๋ฅผ ๋์์ ๋ ๋๋งํ ๊ฒฝ์ฐ GPU์ CPU ๋ฉ๋ชจ๋ฆฌ ๋ชจ๋ ๋น ๋ฅด๊ฒ ํ๊ณ์ ๋๋ฌํฉ๋๋ค. Cornerstone3D๋ ์ด ๋ฌธ์ ๋ฅผ ๋จ์ผ ๊ธฐ์ ์ด ์๋ ์ฌ๋ฌ ๋ ์ด์ด์ ์ต์ ํ ์ ๋ต์ ์กฐํฉํด ํด๊ฒฐํฉ๋๋ค.
์์ ๋ก๋ฉ ์ ์ฒด ํ์ดํ๋ผ์ธ
DICOMweb ์๋ฒ์์ ์์์ ์์ฒญํด ํ๋ฉด์ ํ์ํ๊ธฐ๊น์ง, Cornerstone3D ๋ด๋ถ์์ ์ด๋ค ๋จ๊ณ๊ฐ ์์๋๋ก ์คํ๋๋์ง ์ ์ฒด ํ๋ฆ์ ์ดํด๋ด ๋๋ค.
์คํ๋ฉ์ธ์ค๋ ๋ ๋์ฝ๋ฉ โ ํ๋ฉด์ด ๋ฉ์ถ์ง ์๋ ์ด์
๋ธ๋ผ์ฐ์ ์ ๋ฉ์ธ ์ค๋ ๋์์ DICOM ํ์ผ์ ๋์ฝ๋ฉํ๋ฉด UI๊ฐ ์์ ํ ๋ฉ์ถฅ๋๋ค. Cornerstone3D๋ Web Worker๋ฅผ ํตํด ๋์ฝ๋ฉ ์ฐ์ฐ์ ์์ ํ ๋ฉ์ธ ์ค๋ ๋ ๋ฐ์ผ๋ก ๋ถ๋ฆฌํฉ๋๋ค. v2.0๋ถํฐ๋ HTTP Fetch์ Decode๋ฅผ ๋ ๋ฆฝ์ ์ผ๋ก ๋น๋๊ธฐ ์ฒ๋ฆฌํด ํ์ดํ๋ผ์ธ ํจ์จ์ ๊ทน๋ํํฉ๋๋ค.
๋ฉ์ธ ์ค๋ ๋ ์์ฐจ ์ฒ๋ฆฌ
- fetch โ decode โ render ์์ฐจ ์คํ
- ๋์ฝ๋ฉ ์ค UI ์์ ๋ธ๋กํน
- HTTP ์์ฒญ 6๊ฐ ๋จ์ ๋ฌถ์ ์ฒ๋ฆฌ
- ํ ์ด๋ฏธ์ง ์ง์ฐ ์ ์ ์ฒด ๋๊ธฐ
Fetch / Decode ์์ ๋ถ๋ฆฌ
- Fetch์ Decode๋ฅผ ๋ ๋ฆฝ ๋น๋๊ธฐ ํ๋ก ์ด์
- ๋์ฝ๋ฉ ์ค์๋ UI ๋ฐ์ ์ ์ง
- Worker Pool๋ก ๋ค์ค ๋ณ๋ ฌ ๋์ฝ๋ฉ
- Transferable ๊ฐ์ฒด๋ก ๋ณต์ฌ ์์ด ๋ฐ์ดํฐ ์ ๋ฌ
์ง์ํ๋ ์ฝ๋ฑ ๋์ฝ๋๋ Workers ๋ด์์ ๋์ ๋ก๋๋ฉ๋๋ค.
| Transfer Syntax | ํฌ๋งท | ๋์ฝ๋ | ํน์ง |
|---|---|---|---|
| 1.2.840.10008.1.2.1 | Uncompressed | Native | ๋ณ๋ ๋์ฝ๋ ๋ถํ์ |
| 1.2.840.10008.1.2.4.90 | JPEG 2000 Lossless | openjpeg (WASM) | ๊ณ ํ์ง ๋ฌด์์ค |
| 1.2.840.10008.1.2.4.202 | HTJ2K | openhtj2k (WASM) | Progressive ๋์ฝ๋ฉ ์ง์ |
| 1.2.840.10008.1.2.4.70 | JPEG Lossless | libjpeg (WASM) | โ |
| 1.2.840.10008.1.2.5 | RLE Lossless | Built-in | โ |
Request Pool Manager โ ์์ฒญ ์ฐ์ ์์ ์ ์ด
Cornerstone3D๋ ๋ชจ๋ ์ด๋ฏธ์ง ์์ฒญ์ ๋จ์ํ FIFO๋ก ์ฒ๋ฆฌํ์ง ์์ต๋๋ค. RequestPoolManager๊ฐ ์์ฒญ์ 3๋จ๊ณ ์ฐ์ ์์ ํ๋ก ๋ถ๋ฅํ์ฌ ์ฌ์ฉ์ ์ธํฐ๋์ ์ ์ํฅ์ด ์๋๋ก ์ค์ผ์ค๋งํฉ๋๋ค.
interaction
์ฌ์ฉ์๊ฐ ์ง์ ๋ณด๊ณ ์๋ ์ฌ๋ผ์ด์ค. ์ฆ์ ๋ก๋. ๋ค๋ฅธ ์์ฒญ๋ณด๋ค ํญ์ ์ฐ์ .
prefetch
ํ์ฌ ์ฌ๋ผ์ด์ค ์ธ๊ทผ ํ๋ฆฌํจ์น. ๋ฐฑ๊ทธ๋ผ์ด๋์์ ์กฐ์ฉํ ๋ก๋.
thumbnail
์ธ๋ค์ผยท์ ํด์๋ ๋ฏธ๋ฆฌ๋ณด๊ธฐ. ๊ฐ์ฅ ๋ฎ์ ์ฐ์ ์์.
// Request Pool ์ฐ์ ์์ ์ค์ ์์ import { requestPoolManager } from '@cornerstonejs/core'; // ์ต๋ ๋์ ์์ฒญ ์ ์กฐ์ requestPoolManager.setMaxSimultaneousRequests( 'interaction', 6 // ๊ธฐ๋ณธ๊ฐ: 6 ); requestPoolManager.setMaxSimultaneousRequests( 'prefetch', 10 // ๊ธฐ๋ณธ๊ฐ: 10 ); // ์๋์ผ๋ก interaction ์ฐ์ ์์ ์์ฒญ ์ถ๊ฐ requestPoolManager.addRequest( requestFn, 'interaction', /* addToBeginning = */ true );
์คํธ๋ฆฌ๋ฐ ๋ณผ๋ฅจ ๋ก๋ โ ์ ์ง์ 3D ๋ ๋๋ง
์ผ๋ฐ์ ์ธ ๋ณผ๋ฅจ ๋ก๋๋ ๋ชจ๋ ์ฌ๋ผ์ด์ค๋ฅผ ๋ฉ๋ชจ๋ฆฌ์ ์ฌ๋ฆฐ ํ์์ผ ๋ ๋๋ง์ ์์ํฉ๋๋ค. Cornerstone3D์ StreamingImageVolume์ ์ฌ๋ผ์ด์ค๊ฐ ํ๋์ฉ ๋์ฐฉํ๋ ์ฆ์ GPU ํ ์ค์ฒ๋ฅผ ๋ถ๋ถ ์ ๋ฐ์ดํธํด ๋ ๋๋งํฉ๋๋ค.
// ์คํธ๋ฆฌ๋ฐ ๋ณผ๋ฅจ ์์ฑ & ๋ก๋ import { volumeLoader } from '@cornerstonejs/core'; const volumeId = 'cornerstoneStreamingImageVolume:CT_VOLUME'; // 1๋จ๊ณ: ๋ฉํ๋ฐ์ดํฐ ๊ธฐ๋ฐ ์ฌ์ ํ ๋น const volume = await volumeLoader.createAndCacheVolume(volumeId, { imageIds: ctImageIds, // 300์ฅ ์ฌ๋ผ์ด์ค imageId ๋ฐฐ์ด }); // 2๋จ๊ณ: ์คํธ๋ฆฌ๋ฐ ๋ก๋ ์์ (์ฆ์ ๋ ๋๋ง ๊ฐ๋ฅ) volume.load(); // await ๋ถํ์ โ ์ฌ๋ผ์ด์ค ๋์ฐฉ ์ ์๋ ๋ ๋๋ง // viewport์ ๋ณผ๋ฅจ ์ฐ๊ฒฐ viewport.setVolumes([{ volumeId }]);
HTJ2K Progressive Loading โ 66ms ์ฒซ ๋ ๋
HTJ2K(High Throughput JPEG 2000)๋ DICOM ํ์ค์ ์๋ก ์ถ๊ฐ๋ ์์ถ ํฌ๋งท์ผ๋ก, ์ฒซ N ๋ฐ์ดํธ๋ง์ผ๋ก ์ ํด์๋ ๋ฒ์ ์ ์ฆ์ ๋์ฝ๋ฉํ ์ ์์ต๋๋ค. Cornerstone3D๋ ์ด๋ฅผ ํ์ฉํด ์ ์ฒด ์ด๋ฏธ์ง๊ฐ ๋์ฐฉํ๊ธฐ ์ ์ ๋จผ์ ์ ํด์๋๋ฅผ ํ์ํ๊ณ , ๋ฐ์ดํฐ๊ฐ ๋์ ๋ ์๋ก ํด์๋๋ฅผ ์ ์ง์ ์ผ๋ก ํฅ์์ํต๋๋ค.
* 3036ร3036 CT ์ด๋ฏธ์ง ๊ธฐ์ค, ์ฒซ ๋ ๋ ์๊ฐ (4G ๋คํธ์ํฌ)
Progressive Loading์ 2๋จ๊ณ Retrieve ์ค์ ์ผ๋ก ๋์ํฉ๋๋ค.
// HTJ2K Progressive Loading ์ค์ import { utilities } from '@cornerstonejs/core'; const retrieveConfig = { stages: [ { id: 'initialImages', retrieveType: 'singleFast' }, // ์ ํด์๋ ๋จผ์ { id: 'fullResolution', retrieveType: 'singleFinal' }, // ์ ์ฒด ํ์ง ], retrieveOptions: { singleFast: { streaming: true, urlParameters: 'accept=image/jhc', // HTJ2K ์์ฒญ }, singleFinal: { streaming: true }, }, }; // ์คํ ์ ์ฒด์ ์ ์ฉ utilities.imageRetrieveMetadataProvider.add('stack', retrieveConfig); // ๋๋ ๋ณผ๋ฅจ ์ ์ฒด์ ์ ์ฉ utilities.imageRetrieveMetadataProvider.add(volumeId, retrieveConfig);
๋ฉ๋ชจ๋ฆฌ ์ต์ ํ โ VoxelManager & ๋จ์ผ ์บ์
Cornerstone3D 2.0์ ๊ฐ์ฅ ํฐ ๋ด๋ถ ๋ณํ๋ VoxelManager ๋์ ๊ณผ ๋จ์ผ Image Cache ์ํคํ ์ฒ๋ก์ ์ ํ์ ๋๋ค. ๊ธฐ์กด์๋ Image Cache์ Volume Cache๊ฐ ๋ณ๋๋ก ์กด์ฌํด ๊ฐ์ ๋ฐ์ดํฐ๊ฐ ์ด์ค์ผ๋ก ๋ฉ๋ชจ๋ฆฌ๋ฅผ ์ฐจ์งํ์ต๋๋ค.
์ด์ค ์บ์ ๊ตฌ์กฐ
- Image Cache + Volume Cache ๋ณ๋ ์ด์
- ๊ฐ์ ์ฌ๋ผ์ด์ค ๋ฐ์ดํฐ ๋ ๊ณณ์ ์ค๋ณต ์ ์ฅ
- ๋ํ ์ค์นผ๋ผ ๋ฐฐ์ด(scalarData) CPU์ ์ ์ง
- ๋ณผ๋ฅจ ์ ์ฒด ๋ฉ๋ชจ๋ฆฌ = CPU ๋ฐฐ์ด + GPU ํ ์ค์ฒ
- Stack โ Volume ์ธ๊ทธ๋ฉํ ์ด์ ๋๊ธฐํ ๋ฒ๊ทธ
๋จ์ผ Image Cache + VoxelManager
- ์ค์ง Image Cache ํ๋๋ง ์ฌ์ฉ (๋จ์ผ ์ง์ค)
- ๋ณผ๋ฅจ ๋ ๋๋ง ์ CPU ์ค์นผ๋ผ ๋ฐฐ์ด ๋ถํ์
- Image โ GPU ์ง์ ์คํธ๋ฆฌ๋ฐ (CPU ์ฐํ)
- Native ๋ฐ์ดํฐ ํ์ ์ ์ง โ ๋ ๋ ์ ๋ณํ
- ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋ ์ฝ 50% ์ ๊ฐ
// v1: scalarData ์ง์ ์ ๊ทผ (๋น๊ถ์ฅ) const scalarData = volume.scalarData; // โ v2์์ ์ ๊ฑฐ๋จ // v2: VoxelManager๋ก ํจ์จ์ ์ ๊ทผ const vm = volume.voxelManager; const value = vm.getAtIndex(flatIndex); // โ ๋จ์ผ ๋ณต์ ์ ๊ทผ const value = vm.getAtIJK(i, j, k); // โ IJK ์ขํ ์ ๊ทผ // ๋๋ ์ฒ๋ฆฌ ์ forEach ํ์ฉ vm.forEach(({ value, index }) => { if (value > 100) vm.setAtIndex(index, 100); }); // โ ๏ธ ์ตํ ์๋จ์ผ๋ก๋ง ์ฌ์ฉ (๋ฉ๋ชจ๋ฆฌ 2๋ฐฐ ์ ์ ) const fullArray = vm.getCompleteScalarDataArray();
Cache API โ ๋ฉ๋ชจ๋ฆฌ ํ๋ & ์๋ ํด์
๋ํ ๋ณผ๋ฅจ์ ์ฌ๋ฌ ๊ฐ ๋ก๋ํ๋ฉด ๋ธ๋ผ์ฐ์ ๊ฐ OOM(Out of Memory)์ผ๋ก ํญ์ ์ข ๋ฃํฉ๋๋ค. Cornerstone3D์ Cache API๋ ์ต๋ ๋ฉ๋ชจ๋ฆฌ ํ๋๋ฅผ ์ค์ ํ๊ณ , ์ด๊ณผ ์ ์๋์ผ๋ก ์ค๋๋ ๋ฐ์ดํฐ๋ฅผ ํด์ ํ๋ LRU ๋ฐฉ์์ ์ ๊ณตํฉ๋๋ค.
์ต๋ ๋ฉ๋ชจ๋ฆฌ ํ๋ ์ค์
์ ์ฒด ์ด๋ฏธ์ง ์บ์๊ฐ ์ฐจ์งํ ์ ์๋ ์ต๋ ๋ฐ์ดํธ๋ฅผ ์ง์ ํฉ๋๋ค. ์ด๊ณผ ์ ๊ฐ์ฅ ์ค๋ ์ฌ์ฉ๋์ง ์์ ์ด๋ฏธ์ง๋ถํฐ ํด์ ๋ฉ๋๋ค.
์๋ ๊ณต๊ฐ ํ๋ณด
์ ๋ณผ๋ฅจ ํ ๋น ์ decacheIfNecessaryUntilBytesAvailable()๋ก ํ์ํ ๊ณต๊ฐ์ ์๋ ํ๋ณดํฉ๋๋ค.
// ์บ์ ์ต๋ ๋ฉ๋ชจ๋ฆฌ ์ค์ (๊ธฐ๋ณธ: 1GB) import { cache } from '@cornerstonejs/core'; cache.setMaxCacheSize(2 * 1024 * 1024 * 1024); // 2GB // ํ์ฌ ์ฌ์ฉ๋ ํ์ธ const { numImagesCached, cacheSizeInBytes } = cache.getCacheStatus(); // ํน์ ๋ณผ๋ฅจ ์ ๊ฑฐ cache.purgeVolumeCache(volumeId); // ์ ์ฒด ์บ์ ๋น์ฐ๊ธฐ cache.purgeCache();
Shared Volume Mapper โ GPU ํ ์ค์ฒ 1ํ ์ ๋ก๋
PET-CT Fusion์ฒ๋ผ ๊ฐ์ ๋ณผ๋ฅจ ๋ฐ์ดํฐ๋ฅผ ์ฌ๋ฌ ๋ทฐํฌํธ์์ ๋ค๋ฅธ ์นด๋ฉ๋ผ ๊ฐ๋๋ก ํ์ํด์ผ ํ ๋, ๋์ด๋ธํ ๊ตฌํ์ ๋ทฐํฌํธ ์๋งํผ GPU ํ ์ค์ฒ๋ฅผ ๋ณต์ ํฉ๋๋ค. Cornerstone3D๋ Shared Volume Mapper๋ก GPU ํ ์ค์ฒ๋ฅผ ๋จ 1ํ๋ง ์ ๋ก๋ํ๊ณ ๋ชจ๋ ๋ทฐํฌํธ๊ฐ ์ฌ์ฌ์ฉํฉ๋๋ค.
texSubImage3D()๋ก ์ฌ๋ผ์ด์ค ๋จ์ ๋ถ๋ถ ์
๋ฐ์ดํธ๋ฅผ ๊ตฌํํฉ๋๋ค. ์ ์ฒด ํ
์ค์ฒ๋ฅผ ๊ต์ฒดํ๋ ๊ฒ์ด ์๋๋ผ ํด๋น Z ์ฌ๋ผ์ด์ค ์คํ์
๋ง ๊ฐฑ์ ํ๋ฏ๋ก ์คํธ๋ฆฌ๋ฐ ์ค์๋ GPU ๋ถํ๊ฐ ์ต์ํ๋ฉ๋๋ค.
Position-aware Prefetch โ ์์น ๊ธฐ๋ฐ ์ค๋งํธ ํ๋ฆฌํจ์น
๋จ์ ์์ฐจ ํ๋ฆฌํจ์น(1๋ฒ โ 2๋ฒ โ 3๋ฒ ...)๋ ์ฌ์ฉ์๊ฐ ์ค๊ฐ ์ฌ๋ผ์ด์ค์์ ์์ํ๋ฉด ๋นํจ์จ์ ์ ๋๋ค. Cornerstone3D์ stackPrefetch๋ ํ์ฌ ์ฌ๋ผ์ด์ค ์์น๋ฅผ ๊ธฐ์ค์ผ๋ก ๊ฐ๊น์ด ์ฌ๋ผ์ด์ค๋ถํฐ ์ฐ์ ๋ก๋ํ๊ณ , ์ฌ๋ฌ ๋ทฐํฌํธ์ ํ๋ฆฌํจ์น๊ฐ ์๋ก ๊ฐ์ญํ์ง ์๋๋ก ์กฐ์จํฉ๋๋ค.
Position-aware (์์น ์ธ์)
ํ์ฌ ์ฌ๋ผ์ด์ค ์ธ๋ฑ์ค๋ฅผ ์ค์ฌ์ผ๋ก ยฑ๋ฐฉํฅ์ผ๋ก ๊ต์ฐจํ๋ฉฐ ๋ก๋ํฉ๋๋ค. ์ฌ๋ผ์ด์ค 100์์ ์์ํ๋ฉด 101, 99, 102, 98 ์์ผ๋ก ํ๋ฆฌํจ์นํฉ๋๋ค.
Multi-viewport ์ธ์
์ฌ๋ฌ ๋ทฐํฌํธ๊ฐ ๋์ผํ Request Pool์ ๊ณต์ ํฉ๋๋ค. ํ ๋ทฐํฌํธ์ ํ๋ฆฌํจ์น๊ฐ ๋ค๋ฅธ ๋ทฐํฌํธ์ interaction ์์ฒญ์ ๋ง์ง ์๋๋ก ์ฐ์ ์์๋ฅผ ์กฐ์จํฉ๋๋ค.
// stackPrefetch ํ์ฑํ import { StackScrollTool, ToolGroupManager } from '@cornerstonejs/tools'; import { cornerstoneTools } from '@cornerstonejs/tools'; const { StackScrollTool, StackScrollMouseWheelTool, PanTool, ZoomTool, ToolGroupManager, Enums: csToolsEnums } = cornerstoneTools; // ToolGroup์ Prefetch ํ์ฑํ cornerstoneTools.addTool(cornerstoneTools.StackScrollTool); toolGroup.addTool(StackScrollTool.toolName, { configuration: { prefetchCount: 10, // ์๋ค 10์ฅ์ฉ ํ๋ฆฌํจ์น preventHandleOutsideImage: false, }, });
์ต์ ํ ๊ธฐ์ ํ๋์ ๋ณด๊ธฐ
| ๊ธฐ์ | ์ ์ฉ ์์น | ํจ๊ณผ | ๋์ ๋ฒ์ |
|---|---|---|---|
| ์คํ์คํฌ๋ฆฐ WebGL ์ปจํ ์คํธ | RenderingEngine | ๋ทฐํฌํธ N๊ฐ๋ WebGL ์ปจํ ์คํธ 1๊ฐ | v1.0 |
| Shared Volume Mapper | GPU ๋ ๋๋ง | ๋์ผ ๋ณผ๋ฅจ GPU ํ ์ค์ฒ 1ํ ์ ๋ก๋ | v1.0 |
| Web Worker ๋ถ๋ฆฌ ๋์ฝ๋ฉ | DICOM ๋ก๋ฉ | ๋ฉ์ธ ์ค๋ ๋ ๋ธ๋กํน ์์ | v1.0 |
| Fetch / Decode ๋ ๋ฆฝ ๋น๋๊ธฐ | Request Pool | ํ์ดํ๋ผ์ธ ์ฒ๋ฆฌ๋ ์ฆ๊ฐ | v2.0 |
| ์คํธ๋ฆฌ๋ฐ ๋ณผ๋ฅจ ๋ก๋ | Volume ๋ก๋ฉ | ์ฌ๋ผ์ด์ค ๋์ฐฉ ์ฆ์ ๋ ๋๋ง | v1.0 |
| skipCreateImage | Volume ๋ก๋ฉ | Image ๊ฐ์ฒด ์์ฑ ์ค๋ฒํค๋ ์ ๊ฑฐ | v1.0 |
| texSubImage3D ๋ถ๋ถ ์ ๋ฐ์ดํธ | GPU ์ ๋ก๋ | ์คํธ๋ฆฌ๋ฐ ์ค GPU ๋ถํ ์ต์ํ | v1.0 |
| HTJ2K Progressive Loading | ๋คํธ์ํฌ | ์ฒซ ๋ ๋ 66ms (vs 4,586ms) | v1.x |
| Priority Queue (3๋จ๊ณ) | Request Pool | ์ธํฐ๋์ ์ค ๋๊น ๋ฐฉ์ง | v1.x |
| Position-aware Prefetch | Stack ๋ทฐ์ด | ํ์ฌ ์์น ๊ธฐ์ค ์ค๋งํธ ํ๋ฆฌํจ์น | v1.x |
| VoxelManager | ๋ฉ๋ชจ๋ฆฌ ๊ด๋ฆฌ | ๋ฉ๋ชจ๋ฆฌ ~50% ์ ๊ฐ, CPU ๋ฐฐ์ด ์ ๊ฑฐ | v2.0 |
| ๋จ์ผ Image Cache | ์บ์ ๊ตฌ์กฐ | Image/Volume ์ด์ค ์บ์ ์ ๊ฑฐ | v2.0 |
| Native ํ์ ์ ์ฅ | ์บ์ | ๋ถํ์ํ ํ์ ๋ณํ ์ ๊ฑฐ | v2.0 |