Семантическая сегментация с глубоким обучением

Анализируйте обучающие данные для семантической сегментации

Для обучения семантической сети сегментации вам нужна набор изображений и соответствующая ей набор пиксельных маркированных изображений. Пиксельное маркированное изображение является изображением, где каждое значение пикселя представляет категориальную метку этого пикселя.

Следующий код загружает небольшой набор изображений и соответствующих пиксельных маркированных изображений:

dataDir = fullfile(toolboxdir('vision'),'visiondata');
imDir = fullfile(dataDir,'building');
pxDir = fullfile(dataDir,'buildingPixelLabels');

Загрузите данные изображения с помощью imageDatastore. image datastore может эффективно представлять большой набор изображений, потому что изображения считываются в память только при необходимости.

imds = imageDatastore(imDir);

Чтение и отображение первого изображения.

I = readimage(imds,1);
figure
imshow(I)

Figure contains an axes. The axes contains an object of type image.

Загрузите изображения меток пикселей с помощью pixelLabelDatastore для определения отображения между идентификаторами меток и категориальными именами. В используемом здесь наборе данных метки «небо», «трава», «здание» и «тротуар». Идентификаторы меток для этих классов являются 1, 2, 3, 4, соответственно.

Задайте имена классов.

classNames = ["sky" "grass" "building" "sidewalk"];

Определите идентификатор метки для каждого имени класса.

pixelLabelID = [1 2 3 4];

Создайте pixelLabelDatastore.

pxds = pixelLabelDatastore(pxDir,classNames,pixelLabelID);

Считайте первое изображение метки пикселя.

C = readimage(pxds,1);

Область выхода C является категориальной матрицей, где C(i,j) - категориальная метка пикселя I(i,j).

C(5,5)
ans = categorical
     sky 

Наложите пиксельные метки на изображение, чтобы увидеть, как маркируются различные части изображения.

B = labeloverlay(I,C);
figure
imshow(B)

Figure contains an axes. The axes contains an object of type image.

Категориальный выход упрощает задачи, которые требуют делать вещи по именам классов. Для образца можно создать двоичную маску только из создания:

buildingMask = C == 'building';

figure
imshowpair(I, buildingMask,'montage')

Figure contains an axes. The axes contains an object of type image.

Создайте сеть семантической сегментации

Создайте простую семантическую сеть сегментации и узнайте об общих слоях, найденных во многих семантических сетях сегментации. Общий шаблон в семантических сетях сегментации требует понижающей дискретизации изображения между сверточным и ReLU слоями, а затем повышает выход, чтобы соответствовать размеру входа. Эта операция аналогична стандартному анализу масштабного пространства с использованием пирамид изображений. Однако в течение этого процесса сеть выполняет операции с использованием нелинейных фильтров, оптимизированных для определенного набора классов, которые необходимо сегментировать.

Создайте входной слой изображения

Семантическая сеть сегментации начинается со imageInputLayer, который определяет наименьший размер изображения, который может обработать сеть. Большинство семантических сетей сегментации полностью сверточны, что означает, что они могут обрабатывать изображения, которые больше заданного размера входа. Здесь для обработки изображений RGB 64x64 в сети используется изображение размером [32 32 3].

inputSize = [32 32 3];
imgLayer = imageInputLayer(inputSize)
imgLayer = 
  ImageInputLayer with properties:

                Name: ''
           InputSize: [32 32 3]

   Hyperparameters
    DataAugmentation: 'none'
       Normalization: 'zerocenter'

Создайте сеть понижающей дискретизации

Начните со слоев свертки и ReLU. Заполнение слоя свертки выбирают таким образом, чтобы выходной размер слоя свертки совпадал с входом. Это облегчает построение сети, потому что входные и выходные размеры между большинством слоев остаются такими же, как вы прогрессируете через сеть.

filterSize = 3;
numFilters = 32;
conv = convolution2dLayer(filterSize,numFilters,'Padding',1);
relu = reluLayer();

Понижающая дискретизация выполняется с использованием максимального слоя объединения. Создайте максимальный слой объединения, чтобы уменьшить вход сигнала в 2 раза путем установки 'Stride'параметр, равный 2.

