Esta página aún no se ha traducido para esta versión. Puede ver la versión más reciente de esta página en inglés.

Segmentación semántica de imágenes multiespectrales mediante aprendizaje profundo

Este ejemplo muestra cómo entrenar una red neuronal convolucional U-Net para realizar la segmentación semántica de una imagen multiespectral con siete canales: tres canales de color, tres canales de infrarrojo cercano y una máscara.

El ejemplo muestra cómo entrenar una red U-Net y también proporciona una red U-Net previamente entrenada. Si decide entrenar la red U-Net, se recomienda utilizar una GPU NVIDIA™ compatible con CUDA con capacidad de computación 3.0 o superior (requiere Parallel Computing Toolbox™).

Introducción

La segmentación semántica implica etiquetar cada píxel de una imagen con una clase. Una aplicación de la segmentación semántica es el seguimiento de la deforestación, que es el cambio en la cobertura forestal a lo largo del tiempo. Los organismos ambientales rastrean la deforestación para evaluar y cuantificar la salud ambiental y ecológica de una región.

La segmentación semántica basada en el aprendizaje profundo puede producir una medición precisa de la cubierta vegetal a partir de fotografías aéreas de alta resolución. Un desafío es diferenciar las clases con características visuales similares, como tratar de clasificar un píxel verde como hierba, arbustos o árbol. Para aumentar la precisión de la clasificación, algunos conjuntos de datos contienen imágenes multiespectrales que proporcionan información adicional sobre cada píxel. Por ejemplo, el conjunto de datos del Parque Estatal de Hamlin Beach complementa las imágenes en color con canales casi infrarrojos que proporcionan una separación más clara de las clases.

En este ejemplo se muestra cómo utilizar técnicas de segmentación semántica basadas en aprendizaje profundo para calcular el porcentaje de cobertura de vegetación en una región a partir de un conjunto de imágenes multiespectrales.

Descargar datos

En este ejemplo se utiliza un conjunto de datos multiespectral de alta resolución para entrenar la red [1]. El conjunto de imágenes fue capturado usando un dron sobre el Parque Estatal Hamlin Beach, NY. Los datos contienen conjuntos de pruebas, validación y entrenamiento etiquetados, con 18 etiquetas de clase de objeto. El tamaño del archivo de datos es de 3,0 GB.

Descargue la versión de archivo MAT del conjunto de datos mediante la función auxiliar.downloadHamlinBeachMSIData Esta función se adjunta al ejemplo como un archivo auxiliar.

imageDir = tempdir; url = 'http://www.cis.rit.edu/~rmk6217/rit18_data.mat'; downloadHamlinBeachMSIData(url,imageDir);

Además, descargue una versión previamente entrenada de U-Net para este conjunto de datos mediante la función auxiliar (adjunta al ejemplo como un archivo auxiliar).downloadTrainedUnet El modelo entrenado previamente le permite ejecutar el ejemplo completo sin tener que esperar a que se complete el entrenamiento.

trainedUnet_url = 'https://www.mathworks.com/supportfiles/vision/data/multispectralUnet.mat'; downloadTrainedUnet(trainedUnet_url,imageDir);

Inspeccionar datos de entrenamiento

Cargue el conjunto de datos en el área de trabajo.

load(fullfile(imageDir,'rit18_data','rit18_data.mat'));

Examine la estructura de los datos.

whos train_data val_data test_data
  Name            Size                         Bytes  Class     Attributes    test_data       7x12446x7654            1333663576  uint16                 train_data      7x9393x5642              741934284  uint16                 val_data        7x8833x6918              855493716  uint16               

Los datos de imagen multiespectral se organizan como matrices -por-por-.numChannelswidthheight Sin embargo, en MATLAB®, las imágenes multicanal se organizan como matrices -by--by--.widthheightnumChannels Para cambiar la forma de los datos para que los canales estén en la tercera dimensión, utilice la función auxiliar, .switchChannelsToThirdPlane Esta función se adjunta al ejemplo como un archivo auxiliar.

train_data = switchChannelsToThirdPlane(train_data); val_data   = switchChannelsToThirdPlane(val_data); test_data  = switchChannelsToThirdPlane(test_data);

Confirme que los datos tienen la estructura correcta.

