안녕하세요.

이번 글에서는 Segmentation을 하기 위해 데이터셋을 어떻게 세팅해놓는지에 대해 설명하려고 합니다.

 

코드아래 사이트기반으로 수정하였으니 아래 영상을 먼저 참고하시면 글을 이해하시는데 도움이 될 것으로 생각됩니다.

 

https://www.youtube.com/watch?v=fWmRYmjF-Xw 

 

 

 

 

 

1. 데이터 다운받기 (Feat. ISBI 2012 EM segmentation Challenge)

보통 segmentation을 하기 위한 public 데이터들은 Kaggle, MICCAI 같은 곳에서 열리는 segmentation challenge에서 구할 수 있습니다. 또는 과거에 진행되었거나 현재에 진행되는 다른 segmentation challenge에서도 구할 수 있습니다.

 

보통 유명한 데이터셋들 중에 크기가 작은 데이터들은 github에 올려놓는 경우도 있습니다.

 

이번 글에서는 "ISBI 2012 EM segmentation Challenge"에서 사용되었던 membrane 데이터셋github에서 다운받아 사용해보려고 합니다.

 

방법은 간단합니다.

먼저 "ISBI 2012 github", "ISBI 2012 segmentation" 등의 키워드구글검색하시면 다양한 github 사이트가 노출이 될 겁니다.

 

제가 찾은 사이트는 아래 사이트인데, 먼저 아래 사이트접속해보도록 하겠습니다.

https://github.com/alexklibisz/isbi-2012

 

GitHub - alexklibisz/isbi-2012: Image Segmentation Techniques on the ISBI 2012 dataset: http://brainiac2.mit.edu/isbi_challenge/

Image Segmentation Techniques on the ISBI 2012 dataset: http://brainiac2.mit.edu/isbi_challenge/ - GitHub - alexklibisz/isbi-2012: Image Segmentation Techniques on the ISBI 2012 dataset: http://bra...

github.com

 

 

 

해당 사이트에 접속하시면 data 폴더"ISBI 2012 EM" 데이터들이 들어 있다는걸 확인할 수 있습니다.

아래 "그림1"처럼 github repository다운 받습니다 ("우측 상단Code를 누른 후, Download ZIP을 클릭해 주세요") 

그림1

 

 

다운받은 데이터는 아래 그림과 같이 되어 있습니다.

그림2

 

 

 

해당 데이터에 데한 정보는 다음과 같습니다.

  • train-volume.tif : input image data for training
  • train-label.tif : label image data for training
  • test-volume.tif : test image data

그림3

 

위의 세 가지 데이터 모두 512×512×30으로 설정되어 있습니다.

여기서 "30"이 의미하는 것은 무엇일까요?

 

해당 이미지 속성을 보면 slices=30이라는 부분이 눈에 띕니다. 

 

그림4

 

 

즉, "train-volume.tif" 이미지 파일은 30개별도이미지를 포함하고 있다는 뜻인데, 이것을 그림으로 표현하면 아래와 같습니다.

그림5

 

 

 

 

2. 개별 이미지 추출하기

 

앞서 다운받은 이미지 형식이 무엇을 의미하는지 설명했습니다.

그럼 이제부터 "512×512×30" 형식의 이미지에서 개별 이미지들을 따로 추출해보겠습니다.

 

먼저, UNet_segmentation이라는 폴더를 만든 후, 이전에 다운 받은 data 폴더를 복사, 붙여놓기 합니다.

그리고, 개별 이미지들을 저장시킬 디렉터리 ('test', 'train', 'val') 폴더 만들어 줍니다.

 

 

그림6

 

추가적으로 각각의 폴더 ('train', 'val', 'test')input, label 데이터를 저장시킬 별도의 폴더를 아래와 같이 만들어 줍니다.

그림7

 

 

 

 

Visual Studio에서 위의 개별 이미지들을 나누는 코드작성해보도록 하겠습니다.

그림8

 

 

 