poolSize = 2;
maxPoolDownsample2x = maxPooling2dLayer(poolSize,'Stride',2);

Сложите свертки, ReLU и максимальные слои объединения, чтобы создать сеть, которая понижает свой вход в 4 раза.

downsamplingLayers = [
    conv
    relu
    maxPoolDownsample2x
    conv
    relu
    maxPoolDownsample2x
    ]
downsamplingLayers = 
  6x1 Layer array with layers:

     1   ''   Convolution   32 3x3 convolutions with stride [1  1] and padding [1  1  1  1]
     2   ''   ReLU          ReLU
     3   ''   Max Pooling   2x2 max pooling with stride [2  2] and padding [0  0  0  0]
     4   ''   Convolution   32 3x3 convolutions with stride [1  1] and padding [1  1  1  1]
     5   ''   ReLU          ReLU
     6   ''   Max Pooling   2x2 max pooling with stride [2  2] and padding [0  0  0  0]

Создайте сеть повышающей дискретизации

Увеличение дискретизации осуществляется с использованием слоя транспонированной свертки (также обычно называемого слоем «deconv» или «deconvolution»). Когда транспонированная свертка используется для увеличения дискретизации, она выполняет усиление дискретизации и фильтрацию одновременно.

Создайте транспонированный слой свертки для увеличения на 2.

filterSize = 4;
transposedConvUpsample2x = transposedConv2dLayer(4,numFilters,'Stride',2,'Cropping',1);

Параметр 'Cropping' устанавливается равным 1, чтобы выходной размер равнялся вдвое большему входу.

Сложите транспонированные слои свертки и relu. Вход в этот набор слоев увеличен на 4.

upsamplingLayers = [
    transposedConvUpsample2x
    relu
    transposedConvUpsample2x
    relu
    ]
upsamplingLayers = 
  4x1 Layer array with layers:

     1   ''   Transposed Convolution   32 4x4 transposed convolutions with stride [2  2] and output cropping [1  1]
     2   ''   ReLU                     ReLU
     3   ''   Transposed Convolution   32 4x4 transposed convolutions with stride [2  2] and output cropping [1  1]
     4   ''   ReLU                     ReLU

Создайте слой классификации пикселей

Конечный набор слоев отвечает за создание классификаций пикселей. Эти конечные слои обрабатывают вход, который имеет те же пространственные размерности (высота и ширина), что и входное изображение. Однако количество каналов (третья размерность) больше и равно количеству фильтров в последнем транспонированном слое свертки. Эта третья размерность необходимо сжать до количества классов, которые мы хотим сегментировать. Это может быть сделано с использованием слоя свертки 1 на 1, количество фильтров которого равно количеству классов, например 3.

Создайте слой свертки, чтобы объединить третий размер входного элемента с количеством классов.

numClasses = 3;
conv1x1 = convolution2dLayer(1,numClasses);

Следом за этим слоем свертки 1 на 1 идут слои классификации softmax и пикселей. Эти два слоя объединяются, чтобы предсказать категориальную метку для каждого пикселя изображения.

finalLayers = [
    conv1x1
    softmaxLayer()
    pixelClassificationLayer()
    ]
finalLayers = 
  3x1 Layer array with layers:

     1   ''   Convolution                  3 1x1 convolutions with stride [1  1] and padding [0  0  0  0]
     2   ''   Softmax                      softmax
     3   ''   Pixel Classification Layer   Cross-entropy loss 

Стекать все слои

Стекайте все слои, чтобы завершить сеть семантической сегментации.

net = [
    imgLayer    
    downsamplingLayers
    upsamplingLayers
    finalLayers
    ]
net = 
  14x1 Layer array with layers:

     1   ''   Image Input                  32x32x3 images with 'zerocenter' normalization
     2   ''   Convolution                  32 3x3 convolutions with stride [1  1] and padding [1  1  1  1]
     3   ''   ReLU                         ReLU
     4   ''   Max Pooling                  2x2 max pooling with stride [2  2] and padding [0  0  0  0]
     5   ''   Convolution                  32 3x3 convolutions with stride [1  1] and padding [1  1  1  1]
     6   ''   ReLU                         ReLU
     7   ''   Max Pooling                  2x2 max pooling with stride [2  2] and padding [0  0  0  0]
     8   ''   Transposed Convolution       32 4x4 transposed convolutions with stride [2  2] and output cropping [1  1]
     9   ''   ReLU                         ReLU
    10   ''   Transposed Convolution       32 4x4 transposed convolutions with stride [2  2] and output cropping [1  1]
    11   ''   ReLU                         ReLU
    12   ''   Convolution                  3 1x1 convolutions with stride [1  1] and padding [0  0  0  0]
    13   ''   Softmax                      softmax
    14   ''   Pixel Classification Layer   Cross-entropy loss 