whos train_data val_data test_data
  Name                Size                     Bytes  Class     Attributes    test_data       12446x7654x7            1333663576  uint16                 train_data       9393x5642x7             741934284  uint16                 val_data         8833x6918x7             855493716  uint16               

Los canales de color RGB son los canales de imagen 3o, 2o y 1o. Muestre el componente de color de las imágenes de entrenamiento, validación y prueba como un montaje. Para que las imágenes aparezcan más brillantes en la pantalla, iguale sus histogramas utilizando la función.histeq

figure montage(...     {histeq(train_data(:,:,[3 2 1])), ...     histeq(val_data(:,:,[3 2 1])), ...     histeq(test_data(:,:,[3 2 1]))}, ...     'BorderSize',10,'BackgroundColor','white') title('RGB Component of Training Image (Left), Validation Image (Center), and Test Image (Right)')

Muestre los tres últimos canales igualados por histograma de los datos de entrenamiento como un montaje. Estos canales corresponden a las bandas de infrarrojo cercano y resaltan diferentes componentes de la imagen en función de sus firmas de calor. Por ejemplo, los árboles cerca del centro de la imagen del segundo canal muestran más detalles que los árboles de los otros dos canales.

figure montage(...     {histeq(train_data(:,:,4)), ...     histeq(train_data(:,:,5)), ...     histeq(train_data(:,:,6))}, ...     'BorderSize',10,'BackgroundColor','white') title('IR Channels 1 (Left), 2, (Center), and 3 (Right) of Training Image')

Canal 7 es una máscara que indica la región de segmentación válida. Muestre la máscara para las imágenes de entrenamiento, validación y prueba.

figure montage(...     {train_data(:,:,7), ...     val_data(:,:,7), ...     test_data(:,:,7)}, ...     'BorderSize',10,'BackgroundColor','white') title('Mask of Training Image (Left), Validation Image (Center), and Test Image (Right)')

Las imágenes etiquetadas contienen los datos de la verdad del terreno para la segmentación, con cada píxel asignado a una de las 18 clases. Obtenga una lista de las clases con sus respectivos iDs.

disp(classes)
0. Other Class/Image Border       1. Road Markings                  2. Tree                           3. Building                       4. Vehicle (Car, Truck, or Bus)   5. Person                         6. Lifeguard Chair                7. Picnic Table                   8. Black Wood Panel               9. White Wood Panel               10. Orange Landing Pad            11. Water Buoy                    12. Rocks                         13. Other Vegetation              14. Grass                         15. Sand                          16. Water (Lake)                  17. Water (Pond)                  18. Asphalt (Parking Lot/Walkway) 

Cree un vector de nombres de clase.

classNames = [ "RoadMarkings","Tree","Building","Vehicle","Person", ...                "LifeguardChair","PicnicTable","BlackWoodPanel",...                "WhiteWoodPanel","OrangeLandingPad","Buoy","Rocks",...                "LowLevelVegetation","Grass_Lawn","Sand_Beach",...                "Water_Lake","Water_Pond","Asphalt"]; 

Superponga las etiquetas en la imagen de entrenamiento RGB igualada por histograma. Agregue una barra de colores a la imagen.

cmap = jet(numel(classNames)); B = labeloverlay(histeq(train_data(:,:,4:6)),train_labels,'Transparency',0.8,'Colormap',cmap);  figure title('Training Labels') imshow(B) N = numel(classNames); ticks = 1/(N*2):1/N:1; colorbar('TickLabels',cellstr(classNames),'Ticks',ticks,'TickLength',0,'TickLabelInterpreter','none'); colormap(cmap)

Guarde los datos de entrenamiento como un archivo MAT y las etiquetas de entrenamiento como un archivo PNG.

save('train_data.mat','train_data'); imwrite(train_labels,'train_labels.png');

Crear almacén de datos de extracción aleatoria de parches para el entrenamiento

Utilice un almacén de datos de extracción de parches aleatorio para enviar los datos de entrenamiento a la red. Este almacén de datos extrae varios parches aleatorios correspondientes de un almacén de datos de imagen y un almacén de datos de etiquetas de píxeles que contienen imágenes de verdad de terreno y datos de etiquetas de píxeles. La aplicación de parches es una técnica común para evitar quedarse sin memoria para imágenes grandes y para aumentar eficazmente la cantidad de datos de entrenamiento disponibles.