우선 전체 코드를 보여드리면 아래와 같습니다.

## 필요한 패키지 등록
import os
import numpy as np
from PIL import Image
import matplotlib.pyplot as plt

## 1) 데이터 불러오기
dir_data = './data'

name_label = 'train-labels.tif'
name_input = 'train-volume.tif'

img_label = Image.open(os.path.join(dir_data, name_label))
img_input = Image.open(os.path.join(dir_data, name_input))

ny, nx = img_label.size
nframe = img_label.n_frames

## 2) training, validation, test 이미지 데이터 개수 정해주기
nframe_train = 24
nframe_val = 3
nframe_test = 3

dir_save_train_input = os.path.join(dir_data, 'train/input')
dir_save_train_label = os.path.join(dir_data, 'train/label')
dir_save_val_input = os.path.join(dir_data, 'val/input')
dir_save_val_label = os.path.join(dir_data, 'val/label')

dir_save_test_input = os.path.join(dir_data, 'test/input')
dir_save_test_label = os.path.join(dir_data, 'test/label')

## 3) 512x512x30에서 선별할 frame shuffle해서 추출하기 
id_frame = np.arange(nframe)
np.random.shuffle(id_frame) #사실 shuffle은 굳이 안해줘도 됨

## 4) 24개의 training data (input, label)를 추출하기
offset_nframe = 0

for i in range(nframe_train):
    img_label.seek(id_frame[i + offset_nframe])
    img_input.seek(id_frame[i + offset_nframe])

    # img_label.save(os.path.join(dir_save_train, 'label_%03d.tif' % i))
    # img_input.save(os.path.join(dir_save_train, 'input_%03d.tif' % i))

    label_ = np.asarray(img_label)
    input_ = np.asarray(img_input)

    np.save(os.path.join(dir_save_train_label, 'label_%03d.npy' % i), label_)
    np.save(os.path.join(dir_save_train_input, 'input_%03d.npy' % i), input_)

## 5) 3개의 validation data (input, label)를 추출하기
offset_nframe = nframe_train

for i in range(nframe_val):
    img_label.seek(id_frame[i + offset_nframe])
    img_input.seek(id_frame[i + offset_nframe])

    # img_label.save(os.path.join(dir_save_val, 'label_%03d.tif' % i))
    # img_input.save(os.path.join(dir_save_val, 'input_%03d.tif' % i))

    label_ = np.asarray(img_label)
    input_ = np.asarray(img_input)

    np.save(os.path.join(dir_save_val_label, 'label_%03d.npy' % i), label_)
    np.save(os.path.join(dir_save_val_input, 'input_%03d.npy' % i), input_)

## 6) 3개의 test data (input, label)를 추출하기
offset_nframe = nframe_train + nframe_val

for i in range(nframe_test):
    img_label.seek(id_frame[i + offset_nframe])
    img_input.seek(id_frame[i + offset_nframe])

    # img_label.save(os.path.join(dir_save_test, 'label_%03d.tif' % i))
    # img_input.save(os.path.join(dir_save_test, 'input_%03d.tif' % i))

    label_ = np.asarray(img_label)
    input_ = np.asarray(img_input)

    np.save(os.path.join(dir_save_test_label, 'label_%03d.npy' % i), label_)
    np.save(os.path.join(dir_save_test_input, 'input_%03d.npy' % i), input_)

 

 

 

2-1) Image 모듈

사실 위에 있는 코드를 따로 설명한다기보다 Image 모듈attribute, function 들을 소개하는 것이 더 좋을 것 같아 아래 링크를 첨부하도록 하겠습니다.

 

https://89douner.tistory.com/310

 

2. Image 모듈 (Image.open(), Image.seek())

안녕하세요. 이번 글에서는 Pillow 패키지의 가장 기본이 되는 모듈인 Image 모듈에 대해서 설명하려고 합니다. 1. Image 모듈이란? Image 모듈은 기본적으로 이미지 파일을 로드하거나 새로운 이미지

89douner.tistory.com