Эта сеть готова к обучению с помощью trainNetwork из Deep Learning Toolbox™.

Обучите сеть семантической сегментации

Загрузите обучающие данные.

dataSetDir = fullfile(toolboxdir('vision'),'visiondata','triangleImages');
imageDir = fullfile(dataSetDir,'trainingImages');
labelDir = fullfile(dataSetDir,'trainingLabels');

Создайте datastore для изображений.

imds = imageDatastore(imageDir);

Создайте pixelLabelDatastore для меток основной истины пикселей.

classNames = ["triangle","background"];
labelIDs   = [255 0];
pxds = pixelLabelDatastore(labelDir,classNames,labelIDs);

Визуализируйте обучающие изображения и основную истину пиксельные метки.

I = read(imds);
C = read(pxds);

I = imresize(I,5);
L = imresize(uint8(C{1}),5);
imshowpair(I,L,'montage')

Создайте сеть семантической сегментации. Эта сеть использует простую сеть семантической сегментации, основанную на проекте понижающей и повышающей дискретизации.

numFilters = 64;
filterSize = 3;
numClasses = 2;
layers = [
    imageInputLayer([32 32 1])
    convolution2dLayer(filterSize,numFilters,'Padding',1)
    reluLayer()
    maxPooling2dLayer(2,'Stride',2)
    convolution2dLayer(filterSize,numFilters,'Padding',1)
    reluLayer()
    transposedConv2dLayer(4,numFilters,'Stride',2,'Cropping',1);
    convolution2dLayer(1,numClasses);
    softmaxLayer()
    pixelClassificationLayer()
    ]
layers = 
  10×1 Layer array with layers:

     1   ''   Image Input                  32×32×1 images with 'zerocenter' normalization
     2   ''   Convolution                  64 3×3 convolutions with stride [1  1] and padding [1  1  1  1]
     3   ''   ReLU                         ReLU
     4   ''   Max Pooling                  2×2 max pooling with stride [2  2] and padding [0  0  0  0]
     5   ''   Convolution                  64 3×3 convolutions with stride [1  1] and padding [1  1  1  1]
     6   ''   ReLU                         ReLU
     7   ''   Transposed Convolution       64 4×4 transposed convolutions with stride [2  2] and cropping [1  1  1  1]
     8   ''   Convolution                  2 1×1 convolutions with stride [1  1] and padding [0  0  0  0]
     9   ''   Softmax                      softmax
    10   ''   Pixel Classification Layer   Cross-entropy loss 

Setup опций обучения.

opts = trainingOptions('sgdm', ...
    'InitialLearnRate',1e-3, ...
    'MaxEpochs',100, ...
    'MiniBatchSize',64);

Объедините изображение и pixel label datastore для обучения.

trainingData = pixelLabelImageDatastore(imds,pxds);

Обучите сеть.

net = trainNetwork(trainingData,layers,opts);
Training on single CPU.
Initializing input data normalization.
|========================================================================================|
|  Epoch  |  Iteration  |  Time Elapsed  |  Mini-batch  |  Mini-batch  |  Base Learning  |
|         |             |   (hh:mm:ss)   |   Accuracy   |     Loss     |      Rate       |
|========================================================================================|
|       1 |           1 |       00:00:00 |       58.11% |       1.3458 |          0.0010 |
|      17 |          50 |       00:00:11 |       97.30% |       0.0924 |          0.0010 |
|      34 |         100 |       00:00:23 |       98.09% |       0.0575 |          0.0010 |
|      50 |         150 |       00:00:34 |       98.56% |       0.0424 |          0.0010 |
|      67 |         200 |       00:00:46 |       98.48% |       0.0435 |          0.0010 |
|      84 |         250 |       00:00:58 |       98.66% |       0.0363 |          0.0010 |
|     100 |         300 |       00:01:09 |       98.90% |       0.0310 |          0.0010 |
|========================================================================================|