Comience almacenando las imágenes de entrenamiento en un archivo .'train_data.mat'imageDatastore Dado que el formato de archivo MAT es un formato de imagen no estándar, debe utilizar un lector de archivos MAT para habilitar la lectura de los datos de imagen. Puede utilizar el lector de archivos MAT auxiliar, que extrae los primeros seis canales de los datos de entrenamiento y omite el último canal que contiene la máscara.matReader Esta función se adjunta al ejemplo como un archivo auxiliar.

imds = imageDatastore('train_data.mat','FileExtensions','.mat','ReadFcn',@matReader);

Cree a para almacenar las revisiones de etiquetas que contienen las 18 regiones etiquetadas.pixelLabelDatastore

pixelLabelIds = 1:18; pxds = pixelLabelDatastore('train_labels.png',classNames,pixelLabelIds);

Cree un a partir del almacén de datos de imagen y del almacén de datos de etiqueta de píxel.randomPatchExtractionDatastore Cada mini-lote contiene 16 parches de tamaño 256 por 256 píxeles. Se extraen mil mini-lotes en cada iteración de la época.

dsTrain = randomPatchExtractionDatastore(imds,pxds,[256,256],'PatchesPerImage',16000);

El almacén de datos de extracción aleatoria de parches proporciona minilotes de datos a la red en cada iteración de la época.dsTrain Obtenga una vista previa del almacén de datos para explorar los datos.

inputBatch = preview(dsTrain); disp(inputBatch)
        InputImage        ResponsePixelLabelImage     __________________    _______________________      {256×256×6 uint16}     {256×256 categorical}      {256×256×6 uint16}     {256×256 categorical}      {256×256×6 uint16}     {256×256 categorical}      {256×256×6 uint16}     {256×256 categorical}      {256×256×6 uint16}     {256×256 categorical}      {256×256×6 uint16}     {256×256 categorical}      {256×256×6 uint16}     {256×256 categorical}      {256×256×6 uint16}     {256×256 categorical}  

Crear capas de red U-Net

Este ejemplo utiliza una variación de la red U-Net. En U-Net, la serie inicial de capas convolucionales se intercala con capas de agrupación máxima, lo que reduce sucesivamente la resolución de la imagen de entrada. Estas capas son seguidas por una serie de capas convolucionales intercaladas con operadores de upsampling, aumentando sucesivamente la resolución de la imagen de entrada [2]. El nombre U-Net proviene del hecho de que la red se puede dibujar con una forma simétrica como la letra U.

En este ejemplo se modifica la red U para utilizar el relleno cero en las convoluciones, de modo que la entrada y la salida a las convoluciones tengan el mismo tamaño. Utilice la función auxiliar, , para crear una Red U con unos pocos hiperparámetros preseleccionados.createUnet Esta función se adjunta como un archivo auxiliar al ejemplo.