https://89douner.tistory.com/309?category=1002521 

 

1.Pillow 패키지란 무엇인가요?

안녕하세요. 이번 글에서는 Pillow라는 파이썬 패키지에 대해서 소개해드리려고 합니다. 1. Pillow 패키지란? Pillow 패키지를 설명하기 전에 PIL 패키지에 대해 간단히 설명하겠습니다. PIL는 Python Imagi

89douner.tistory.com

 

 

2-2) training dataset

위의 코드에서 한 가지 부분만 말씀드리면 "train-volume.tif" 데이터에는 30개별도 이미지가 있는데, 이중에서 24개 training dataset, 3개validatin dataset, 3개test dataset으로 이용한다는 점입니다.

 

 

 

 

2-3) numpy 형태로 저장하는 이유

위의 코드에서 개별 이미지numpy 형태저장하는 것을 볼 수 있습니다.

numpy 형태로 저장하는 이유는 현재 이미지 shape(512, 512) 이기 때문입니다.

 

    label_ = np.asarray(img_label)
    input_ = np.asarray(img_input)

    np.save(os.path.join(dir_save_train, 'label_%03d.npy' % i), label_)
    np.save(os.path.join(dir_save_train, 'input_%03d.npy' % i), input_)

 

(아래와 같이 "a=label_shape" 코드를 추가한 후,  살펴 보면 (512,512) 형태임을 알 수 있습니다) 

 

 

위의 이미지 형태에서 빠진 것 중 하나가 channel 정보입니다.

Pytorch의 딥러닝 모델학습시키기 위해서는 입력 데이터의 가로, 세로 길이와 channel (1=gray, 3=rgb) 정보가 들어있어야 합니다.

 

하지만 위의 shape을 통해 확인한 결과 현재"(H,W)=(이미지 높이, 이미지 너비)" 구조입니다.

그래서 이러한 구조를  "(H,W,C) = (이미지 높이, 이미지 너비, 이미지 채널)" 만들어 주어야 합니다.

 

numpy 형식을 이용하면 현재 "(H,W)"형태의 구조에서 축 하나를 쉽게 늘려 "(H,W,C)" 형태만들 수 있게 됩니다.

 

 

위의 코드 중에 아래와 같이 주석처리된 부분이 있는데, 만약 본인이 numpy가 아닌 이미지 형식으로 저장시켜 보고 싶다면 아래 주석 부분을 제거해주시면 됩니다. (그럼 개별 이미지들이 tif 형식으로 저장되 직접 확인하실 수 있습니다)

    # img_label.save(os.path.join(dir_save_train, 'label_%03d.tif' % i))
    # img_input.save(os.path.join(dir_save_train, 'input_%03d.tif' % i))

 

 

 

※사실 R,G,B 이미지였다면 (512,512,3) 형태로 저장이 되었을 것이기 때문에, 따로 numpy로 저장할 필요가 없습니다. 하지만, 현재 다루고 있는 이미지가 Gray scale을 따른다면 numpy 형태로 저장할 필요가 있습니다. (사실, gray scale 이미지도 엄격하게 표현하려면 (512,512,1)로 표현되어야 하지만, 이미지에서는 channel에 해당하는 1 부분을 생략하고 (512,512)로 표현하는 경우가 있습니다.)

 

※이 글에서는 slice형태로 이미지가 묶여서 나오기 때문에 지금과 같이 별개의 이미지로 분리하는 작업을 거쳤지만, 만약 이미지 데이터들이 3차원의 별도의 이미지로 제공이 될 경우는 지금까지 설명했던 코드를 구현할 필요는 없습니다.

 

※하지만, 의료 영상에서 다루는 CXR, CT, MRI 이미지들은 대부분 Gray scale이 많기 때문에 medical 분야에서 인공지능을 하시는 분들은 위의 코드를 잘 숙지하고 있으시면 많은 도움이 될 거라 생각합니다.

 

 

감사합니다. 

 

+ Recent posts