NIfTI ์์ถ์ ๋ ๊ฐ๋
.nii.gz vs Zarr
gzip ๋จ์ผ ํ์ผ ์์ถ๊ณผ ์ฒญํฌ ๊ธฐ๋ฐ Zarr ํฌ๋งท์ ๊ตฌ์กฐ์ ์ฐจ์ด๋ถํฐ, NIfTI ๋ฐ์ดํฐ์ ์ ์ฉํ์ ๋ ์น ๋ทฐ์ด์์ ๋ฐ์ํ๋ ๋ฌธ์ ์ ๊ณผ ๊ธฐ์ ์ ์ฉ ๋ฐฉ์๊น์ง ์์ ๋ถ์ํฉ๋๋ค.
์ ์ง๊ธ ์ด ๋น๊ต๊ฐ ์ค์ํ๊ฐ
NIfTI(.nii.gz)๋ ์ ๊ฒฝ์์ ์ฐ๊ตฌ์ ํ์ค ํฌ๋งท์ผ๋ก, ์์ญ ๋ ๊ฐ ๋จ์ํ๊ณ ์์ ์ ์ด์์ต๋๋ค. ํ์ง๋ง AI ๋ชจ๋ธ ์ถ๋ ฅ๋ฌผ, ๋ํ ๋์์, ๊ณ ํด์๋ ์๋ฃ ๋ณผ๋ฅจ์ด ์ผ์ํ๋๋ฉด์ ๋จ์ผ ํ์ผ gzip ์์ถ ๋ฐฉ์์ ํ๊ณ๊ฐ ๋๋ฌ๋๊ณ ์์ต๋๋ค. ํนํ ์น ๋ธ๋ผ์ฐ์ ์์ ๋์ฉ๋ ๋ณผ๋ฅจ์ ์ค์๊ฐ์ผ๋ก ๋ ๋๋งํ๋ ์๋๋ฆฌ์ค์์, .nii.gz๋ ๊ตฌ์กฐ์ ์ผ๋ก ๋ถ๋ฆฌํฉ๋๋ค.
Zarr๋ ๊ธฐํยท์ฒ๋ฌธยท์ ์ ์ฒด ๋ถ์ผ์์ ํํ๋ฐ์ดํธ๊ธ ๋ฐฐ์ด ๋ฐ์ดํฐ๋ฅผ ํด๋ผ์ฐ๋๋ก ์๋นํ๋ ๊ธฐ์ ์ ๋๋ค. ์ต๊ทผ ์ ๊ฒฝ์์ ์ปค๋ฎค๋ํฐ๊ฐ ์ด๋ฅผ NIfTI์ ์ ๋ชฉํ NIfTI-Zarr(nii.zarr) ์คํ์ ์ ์ํ๋ฉด์ ์น ์๋ฃ ์์ ๋ทฐ์ด์์ ์ ์ ์ด ์๊ฒผ์ต๋๋ค.
gzip ์์ถ์ ๋์ ๋ฐฉ์
gzip์ ์คํธ๋ฆผ ๊ธฐ๋ฐ ์์ถ ์๊ณ ๋ฆฌ์ฆ์ ๋๋ค. ๋ฐ์ดํฐ๋ฅผ ์์๋๋ก ์ฝ์ผ๋ฉด์ ๋ฐ๋ณต ํจํด์ LZ77 ์๊ณ ๋ฆฌ์ฆ์ผ๋ก ์์ถํฉ๋๋ค. .nii.gz๋ NIfTI ํค๋ + ๋ณผ๋ฅจ ์ ์ฒด๋ฅผ ํ๋์ gzip ์คํธ๋ฆผ์ผ๋ก ๋ฌถ์ ๋จ์ผ ํ์ผ์ ๋๋ค.
.nii.gz ์ฅ์
- ๋จ์ผ ํ์ผ โ ๊ด๋ฆฌยท๋ฐฐํฌยท๊ณต์ ๊ฐ๋จ
- ๋์ ์์ถ๋ฅ (์๋ฃ ๋ฐ์ดํฐ 3~5:1)
- ๋ชจ๋ NIfTI ๋๊ตฌ ์๋ฒฝ ํธํ
- ์์ฐจ ์ฒ๋ฆฌ์ ์ต์ (AI ํ์ต ๋ฑ)
- ๊ตฌํ ๋ณต์ก๋ ๋ฎ์
.nii.gz ๋จ์
- ์์ ์ ๊ทผ ๋ถ๊ฐ โ ์ ์ฒด ํด์ ํ์
- ์น ์คํธ๋ฆฌ๋ฐ ๋ถ๊ฐ (gzip์ ์คํธ๋ฆผ)
- ๋ณ๋ ฌ ์ฒ๋ฆฌ ๋ถ๊ฐ (์์ฐจ ์์กด)
- ๋ฉํฐํด์๋ ์ ์ฅ ๋ถ๊ฐ
- ๋์ฉ๋ ์ด๊ธฐ ๋ก๋ฉ ์์ญ ์ด
Zarr์ ์ฒญํฌ ๊ธฐ๋ฐ ์ํคํ ์ฒ
Zarr๋ N์ฐจ์ ๋ฐฐ์ด์ ๋ ๋ฆฝ์ ์ธ ์ฒญํฌ(chunk) ๋จ์๋ก ๋ถํ ์ ์ฅํ๋ ํฌ๋งท์ ๋๋ค. ๊ฐ ์ฒญํฌ๋ ๊ฐ๋ณ ํ์ผ(๋๋ ๊ฐ์ฒด)๋ก ์ ์ฅ๋๊ณ , ๋ ๋ฆฝ์ ์ผ๋ก ์์ถ๋์ด ์์ด ํน์ ์ฒญํฌ๋ง HTTP ์์ฒญ์ผ๋ก ๊ฐ์ ธ์ฌ ์ ์์ต๋๋ค. ๋๋ ํ ๋ฆฌ ๊ธฐ๋ฐ ๊ตฌ์กฐ์ด๋ฉฐ JSON ๋ฉํ๋ฐ์ดํฐ๊ฐ ์๊ธฐ ์์ ์ (self-describing)์ผ๋ก ํฌํจ๋ฉ๋๋ค.
Zarr ์ฅ์
- ์์ ์ ๊ทผ โ ํน์ ์ฒญํฌ๋ง HTTP GET
- ๋ณ๋ ฌ ์ฒ๋ฆฌ โ ์ฒญํฌ ๋์ fetch ๊ฐ๋ฅ
- ๋ฉํฐํด์๋ ํผ๋ผ๋ฏธ๋ ๋ด์ฅ
- Progressive ๋ ๋๋ง (์ฒญํฌ ๋์ฐฉ์)
- ํด๋ผ์ฐ๋(S3, GCS) ๋ค์ดํฐ๋ธ
- ์ ์ HTTP ์๋ฒ๋ง์ผ๋ก ์๋น ๊ฐ๋ฅ
- ๋ค์ํ ์์ถ ์๊ณ ๋ฆฌ์ฆ ์ ํ
Zarr ๋จ์
- ํ์ผ ์ ํญ๋ฐ (์๋ฐฑ~์์ฒ ๊ฐ ์ฒญํฌ)
- ๊ธฐ์กด NIfTI ๋๊ตฌ ๋ฏธํธํ
- ๋ณํ(์ ์ฒ๋ฆฌ) ํ์ดํ๋ผ์ธ ํ์
- ๋ฉํ๋ฐ์ดํฐ ๋๊ธฐํ ๊ด๋ฆฌ ํ์
- ์ํ ๋ณผ๋ฅจ์์๋ ์ค๋ฒํค๋
- NIfTI qform/sform ์ ์ฅ ๋ถํ์ค
.nii.gz vs Zarr โ ํญ๋ชฉ๋ณ ๋์กฐ
| ํญ๋ชฉ | .nii.gz | Zarr | ์น ๋ทฐ์ด ์ํฅ |
|---|---|---|---|
| ์ ์ฅ ๋จ์ | ๋จ์ผ ํ์ผ | ์ฒญํฌ ๋๋ ํ ๋ฆฌ | Zarr๊ฐ ์ฌ๋ผ์ด์ค ๋จ์ fetch ๊ฐ๋ฅ |
| ์์ ์ ๊ทผ | ๋ถ๊ฐ | ๊ฐ๋ฅ (์ฒญํฌ ๋จ์) | Zarr๋ง ํ์ฌ ์ฌ๋ผ์ด์ค ์ฆ์ ํ์ |
| ์์ถ ์๊ณ ๋ฆฌ์ฆ | gzip ์ ์ฉ | gzip, zstd, blosc, lz4 ๋ฑ ์ ํ | Zarr+blosc/zstd: ํด์ ์๋ 5~10รโ |
| ์์ถ ๋ฒ์ | ํ์ผ ์ ์ฒด ๋จ์ผ ์์ถ | ์ฒญํฌ๋ณ ๋ ๋ฆฝ ์์ถ | ํ์ํ ์ฒญํฌ๋ง ํด์ , CPU ์ ์ฝ |
| ๋ฉํฐํด์๋ | ์์ | ํผ๋ผ๋ฏธ๋ ๋ด์ฅ | Zarr: ์ ํด์๋ ๋จผ์ โ ๊ณ ํด์๋ ์ |
| ๋ณ๋ ฌ fetch | ๋ถ๊ฐ | ๊ฐ๋ฅ (์ฒญํฌ ๋ ๋ฆฝ) | ์ฌ๋ฌ ์ฒญํฌ ๋์ ์์ฒญ ๊ฐ๋ฅ |
| ์๋ฒ ์๊ตฌ์ฌํญ | Range Request ์ง์ ํ์ | ์ ์ HTTP๋ง์ผ๋ก ๊ฐ๋ฅ | Zarr: S3/CDN ์ง๊ฒฐ ์๋น ๊ฐ๋ฅ |
| ์ฒซ ๋ ๋ ์๊ฐ | ์ ์ฒด ๋ค์ด๋ก๋ ํ | ํด๋น ์ฒญํฌ ๋์ฐฉ ์ฆ์ | ๋์ฉ๋์์ ์์ญ ์ด vs ์ ์ด ์ฐจ์ด |
| ๋๊ตฌ ํธํ์ฑ | ์๋ฒฝ ํธํ | ์ ์ฉ ๋ผ์ด๋ธ๋ฌ๋ฆฌ | ๊ธฐ์กด NIfTI ํ์ดํ๋ผ์ธ ์์ ํ์ |
| ํ์ผ ์ | 1๊ฐ | ์๋ฐฑ~์์ฒ ๊ฐ | ํ์ผ์์คํ ๊ด๋ฆฌ ๋ณต์ก๋ ์ฆ๊ฐ |
| ๋ณํ ํ์ | ๋ถํ์ | niiโzarr ์ ์ฒ๋ฆฌ | ์๋ฒ ํ์ดํ๋ผ์ธ ๊ตฌ์ฑ ํ์ |
์์ถ/ํด์ ์๋ ๋น๊ต (1024ร1024ร300 ๋ณผ๋ฅจ ๊ธฐ์ค)
* ์ ์ฒด ๋ณผ๋ฅจ ๊ธฐ์ค์ด ์๋ ๋จ์ผ ์ฌ๋ผ์ด์ค ํด๋น ์ฒญํฌ ํด์ ๊ธฐ์ค / ์คํ๊ฒฝ ์ฐจ์ด ์กด์ฌ
NIfTI์ Zarr๋ฅผ ์ ์ฉํ๋ค๋ฉด โ NIfTI-Zarr
์ ๊ฒฝ์์ ์ปค๋ฎค๋ํฐ๋ ๋ ํฌ๋งท์ ์ฅ์ ์ ๊ฒฐํฉํ NIfTI-Zarr(nii.zarr) ์คํ์ ์ ์ํ์ต๋๋ค. OME-Zarr๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ํ๋ NIfTI ํค๋(qform/sform ๋ฑ)๋ฅผ Zarr ๋ด๋ถ์ raw bytes๋ก ๋ณด์กดํ์ฌ, ๊ธฐ์กด NIfTI ๋ฉํ๋ฐ์ดํฐ์ Zarr์ ์ฒญํฌ ์ ๊ทผ์ ๋์์ ์ง์ํฉ๋๋ค.
nii2zarr)๋ก ๋ณํ ๊ฐ๋ฅํฉ๋๋ค.
์ฒญํฌ ํฌ๊ธฐ ์ ํ ์ ๋ต
์ฒญํฌ ํฌ๊ธฐ๋ Zarr์์ ๊ฐ์ฅ ์ค์ํ ์ฑ๋ฅ ๊ฒฐ์ ์์์ ๋๋ค. ๋๋ฌด ์์ผ๋ฉด HTTP ์์ฒญ ์๊ฐ ํญ๋ฐํ๊ณ , ๋๋ฌด ํฌ๋ฉด gzip๊ณผ ๋ค๋ฅผ ๋ฐ ์์ด์ง๋๋ค.
| ์ฒญํฌ ํฌ๊ธฐ | HTTP ์์ฒญ ์ (์ฌ๋ผ์ด์ค 1์ฅ) | ์ฅ์ | ๋จ์ | ์ถ์ฒ ์๋๋ฆฌ์ค |
|---|---|---|---|---|
| 16ร16ร16 | ~16๊ฐ ์์ฒญ | ์ธ๋ฐํ ๋ถ๋ถ ์ ๊ทผ | ์์ฒญ ์ ํญ๋ฐ, ์ค๋ฒํค๋ ํผ | ๋น๊ถ์ฅ |
| 64ร64ร64 | ~4๊ฐ ์์ฒญ | ๊ท ํ์ ์ฑ๋ฅ | ์ผ๋ถ ๋ถํ์ ๋ฐ์ดํฐ ํฌํจ | 3D ๋ทฐ์ด ๊ถ์ฅ |
| 1ร512ร512 | 1๊ฐ ์์ฒญ | ์ฌ๋ผ์ด์ค ๋จ์ ์๋ฒฝ ์ ๊ทผ | Z๋ฐฉํฅ ์ ๊ทผ ์ ๋ค์ ์์ฒญ | 2D MPR ๊ถ์ฅ |
| 512ร512ร1 | 1๊ฐ ์์ฒญ | Axial ๋ทฐ ์ต์ | Sagittal/Coronal ๋นํจ์จ | Axial ์ ์ฉ |
์น ๋ทฐ์ด์์์ Zarr ์ ์ฉ ์ํคํ ์ฒ
Cornerstone3D + NIfTI-Zarr ์กฐํฉ์์ ๋ฐ์ดํฐ๊ฐ ๋ธ๋ผ์ฐ์ ์ ๋๋ฌํ๋ ์ ์ฒด ํ๋ฆ์ ๋๋ค. ๊ธฐ์กด .nii.gz ๋ฐฉ์๊ณผ ๋ฌ๋ฆฌ ๊ฐ ์ฌ๋ผ์ด์ค ์์ฒญ์ด ๋ ๋ฆฝ์ ์ธ HTTP ์ฒญํฌ fetch๋ก ๋์ฒด๋ฉ๋๋ค.
nii2zarr --chunk 64 --levels 4 --compressor blosc brain.nii.gz brain.nii.zarr/
Cornerstone3D ์ปค์คํ Image Loader ๊ตฌํ
Cornerstone3D์๋ Zarr ๊ณต์ ๋ก๋๊ฐ ์์ผ๋ฏ๋ก, ์ปค์คํ ImageLoader๋ฅผ ๊ตฌํํด์ผ ํฉ๋๋ค.
// zarr-image-loader.ts โ ์ปค์คํ Zarr ์ฒญํฌ ๊ธฐ๋ฐ Image Loader import * as zarr from 'zarrita'; // ๋๋ zarr-js import { imageLoader } from '@cornerstonejs/core'; let zarrStore: zarr.Array | null = null; // 1. Zarr ์คํ ์ด ์ด๊ธฐํ (zarr.json ์ฝ๊ธฐ) export async function initZarrStore(url: string) { const store = new zarr.FetchStore(url); zarrStore = await zarr.open(store, { kind: 'array' }); } // 2. ์ฌ๋ผ์ด์ค ๋จ์ ImageLoader ๋ฑ๋ก export function zarritaImageLoader(imageId: string) { const sliceIndex = parseSliceIndex(imageId); // zarr://url#150 const promise = (async () => { // ํด๋น ์ฌ๋ผ์ด์ค์ ์ฒญํฌ๋ง fetch (zarrita๊ฐ ์๋์ผ๋ก ์ฒญํฌ ์ขํ ๊ณ์ฐ) const sliceData = await zarr.get(zarrStore, [sliceIndex, null, null]); return { imageId, minPixelValue: min(sliceData), maxPixelValue: max(sliceData), rows: sliceData.shape[0], columns: sliceData.shape[1], getPixelData: () => new Float32Array(sliceData.data), }; })(); return { promise }; } // 3. Cornerstone3D์ ๋ฑ๋ก imageLoader.registerImageLoader('zarr', zarritaImageLoader);
Zarr ๋์ ์ ์์๋๋ ๋ฌธ์ ์
๋์: Zarr v3์ Sharding ๊ธฐ๋ฅ์ผ๋ก ์ฌ๋ฌ ์ฒญํฌ๋ฅผ ํ๋์ shard ํ์ผ๋ก ๋ฌถ์ด ํ์ผ ์๋ฅผ ํ๊ธฐ์ ์ผ๋ก ์ค์ ๋๋ค. ๋๋ ZIP ๊ธฐ๋ฐ Zarr store(
.ome.zarr.zip)๋ก ๋จ์ผ ํ์ผ๋ก ํจํค์งํฉ๋๋ค.๋์: ์๋ฒ์
Access-Control-Allow-Origin: * + Access-Control-Allow-Headers: Range ์ค์ . ๋์ผ ๋๋ฉ์ธ ์๋น์ผ๋ก CORS ์์ฒด๋ฅผ ์ ๊ฑฐํ๋ ๊ฒ์ด ์ต์ ์
๋๋ค.๋์: zarr.json ์์ฑ์ NIfTI ํค๋ ์ ๋ณด๋ฅผ JSON์ผ๋ก ์ถ๊ฐ ์ ์ฅํ๊ณ , ImageLoader ์ด๊ธฐํ ์ MetaData Provider์ ๋ฑ๋กํ๋ ์ ํธ๋ฆฌํฐ ํจ์๋ฅผ ๊ตฌํํฉ๋๋ค.
๋์: ์ฌ๋ผ์ด์ค ์ถ(Z) ๋ฐฉํฅ์ ์ฒญํฌ ํฌ๊ธฐ๋ฅผ 1๋ก ์ค์ (
1ร512ร512)ํ๋ฉด ์ฌ๋ผ์ด์ค ๋จ์ ์ฒญํฌ๊ฐ ๋์ด MPR ๋ทฐ์ด์์ ์ํฐํฉํธ๊ฐ ์์ต๋๋ค. Volume Rendering์๋ 64ร64ร64 ์
๋ฐฉํ ์ฒญํฌ๊ฐ ์ ํฉํฉ๋๋ค.๋์: HTTP/2 ๋ฉํฐํ๋ ์ฑ์ผ๋ก ๋ณ๋ ฌ ์์ฒญ์ ๋จ์ผ ์ฐ๊ฒฐ์ ๋ฌถ์ด ํด๊ฒฐํฉ๋๋ค. ์ฒญํฌ ํฌ๊ธฐ๋ฅผ ํค์ฐ๊ฑฐ๋(128ร128ร128) Zarr v3 Sharding์ ์ฌ์ฉํด ์์ฒญ ์๋ฅผ ์ค์ ๋๋ค.
๋์: AI ์ถ๋ก ์๋ฃ ํ ๋น๋๊ธฐ ๋ฐฑ๊ทธ๋ผ์ด๋ ์์ (Celery, AWS Lambda)์ผ๋ก ๋ณํํ๊ณ , ๋ณํ ์๋ฃ ์ ์๋ .nii.gz ์๋ณธ์ ์ ๊ณตํฉ๋๋ค. ์ต์ข ์ ์ผ๋ก AI ๋ชจ๋ธ์ด ์ง์ Zarr๋ก ์ถ๋ ฅํ๋๋ก ํ์ดํ๋ผ์ธ์ ์์ ํฉ๋๋ค.
์ค์ ๋์ ๋ก๋๋งต โ ๋จ๊ณ๋ณ ์ ํ
๋ชจ๋ ๋ฐ์ดํฐ๋ฅผ ํ ๋ฒ์ Zarr๋ก ์ ํํ๋ ๊ฒ์ ์ํํฉ๋๋ค. ๊ธฐ์กด .nii.gz์ Zarr๋ฅผ ๊ณต์กดํ๋ ๋จ๊ณ์ ์ ํ ์ ๋ต์ ๊ถ์ฅํฉ๋๋ค.
Accept: application/zarr ํค๋๋ก Zarr๋ฅผ ์์ฒญํ๋ฉด Zarr ์คํ ์ด URL์ ์๋ตํ๊ณ , ๊ทธ๋ ์ง ์์ผ๋ฉด ๊ธฐ์กด .nii.gz URL์ ๋ฐํํฉ๋๋ค. ๊ธฐ์กด ์์คํ
์ ์ ํ ๊ฑด๋๋ฆฌ์ง ์์ ์์ ํฉ๋๋ค.# ์๋ฒ ์ฌ์ด๋: NIfTI โ Zarr ๋ณํ ํ์ดํ๋ผ์ธ (Python) from niizarr import nii2zarr import zarr import nibabel as nib # ๋ฐฉ๋ฒ 1: nii2zarr CLI ๋๊ตฌ # nii2zarr --chunk 64 --levels 4 --compressor blosc input.nii.gz output.nii.zarr # ๋ฐฉ๋ฒ 2: Python API๋ก ์ง์ ๋ณํ img = nib.load('brain.nii.gz') data = img.get_fdata() store = zarr.open('brain.nii.zarr', mode='w') # ์ฒญํฌ: Z์ถ 1์ฌ๋ผ์ด์ค = MPR ์ต์ , X/Y๋ ์ ์ฒด arr = store.require_dataset( 'data', shape=data.shape, chunks=(1, data.shape[1], data.shape[2]), # 1รHรW dtype=data.dtype, compressor=zarr.Blosc(cname='zstd', clevel=3, shuffle=zarr.Blosc.BITSHUFFLE) ) arr[:] = data # NIfTI ํค๋๋ฅผ zarr.json ์์ฑ์ ์ ์ฅ store['data'].attrs['nifti_header'] = { 'pixdim': img.header['pixdim'].tolist(), 'sform_code': int(img.header['sform_code']), 'srow_x': img.header['srow_x'].tolist(), 'srow_y': img.header['srow_y'].tolist(), 'srow_z': img.header['srow_z'].tolist(), }
๊ฒฐ๋ก โ ์ธ์ .nii.gz๋ฅผ, ์ธ์ Zarr๋ฅผ ์ ํํด์ผ ํ๋
| ์๋๋ฆฌ์ค | .nii.gz | Zarr |
|---|---|---|
| AI ํ์ต ๋ฐ์ดํฐ ์ฒ๋ฆฌ (์์ฐจ ์ฝ๊ธฐ) | ์ ํฉ | ์ค๋ฒํค๋ |
| ๋ก์ปฌ ๋ถ์ ๋๊ตฌ (FSL, FreeSurfer) | ์๋ฒฝ ํธํ | ๋ณ๋ ๋ณํ ํ์ |
| ์น ๋ทฐ์ด ๋์ฉ๋ ๋ณผ๋ฅจ ์๋น | ์ด๊ธฐ ๋ก๋ฉ ๋๋ฆผ | ์ฒญํฌ ๋จ์ ์ฆ์ ํ์ |
| ํด๋ผ์ฐ๋(S3) ์ ์ฅ & ์๋น | Range Request ํ์ | ์ ์ HTTP ๋ฐ๋ก ๊ฐ๋ฅ |
| ๋ฉํฐํด์๋ ์ ์ง์ ๋ ๋๋ง | ๋ถ๊ฐ | ํผ๋ผ๋ฏธ๋ ๋ด์ฅ |
| ์์ฉ๋ ๋ณผ๋ฅจ (512ร512ร50 ์ดํ) | ๊ฐ๋จํ๊ณ ์ถฉ๋ถ | ์ ํ ๋น์ฉ ๋๋น ์ด๋ ์ ์ |
| 1024ร1024+ ๊ณ ํด์๋ ๋ณผ๋ฅจ | gzip ํด์ ์๋ถ ์์ | ํ์ ์ฒญํฌ๋ง ์ ์ด |
| ์ค์๊ฐ ํ์ / ๋ณ๋ ฌ ์ ๊ทผ | ๋จ์ผ ์คํธ๋ฆผ | ์ฒญํฌ ๋ณ๋ ฌ ์ฒ๋ฆฌ |