Чтение и отображение тестового изображения.

testImage = imread('triangleTest.jpg');
imshow(testImage)

Сегментируйте тестовое изображение и отображайте результаты.

C = semanticseg(testImage,net);
B = labeloverlay(testImage,C);
imshow(B)

Улучшите результаты

Сеть не смогла сегментировать треугольники и классифицировала каждый пиксель как «фон». Обучение, по-видимому, идет хорошо с точностями обучения более 90%. Однако сеть научилась только классифицировать фоновый класс. Чтобы понять, почему это произошло, можно подсчитать вхождение каждой пиксельной метки на наборе данных.

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

    {'triangle'  }         10326       2.048e+05   
    {'background'}    1.9447e+05       2.048e+05   

Большинство меток пикселей предназначены для фона. Плохие результаты обусловлены классом дисбалансом. Классовый дисбаланс смещает процесс обучения в пользу доминирующего класса. Вот почему каждый пиксель классифицируется как «фон». Чтобы исправить это, используйте взвешивание классов для балансировки классов. Существует несколько методов вычисления весов классов. Одним из распространенных методов является обратное взвешивание частот, где веса классов являются обратными частотам классов. Это увеличивает вес, придаваемый недостаточно представленным классам.

totalNumberOfPixels = sum(tbl.PixelCount);
frequency = tbl.PixelCount / totalNumberOfPixels;
classWeights = 1./frequency
classWeights = 2×1

   19.8334
    1.0531

Веса классов могут быть заданы с помощью pixelClassificationLayer. Обновите последний слой, чтобы использовать pixelClassificationLayer с обратными весами классов.

layers(end) = pixelClassificationLayer('Classes',tbl.Name,'ClassWeights',classWeights);

Обучите сеть снова.

net = trainNetwork(trainingData,layers,opts);
Training on single CPU.
Initializing input data normalization.
|========================================================================================|
|  Epoch  |  Iteration  |  Time Elapsed  |  Mini-batch  |  Mini-batch  |  Base Learning  |
|         |             |   (hh:mm:ss)   |   Accuracy   |     Loss     |      Rate       |
|========================================================================================|
|       1 |           1 |       00:00:00 |       72.27% |       5.4135 |          0.0010 |
|      17 |          50 |       00:00:11 |       94.84% |       0.1188 |          0.0010 |
|      34 |         100 |       00:00:23 |       96.52% |       0.0871 |          0.0010 |
|      50 |         150 |       00:00:35 |       97.29% |       0.0599 |          0.0010 |
|      67 |         200 |       00:00:47 |       97.46% |       0.0628 |          0.0010 |
|      84 |         250 |       00:00:59 |       97.64% |       0.0586 |          0.0010 |
|     100 |         300 |       00:01:10 |       97.99% |       0.0451 |          0.0010 |
|========================================================================================|

Повторите попытку сегментации тестового изображения.

C = semanticseg(testImage,net);
B = labeloverlay(testImage,C);
imshow(B)

Использование взвешивания классов для балансировки классов дало лучший результат сегментации. Дополнительные шаги по улучшению результатов включают увеличение количества эпох, используемых для обучения, добавление дополнительных обучающих данных или изменение сети.

Оценка и просмотр результатов семантической сегментации

Импортируйте набор тестовых данных, запустите предварительно обученную сеть семантической сегментации и оцените и проверьте метрики качества семантической сегментации для предсказанных результатов.

Импорт набора данных

The triangleImages набор данных имеет 100 тестовых изображений с основной истиной метками. Определите местоположение набора данных.

dataSetDir = fullfile(toolboxdir('vision'),'visiondata','triangleImages');

Определите местоположение тестовых изображений.

testImagesDir = fullfile(dataSetDir,'testImages');

Создайте imageDatastore объект, содержащий тестовые изображения.

imds = imageDatastore(testImagesDir);

Определите местоположение меток основной истины.

testLabelsDir = fullfile(dataSetDir,'testLabels');

