Main Content

딥러닝을 사용한 의미론적 분할

이 예제에서는 의미론적 분할 신경망을 사용하여 영상을 분할하는 방법을 보여줍니다.

의미론적 분할 신경망은 영상의 모든 픽셀을 분류하여 클래스별로 분할된 영상을 산출합니다. 의미론적 분할의 응용 분야에는 자율 주행을 위한 도로 분할, 의료 진단을 위한 암 세포 분할 등이 있습니다. 자세한 내용은 딥러닝을 사용한 의미론적 분할 시작하기 (Computer Vision Toolbox) 항목을 참조하십시오.

이 예제에서는 먼저 의미론적 영상 분할을 위해 설계된 컨벌루션 신경망(CNN)의 일종인 사전 훈련된 Deeplab v3+ [1] 신경망을 사용하여 영상을 분할하는 방법을 보여줍니다. 의미론적 분할을 위한 또 다른 유형의 신경망은 U-Net입니다. 그런 다음 선택적으로 데이터셋을 다운로드하고 전이 학습을 사용하여 Deeplab v3 신경망을 훈련시킬 수 있습니다. 여기에서 설명하는 훈련 과정은 다른 유형의 의미론적 분할 신경망에 적용할 수 있습니다.

훈련 과정을 설명하기 위해 이 예제에서는 University of Cambridge의 CamVid 데이터셋[2]을 사용합니다. 이 데이터셋은 주행하면서 얻은 거리 수준 뷰가 들어있는 영상 모음입니다. 이 데이터셋은 자동차, 보행자, 도로를 비롯한 32개의 의미론적 클래스에 대해 픽셀 수준의 레이블을 제공합니다.

이 예제를 실행할 때는 CUDA 지원 NVIDIA™ GPU를 사용하는 것이 권장됩니다. GPU를 사용하려면 Parallel Computing Toolbox™가 필요합니다. 지원되는 Compute Capability에 대한 자세한 내용은 GPU 연산 요구 사항 (Parallel Computing Toolbox) 항목을 참조하십시오.

사전 훈련된 의미론적 분할 신경망 다운로드하기

CamVid 데이터셋에 대해 훈련된 DeepLab v3+의 사전 훈련된 버전을 다운로드합니다.

pretrainedURL = "https://ssd.mathworks.com/supportfiles/vision/data/deeplabv3plusResnet18CamVid_v2.zip";
pretrainedFolder = fullfile(tempdir,"pretrainedNetwork");
pretrainedNetworkZip = fullfile(pretrainedFolder,"deeplabv3plusResnet18CamVid_v2.zip"); 
if ~exist(pretrainedNetworkZip,'file')
    mkdir(pretrainedFolder);
    disp("Downloading pretrained network (58 MB)...");
    websave(pretrainedNetworkZip,pretrainedURL);
end
Downloading pretrained network (58 MB)...
unzip(pretrainedNetworkZip, pretrainedFolder)

사전 훈련된 신경망을 불러옵니다.

pretrainedNetwork = fullfile(pretrainedFolder,"deeplabv3plusResnet18CamVid_v2.mat");  
data = load(pretrainedNetwork);
net = data.net;

이 신경망이 분류하도록 훈련된 클래스를 설정합니다.

classes = getClassNames()
classes = 11×1 string
    "Sky"
    "Building"
    "Pole"
    "Road"
    "Pavement"
    "Tree"
    "SignSymbol"
    "Fence"
    "Car"
    "Pedestrian"
    "Bicyclist"

의미론적 영상 분할 수행하기

신경망이 분류하도록 훈련된 클래스를 포함하는 영상을 읽어 들입니다.

I = imread("parkinglot_left.png");

영상의 크기를 신경망의 입력 크기에 맞게 조정합니다.

inputSize = net.Layers(1).InputSize;
I = imresize(I,inputSize(1:2));

semanticseg 함수와 사전 훈련된 신경망을 사용하여 의미론적 분할을 수행합니다.

C = semanticseg(I,net);

