주요 콘텐츠

사전 훈련된 3차원 U-Net 신경망을 사용한 뇌 MRI 분할

이 예제에서는 심층 신경망을 사용하여 뇌 MRI를 분할하는 방법을 보여줍니다.

뇌 스캔 영상의 분할을 사용하여 각각의 뇌 구조를 시각화할 수 있습니다. 뇌 분할은 건강한 개체군과 질병 개체군의 특징을 파악하기 위해 정량적 볼륨 분석과 형태 분석에도 일반적으로 사용됩니다. 임상 전문가의 수동 분할은 분할에 있어 최고의 표준으로 간주됩니다. 그러나 그 과정은 아주 오랜 시간이 소요되어 대규모 데이터 세트에 레이블을 지정하는 데는 실용적이지 않습니다. 또한 레이블 지정에는 신경 해부학에 대한 전문 지식이 필요하고, 평가자 간 재현성과 평가자 내 재현성에서 오차가 발생하기 쉽고 한계가 있습니다. 컨벌루션 신경망과 같은 훈련된 분할 알고리즘은 대규모 임상 데이터 세트의 레이블 지정을 자동화할 수 있는 가능성을 지녔습니다.

이 예제에서는 뇌 MRI 분할을 위해 3차원 U-Net인 사전 훈련된 SynthSeg 신경망[1]을 사용합니다. SynthSeg는 재훈련이나 미세 조정 없이 모든 대비와 해상도의 뇌 스캔 영상을 분할하는 데 사용할 수 있습니다. SynthSeg는 젊고 건강한 피험자부터 고령의 질병 있는 피험자까지 다양한 피험자 개체군에 견고하게 사용할 수 있으며, 바이어스 필드 보정, 두개골 박리, 명암 정규화, 템플릿 정합을 포함한 전처리 유무에 상관없이 백질 병변 같은 다양한 스캔 조건에도 안정되게 사용할 수 있습니다.

Comparison of Predicted Segmentation Map and Ground Truth Segmentation Map

뇌 MRI 및 레이블 데이터 다운로드하기

이 예제에서는 CANDI 데이터 세트[2] [3]의 서브셋을 사용합니다. 서브셋은 한 환자의 뇌 MRI 볼륨과 그에 대응하는 ground truth 레이블 볼륨으로 구성됩니다. 두 파일 모두 NIfTI 파일 형식입니다. 데이터 파일의 총 크기는 최대 2.5MB입니다.

아래 코드를 실행하여 MathWorks® 웹사이트에서 데이터셋을 다운로드하고 다운로드한 폴더의 압축을 풉니다.

zipFile = matlab.internal.examples.downloadSupportFile("image","data/brainSegData.zip");
filepath = fileparts(zipFile);
unzip(zipFile,filepath)

dataDir 폴더에 다운로드하고 압축을 푼 데이터셋이 포함되어 있습니다.

dataDir = fullfile(filepath,"brainSegData");

사전 훈련된 신경망 다운로드 및 불러오기

downloadTrainedNetwork 헬퍼 함수를 사용하여 사전 훈련된 신경망을 다운로드합니다. 헬퍼 함수는 이 예제에 지원 파일로 첨부되어 있습니다.

trainedBrainCANDINetwork_url = "https://www.mathworks.com/supportfiles/"+ ...
    "image/data/trainedSynthSegModel.zip";
downloadTrainedNetwork(trainedBrainCANDINetwork_url,dataDir)

importNetworkFromTensorFlow (Deep Learning Toolbox) 함수를 사용하여 사전 훈련된 신경망을 불러옵니다. importNetworkFromTensorFlow 함수를 사용하려면 Deep Learning Toolbox™ Converter for TensorFlow Models 지원 패키지가 필요합니다. 이 지원 패키지가 설치되어 있지 않으면 함수에서 다운로드 링크를 제공합니다.

net = importNetworkFromTensorFlow(fullfile(dataDir,"trainedSynthSegModel"))
Importing the saved model...
Translating the model, this may take a few minutes...
Finished translation. Assembling network...
Import finished.
net = 
  dlnetwork with properties:

         Layers: [60×1 nnet.cnn.layer.Layer]
    Connections: [63×2 table]
     Learnables: [56×3 table]
          State: [18×3 table]
     InputNames: {'unet_input'}
    OutputNames: {'unet_prediction'}
    Initialized: 1

  View summary with summary.

테스트 데이터 불러오기

niftiinfo 함수를 사용하여 뇌 MRI 볼륨에서 메타데이터를 읽어 들입니다. niftiread 함수를 사용하여 뇌 MRI 볼륨을 읽어 들입니다.

imFile = fullfile(dataDir,"anat.nii.gz");
metaData = niftiinfo(imFile);
vol = niftiread(metaData);

