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

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

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

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

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

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

imds = imageDatastore(imDir);

Считайте и отобразите первое изображение.

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

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

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

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

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

Задайте метку ID для каждого имени класса.

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, который задает самый маленький размер изображения, сеть может обработать. Большинство сетей семантической сегментации является полностью сверточным, что означает, что они могут обработать изображения, которые больше, чем заданный входной размер. Здесь, размер изображения [32 32 3] используется для сети к процессу 64x64 изображения RGB.

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" или слоя "развертки"). Когда транспонированная свертка используется для повышающей дискретизации, она выполняет повышающую дискретизацию и фильтрацию одновременно.

Создайте транспонированный слой свертки, чтобы сверхдискретизировать 2.

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

Параметр 'Обрезки' устанавливается на 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, количество которого фильтров равняются количеству классов, e.g. 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);

Объедините изображение и пиксельный 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)

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

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

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

Импортируйте набор данных

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

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

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

testImagesDir = fullfile(dataSetDir,'testImages');

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

imds = imageDatastore(testImagesDir);

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

testLabelsDir = fullfile(dataSetDir,'testLabels');

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

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] использует числовую метку IDs между 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 и 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, метка ID [0 0 0] используется, чтобы определять "пустой" класс. Алгоритмы настройки и алгоритмы оценки, как ожидают, не будут включать эти метки ни в какие расчеты.

"Пустой" класс нельзя явным образом назвать при использовании pixelLabelDatastore. Любая метка ID, которая не сопоставлена с именем класса, автоматически помечена "неопределенной" и исключена из расчетов. Чтобы видеть неопределенные пиксели, используйте isundefined создать маску и затем отобразить его сверху изображения.

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

Объедините классы

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

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

Затем метка IDs группы с помощью массива ячеек матриц M-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 использование нового класса и метки IDs.

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)

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

Ссылки

[1] Brostow, Габриэль Дж., Жюльен Фокер и Роберто Сиполья. "Семантические классы объектов в видео: база данных основной истины высокой четкости". Буквы Распознавания образов 30.2 (2009): 88-97.

[2] Everingham, M., и др. "Классы визуального объекта PASCAL бросают вызов 2 012 результатам". См. http://www. сеть Паскаля. org/challenges/VOC/voc2012/workshop/index. html. Издание 5. 2012.