labeloverlay를 사용하여 분할 결과를 영상 위에 겹쳐 놓습니다. 오버레이 컬러맵을 CamVid 데이터셋[2]으로 정의된 컬러맵 값으로 설정합니다.

cmap = camvidColorMap;
B = labeloverlay(I,C,Colormap=cmap,Transparency=0.4);
figure
imshow(B)
pixelLabelColorbar(cmap, classes);

이 신경망은 시내 주행 영상에 대해 사전 훈련된 것이지만 주차장 장면에 대해서도 적절한 결과를 생성합니다. 분할 결과를 개선하려면 주차장 장면이 포함된 추가 영상으로 신경망을 다시 훈련시켜야 합니다. 이 예제의 나머지 부분에서는 전이 학습을 사용하여 의미론적 분할 신경망을 훈련시키는 방법을 보여줍니다.

의미론적 분할 신경망 훈련시키기

이 예제에서는 사전 훈련된 Resnet-18 신경망으로부터 초기화된 가중치를 사용하여 Deeplab v3+ 신경망을 훈련시킵니다. ResNet-18은 처리 리소스가 한정적인 응용 사례에 적합한 효율적인 신경망입니다. 응용 사례의 요구 사항에 따라 MobileNet v2나 ResNet-50과 같은 여타 사전 훈련된 신경망도 사용할 수 있습니다. 자세한 내용은 사전 훈련된 심층 신경망 항목을 참조하십시오.

imagePretrainedNetwork 함수를 사용하여 사전 훈련된 ResNet-18 신경망을 가져옵니다. ResNet-18을 사용하려면 Deep Learning Toolbox™ Model for ResNet-18 Network 지원 패키지가 필요합니다. 이 지원 패키지가 설치되어 있지 않으면 함수에서 다운로드 링크를 제공합니다.

imagePretrainedNetwork("resnet18")
ans = 
  dlnetwork with properties:

         Layers: [70×1 nnet.cnn.layer.Layer]
    Connections: [77×2 table]
     Learnables: [82×3 table]
          State: [40×3 table]
     InputNames: {'data'}
    OutputNames: {'prob'}
    Initialized: 1

  View summary with summary.

CamVid 데이터셋 다운로드하기

다음 URL에서 CamVid 데이터셋을 다운로드합니다.

imageURL = "http://web4.cs.ucl.ac.uk/staff/g.brostow/MotionSegRecData/files/701_StillsRaw_full.zip";
labelURL = "http://web4.cs.ucl.ac.uk/staff/g.brostow/MotionSegRecData/data/LabeledApproved_full.zip";
 
outputFolder = fullfile(tempdir,"CamVid"); 
labelsZip = fullfile(outputFolder,"labels.zip");
imagesZip = fullfile(outputFolder,"images.zip");

if ~exist(labelsZip, 'file') || ~exist(imagesZip,'file')   
    mkdir(outputFolder)
       
    disp("Downloading 16 MB CamVid dataset labels..."); 
    websave(labelsZip, labelURL);
    unzip(labelsZip, fullfile(outputFolder,"labels"));
    
    disp("Downloading 557 MB CamVid dataset images...");  
    websave(imagesZip, imageURL);       
    unzip(imagesZip, fullfile(outputFolder,"images"));    
end
Downloading 16 MB CamVid dataset labels...
Downloading 557 MB CamVid dataset images...

참고: 데이터 다운로드에 걸리는 시간은 인터넷 연결에 따라 달라집니다. 위에서 사용한 명령은 다운로드가 완료될 때까지 MATLAB을 차단합니다. 또는 웹 브라우저를 사용하여 먼저 로컬 디스크에 데이터셋을 다운로드할 수도 있습니다. 웹에서 다운로드한 파일을 사용하려면 위에서 outputFolder 변수를 다운로드한 파일의 위치로 변경하십시오.

CamVid 영상 불러오기

imageDatastore를 사용하여 CamVid 영상을 불러옵니다. imageDatastore를 사용하면 대규모의 영상 모음을 디스크에 효율적으로 불러올 수 있습니다.

