딥러닝을 사용한 다중분광 영상의 의미론적 분할
이 예제에서는 U-Net을 사용하여 7가지 채널을 갖는 다중분광 영상에 대한 의미론적 분할을 수행하는 방법을 보여줍니다.
의미론적 분할은 영상의 각 픽셀에 클래스 레이블을 지정하는 작업을 수반합니다. 의미론적 분할의 응용 사례 중 하나가 삼림 파괴 추적입니다. 즉 시간의 경과에 따른 산림 피복(forest cover)의 변화를 추적하는 것입니다. 환경 보호 기관은 어떤 지역의 환경 및 생태 건강을 평가하고 정량화하기 위해 삼림 파괴를 추적합니다.
딥러닝 기반의 의미론적 분할을 통해 고해상도 항공 사진에서 식생 피복을 정확히 측정할 수 있습니다. 분류의 정확도를 높이기 위해 일부 데이터 세트는 각 픽셀에 대한 추가 정보를 제공해 주는 다중분광 영상을 포함하고 있습니다. 예를 들어, Hamlin Beach State Park 데이터 세트는 이 클래스를 더 확실하게 구별하도록 3개의 근적외선 채널로 컬러 영상을 보완합니다.
이 예제에서는 먼저 사전 훈련된 U-Net을 사용하여 의미론적 분할을 수행한 다음 분할 결과를 사용하여 식생 피복의 규모를 계산하는 방법을 보여줍니다. 그런 다음 원한다면 패치 기반 훈련 방법론을 사용하여 Hamlin Beach State Park 데이터 세트에 대해 U-Net 신경망을 훈련시킬 수 있습니다.
사전 훈련된 U-Net 다운로드하기
dataDir
을 훈련된 신경망 및 데이터 세트의 원하는 위치로 지정합니다.
dataDir = fullfile(tempdir,"rit18_data");
사전 훈련된 U-Net 신경망을 다운로드합니다.
trainedNet_url = "https://ssd.mathworks.com/supportfiles/"+ ... "vision/data/trainedMultispectralUnetModel_v2.zip"; downloadTrainedNetwork(trainedNet_url,dataDir); load(fullfile(dataDir,"trainedMultispectralUnetModel", ... "trainedMultispectralUnetModel_v2.mat"));
데이터 세트 다운로드하기
이 예제에서는 신경망 훈련에 고해상도 다중분광 데이터 세트를 사용합니다 [1]. 이 영상 세트는 뉴욕 햄린 비치 주립 공원 위에서 드론으로 촬영한 것입니다. 이 데이터에는 레이블이 지정된 훈련 세트, 검증 세트, 테스트 세트와 18가지 사물 클래스 레이블이 포함되어 있습니다. 데이터 파일의 크기는 3.0GB입니다.
downloadHamlinBeachMSIData
헬퍼 함수를 사용하여 MAT 파일 버전의 데이터 세트를 다운로드합니다. 이 함수는 예제에 지원 파일로 첨부되어 있습니다.
downloadHamlinBeachMSIData(dataDir);
데이터 세트를 불러옵니다.
load(fullfile(dataDir,"rit18_data.mat")); whos train_data val_data test_data
Name Size Bytes Class Attributes test_data 7x12446x7654 1333663576 uint16 train_data 7x9393x5642 741934284 uint16 val_data 7x8833x6918 855493716 uint16
다중분광 영상 데이터는 채널 수×너비×높이 배열로 구성되어 있습니다. 그러나 MATLAB®에서는 다중채널 영상이 너비×높이×채널 수 배열로 구성됩니다. 데이터의 형태를 변경하여 채널을 세 번째 차원으로 만들려면 permute
함수를 사용하십시오.
train_data = permute(train_data,[2 3 1]); val_data = permute(val_data,[2 3 1]); test_data = permute(test_data,[2 3 1]);
데이터가 올바른 구조인지 확인합니다.
whos train_data val_data test_data
Name Size Bytes Class Attributes test_data 12446x7654x7 1333663576 uint16 train_data 9393x5642x7 741934284 uint16 val_data 8833x6918x7 855493716 uint16
다중분광 데이터 시각화하기
각 스펙트럼 대역의 중심을 나노미터 단위로 표시합니다.
disp(band_centers)
490 550 680 720 800 900
이 데이터 세트에서 RGB 색 채널은 각각 3번째, 2번째, 1번째 영상 채널입니다. 훈련 영상, 검증 영상, 테스트 영상의 RGB 성분을 몽타주로 표시합니다. 영상이 화면에 더 밝게 표시되게 하려면 histeq
(Image Processing Toolbox) 함수를 사용하여 히스토그램을 평활화하십시오.
rgbTrain = histeq(train_data(:,:,[3 2 1])); rgbVal = histeq(val_data(:,:,[3 2 1])); rgbTest = histeq(test_data(:,:,[3 2 1])); montage({rgbTrain,rgbVal,rgbTest},BorderSize=10,BackgroundColor="white") title("RGB Component of Training, Validation, and Test Image (Left to Right)")
데이터의 4번째, 5번째, 6번째 채널은 근적외선 대역에 해당합니다. 훈련 영상에 대한 이러한 3개 채널의 히스토그램을 평활화한 다음 채널을 몽타주로 표시합니다. 채널은 각각의 열 신호에 따라 영상의 각기 다른 성분을 강조 표시합니다. 예를 들어, 나무는 나머지 두 적외선 채널보다 4번째 채널에서 더 어둡습니다.
ir4Train = histeq(train_data(:,:,4)); ir5Train = histeq(train_data(:,:,5)); ir6Train = histeq(train_data(:,:,6)); montage({ir4Train,ir5Train,ir6Train},BorderSize=10,BackgroundColor="white") title("Infrared Channels 4, 5, and 6 (Left to Right) of Training Image ")
데이터의 7번째 채널은 유효한 분할 지역을 나타내는 이진 마스크입니다. 훈련 영상, 검증 영상, 테스트 영상을 위한 마스크를 표시합니다.
maskTrain = train_data(:,:,7); maskVal = val_data(:,:,7); maskTest = test_data(:,:,7); montage({maskTrain,maskVal,maskTest},BorderSize=10,BackgroundColor="white") title("Mask of Training, Validation, and Test Image (Left to Right)")
Ground Truth 레이블 시각화하기
레이블이 지정된 영상에는 분할을 위한 ground truth 데이터가 들어 있으며, 각 픽셀에 18개 클래스 중 하나가 지정되어 있습니다. 클래스 및 그 ID의 목록을 얻습니다.
disp(classes)
0. Other Class/Image Border 1. Road Markings 2. Tree 3. Building 4. Vehicle (Car, Truck, or Bus) 5. Person 6. Lifeguard Chair 7. Picnic Table 8. Black Wood Panel 9. White Wood Panel 10. Orange Landing Pad 11. Water Buoy 12. Rocks 13. Other Vegetation 14. Grass 15. Sand 16. Water (Lake) 17. Water (Pond) 18. Asphalt (Parking Lot/Walkway)
이 예제의 목적은 영상을 식생과 비식생의 두 가지 클래스로 분할하는 것입니다. 목표 클래스 이름을 정의합니다.
classNames = ["NotVegetation" "Vegetation"];
18개의 원래 클래스를 훈련 데이터와 검증 데이터에 대한 두 개의 목표 클래스로 그룹화합니다. "Vegetation"은 클래스 ID 2, 13, 14를 갖는 원래 클래스 "Tree", "Other Vegetation", "Grass"를 조합한 것입니다. 클래스 ID가 0인 원래 클래스 "Other Class/Image Border"는 배경 클래스에 속합니다. 그 외 모든 원래 클래스는 목표 레이블 "NotVegetation"에 속합니다.
vegetationClassIDs = [2 13 14]; nonvegetationClassIDs = setdiff(1:length(classes),vegetationClassIDs); labelsTrain = zeros(size(train_labels),"uint8"); labelsTrain(ismember(train_labels,nonvegetationClassIDs)) = 1; labelsTrain(ismember(train_labels,vegetationClassIDs)) = 2; labelsVal = zeros(size(val_labels),"uint8"); labelsVal(ismember(val_labels,nonvegetationClassIDs)) = 1; labelsVal(ismember(val_labels,vegetationClassIDs)) = 2;
ground truth 검증 레이블을 PNG 파일로 저장합니다. 이 예제에서는 이 파일을 사용하여 정확도 메트릭을 계산합니다.
imwrite(labelsVal,"gtruth.png");
히스토그램을 평활화한 RGB 훈련 영상 위에 레이블을 겹쳐 놓습니다. 영상에 컬러바를 추가합니다.
cmap = [1 0 1;0 1 0]; B = labeloverlay(rgbTrain,labelsTrain,Transparency=0.8,Colormap=cmap); imshow(B,cmap) title("Training Labels") numClasses = numel(classNames); ticks = 1/(numClasses*2):1/numClasses:1; colorbar(TickLabels=cellstr(classNames), ... Ticks=ticks,TickLength=0,TickLabelInterpreter="none");
테스트 영상에 의미론적 분할 수행하기
영상의 크기로 인해 전체 영상을 한 번에 분할할 수 없습니다. 대신 블록 형식 영상 방식을 사용하여 영상을 분할합니다. 이 방식은 한 번에 하나의 데이터 블록을 불러와 처리하므로 매우 큰 파일로 스케일링할 수 있습니다.
blockedImage
(Image Processing Toolbox) 함수를 사용하여 테스트 데이터의 스펙트럼 채널을 6개 포함하는 블록 형식 영상을 만듭니다.
patchSize = [1024 1024]; bimTest = blockedImage(test_data(:,:,1:6),BlockSize=patchSize);
semanticseg
(Computer Vision Toolbox) 함수를 사용하여 데이터 블록을 분할합니다. apply
함수를 사용하여 블록 형식 영상의 모든 블록에 대해 sematicseg
함수를 호출합니다.
bimSeg = apply(bimTest,@(bs)semanticseg(bs.Data,net,Outputtype="uint8"), ... PadPartialBlocks=true,PadMethod=0);
gather
함수를 사용하여, 분할된 모든 블록을 작업 공간에 단일 영상으로 조합합니다.
segmentedImage = gather(bimSeg);
분할에서 유효한 부분만 추출하려면 분할된 영상에 테스트 데이터의 마스크 채널을 곱하십시오.
segmentedImage = segmentedImage .* uint8(maskTest~=0);
imshow(segmentedImage,[])
title("Segmented Image")
의미론적 분할의 출력에 잡음이 있습니다. 잡음 및 고립된 픽셀을 제거하려면 영상 후처리를 수행하십시오. medfilt2
(Image Processing Toolbox) 함수를 사용하여 분할에 섞여 있는 점잡음을 제거하십시오. 잡음을 제거한 분할된 영상을 표시합니다.
segmentedImage = medfilt2(segmentedImage,[7 7]);
imshow(segmentedImage,[]);
title("Segmented Image with Noise Removed")
히스토그램을 평활화한 RGB 검증 영상 위에 분할된 영상을 겹쳐 놓습니다.
B = labeloverlay(rgbTest,segmentedImage,Transparency=0.8,Colormap=cmap); imshow(B,cmap) title("Labeled Segmented Image") colorbar(TickLabels=cellstr(classNames), ... Ticks=ticks,TickLength=0,TickLabelInterpreter="none");
식생 피복의 규모 계산하기
의미론적 분할 결과를 활용하여 관련 생태학적 질문에 답할 수 있습니다. 예를 들어 '육지 영역의 몇 퍼센트가 식생으로 덮여 있습니까?'라는 질문에 답해야 한다고 가정합니다. 이 질문에 답하려면 분할된 테스트 영상에서 식생으로 레이블이 지정된 픽셀의 수를 확인해야 합니다. 또한 분할된 영상에 있는 0이 아닌 픽셀의 수를 세어 ROI에 있는 총 픽셀 수를 구합니다.
vegetationPixels = ismember(segmentedImage(:),vegetationClassIDs); numVegetationPixels = sum(vegetationPixels(:)); numROIPixels = nnz(segmentedImage);
식생 픽셀의 수를 ROI에 있는 픽셀 수로 나누어 식생 피복의 비율을 계산합니다.
percentVegetationCover = (numVegetationPixels/numROIPixels)*100; disp("The percentage of vegetation cover is "+percentVegetationCover+"%");
The percentage of vegetation cover is 60.7028%
이 예제의 나머지 부분에서는 Hamlin Beach 데이터 세트에서 U-Net을 훈련시키는 방법을 보여줍니다.
훈련용 블록 형식 영상 데이터저장소 만들기
블록 형식 영상 데이터저장소를 사용하여 훈련 데이터를 신경망에 입력합니다. 이 데이터저장소는 ground truth 영상이 있는 영상 데이터저장소와 픽셀 레이블 데이터가 있는 픽셀 레이블 데이터저장소로부터 대응하는 여러 패치를 추출합니다.
훈련 영상, 훈련 레이블 및 마스크를 블록 형식 영상으로 읽어 들입니다.
inputTileSize = [256 256]; bim = blockedImage(train_data(:,:,1:6),BlockSize=inputTileSize); bLabels = blockedImage(labelsTrain,BlockSize=inputTileSize); bmask = blockedImage(maskTrain,BlockSize=inputTileSize);
마스크와 겹치는 영상 데이터 블록을 선택합니다.
overlapPct = 0.185; blockOffsets = round(inputTileSize.*overlapPct); bls = selectBlockLocations(bLabels, ... BlockSize=inputTileSize,BlockOffsets=blockOffsets, ... Masks=bmask,InclusionThreshold=0.95);
레이블을 one-hot 형식으로 인코딩합니다.
labelsTrain1hot = onehotencode(labelsTrain,3,ClassNames=1:2); labelsTrain1hot(isnan(labelsTrain1hot)) = 0; bLabels = blockedImage(labelsTrain1hot,BlockSize=inputTileSize);
blockedImageDatastore
(Image Processing Toolbox) 함수를 사용하여 데이터를 블록 형식 영상 데이터저장소에 씁니다.
bimds = blockedImageDatastore(bim,BlockLocationSet=bls,PadMethod=0); bimdsLabels = blockedImageDatastore(bLabels,BlockLocationSet=bls,PadMethod=0);
두 개의 블록 형식 영상 데이터저장소에서 CombinedDatastore
를 만듭니다.
dsTrain = combine(bimds,bimdsLabels);
블록 형식 영상 데이터저장소 dsTrain
은 Epoch가 반복될 때마다 신경망으로 데이터의 미니 배치를 제공합니다. 데이터를 살펴보려면 데이터저장소를 미리 보십시오.
preview(dsTrain)
ans=1×2 cell array
{256×256×6 uint16} {256×256×2 double}
U-Net 신경망 만들기
이 예제에서는 변형된 U-Net 신경망을 사용합니다. U-Net에서는 초기 일련의 컨벌루션 계층의 사이에 일정 간격으로 최댓값 풀링 계층이 배치되어 입력 영상의 해상도를 연이어 낮춥니다. 그다음에 오는 일련의 컨벌루션 계층 사이에는 업샘플링 연산자가 여기저기 배치되어 입력 영상의 해상도를 연속적으로 높입니다 [2]. U-Net이라는 이름은 이 신경망을 문자 U와 같은 대칭 형태로 그릴 수 있다는 사실에서 유래했습니다.
U-Net의 하이퍼파라미터를 지정합니다. 입력 깊이는 초분광 채널 개수인 6입니다.
inputDepth = 6;
unet
(Computer Vision Toolbox) 함수를 사용하여 U-Net 신경망을 만듭니다.
unetNetwork = unet([inputTileSize inputDepth],numClasses);
손실 함수 정의하기
영상에서 마스크 처리되지 않은 모든 픽셀에 대한 교차 엔트로피 손실을 계산하는 lossFcn
이라는 사용자 지정 손실 함수를 정의합니다.
function loss = lossFcn(Y,T) mask = ~isnan(T); T(isnan(T)) = 0; loss = crossentropy(Y,T,Mask=mask); end
훈련 옵션 선택하기
SGDM(Stochastic Gradient Descent with Momentum: 모멘텀을 사용한 확률적 경사하강법)을 사용하여 신경망을 훈련시킵니다. trainingOptions
함수를 사용하여 SGDM의 하이퍼파라미터 설정을 지정합니다. 기울기 제한을 활성화하려면 GradientThreshold
이름-값 인수를 0.05
로 지정하고 GradientThresholdMethod
가 기울기의 L2-노름을 사용하도록 지정합니다.
maxEpochs = 70; minibatchSize = 16; options = trainingOptions("sgdm", ... InitialLearnRate=0.05, ... MaxEpochs=maxEpochs, ... MiniBatchSize=minibatchSize, ... L2Regularization=0.001, ... LearnRateDropPeriod=10, ... LearnRateSchedule="piecewise", ... GradientThreshold=0.05, ... Shuffle="every-epoch", ... VerboseFrequency=200);
신경망 훈련시키기
신경망을 훈련시키려면 다음 코드에서 doTraining
변수를 true
로 설정하십시오. trainnet
함수를 사용하여 모델을 훈련시킵니다. 손실을 사용자 지정 손실 함수 lossFcn
로 지정합니다. 기본적으로 trainnet
함수는 GPU를 사용할 수 있으면 GPU를 사용합니다. GPU에서 훈련시키려면 Parallel Computing Toolbox™ 라이선스와 지원되는 GPU 장치가 필요합니다. 지원되는 장치에 대한 자세한 내용은 GPU 연산 요구 사항 (Parallel Computing Toolbox) 항목을 참조하십시오. GPU를 사용할 수 없는 경우, trainnet
함수는 CPU를 사용합니다. 실행 환경을 지정하려면 ExecutionEnvironment
훈련 옵션을 사용하십시오.
doTraining = false; if doTraining net = trainnet(dsTrain,unetNetwork,@lossFcn,options); modelDateTime = string(datetime("now",Format="yyyy-MM-dd-HH-mm-ss")); save(fullfile(dataDir,"multispectralUnet-"+modelDateTime+".mat"),"net"); end
분할 정확도 평가하기
검증 데이터를 분할합니다.
blockedImage
(Image Processing Toolbox) 함수를 사용하여 검증 데이터의 스펙트럼 채널을 6개 포함하는 블록 형식 영상을 만듭니다.
bimVal = blockedImage(val_data(:,:,1:6),BlockSize=patchSize);
semanticseg
(Computer Vision Toolbox) 함수를 사용하여 데이터 블록을 분할합니다. apply
함수를 사용하여 블록 형식 영상의 모든 블록에 대해 sematicseg
함수를 호출합니다.
bimSeg = apply(bimVal,@(bs)semanticseg(bs.Data,net,Outputtype="uint8"),... PadPartialBlocks=true,PadMethod=0);
gather
함수를 사용하여, 분할된 모든 블록을 작업 공간에 단일 영상으로 조합합니다.
segmentedImage = gather(bimSeg);
분할된 영상을 PNG 파일로 저장합니다.
imwrite(segmentedImage,"results.png");
pixelLabelDatastore
(Computer Vision Toolbox) 함수를 사용하여 분할 결과와 ground truth 레이블을 불러옵니다.
pixelLabelIDs = [1 2]; pxdsResults = pixelLabelDatastore("results.png",classNames,pixelLabelIDs); pxdsTruth = pixelLabelDatastore("gtruth.png",classNames,pixelLabelIDs);
evaluateSemanticSegmentation
(Computer Vision Toolbox) 함수를 사용하여 의미론적 분할의 정확도를 측정합니다. 전역 정확도 점수에 따르면, 픽셀의 96% 이상이 올바르게 분류되었습니다.
ssm = evaluateSemanticSegmentation(pxdsResults,pxdsTruth);
Evaluating semantic segmentation results ---------------------------------------- * Selected metrics: global accuracy, class accuracy, IoU, weighted IoU, BF score. * Processed 1 images. * Finalizing... Done. * Data set metrics: GlobalAccuracy MeanAccuracy MeanIoU WeightedIoU MeanBFScore ______________ ____________ _______ ___________ ___________ 0.9694 0.9704 0.9406 0.94064 0.79474
참고 문헌
[1] Kemker, R., C. Salvaggio, C. Kanan. "High-Resolution Multispectral Dataset for Semantic Segmentation." CoRR, abs/1703.01918. 2017.
[2] Ronneberger, O., P. Fischer, T. Brox. "U-Net: Convolutional Networks for Biomedical Image Segmentation." CoRR, abs/1505.04597. 2015.
[3] Kemker, Ronald, Carl Salvaggio, and Christopher Kanan. "Algorithms for Semantic Segmentation of Multispectral Remote Sensing Imagery Using Deep Learning." ISPRS Journal of Photogrammetry and Remote Sensing, Deep Learning RS Data, 145 (November 1, 2018): 60-77. https://doi.org/10.1016/j.isprsjprs.2018.04.014.
참고 항목
trainnet
| trainingOptions
| unet
(Computer Vision Toolbox) | blockedImage
(Image Processing Toolbox) | apply
(Image Processing Toolbox) | selectBlockLocations
(Image Processing Toolbox) | blockedImageDatastore
(Image Processing Toolbox) | pixelLabelDatastore
(Computer Vision Toolbox) | semanticseg
(Computer Vision Toolbox) | evaluateSemanticSegmentation
(Computer Vision Toolbox)
관련 항목
- 딥러닝을 사용한 의미론적 분할 시작하기 (Computer Vision Toolbox)
- 딥러닝을 사용한 의미론적 분할
- Semantic Segmentation Using Dilated Convolutions
- Datastores for Deep Learning