이번에 인턴십 중에 MONAI를 이용한 프로젝트를 하게 되었다. 내용을 공부할겸 여기에 정리를 해본다.
MONAI
MONAI(Medical Open Network for AI) 는 엔비디아가 만든 헬스케어용 오픈소스 프레임워크(파이토치 기반)다.
UNETR 이란?
이전 글에서 간략하게 다룬적이 있다.
2023.01.11 - [Programming] - [MONAI] UNETR 이란? (feat. Vision Transformers)
UNETR은 U-Net 과 같이 encoder, decoder 형태로 되어있지만, encoder 에 transformer 구조를 사용한것이 차이점.
Preprocessing(전처리)를 위한 transforms
UNETR에 데이터를 넣기 전 필요한 전처리를 하는 방법이다.
transform을 적용하기 위해 먼저 필요한 transform 을 import한다.
참고 : 여기서 말하는 Image 란 multiple slices 의 volume을 의미함 (3D)
from monai.transforms import (
AddChanneld,
Compose,
CropForegroundd,
LoadImaged,
Orientationd,
RandFlipd,
RandCropByPosNegLabeld,
RandShiftIntensityd,
ScaleIntensityRanged,
Spacingd,
RandRotate90d,
ToTensord,
)
Transform의 과정은 크게 3단계로 볼수 있다.
- 이미지 로드
- 원하는 transform들을 한다
- torch sensor 로 convert 해주기
pix_dim = (1.0, 1.0, 1.0)
roi_size = (96, 96, 96)
transforms = Compose( # 적용하려는 transform을 combine 할 수 있음
[
LoadImaged(keys=["image", "label"]), # (nifty files) image 와 label을 load
AddChanneld(keys=["image", "label"]), # 예) tumor segmentation을 하면, tumor의 background 역할을 하는 additional channel이 필요
Orientationd(keys=["image", "label"], axcodes="RAS"), # RAS 는 3D orientation을 의미 (Left, Right), (Posterior, Anterior), (Inferior, Superior)
Spacingd( # voxel dimension(width, height, depth) 값을 변화시켜 generalize 함
keys=["image", "label"],
pixdim=pix_dim,
mode=("bilinear", "nearest"),
),
ScaleIntensityRanged( # image 에만 적용! label의 intensity 값은 안바꿔도 되니까
keys=["image"],
a_min=-175,
a_max=250,
b_min=0.0,
b_max=1.0,
clip=True,
),
CropForegroundd(keys=["image", "label"], source_key="image"), # forground object 를 crop하기
# default 값 select values > 0, 즉 (source_key="image") 이미지에서 값이 0 이상인 애들만 foreground로 간주하겠다는 것 같음
RandCropByPosNegLabeld( # fixed size의 random 한 region을 crop함
keys=["image", "label"],
label_key="label",
spatial_size=roi_size,
pos=1,
neg=1,
num_samples=4,
image_key="image",
image_threshold=0,
),
RandFlipd( # randomly flip, prob 의 확률로
keys=["image", "label"],
spatial_axis=[0],
prob=0.10,
),
RandFlipd(
keys=["image", "label"],
spatial_axis=[1],
prob=0.10,
),
RandFlipd(
keys=["image", "label"],
spatial_axis=[2],
prob=0.10,
),
RandRotate90d( # randomly 90도 rotate, prob의 확률로
keys=["image", "label"],
prob=0.10,
max_k=3,
),
RandShiftIntensityd( # randomly shift image
keys=["image"],
offsets=0.10,
prob=0.50,
),
ToTensord(keys=["image", "label"]), # to convert: transformed data -> tensor
]
)
모든 transform 뒤에 "d" 가 붙는 이유는 우리는 dictionary 값을 넣어주기 때문.
data = {"image": "imagepath.nii.gz",
"label": "labelpath.nii.gz"}
data = transforms(data)
data[0]["image"].shape
# 결과: torch.Size([1, 96, 96, 96])
torch.Size 를 보니 잘 transform 된것 같은데 직접 이미지로 확인하고 싶었음
plt.imshow(tf_data[0]["image"][0,:,:,50]) #background, w,h, slide
plt.imshow(tf_data[0]["label"][0,:,:,50])
확인해보니 원하는대로 잘 되었다. Original data 와도 비교해봤다.
궁금증
- RandCropByPosNegLabeld 를 왜 해줘야하는지 이해가 잘 안됨, crop을 하면 예를들어 심장의 어떤 한 part 만 보여주는데 이게 과연 좋은건지..? 그니까 crop의 필요성에 대해서 잘몰랐는데...
궁금증에 대한 답
- 요즘 대부분의 이미지가 high resolution이고 이를 hanlde하기 위해서는 전체 이미지보다는 crop한 이미지 patch를 사용하는게 좋음
- computational 효율도 당연히 patch를 사용할때가 좋고
- vision transformer를 사용하는 모델의 경우 input으로 patch를 필요로하기도 함
RandCropByPosNegLabeld에 대한 좀 더 자세한 이야기는 이곳에서..
배움을 기록하기 위한 공간입니다.
수정이 필요한 내용이나 공유하고 싶은 것이 있다면 언제든 댓글로 남겨주시면 환영입니다 :D
'배움 기록 > Deep Learning' 카테고리의 다른 글
[Segmentation, MONAI] CUDA out of memory 와의 전쟁 -2. Crop 을 통해 이미지 크기 줄이 (0) | 2023.07.07 |
---|---|
[Segmentation, MONAI] CUDA out of memory 와의 전쟁 -1. Spacingd를 통한 해상도 조정 (0) | 2023.07.07 |
[sklearn] 데이터 정규화 하는법 (feat. ChatGPT 를 처음 사용하다...) (0) | 2023.03.30 |
[PyTorch, MONAI] UNETR 모델 생성 및 Forward (0) | 2023.01.12 |
[MONAI] UNETR 이란? (feat. Vision Transformers) (0) | 2023.01.12 |