imgDir = fullfile(outputFolder,"images","701_StillsRaw_full");
imds = imageDatastore(imgDir);

영상 하나를 표시합니다.

I = readimage(imds,559);
I = histeq(I);
imshow(I)

픽셀에 레이블이 지정된 CamVid 영상 불러오기

pixelLabelDatastore (Computer Vision Toolbox)를 사용하여 픽셀에 레이블이 지정된 CamVid 영상 데이터를 불러옵니다. pixelLabelDatastore는 픽셀 레이블 데이터와 레이블 ID를 클래스 이름 매핑으로 캡슐화합니다.

훈련을 보다 쉽게 하기 위해 CamVid에 포함된 32개의 원본 클래스를 11개의 클래스로 그룹화합니다. 32개의 클래스를 11개로 줄이기 위해 원본 데이터셋의 여러 클래스가 하나로 그룹화됩니다. 예를 들어, "Car", "SUVPickupTruck", "Truck_Bus", "Train", "OtherMoving"이 모두 "Car"로 그룹화됩니다. 그룹화된 레이블 ID를 지원 함수 camvidPixelLabelIDs(이 예제의 마지막에 나열되어 있음)를 사용하여 반환합니다.

labelIDs = camvidPixelLabelIDs();

클래스와 레이블 ID를 사용하여 pixelLabelDatastore.를 만듭니다.

labelDir = fullfile(outputFolder,"labels");
pxds = pixelLabelDatastore(labelDir,classes,labelIDs);

픽셀에 레이블이 지정된 영상 중 하나를 읽어 들이고 영상 위에 중첩하여 표시합니다. 색 중첩이 없는 영역은 픽셀 레이블을 갖지 않으며 훈련 중에 사용되지 않습니다.

C = readimage(pxds,559);
cmap = camvidColorMap;
B = labeloverlay(I,C,ColorMap=cmap);
imshow(B)
pixelLabelColorbar(cmap,classes);

데이터셋 통계량 분석하기

CamVid 데이터셋의 클래스 레이블의 분포를 보려면 countEachLabel (Computer Vision Toolbox)을 사용하십시오. 이 함수는 클래스 레이블을 기준으로 픽셀 개수를 셉니다.

tbl = countEachLabel(pxds)
tbl=11×3 table
         Name         PixelCount    ImagePixelCount
    ______________    __________    _______________

    {'Sky'       }    7.6801e+07      4.8315e+08   
    {'Building'  }    1.1737e+08      4.8315e+08   
    {'Pole'      }    4.7987e+06      4.8315e+08   
    {'Road'      }    1.4054e+08      4.8453e+08   
    {'Pavement'  }    3.3614e+07      4.7209e+08   
    {'Tree'      }    5.4259e+07       4.479e+08   
    {'SignSymbol'}    5.2242e+06      4.6863e+08   
    {'Fence'     }    6.9211e+06       2.516e+08   
    {'Car'       }    2.4437e+07      4.8315e+08   
    {'Pedestrian'}    3.4029e+06      4.4444e+08   
    {'Bicyclist' }    2.5912e+06      2.6196e+08   

클래스별 픽셀 개수를 시각화합니다.

frequency = tbl.PixelCount/sum(tbl.PixelCount);

bar(1:numel(classes),frequency)
xticks(1:numel(classes)) 
xticklabels(tbl.Name)
xtickangle(45)
ylabel("Frequency")

이상적인 경우라면 모든 클래스가 동일한 개수의 관측값을 갖습니다. 그러나 CamVid에 포함된 클래스 간에 균형이 맞지 않습니다. 이것은 거리 장면으로 구성된 자동차 데이터 세트에서 일반적으로 나타나는 문제입니다. 이러한 장면에서는 하늘과 건물, 도로가 영상에서 더 많은 영역을 차지하기 때문에 보행자, 자전거 운전자의 픽셀보다 하늘, 건물, 도로의 픽셀이 더 많습니다. 이러한 불균형을 올바르게 처리하지 않으면 비중이 더 큰 클래스로 인해 학습에 편향이 발생하여 학습을 저해할 수 있습니다. 이 예제의 뒷부분에서 클래스 가중치를 사용하여 이 문제를 처리합니다.