Задайте имена классов и связанные с ними идентификаторы меток. Идентификаторы меток являются пиксельными значениями, используемыми в файлах изображений для представления каждого класса.

classNames = ["triangle" "background"];
labelIDs = [255 0];

Создайте pixelLabelDatastore объект, содержащий основную истину пиксельных меток для тестовых изображений.

pxdsTruth = pixelLabelDatastore(testLabelsDir,classNames,labelIDs);

Запуск классификатора семантической сегментации

Загрузите сеть семантической сегментации, которая была обучена на обучающих изображениях triangleImages.

net = load('triangleSegmentationNetwork.mat');
net = net.net;

Запустите сеть на тестовых изображениях. Предсказанные метки записываются на диск во временной директории и возвращаются как pixelLabelDatastore объект.

pxdsResults = semanticseg(imds,net,"WriteLocation",tempdir);
Running semantic segmentation network
-------------------------------------
* Processed 100 images.

Оцените качество предсказания

Предсказанные метки сравниваются с основной истиной метками. В то время как метрики семантической сегментации вычисляются, прогресс печатается в Командном окне.

metrics = evaluateSemanticSegmentation(pxdsResults,pxdsTruth);
Evaluating semantic segmentation results
----------------------------------------
* Selected metrics: global accuracy, class accuracy, IoU, weighted IoU, BF score.
* Processed 100 images.
* Finalizing... Done.
* Data set metrics:

    GlobalAccuracy    MeanAccuracy    MeanIoU    WeightedIoU    MeanBFScore
    ______________    ____________    _______    ___________    ___________

       0.90624          0.95085       0.61588      0.87529        0.40652  

Смотрите метрики классов

Отображение точности классификации, пересечения между объединением (IoU) и балла контура F-1 для каждого класса в наборе данных.

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

    triangle            1     0.33005     0.028664  
    background     0.9017      0.9017      0.78438  

Отображение матрицы неточностей

Отобразите матрицу неточностей.

metrics.ConfusionMatrix
ans=2×2 table
                  triangle    background
                  ________    __________

    triangle        4730            0   
    background      9601        88069   

Визуализируйте нормированную матрицу неточностей как тепловую карту в окне рисунка.

normConfMatData = metrics.NormalizedConfusionMatrix.Variables;
figure
h = heatmap(classNames,classNames,100*normConfMatData);
h.XLabel = 'Predicted Class';
h.YLabel = 'True Class';
h.Title = 'Normalized Confusion Matrix (%)';

Figure contains an object of type heatmap. The chart of type heatmap has title Normalized Confusion Matrix (%).

Просмотр метрики изображения

Визуализируйте гистограмму пересечения изображений по объединению (IoU).

imageIoU = metrics.ImageMetrics.MeanIoU;
figure
histogram(imageIoU)
title('Image Mean IoU')

Figure contains an axes. The axes with title Image Mean IoU contains an object of type histogram.

Найдите тестовое изображение с самым низким IoU.

[minIoU, worstImageIndex] = min(imageIoU);
minIoU = minIoU(1);
worstImageIndex = worstImageIndex(1);

Прочтите тестовое изображение с худшим IoU, его основной истиной метками и предсказанными метками для сравнения.

worstTestImage = readimage(imds,worstImageIndex);
worstTrueLabels = readimage(pxdsTruth,worstImageIndex);
worstPredictedLabels = readimage(pxdsResults,worstImageIndex);

Преобразуйте изображения меток в изображения, которые могут отображаться в окне рисунка.

worstTrueLabelImage = im2uint8(worstTrueLabels == classNames(1));
worstPredictedLabelImage = im2uint8(worstPredictedLabels == classNames(1));

Отобразите худшее тестовое изображение, основную истину и прогноз.

worstMontage = cat(4,worstTestImage,worstTrueLabelImage,worstPredictedLabelImage);
worstMontage = imresize(worstMontage,4,"nearest");
figure
montage(worstMontage,'Size',[1 3])
title(['Test Image vs. Truth vs. Prediction. IoU = ' num2str(minIoU)])

Figure contains an axes. The axes with title Test Image vs. Truth vs. Prediction. IoU = 0.55519 contains an object of type image.

Точно так же найдите тестовое изображение с самым высоким IoU.

