exponenta event banner

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

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

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

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

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

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

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

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');

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

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 

Настройка параметров обучения.

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

Объединение хранилища данных изображения и пиксельной метки для обучения.

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');

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

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 и 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)

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

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

Класс «» void «» не должен иметь явное имя при использовании pixelLabelDatastore. Любой идентификатор метки, не сопоставленный с именем класса, автоматически помечается как «undefined» и исключается из вычислений. Чтобы увидеть неопределенные пикселы, используйте 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)

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

Ссылки

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

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