훈련 세트, 검증 세트와 테스트 세트 준비하기

Deeplab v3+는 데이터셋의 영상 중 60%를 사용하여 훈련되었습니다. 나머지 영상은 검증용과 테스트용으로 각각 20%와 20%로 균일하게 분할되었습니다. 다음 코드는 영상과 픽셀 레이블 데이터를 훈련 세트, 검증 세트 및 테스트 세트로 임의 분할합니다.

[imdsTrain, imdsVal, imdsTest, pxdsTrain, pxdsVal, pxdsTest] = partitionCamVidData(imds,pxds);

60/20/20 분할을 통해 다음과 같은 개수의 훈련 영상, 검증 영상 및 테스트 영상을 얻게 됩니다.

numTrainingImages = numel(imdsTrain.Files)
numTrainingImages = 421
numValImages = numel(imdsVal.Files)
numValImages = 140
numTestingImages = numel(imdsTest.Files)
numTestingImages = 140

검증 데이터를 정의합니다.

dsVal = combine(imdsVal,pxdsVal);

데이터 증대

데이터 증대는 훈련 중에 원본 데이터를 무작위로 변환함으로써 신경망 정확도를 개선하는 데 사용됩니다. 데이터 증대를 사용하면 레이블이 지정된 훈련 샘플의 개수를 늘리지 않고도 훈련 데이터에 다양성을 더할 수 있습니다. 영상과 픽셀 레이블 데이터 모두에 동일한 무작위 변환을 적용하려면 데이터저장소 combinetransform을 사용하십시오. 먼저, imdsTrainpxdsTrain을 결합합니다.

dsTrain = combine(imdsTrain,pxdsTrain);

다음으로, 데이터저장소 transform을 사용하여 지원 함수 augmentImageAndLabel에 정의된 원하는 데이터 증대를 적용합니다. 여기서는 데이터 증대를 위해 무작위 좌우 반사와 +/- 10개 픽셀의 X/Y 무작위 평행 이동을 사용합니다.

xTrans = [-10 10];
yTrans = [-10 10];
dsTrain = transform(dsTrain, @(data)augmentImageAndLabel(data,xTrans,yTrans));

테스트 데이터와 검증 데이터에는 데이터 증대가 적용되지 않는다는 것에 유의하십시오. 이상적인 경우라면 테스트 데이터와 검증 데이터가 원본 데이터를 대표해야 하므로 편향되지 않은 평가를 위해 수정되지 않은 상태로 남겨 두는 것이 좋습니다.

신경망 만들기

신경망 영상 크기를 지정합니다. 이 크기는 일반적으로 훈련 영상 크기와 동일합니다.

imageSize = [720 960 3];

클래스 개수를 지정합니다.

numClasses = numel(classes);

deeplabv3plus 함수를 사용하여 ResNet-18을 기반으로 하는 DeepLab v3+ 신경망을 만듭니다. 응용 사례에 가장 적합한 신경망을 선택하려면 경험적 분석이 필요하며 이것은 또 다른 수준의 하이퍼파라미터 조정이 됩니다. 예를 들어, ResNet-50이나 MobileNet v2와 같은 다른 기본 신경망으로 실험해 보거나 U-Net과 같은 다른 의미론적 분할 신경망 아키텍처를 사용해 볼 수 있습니다.

network = deeplabv3plus(imageSize,numClasses,"resnet18");

클래스 가중치를 사용하여 클래스 간의 균형 맞추기

앞에서 보았듯이 CamVid는 클래스 간에 균형이 맞지 않습니다. 훈련의 향상을 위해 클래스 가중치를 사용하여 클래스 간의 균형을 맞출 수 있습니다. 앞부분에서 countEachLabel (Computer Vision Toolbox) 함수로 계산한 픽셀 레이블 개수를 사용하여 중앙값 빈도 클래스 가중치를 계산합니다.

imageFreq = tbl.PixelCount ./ tbl.ImagePixelCount;
classWeights = median(imageFreq) ./ imageFreq;