이 예제에서는 뇌를 해부학적 구조에 따라 32개의 클래스로 분할합니다. getBrainCANDISegmentationLabels 헬퍼 함수를 사용하여 각 클래스 레이블의 이름과 숫자 식별자를 읽어 들입니다. 헬퍼 함수는 이 예제에 지원 파일로 첨부되어 있습니다.

labelDirs = fullfile(dataDir,"groundTruth");
[classNames,labelIDs] = getBrainCANDISegmentationLabels;

테스트 데이터 전처리하기

preProcessBrainCANDIData 헬퍼 함수를 사용하여 MRI 볼륨을 전처리합니다. 헬퍼 함수는 이 예제에 지원 파일로 첨부되어 있습니다. 헬퍼 함수는 다음 단계를 수행합니다.

  • 리샘플링 — resampletrue이면 데이터를 등방성 복셀 크기 1×1×1mm로 리샘플링합니다. 기본적으로 resample은 false이며 함수는 리샘플링을 수행하지 않습니다. 사전 훈련된 신경망을 서로 다른 복셀 크기의 영상에 대해 테스트하려면 입력값이 등방성이 아닌 경우 resampletrue로 설정하십시오.

  • 정렬 — 볼륨을 표준화된 RAS 방향으로 회전합니다.

  • 자르기 — 볼륨을 각 차원에서 최대 크기 192 복셀로 자릅니다.

  • 정규화 — 볼륨의 명암 값을 [0, 1] 범위의 값으로 정규화합니다. 그러면 대비가 향상됩니다.

resample = false;
cropSize = 192;
[volProc,cropIdx,imSize] = preProcessBrainCANDIData(vol,metaData,cropSize,resample);
inputSize = size(volProc);

전처리된 MRI 볼륨을 dlarray (Deep Learning Toolbox)를 사용하여 SSSCB(공간, 공간, 공간, 채널, 배치) 형식으로 형식 지정된 딥러닝 배열로 변환합니다.

volDL = dlarray(volProc,"SSSCB");

테스트 데이터를 사용하여 예측하기

신경망 출력값 예측하기

전처리된 MRI 볼륨에 대한 분할 출력값을 예측합니다. 분할 출력값 predictIm에는 "background", "leftCerebralCortex", "rightThalamus"와 같은 분할 레이블 클래스에 대응하는 32개 채널이 포함되어 있습니다. predictIm 출력값이 모든 클래스의 각 복셀에 신뢰 점수를 할당합니다. 신뢰 점수는 복셀이 대응하는 클래스에 속할 가능성을 나타냅니다. 이 예측은 각 복셀을 정확히 하나의 클래스에 할당하는 최종 의미론적 분할 출력값과 다릅니다.

predictIm = predict(net,volDL);

테스트 타임 증강

이 예제에서는 테스트 타임 증강을 사용하여 분할 정확도를 향상시킵니다. 일반적으로, 증강은 영상에 무작위 변환을 적용하여 데이터 세트의 가변성을 높입니다. 신경망 훈련 전에 증강을 사용하여 훈련 데이터 세트의 크기를 늘릴 수 있습니다. 테스트 타임 증강은 테스트 영상에 무작위 변환을 적용하여 여러 버전의 테스트 영상을 만듭니다. 그러면 각 버전의 테스트 영상을 예측을 위해 신경망에 전달할 수 있습니다. 신경망은 전체 분할 결과를 계산하여 테스트 영상의 모든 버전에 대한 평균 예측값을 구합니다. 테스트 타임 증강은 개별 신경망 예측에서 발생하는 랜덤 오차의 평균을 구하여 분할 정확도를 높입니다.

기본적으로 이 예제에서는 MRI 볼륨을 좌우 방향으로 뒤집으며, 그 결과로 뒤집힌 볼륨 flippedData가 생성됩니다. 뒤집힌 볼륨에 대한 신경망 출력값은 flipPredictIm입니다. 테스트 타임 증강을 건너뛰고 예측 속도를 높이려면 flipValfalse로 설정합니다.

flipVal = true;
if flipVal
    flippedData = fliplr(volProc);  
    flippedData = flip(flippedData,2);
    flippedData = flip(flippedData,1);
    flippedData = dlarray(flippedData,"SSSCB");
    flipPredictIm = predict(net,flippedData);
else
    flipPredictIm = [];  
end

후처리 분할 예측

최종 분할 맵을 얻기 위해 postProcessBrainCANDIData 헬퍼 함수를 사용하여 신경망 출력값을 후처리합니다. 헬퍼 함수는 이 예제에 지원 파일로 첨부되어 있습니다. postProcessBrainCANDIData 함수는 다음 단계를 수행합니다.

  • 평활화 — 예측된 분할 마스크의 잡음을 줄이기 위해 3차원 가우스 평활화 필터를 적용합니다.

  • 모폴로지 필터링 — 예측된 분할 마스크에서 가장 큰 연결성분만 유지하여 추가 잡음을 제거합니다.

  • 분할 — 각 복셀을 해당 복셀에 대한 신뢰 점수가 가장 높은 레이블 클래스에 할당합니다.

  • 크기 조정 — 분할 맵의 크기를 원래 입력 볼륨 크기로 조정합니다. 레이블 영상의 크기를 조정하면 레이블을 회색조 MRI 볼륨에 겹쳐지도록 시각화할 수 있습니다.

  • 정렬 — 분할 맵을 원래 입력 MRI 볼륨의 방향으로 다시 회전합니다.