inputTileSize = [256,256,6]; lgraph = createUnet(inputTileSize); disp(lgraph.Layers)
  58x1 Layer array with layers:       1   'ImageInputLayer'                        Image Input                  256x256x6 images with 'zerocenter' normalization      2   'Encoder-Section-1-Conv-1'               Convolution                  64 3x3x6 convolutions with stride [1  1] and padding [1  1  1  1]      3   'Encoder-Section-1-ReLU-1'               ReLU                         ReLU      4   'Encoder-Section-1-Conv-2'               Convolution                  64 3x3x64 convolutions with stride [1  1] and padding [1  1  1  1]      5   'Encoder-Section-1-ReLU-2'               ReLU                         ReLU      6   'Encoder-Section-1-MaxPool'              Max Pooling                  2x2 max pooling with stride [2  2] and padding [0  0  0  0]      7   'Encoder-Section-2-Conv-1'               Convolution                  128 3x3x64 convolutions with stride [1  1] and padding [1  1  1  1]      8   'Encoder-Section-2-ReLU-1'               ReLU                         ReLU      9   'Encoder-Section-2-Conv-2'               Convolution                  128 3x3x128 convolutions with stride [1  1] and padding [1  1  1  1]     10   'Encoder-Section-2-ReLU-2'               ReLU                         ReLU     11   'Encoder-Section-2-MaxPool'              Max Pooling                  2x2 max pooling with stride [2  2] and padding [0  0  0  0]     12   'Encoder-Section-3-Conv-1'               Convolution                  256 3x3x128 convolutions with stride [1  1] and padding [1  1  1  1]     13   'Encoder-Section-3-ReLU-1'               ReLU                         ReLU     14   'Encoder-Section-3-Conv-2'               Convolution                  256 3x3x256 convolutions with stride [1  1] and padding [1  1  1  1]     15   'Encoder-Section-3-ReLU-2'               ReLU                         ReLU     16   'Encoder-Section-3-MaxPool'              Max Pooling                  2x2 max pooling with stride [2  2] and padding [0  0  0  0]     17   'Encoder-Section-4-Conv-1'               Convolution                  512 3x3x256 convolutions with stride [1  1] and padding [1  1  1  1]     18   'Encoder-Section-4-ReLU-1'               ReLU                         ReLU     19   'Encoder-Section-4-Conv-2'               Convolution                  512 3x3x512 convolutions with stride [1  1] and padding [1  1  1  1]     20   'Encoder-Section-4-ReLU-2'               ReLU                         ReLU     21   'Encoder-Section-4-DropOut'              Dropout                      50% dropout     22   'Encoder-Section-4-MaxPool'              Max Pooling                  2x2 max pooling with stride [2  2] and padding [0  0  0  0]     23   'Mid-Conv-1'                             Convolution                  1024 3x3x512 convolutions with stride [1  1] and padding [1  1  1  1]     24   'Mid-ReLU-1'                             ReLU                         ReLU     25   'Mid-Conv-2'                             Convolution                  1024 3x3x1024 convolutions with stride [1  1] and padding [1  1  1  1]     26   'Mid-ReLU-2'                             ReLU                         ReLU     27   'Mid-DropOut'                            Dropout                      50% dropout     28   'Decoder-Section-1-UpConv'               Transposed Convolution       512 2x2x1024 transposed convolutions with stride [2  2] and cropping [0  0  0  0]     29   'Decoder-Section-1-UpReLU'               ReLU                         ReLU     30   'Decoder-Section-1-DepthConcatenation'   Depth concatenation          Depth concatenation of 2 inputs     31   'Decoder-Section-1-Conv-1'               Convolution                  512 3x3x1024 convolutions with stride [1  1] and padding [1  1  1  1]     32   'Decoder-Section-1-ReLU-1'               ReLU                         ReLU     33   'Decoder-Section-1-Conv-2'               Convolution                  512 3x3x512 convolutions with stride [1  1] and padding [1  1  1  1]     34   'Decoder-Section-1-ReLU-2'               ReLU                         ReLU     35   'Decoder-Section-2-UpConv'               Transposed Convolution       256 2x2x512 transposed convolutions with stride [2  2] and cropping [0  0  0  0]     36   'Decoder-Section-2-UpReLU'               ReLU                         ReLU     37   'Decoder-Section-2-DepthConcatenation'   Depth concatenation          Depth concatenation of 2 inputs     38   'Decoder-Section-2-Conv-1'               Convolution                  256 3x3x512 convolutions with stride [1  1] and padding [1  1  1  1]     39   'Decoder-Section-2-ReLU-1'               ReLU                         ReLU     40   'Decoder-Section-2-Conv-2'               Convolution                  256 3x3x256 convolutions with stride [1  1] and padding [1  1  1  1]     41   'Decoder-Section-2-ReLU-2'               ReLU                         ReLU     42   'Decoder-Section-3-UpConv'               Transposed Convolution       128 2x2x256 transposed convolutions with stride [2  2] and cropping [0  0  0  0]     43   'Decoder-Section-3-UpReLU'               ReLU                         ReLU     44   'Decoder-Section-3-DepthConcatenation'   Depth concatenation          Depth concatenation of 2 inputs     45   'Decoder-Section-3-Conv-1'               Convolution                  128 3x3x256 convolutions with stride [1  1] and padding [1  1  1  1]     46   'Decoder-Section-3-ReLU-1'               ReLU                         ReLU     47   'Decoder-Section-3-Conv-2'               Convolution                  128 3x3x128 convolutions with stride [1  1] and padding [1  1  1  1]     48   'Decoder-Section-3-ReLU-2'               ReLU                         ReLU     49   'Decoder-Section-4-UpConv'               Transposed Convolution       64 2x2x128 transposed convolutions with stride [2  2] and cropping [0  0  0  0]     50   'Decoder-Section-4-UpReLU'               ReLU                         ReLU     51   'Decoder-Section-4-DepthConcatenation'   Depth concatenation          Depth concatenation of 2 inputs     52   'Decoder-Section-4-Conv-1'               Convolution                  64 3x3x128 convolutions with stride [1  1] and padding [1  1  1  1]     53   'Decoder-Section-4-ReLU-1'               ReLU                         ReLU     54   'Decoder-Section-4-Conv-2'               Convolution                  64 3x3x64 convolutions with stride [1  1] and padding [1  1  1  1]     55   'Decoder-Section-4-ReLU-2'               ReLU                         ReLU     56   'Final-ConvolutionLayer'                 Convolution                  18 1x1x64 convolutions with stride [1  1] and padding [0  0  0  0]     57   'Softmax-Layer'                          Softmax                      softmax     58   'Segmentation-Layer'                     Pixel Classification Layer   Cross-entropy loss  