훈련 옵션 선택하기

훈련에 사용되는 최적화 알고리즘은 SGDM(모멘텀을 사용한 확률적 경사하강법)입니다. trainingOptions를 사용하여 SGDM에 사용할 하이퍼파라미터를 지정합니다.

학습률은 조각별 스케줄을 사용합니다. Epoch 6회마다 학습률이 0.1배만큼 줄어듭니다. 이렇게 하면 신경망이 더 높은 초기 학습률로 빠르게 학습할 수 있으면서도 학습률이 떨어지기 시작하면 국소 최적해에 가까운 해를 구할 수 있습니다.

ValidationData 이름-값 인수를 설정함으로써 신경망이 매 Epoch마다 검증 데이터에 테스트됩니다. ValidationPatience는 검증 정확도가 수렴하면 훈련이 조기에 중지되도록 4로 설정되었습니다. 이렇게 하면 신경망이 훈련 데이터셋에 대해 과적합되는 것이 방지됩니다.

훈련 중에 사용되는 메모리를 줄이기 위해 미니 배치의 크기는 4를 사용합니다. 시스템의 GPU 메모리 양에 따라 이 값을 늘리거나 줄일 수 있습니다.

또한 CheckpointPath가 임시 위치로 설정됩니다. 이 이름-값 인수를 사용하면 매 훈련 Epoch가 끝날 때마다 신경망 검사 지점을 저장할 수 있습니다. 시스템 장애나 정전으로 인해 훈련이 중단된 경우, 저장된 검사 지점에서 훈련을 재개할 수 있습니다. CheckpointPath로 지정하는 위치에 신경망 검사 지점을 저장할 수 있을 만큼 충분한 공간이 있는지 확인하십시오. 예를 들어, 각 Deeplab v3+ 검사 지점은 61MB이므로 검사 지점을 100개 저장하려면 약 6GB의 디스크 공간이 필요합니다.

options = trainingOptions("sgdm",...
    LearnRateSchedule="piecewise",...
    LearnRateDropPeriod=6,...
    LearnRateDropFactor=0.1,...
    Momentum=0.9,...
    InitialLearnRate=1e-2,...
    L2Regularization=0.005,...
    ValidationData=dsVal,...
    MaxEpochs=18,...  
    MiniBatchSize=4,...
    Shuffle="every-epoch",...
    CheckpointPath=tempdir,...
    VerboseFrequency=10,...
    ValidationPatience=4);

훈련 시작하기

신경망을 훈련시키려면 다음 코드에서 doTraining 변수를 true로 설정하십시오. trainnet 함수를 사용하여 신경망을 훈련시킵니다. modelLoss 헬퍼 함수로 지정된 사용자 지정 손실 함수를 사용합니다. 기본적으로 trainnet 함수는 GPU를 사용할 수 있으면 GPU를 사용합니다. GPU에서 훈련시키려면 Parallel Computing Toolbox™ 라이선스와 지원되는 GPU 장치가 필요합니다. 지원되는 장치에 대한 자세한 내용은 GPU 연산 요구 사항 (Parallel Computing Toolbox) 항목을 참조하십시오. GPU를 사용할 수 없는 경우, trainnet 함수는 CPU를 사용합니다. 실행 환경을 지정하려면 ExecutionEnvironment 훈련 옵션을 사용하십시오.

참고: 이 훈련은 24GB의 메모리가 탑재된 NVIDIA™ GeForce RTX 3090 Ti에서 검증되었습니다. GPU 메모리가 이보다 적으면 훈련 중에 메모리 부족이 발생할 수 있습니다. 메모리 부족이 발생하면 trainingOptions에서 MiniBatchSize를 1로 설정해 보거나 신경망 입력 크기를 줄이고 훈련 데이터의 크기를 조정해 보십시오. 이 신경망을 훈련시키는 데는 약 50분이 걸립니다. GPU 하드웨어에 따라 시간이 더 걸릴 수도 있습니다.

