배움 기록/Deep Learning

[MONAI, PyTorch] MONAI를 이용해 데이터 전처리 하기

Spezi 2023. 1. 12. 03:18
반응형

이번에 인턴십 중에 MONAI를 이용한 프로젝트를 하게 되었다. 내용을 공부할겸 여기에 정리를 해본다.

 

MONAI

MONAI(Medical Open Network for AI) 는 엔비디아가 만든 헬스케어용 오픈소스 프레임워크(파이토치 기반)다.


UNETR 이란?

이전 글에서 간략하게 다룬적이 있다.

2023.01.11 - [Programming] - [MONAI] UNETR 이란? (feat. Vision Transformers)

 

[MONAI] UNETR 이란? (feat. Vision Transformers)

이 설명은 당장 UNETR를 써야하는데 빨리 뭔지 대충 알고 싶은 경우만 살짝 도움이 될뿐 자세한 내용은 아래의 논문에서 확인가능 https://arxiv.org/abs/2103.10504 Background 한 줄 정리 FCNN(Fully Convolutional N

jedemanfangwohnteinzauberinne.tistory.com

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단계로 볼수 있다.

  1. 이미지 로드
  2. 원하는 transform들을 한다
  3. 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에 대한 좀 더 자세한 이야기는 이곳에서..

2023.07.06 - [배움 기록/Deep Learning] - [Segmentation, MONAI] CUDA out of memory 와의 전쟁 -2. Crop 을 통해 이미지 크기 줄이

 

[Segmentation, MONAI] CUDA out of memory 와의 전쟁 -2. Crop 을 통해 이미지 크기 줄이

2023.07.06 - [배움 기록/Deep Learning] - [Segmentation, MONAI] CUDA out of memory 와의 전쟁 -1. Spacingd를 통한 해상도 조정 [Segmentation, MONAI] CUDA out of memory 와의 전쟁 -1. Spacingd를 통한 해상도 조정 요즘 segmentaion을

jedemanfangwohnteinzauberinne.tistory.com

 

 

 


배움을 기록하기 위한 공간입니다. 

수정이 필요한 내용이나 공유하고 싶은 것이 있다면 언제든 댓글로 남겨주시면 환영입니다 :D

반응형