[maxIoU, bestImageIndex] = max(imageIoU);
maxIoU = maxIoU(1);
bestImageIndex = bestImageIndex(1);

Повторите предыдущие шаги, чтобы считать, преобразовать и отобразить тестовое изображение с лучшим IoU с его основной истиной и предсказанными метками.

bestTestImage = readimage(imds,bestImageIndex);
bestTrueLabels = readimage(pxdsTruth,bestImageIndex);
bestPredictedLabels = readimage(pxdsResults,bestImageIndex);

bestTrueLabelImage = im2uint8(bestTrueLabels == classNames(1));
bestPredictedLabelImage = im2uint8(bestPredictedLabels == classNames(1));

bestMontage = cat(4,bestTestImage,bestTrueLabelImage,bestPredictedLabelImage);
bestMontage = imresize(bestMontage,4,"nearest");
figure
montage(bestMontage,'Size',[1 3])
title(['Test Image vs. Truth vs. Prediction. IoU = ' num2str(maxIoU)])

Figure contains an axes. The axes with title Test Image vs. Truth vs. Prediction. IoU = 0.72502 contains an object of type image.

Задайте метрики для оценки

Опционально перечислите метрики, которые вы хотите вычислить с помощью 'Metrics' параметр.

Определите метрики, которые нужно вычислить.

evaluationMetrics = ["accuracy" "iou"];

Вычислите эти метрики для triangleImages тестовые данные.

metrics = evaluateSemanticSegmentation(pxdsResults,pxdsTruth,"Metrics",evaluationMetrics);
Evaluating semantic segmentation results
----------------------------------------
* Selected metrics: class accuracy, IoU.
* Processed 100 images.
* Finalizing... Done.
* Data set metrics:

    MeanAccuracy    MeanIoU
    ____________    _______

      0.95085       0.61588

Отображение выбранных метрик для каждого класса.

metrics.ClassMetrics
ans=2×2 table
                  Accuracy      IoU  
                  ________    _______

    triangle            1     0.33005
    background     0.9017      0.9017

Импорт маркированного пикселем набора данных для семантической сегментации

В этом примере показано, как импортировать пиксельный набор данных для семантических сетей сегментации.

Маркированный пикселями набор данных является набором изображений и соответствующим набором меток основной истины пикселей, используемых для настройки семантических сетей сегментации. Существует много общедоступных наборов данных, которые обеспечивают аннотированные изображения с метками в относительных пикселях. Чтобы проиллюстрировать шаги для импорта этих типов наборов данных, в примере используется набор данных CamVid из Кембриджского университета [1].

Набор данных CamVid представляет собой набор изображений, содержащих виды уличного уровня, полученные во время вождения. Набор данных обеспечивает метки пиксельного уровня для 32 семантических классов, включая автомобиль, пешехода и дорогу. Шаги, показанные для импорта CamVid, могут использоваться для импорта других наборов данных с метками пикселей.

Загрузить набор данных CamVid

Загрузите данные изображения CamVid со следующих URL-адресов:

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');
imageDir = fullfile(outputFolder,'images');
labelDir = fullfile(outputFolder,'labels');

if ~exist(outputFolder, 'dir')
    disp('Downloading 557 MB CamVid data set...');
    
    unzip(imageURL, imageDir);
    unzip(labelURL, labelDir);
end

Примечание: Время загрузки данных зависит от вашего подключения к Интернету. Команды, используемые выше, будут блокировать MATLAB ® до завершения загрузки. Также можно использовать веб-браузер для первой загрузки набора данных на локальный диск. Чтобы использовать загруженный из Интернета файл, измените outputFolder переменная выше в местоположении загруженного файла.

Пиксельные метки CamVid

Набор данных CamVid кодирует пиксельные метки как изображения RGB, где каждый класс представлен цветом RGB. Вот классы, которые задает набор данных, наряду с их кодировками RGB.