doTraining = false;
if doTraining
    [net,info] = trainnet(dsTrain,network,@(Y,T) modelLoss(Y,T,classWeights),options);
end

한 개 영상으로 신경망 테스트하기

훈련된 신경망을 한 개의 테스트 영상에 대해 실행합니다.

I = readimage(imdsTest,35);
C = semanticseg(I,net,Classes=classes);

결과를 표시합니다.

B = labeloverlay(I,C,Colormap=cmap,Transparency=0.4);
imshow(B)
pixelLabelColorbar(cmap, classes);

C의 결과를 pxdsTest에 저장된 예상 ground truth와 비교합니다. 녹색 영역과 자홍색 영역은 분할 결과가 예상 ground truth와 다른 영역을 가리킵니다.

expectedResult = readimage(pxdsTest,35);
actual = uint8(C);
expected = uint8(expectedResult);
imshowpair(actual, expected)

의미론적 분할 결과가 도로, 하늘, 나무, 건물에 대해 잘 중첩되는 것을 볼 수 있습니다. 그러나 보행자, 자동차와 같이 상대적으로 크기가 작은 사물은 그만큼 정확하지 않습니다. 각 클래스마다 중첩되는 정도는 Jaccard 지수라고도 부르는 IoU(intersection-over-union) 메트릭을 사용하여 측정할 수 있습니다. jaccard (Image Processing Toolbox) 함수를 사용하여 IoU를 측정합니다.

iou = jaccard(C,expectedResult);
table(classes,iou)
ans=11×2 table
      classes         iou  
    ____________    _______

    "Sky"           0.93632
    "Building"      0.87723
    "Pole"          0.40475
    "Road"          0.95332
    "Pavement"       0.8558
    "Tree"          0.92632
    "SignSymbol"    0.62978
    "Fence"         0.82389
    "Car"           0.75381
    "Pedestrian"    0.26717
    "Bicyclist"      0.7058

IoU 메트릭을 통해 시각적 결과를 확인할 수 있습니다. 도로, 하늘, 나무, 건물 클래스는 IoU 점수가 높고 보행자, 자동차 클래스는 IoU 점수가 낮습니다. 그 밖에 널리 사용되는 분할 메트릭에는 dice (Image Processing Toolbox)bfscore (Image Processing Toolbox) 윤곽선 일치 점수가 있습니다.

훈련된 신경망 평가하기

여러 테스트 영상에 대한 정확도를 측정하려면 전체 테스트 세트에 대해 semanticseg (Computer Vision Toolbox)를 실행하십시오. 영상 분할 시 사용되는 메모리를 줄이기 위해 미니 배치의 크기는 4를 사용합니다. 시스템의 GPU 메모리 양에 따라 이 값을 늘리거나 줄일 수 있습니다.

pxdsResults = semanticseg(imdsTest,net, ...
    Classes=classes, ...
    MiniBatchSize=4, ...
    WriteLocation=tempdir, ...
    Verbose=false);

semanticseg는 테스트 세트에 대한 결과를 pixelLabelDatastore 객체로 반환합니다. imdsTest의 각 테스트 영상에 대한 실제 픽셀 레이블 데이터는 WriteLocation 이름-값 인수로 지정된 위치에 있는 디스크에 쓰입니다. evaluateSemanticSegmentation (Computer Vision Toolbox)을 사용하여 테스트 세트 결과에 대한 의미론적 분할 메트릭을 측정합니다.

metrics = evaluateSemanticSegmentation(pxdsResults,pxdsTest,Verbose=false);

evaluateSemanticSegmentation은 전체 데이터셋, 개별 클래스 및 각 테스트 영상에 대한 다양한 메트릭을 반환합니다. 데이터셋 수준 메트릭을 보려면 metrics.DataSetMetrics를 조사하십시오. 데이터셋 메트릭은 신경망 성능에 대한 개요를 제공합니다.

metrics.DataSetMetrics
ans=1×5 table
    GlobalAccuracy    MeanAccuracy    MeanIoU    WeightedIoU    MeanBFScore
    ______________    ____________    _______    ___________    ___________

       0.90748          0.88828       0.69573      0.84904        0.74304  