최종 분할 결과 predictedSegMaps는 원래 입력 볼륨과 크기가 같은 3차원 categorical형 배열입니다. 각 요소는 하나의 복셀에 대응하며 categorical형 레이블을 한 개 가집니다.

predictedSegMaps = postProcessBrainCANDIData(predictIm,flipPredictIm,imSize, ...
    cropIdx,metaData,classNames,labelIDs);

예측된 분할 맵의 슬라이스를 labeloverlay 함수를 사용하여 입력 볼륨에서 그에 대응하는 슬라이스에 겹쳐 놓습니다. background 레이블을 제외한 모든 뇌 구조 레이블을 포함시킵니다.

sliceIdx = 80;
testSlice = rescale(vol(:,:,sliceIdx));
predSegMap = predictedSegMaps(:,:,sliceIdx);
B = labeloverlay(testSlice,predSegMap,"IncludedLabels",2:32);
figure
montage({testSlice,B})

분할 정확도 수량화하기

예측된 분할 레이블을 임상 전문가가 그린 ground truth 레이블과 비교하여 분할 정확도를 측정합니다.

레이블을 저장할 pixelLabelDatastore (Computer Vision Toolbox)를 만듭니다. NIfTI 파일 형식이 비표준 영상 형식이므로, NIfTI 파일 읽기 프로그램을 사용하여 픽셀 레이블 데이터를 읽어 들여야 합니다. 이 예제의 아래쪽에 정의된 헬퍼 NIfTI 파일 읽기 프로그램인 niftiReader를 사용할 수 있습니다.

pxds = pixelLabelDatastore(labelDirs,classNames,labelIDs,FileExtensions=".gz",...
    ReadFcn=@(X)uint8(niftiread(X)));

픽셀 레이블 데이터저장소에서 ground truth 레이블을 읽어 들입니다.

groundTruthLabel = read(pxds);
groundTruthLabel = groundTruthLabel{1};

dice 함수를 사용하여 분할 정확도를 측정합니다. 이 함수는 예측된 분할과 ground truth 분할 간의 다이스 지수를 계산합니다.

diceResult = zeros(length(classNames),1);
for j = 1:length(classNames)
    diceResult(j)= dice(groundTruthLabel==classNames(j),...
        predictedSegMaps==classNames(j));
end

MRI 볼륨의 모든 레이블에 대해 평균 다이스 지수를 계산합니다.

meanDiceScore = mean(diceResult);
disp("Average Dice score across all labels = " +num2str(meanDiceScore))
Average Dice score across all labels = 0.7579

모든 레이블 클래스에 대한 다이스 지수 통계량을 상자 차트로 시각화합니다. 플롯의 가운데 파란색 선이 다이스 지수의 중앙값입니다. 파란색 상자의 상한과 하한은 각각 25번째 백분위수와 75번째 백분위수를 나타냅니다. 검은색 수염은 가장 극단에 있는 데이터 점 중에서 이상값이 아닌 데이터 점까지 연장됩니다.

figure
boxchart(diceResult)
title("Dice Accuracy")
xticklabels("All Label Classes")
ylabel("Dice Coefficient")

참고 문헌

[1] Billot, Benjamin, Douglas N. Greve, Oula Puonti, Axel Thielscher, Koen Van Leemput, Bruce Fischl, Adrian V. Dalca, and Juan Eugenio Iglesias. “SynthSeg: Domain Randomisation for Segmentation of Brain Scans of Any Contrast and Resolution.” ArXiv:2107.09559 [Cs, Eess], December 21, 2021. https://arxiv.org/abs/2107.09559.

[2] “NITRC: CANDI Share: Schizophrenia Bulletin 2008: Tool/Resource Info.” Accessed October 17, 2022. https://www.nitrc.org/projects/cs_schizbull08/.

[3] Frazier, J. A., S. M. Hodge, J. L. Breeze, A. J. Giuliano, J. E. Terry, C. M. Moore, D. N. Kennedy, et al. “Diagnostic and Sex Effects on Limbic Volumes in Early-Onset Bipolar Disorder and Schizophrenia.” Schizophrenia Bulletin 34, no. 1 (October 27, 2007): 37–46. https://doi.org/10.1093/schbul/sbm120.

참고 항목

| (Deep Learning Toolbox) | (Deep Learning Toolbox) | (Computer Vision Toolbox) | |

도움말 항목