classNames = [ ...
    "Animal", ...
    "Archway", ...
    "Bicyclist", ...
    "Bridge", ...
    "Building", ...
    "Car", ...
    "CartLuggagePram", ...
    "Child", ...
    "Column_Pole", ...
    "Fence", ...
    "LaneMkgsDriv", ...
    "LaneMkgsNonDriv", ...
    "Misc_Text", ...
    "MotorcycleScooter", ...
    "OtherMoving", ...
    "ParkingBlock", ...
    "Pedestrian", ...
    "Road", ...
    "RoadShoulder", ...
    "Sidewalk", ...
    "SignSymbol", ...
    "Sky", ...
    "SUVPickupTruck", ...
    "TrafficCone", ...
    "TrafficLight", ...
    "Train", ...
    "Tree", ...
    "Truck_Bus", ...
    "Tunnel", ...
    "VegetationMisc", ...
    "Wall"];

Определите отображение между индексами меток и именами классов, так что classNames(k) соответствует labelIDs(k,:).

labelIDs = [ ...
    064 128 064; ... % "Animal"
    192 000 128; ... % "Archway"
    000 128 192; ... % "Bicyclist"
    000 128 064; ... % "Bridge"
    128 000 000; ... % "Building"
    064 000 128; ... % "Car"
    064 000 192; ... % "CartLuggagePram"
    192 128 064; ... % "Child"
    192 192 128; ... % "Column_Pole"
    064 064 128; ... % "Fence"
    128 000 192; ... % "LaneMkgsDriv"
    192 000 064; ... % "LaneMkgsNonDriv"
    128 128 064; ... % "Misc_Text"
    192 000 192; ... % "MotorcycleScooter"
    128 064 064; ... % "OtherMoving"
    064 192 128; ... % "ParkingBlock"
    064 064 000; ... % "Pedestrian"
    128 064 128; ... % "Road"
    128 128 192; ... % "RoadShoulder"
    000 000 192; ... % "Sidewalk"
    192 128 128; ... % "SignSymbol"
    128 128 128; ... % "Sky"
    064 128 192; ... % "SUVPickupTruck"
    000 000 064; ... % "TrafficCone"
    000 064 064; ... % "TrafficLight"
    192 064 128; ... % "Train"
    128 128 000; ... % "Tree"
    192 128 192; ... % "Truck_Bus"
    064 000 064; ... % "Tunnel"
    192 192 000; ... % "VegetationMisc"
    064 192 000];    % "Wall"

Обратите внимание, что другие наборы данных имеют различные форматы данных кодирования. Для примера набор данных PASCAL VOC [2] использует числовые идентификаторы меток от 0 до 21, чтобы кодировать их метки классов.

Визуализируйте пиксельные метки для одного из изображений CamVid.

labels = imread(fullfile(labelDir,'0001TP_006690_L.png'));
figure
imshow(labels)

% Add colorbar to show class to color mapping.
N = numel(classNames);
ticks = 1/(N*2):1/N:1;
colorbar('TickLabels',cellstr(classNames),'Ticks',ticks,'TickLength',0,'TickLabelInterpreter','none');
colormap(labelIDs./255)

Загрузка данных CamVid

Маркированный пикселями набор данных может быть загружен с помощью imageDatastore и a pixelLabelDatastore.

Создайте imageDatastore для загрузки изображений CamVid.

imds = imageDatastore(fullfile(imageDir,'701_StillsRaw_full'));

Создайте pixelLabelDatastore для загрузки меток пикселей CamVid.

pxds = pixelLabelDatastore(labelDir,classNames,labelIDs);

Считайте 10-е изображение и соответствующее пиксельное изображение с меткой.

I = readimage(imds,10);
C = readimage(pxds,10);

Изображение метки пикселя возвращается как категориальный массив, где C(i,j) - категориальная метка, назначенная пикселю I(i,j). Отобразите пиксельное изображение метки поверх изображения.

B = labeloverlay(I,C,'Colormap',labelIDs./255);
figure
imshow(B)

% Add a colorbar.
N = numel(classNames);
ticks = 1/(N*2):1/N:1;
colorbar('TickLabels',cellstr(classNames),'Ticks',ticks,'TickLength',0,'TickLabelInterpreter','none');
colormap(labelIDs./255)

Неопределенные или аннулированные метки

Обычно наборы данных с метками пикселей включают «неопределенные» или «пустые» метки. Они используются для обозначения пикселей, которые не были помечены. Для примера в CamVid идентификатор метки [0 0 0] используется для обозначения класса «void». Ожидается, что алгоритмы настройки и алгоритмы оценки не будут включать эти метки в какие-либо расчеты.