각 클래스가 전체 성능에 미치는 영향을 보려면 metrics.ClassMetrics를 사용하여 클래스별 메트릭을 조사하십시오.

전반적인 데이터셋 성능은 꽤 우수하지만, 클래스 메트릭을 보면 Pedestrian, Bicyclist, Car처럼 부족하게 표시되었던 클래스는 Road, Sky, Tree, Building 같은 클래스만큼 잘 분할되지 않았음을 알 수 있습니다. 부족하게 표시되었던 클래스의 샘플을 더 많이 포함한 데이터를 추가하면 결과가 개선될 수 있습니다.

metrics.ClassMetrics
ans=11×3 table
                  Accuracy      IoU      MeanBFScore
                  ________    _______    ___________

    Sky            0.9438     0.91456      0.91326  
    Building      0.84484     0.82403      0.69502  
    Pole          0.82513     0.29465      0.65171  
    Road          0.94803     0.93847      0.84376  
    Pavement      0.92135     0.77639      0.80391  
    Tree          0.89106     0.79122      0.76429  
    SignSymbol    0.81774     0.49374       0.5954  
    Fence         0.81991      0.6213      0.63421  
    Car           0.93654      0.8163       0.7784  
    Pedestrian    0.91095     0.50498      0.69317  
    Bicyclist     0.91172     0.67738      0.72119  

지원 함수

function labelIDs = camvidPixelLabelIDs()
% Return the label IDs corresponding to each class.
%
% The CamVid dataset has 32 classes. Group them into 11 classes following
% the original SegNet training methodology [1].
%
% The 11 classes are:
%   "Sky" "Building", "Pole", "Road", "Pavement", "Tree", "SignSymbol",
%   "Fence", "Car", "Pedestrian",  and "Bicyclist".
%
% CamVid pixel label IDs are provided as RGB color values. Group them into
% 11 classes and return them as a cell array of M-by-3 matrices. The
% original CamVid class names are listed alongside each RGB value. Note
% that the Other/Void class are excluded below.
labelIDs = { ...
    
    % "Sky"
    [
    128 128 128; ... % "Sky"
    ]
    
    % "Building" 
    [
    000 128 064; ... % "Bridge"
    128 000 000; ... % "Building"
    064 192 000; ... % "Wall"
    064 000 064; ... % "Tunnel"
    192 000 128; ... % "Archway"
    ]
    
    % "Pole"
    [
    192 192 128; ... % "Column_Pole"
    000 000 064; ... % "TrafficCone"
    ]
    
    % Road
    [
    128 064 128; ... % "Road"
    128 000 192; ... % "LaneMkgsDriv"
    192 000 064; ... % "LaneMkgsNonDriv"
    ]
    
    % "Pavement"
    [
    000 000 192; ... % "Sidewalk" 
    064 192 128; ... % "ParkingBlock"
    128 128 192; ... % "RoadShoulder"
    ]
        
    % "Tree"
    [
    128 128 000; ... % "Tree"
    192 192 000; ... % "VegetationMisc"
    ]
    
    % "SignSymbol"
    [
    192 128 128; ... % "SignSymbol"
    128 128 064; ... % "Misc_Text"
    000 064 064; ... % "TrafficLight"
    ]
    
    % "Fence"
    [
    064 064 128; ... % "Fence"
    ]
    
    % "Car"
    [
    064 000 128; ... % "Car"
    064 128 192; ... % "SUVPickupTruck"
    192 128 192; ... % "Truck_Bus"
    192 064 128; ... % "Train"
    128 064 064; ... % "OtherMoving"
    ]
    
    % "Pedestrian"
    [
    064 064 000; ... % "Pedestrian"
    192 128 064; ... % "Child"
    064 000 192; ... % "CartLuggagePram"
    064 128 064; ... % "Animal"
    ]
    
    % "Bicyclist"
    [
    000 128 192; ... % "Bicyclist"
    192 000 192; ... % "MotorcycleScooter"
    ]
    
    };