Seleccione Opciones de formación

Entrene la red utilizando el descenso de gradiente estocástico con optimización de impulso (SGDM). Especifique la configuración de hiperparámetropara SDGM mediante la función.trainingOptions

Entrenar una red profunda requiere mucho tiempo. Acelere la formación especificando una alta tasa de aprendizaje. Sin embargo, esto puede hacer que los gradientes de la red exploten o crezcan incontrolablemente, evitando que la red entrene con éxito. Para mantener los degradados en un rango significativo, habilite el recorte de degradado especificando como , y especifique que desea utilizar la norma L2 de los degradados.'GradientThreshold'0.05'GradientThresholdMethod'

initialLearningRate = 0.05; maxEpochs = 150; minibatchSize = 16; l2reg = 0.0001;  options = trainingOptions('sgdm',...     'InitialLearnRate',initialLearningRate, ...     'Momentum',0.9,...     'L2Regularization',l2reg,...     'MaxEpochs',maxEpochs,...     'MiniBatchSize',minibatchSize,...     'LearnRateSchedule','piecewise',...         'Shuffle','every-epoch',...     'GradientThresholdMethod','l2norm',...     'GradientThreshold',0.05, ...     'Plots','training-progress', ...     'VerboseFrequency',20);

Entrenar la red

Después de configurar las opciones de entrenamiento y el almacén de datos de extracción aleatoria de parches, entrene la red U-Net mediante la función.trainNetwork Para entrenar la red, establezca el parámetro en el código siguiente en .doTrainingtrue Una GPU NVIDIA™ compatible con CUDA con capacidad de computación 3.0 o superior es muy recomendable para el entrenamiento.

Si mantiene el parámetro en el código siguiente como , el ejemplo devuelve una red U-Net previamente entrenada.doTrainingfalse

Nota: El entrenamiento tarda unas 20 horas en un NVIDIA™ Titan X y puede tardar aún más en función del hardware de tu GPU.

doTraining = false;  if doTraining     modelDateTime = datestr(now,'dd-mmm-yyyy-HH-MM-SS');     [net,info] = trainNetwork(dsTrain,lgraph,options);     save(['multispectralUnet-' modelDateTime '-Epoch-' num2str(maxEpochs) '.mat'],'net','options'); else      load(fullfile(imageDir,'trainedUnet','multispectralUnet.mat')); end

Ahora puede utilizar la red U para segmentar semánticamente la imagen multiespectral.

Predecir resultados en los datos de prueba

Para realizar el paso directo en la red entrenada, utilice la función auxiliar, , con el conjunto de datos de validación.segmentImage Esta función se adjunta al ejemplo como un archivo auxiliar. realiza la segmentación en parches de imagen utilizando la función.segmentImagesemanticseg

predictPatchSize = [1024 1024]; segmentedImage = segmentImage(val_data,net,predictPatchSize);

Para extraer solo la parte válida de la segmentación, multiplique la imagen segmentada por el canal de máscara de los datos de validación.