Класс «void» не должен быть явно назван при использовании pixelLabelDatastore. Любой идентификатор метки, который не сопоставлен с именем класса, автоматически помечается как «неопределенный» и исключается из расчетов. Чтобы увидеть неопределенные пиксели, используйте isundefined чтобы создать маску и отобразить ее поверх изображения.

undefinedPixels = isundefined(C);
B = labeloverlay(I,undefinedPixels);
figure
imshow(B)
title('Undefined Pixel Labels')

Объединение классов

При работе с общедоступными наборами данных, возможно, потребуется объединить некоторые классы, чтобы лучше подойти вашему приложению. Например, вы можете захотеть обучить сеть семантической сегментации, которая сегментирует сцену в 4 класса: дорога, небо, транспортное средство, пешеходный и фон. Для этого с набором данных CamVid сгруппируйте идентификаторы меток, определенные выше, чтобы соответствовать новым классам. Во-первых, задайте новые имена классов.

newClassNames = ["road","sky","vehicle","pedestrian","background"];

Затем сгруппируйте идентификаторы меток, используя массив ячеек из M-by-3 матриц.

groupedLabelIDs = {
    % road
    [
    128 064 128; ... % "Road"
    128 000 192; ... % "LaneMkgsDriv"
    192 000 064; ... % "LaneMkgsNonDriv"
    000 000 192; ... % "Sidewalk" 
    064 192 128; ... % "ParkingBlock"
    128 128 192; ... % "RoadShoulder"
    ]
   
    % "sky"
    [
    128 128 128; ... % "Sky"
    ]
    
    % "vehicle"
    [
    064 000 128; ... % "Car"
    064 128 192; ... % "SUVPickupTruck"
    192 128 192; ... % "Truck_Bus"
    192 064 128; ... % "Train"
    000 128 192; ... % "Bicyclist"
    192 000 192; ... % "MotorcycleScooter"
    128 064 064; ... % "OtherMoving"
    ]
     
    % "pedestrian"
    [
    064 064 000; ... % "Pedestrian"
    192 128 064; ... % "Child"
    064 000 192; ... % "CartLuggagePram"
    064 128 064; ... % "Animal"
    ]
    
    % "background"      
    [
    128 128 000; ... % "Tree"
    192 192 000; ... % "VegetationMisc"    
    192 128 128; ... % "SignSymbol"
    128 128 064; ... % "Misc_Text"
    000 064 064; ... % "TrafficLight"  
    064 064 128; ... % "Fence"
    192 192 128; ... % "Column_Pole"
    000 000 064; ... % "TrafficCone"
    000 128 064; ... % "Bridge"
    128 000 000; ... % "Building"
    064 192 000; ... % "Wall"
    064 000 064; ... % "Tunnel"
    192 000 128; ... % "Archway"
    ]
    };

Создайте pixelLabelDatastore использование новых идентификаторов классов и меток.

pxds = pixelLabelDatastore(labelDir,newClassNames,groupedLabelIDs);

Прочтите изображение метки 10-го пикселя и отобразите его поверх изображения.

C = readimage(pxds,10);
cmap = jet(numel(newClassNames));
B = labeloverlay(I,C,'Colormap',cmap);
figure
imshow(B)

% add colorbar
N = numel(newClassNames);
ticks = 1/(N*2):1/N:1;
colorbar('TickLabels',cellstr(newClassNames),'Ticks',ticks,'TickLength',0,'TickLabelInterpreter','none');
colormap(cmap)

The pixelLabelDatastore с новыми именами классов теперь можно использовать для обучения сети для 4 классов без необходимости изменения исходных меток пикселей CamVid.

Ссылки

[1] Brostow, Gabriel J., Julien Fauqueur, and Roberto Cipolla. Semantic object classes in video: A high-definition основная истина database (неопр.) (недоступная ссылка). Pattern Recognition Letters 30.2 (2009): 88-97.

[2] Everingham, M. et al. «Визуальный объект PASCAL классов результаты задачи 2012». См. http ://www. pascal-network. org/challenges/VOC/voc2012/workshop/index. html. Том 5. 2012.