end
function classes = getClassNames()
classes = [
    "Sky"
    "Building"
    "Pole"
    "Road"
    "Pavement"
    "Tree"
    "SignSymbol"
    "Fence"
    "Car"
    "Pedestrian"
    "Bicyclist"
    ];
end
function pixelLabelColorbar(cmap, classNames)
% Add a colorbar to the current axis. The colorbar is formatted
% to display the class names with the color.

colormap(gca,cmap)

% Add colorbar to current figure.
c = colorbar('peer', gca);

% Use class names for tick marks.
c.TickLabels = classNames;
numClasses = size(cmap,1);

% Center tick labels.
c.Ticks = 1/(numClasses*2):1/numClasses:1;

% Remove tick mark.
c.TickLength = 0;
end
function cmap = camvidColorMap()
% Define the colormap used by CamVid dataset.

cmap = [
    128 128 128   % Sky
    128 0 0       % Building
    192 192 192   % Pole
    128 64 128    % Road
    60 40 222     % Pavement
    128 128 0     % Tree
    192 128 128   % SignSymbol
    64 64 128     % Fence
    64 0 128      % Car
    64 64 0       % Pedestrian
    0 128 192     % Bicyclist
    ];

% Normalize between [0 1].
cmap = cmap ./ 255;
end
function [imdsTrain, imdsVal, imdsTest, pxdsTrain, pxdsVal, pxdsTest] = partitionCamVidData(imds,pxds)
% Partition CamVid data by randomly selecting 60% of the data for training. The
% rest is used for testing.
    
% Set initial random state for example reproducibility.
rng(0); 
numFiles = numpartitions(imds);
shuffledIndices = randperm(numFiles);

% Use 60% of the images for training.
numTrain = round(0.60 * numFiles);
trainingIdx = shuffledIndices(1:numTrain);

% Use 20% of the images for validation
numVal = round(0.20 * numFiles);
valIdx = shuffledIndices(numTrain+1:numTrain+numVal);

% Use the rest for testing.
testIdx = shuffledIndices(numTrain+numVal+1:end);

% Create image datastores for training and test.
imdsTrain = subset(imds,trainingIdx);
imdsVal = subset(imds,valIdx);
imdsTest = subset(imds,testIdx);

% Create pixel label datastores for training and test.
pxdsTrain = subset(pxds,trainingIdx);
pxdsVal = subset(pxds,valIdx);
pxdsTest = subset(pxds,testIdx);
end
function data = augmentImageAndLabel(data, xTrans, yTrans)
% Augment images and pixel label images using random reflection and
% translation.

for i = 1:size(data,1)
    
    tform = randomAffine2d(...
        XReflection=true,...
        XTranslation=xTrans, ...
        YTranslation=yTrans);
    
    % Center the view at the center of image in the output space while
    % allowing translation to move the output image out of view.
    rout = affineOutputView(size(data{i,1}), tform, BoundsStyle='centerOutput');
    
    % Warp the image and pixel labels using the same transform.
    data{i,1} = imwarp(data{i,1}, tform, OutputView=rout);
    data{i,2} = imwarp(data{i,2}, tform, OutputView=rout);
    
end
end
function loss = modelLoss(Y,T,classWeights)
    weights = dlarray(classWeights,"C");
    mask = ~isnan(T);
    T(isnan(T)) = 0;
    loss = crossentropy(Y,T,weights,Mask=mask,NormalizationFactor="mask-included");
end

참고 문헌

[1] Chen, Liang-Chieh et al. “Encoder-Decoder with Atrous Separable Convolution for Semantic Image Segmentation.” ECCV (2018).

[2] Brostow, G. J., J. Fauqueur, and R. Cipolla. "Semantic object classes in video: A high-definition ground truth database." Pattern Recognition Letters. Vol. 30, Issue 2, 2009, pp 88-97.

참고 항목

(Computer Vision Toolbox) | (Computer Vision Toolbox) | (Image Processing Toolbox) | (Computer Vision Toolbox) | | | | | (Computer Vision Toolbox)

관련 항목