segmentedImage = uint8(val_data(:,:,7)~=0) .* segmentedImage;  figure imshow(segmentedImage,[]) title('Segmented Image')

La salida de la segmentación semántica es ruidoso. Realice el procesamiento de imágenes posteriores para eliminar el ruido y los píxeles perdidos. Utilice la función para eliminar el ruido de sal y pimienta de la segmentación.medfilt2 Visualice la imagen segmentada con el ruido eliminado.

segmentedImage = medfilt2(segmentedImage,[7,7]); imshow(segmentedImage,[]); title('Segmented Image  with Noise Removed')

Superponga la imagen segmentada en la imagen de validación RGB igualada por histograma.

B = labeloverlay(histeq(val_data(:,:,[3 2 1])),segmentedImage,'Transparency',0.8,'Colormap',cmap);  figure imshow(B) title('Labeled Validation Image') colorbar('TickLabels',cellstr(classNames),'Ticks',ticks,'TickLength',0,'TickLabelInterpreter','none'); colormap(cmap)

Guarde las etiquetas segmentadas de la imagen y la verdad del suelo como archivos PNG. Se utilizarán para calcular las métricas de precisión.

imwrite(segmentedImage,'results.png'); imwrite(val_labels,'gtruth.png');

Cuantificar la precisión de la segmentación

Cree un para los resultados de segmentación y las etiquetas de verdad del suelo.pixelLabelDatastore

pxdsResults = pixelLabelDatastore('results.png',classNames,pixelLabelIds); pxdsTruth = pixelLabelDatastore('gtruth.png',classNames,pixelLabelIds);

Mida la precisión global de la segmentación semántica utilizando la función.evaluateSemanticSegmentation

ssm = evaluateSemanticSegmentation(pxdsResults,pxdsTruth,'Metrics','global-accuracy');
Evaluating semantic segmentation results ---------------------------------------- * Selected metrics: global accuracy. * Processed 1 images. * Finalizing... Done. * Data set metrics:      GlobalAccuracy     ______________         0.90698     

La puntuación de precisión global indica que poco más del 90% de los píxeles se clasifican correctamente.

Calcular la extensión de la cubierta de vegetación

El objetivo final de este ejemplo es calcular la extensión de la cubierta de vegetación en la imagen multiespectral.

Busque el número de píxeles etiquetados como vegetación. La etiqueta IDs 2 ("Trees"), 13 ("LowLevelVegetation") y 14 ("Grass_Lawn") son las clases de vegetación. También encontrará el número total de píxeles válidos sumando los píxeles en el ROI de la imagen de máscara.

vegetationClassIds = uint8([2,13,14]); vegetationPixels = ismember(segmentedImage(:),vegetationClassIds); validPixels = (segmentedImage~=0);  numVegetationPixels = sum(vegetationPixels(:)); numValidPixels = sum(validPixels(:));

Calcule el porcentaje de cobertura de vegetación dividiendo el número de píxeles de vegetación por el número de píxeles válidos.

percentVegetationCover = (numVegetationPixels/numValidPixels)*100; fprintf('The percentage of vegetation cover is %3.2f%%.',percentVegetationCover);
The percentage of vegetation cover is 51.72%. 

Resumen

Este ejemplo muestra cómo crear y entrenar una red U-Net para la segmentación semántica de una imagen multiespectral de siete canales. Estos son los pasos para entrenar la red:

  • Descargue y reforme los datos de entrenamiento.

  • Cree una para alimentar los datos de entrenamiento a la red.randomPatchExtractionDatastore

  • Defina las capas de la red U-Net.

  • Especifique las opciones de entrenamiento.

  • Entrene la red utilizando la función.trainNetwork

Después de entrenar la red U-Net o cargar una red U-Net previamente entrenada, el ejemplo realiza la segmentación semántica de los datos de validación y mide la precisión de la segmentación.

Referencias

[1] Kemker, R., C. Salvaggio y C. Kanan. "Conjunto de datos multiespectral de alta resolución para segmentación semántica." CoRR, abs/1703.01918. 2017.

[2] Ronneberger, O., P. Fischer y T. Brox. "U-Net: Redes convolucionales para la segmentación de imágenes biomédicas." CoRR, abs/1505.04597. 2015.

Consulte también

| | | | | | | |

Temas relacionados

Sitios web externos