[Segmentación semántica] 3. Utilice mmsegmentation para entrenar su propio conjunto de datos de segmentación


Este blog también tiene múltiples reseñas súper detalladas. Los amigos que estén interesados ​​pueden ir a:

Red neuronal convolucional: una introducción súper detallada a la red neuronal convolucional

Detección de objetos: Introducción súper detallada a la detección de objetos

Segmentación semántica: una introducción súper detallada a la segmentación semántica

NMS: le permite comprender y ver todos los NMS y sus variantes en un solo artículo

Aumento de datos: comprenda el aumento de datos en visión por computadora en un artículo

Función de pérdida: función de pérdida e índice de evaluación en clasificación, detección y segmentación.

Transformer: una encuesta sobre transformadores visuales

Serie práctica de aprendizaje automático: árboles de decisión

Serie YOLO: v1 , v2 , v3 , v4 , scaled-v4 , v5 , v6 , v7 , yolof , yolox , yolos , yolop


1. Introducción a la segmentación mm

enlace de github: https://github.com/open-mmlab/mmsegmentation

2. Introducción al conjunto de datos de paisaje urbano

2.1 Estructura de datos

1. Estructura del conjunto de datos:

  • Images_base: leftImg8bit (5030 elementos, con un total de 11,6 GB, de hecho 5000 elementos)
  • Annotations_base: gtFine (30030 elementos, con un total de 1,1 GB)

2. Número de imágenes

  • 5000 anotaciones finas

    • 2975 imágenes de entrenamiento, 500 imágenes de entrenamiento, 1525 imágenes de prueba
  • 20.000 anotaciones aproximadas (usando polígonos para cubrir objetos individuales)

3. Tamaño de la imagen:

  • 1024x2048

4. Escenario de datos:

  • Escenas callejeras en 50 ciudades diferentes.
  • Las ciudades de train/val/test son todas diferentes

5. Definición de categoría:
inserte la descripción de la imagen aquí

  • *: Está marcado para una sola instancia. Si varios objetos de la misma categoría tienen una distribución cruzada, es decir, el límite de la instancia no es obvio, estos objetos forman un único grupo de instancias, como un grupo de automóviles/bicicletas.
  • +: La etiqueta indicada aún no está incluida en ningún ítem de evaluación, se trata como nula, por lo que elimina estas etiquetas, generalmente decimos que CityScapes contiene 19 categorías.
    inserte la descripción de la imagen aquí

6. Método de etiquetado

Cityscape tiene su propio método de anotación: paisaje urbanoscripts
https://github.com/mcordts/cityscapesScripts/tree/master/cityscapesscripts

Nota: Antes de entrenar, asegúrese de verificar si hay 19 clases en trainlabelid.png. Sería más seguro regenerarlo con paisaje urbanosscripts/helpers/labels.py.

inserte la descripción de la imagen aquí

El archivo paisaje urbanoScripts/helpers/labels.py define los métodos, colores, etc. correspondientes de diferentes categorías y valores de Id, como se muestra a continuación:

  • Categorías totales: 34 categorías
  • Categorías disponibles actualmente: 19 categorías
    inserte la descripción de la imagen aquíinserte la descripción de la imagen aquí
  • Tipo de archivo de etiquetas: las etiquetas de CityScapes no se concentran en un json, sino que cada imagen corresponde a 4 archivos de etiquetas.
    • color.png: Cada color corresponde a una categoría
      inserte la descripción de la imagen aquí

    • instanciaIds.png: por ejemplo, segmentación
      inserte la descripción de la imagen aquí

    • labelIds.png: etiqueta, 34 categorías en total (0-33), el fondo es 0
      inserte la descripción de la imagen aquí

    • TrainLabelId.png: etiqueta para capacitación (19 categorías de capacitación + 1 ignorada), establezca el valor de píxel que no participa en la capacitación en 255 y el valor de píxel de las categorías restantes que participan en la capacitación es 0-18
      inserte la descripción de la imagen aquí

    • polígonos.json

  • _gtFine_polygons.json almacena cada clase y el área correspondiente (la posición del vértice del polígono se usa para representar el límite del área);

2.2 Ejemplos de anotaciones

inserte la descripción de la imagen aquí
inserte la descripción de la imagen aquí
inserte la descripción de la imagen aquí
inserte la descripción de la imagen aquí
inserte la descripción de la imagen aquí
inserte la descripción de la imagen aquí

3. Convierta su conjunto de datos al formato Cityscape

3.1 Convertir los datos marcados con labelme en datos utilizables para el entrenamiento

from pathlib import Path
import os
import glob
import cv2
import png
import numpy as np
import shutil
from PIL import Image


# set add_4 and add_15 folder
seg_folder = Path('./20210311_ground_mask_part1/')

seg_folder_TrainID = Path(os.path.join(seg_folder,"TrainID"))
seg_folder_img = Path(os.path.join(seg_folder,"img"))
seg_folder_LabelID = Path(os.path.join(seg_folder,"LabelID"))
seg_folder_color = Path(os.path.join(seg_folder,"color"))

if not seg_folder_img.exists():
    os.mkdir(seg_folder_img)
if not seg_folder_LabelID.exists():
    os.mkdir(seg_folder_LabelID)
if not seg_folder_TrainID.exists():
    os.mkdir(seg_folder_TrainID)
if not seg_folder_color.exists():
    os.mkdir(seg_folder_color)

LabelID_glob = glob.glob('./20210311_ground_mask_part1/20210222_TJP_freespace_ss/20210222_TJP_freespace_ss_label/*.png')
TrainID_glob = glob.glob('./20210311_ground_mask_part1/20210222_TJP_freespace_ss/20210222_TJP_freespace_ss_label/*.png')
Img_glob = glob.glob('./20210311_ground_mask_part1/20210222_TJP_freespace_ss/20210222_TJP_freespace_ss_extract/*.jpg')
Color_glob = glob.glob('./20210311_ground_mask_part1/20210222_TJP_freespace_ss/20210222_TJP_freespace_ss_label/*.png')
# assert(len(LabelID_glob)==len(Img_glob))

print("len for lable glob",len(LabelID_glob))

# ******************* TrainID process ****************************
print("begin to process TrainID")
for k in range(len(LabelID_glob)):
    transfer_ori = Image.open(TrainID_glob[k])
    transfer_ground = np.array(transfer_ori)
    transfer_ground[transfer_ground == 0] = 255  # ignore
    transfer_ground[transfer_ground == 1] = 0   # freespace
    transfer_ground[transfer_ground == 2] = 1  # white solid lane line
    transfer_ground[transfer_ground == 3] = 2  # white dotted lane line
    # transfer_ground[transfer_ground == 4] = 3  # yellow solid lane line
    # transfer_ground[transfer_ground == 5] = 4  # yellow dotted lane line
    transfer_ground[transfer_ground == 6] = 3  # arrow
    transfer_ground[transfer_ground == 7] = 4  # diamond_sign
    transfer_ground[transfer_ground == 8] = 5  # zebra crossing
    transfer_ground[transfer_ground == 9] = 6  # stop line
    transfer_ground_img = Image.fromarray(transfer_ground)
    transfer_ground_img = transfer_ground_img.resize((2048, 1024))
    transfer_ori_path = os.path.join(seg_folder_TrainID,TrainID_glob[k].split('/')[-1].split('\\')[1])
    transfer_ground_img.save(transfer_ori_path)
    print("the {0} th TrainID img has been processed and save in folder".format(k))
#
# # ******************* LableID process ****************************
print("begin to process LableID")
for k in range(len(LabelID_glob)):
    transfer_ori = Image.open(TrainID_glob[k])
    transfer_ground = np.array(transfer_ori)
    transfer_ground[transfer_ground == 0] = 0  # ignore
    transfer_ground[transfer_ground == 1] = 1  # freespace
    transfer_ground[transfer_ground == 2] = 2  # white solid lane line
    transfer_ground[transfer_ground == 3] = 3  # white dotted lane line
    # transfer_ground[transfer_ground == 4] = 4  # yellow solid lane line
    # transfer_ground[transfer_ground == 5] = 5  # yellow dotted lane line
    transfer_ground[transfer_ground == 6] = 4  # arrow
    transfer_ground[transfer_ground == 7] = 5  # diamond_sign
    transfer_ground[transfer_ground == 8] = 6  # zebra crossing
    transfer_ground[transfer_ground == 9] = 7  # stop line

    transfer_ground_img = Image.fromarray(transfer_ground)
    transfer_ground_img = transfer_ground_img.resize((2048, 1024))
    transfer_ori_path = os.path.join(seg_folder_TrainID, TrainID_glob[k].split('/')[-1].split('\\')[1])
    transfer_ground_img.save(transfer_ori_path)
    print("the {0} th LabelID img has been processed and save in folder".format(k))


# # ******************** resize img ***********************************
for k in range(len(Img_glob)):
    print("copy the {0}th img to add img folder".format(k))
    src_img = Image.open(Img_glob[k])
    src_img = src_img.resize((2048, 1024))
    src_img_save_path = os.path.join(seg_folder_img,Img_glob[k].split('/')[-1].split('\\')[1].split('.')[0])
    src_img.save(src_img_save_path+'.png')
#
# ## ********************* resize color png *****************************
for k in range(len(Color_glob)):
    print("copy the {0}th img to color folder".format(k))
    src_img = Image.open(Color_glob[k])
    src_img = src_img.resize((2048,1024))
    color_img_save_path = os.path.join(seg_folder_color,Color_glob[k].split('/')[-1].split('\\')[1].split('.')[0])
    src_img.save(color_img_save_path+'.png')

3.2 Doble denominación

import os
import glob
import shutil
from pathlib import Path


img_path = './img/'
TrainID_path = './TrainID/'
LabelID_path = './LabelID/'
color_path = './color/'
gtFine_path = './gtFine/'
leftImg8bit_path = './leftImg8bit/'

if not Path(gtFine_path).exists():
    os.mkdir(gtFine_path)
if not Path(leftImg8bit_path).exists():
    os.mkdir(leftImg8bit_path)

img_files = os.listdir(img_path)
TrainID_files = os.listdir(TrainID_path)
LabelID_files = os.listdir(LabelID_path)
color_files = os.listdir(color_path)

m = 0
for file in color_files:
    #import pdb;pdb.set_trace()

    old = color_path + os.sep + color_files[m]
    filename = os.path.splitext(file)[0]

    new = gtFine_path + 'part1_' + filename + '_gtFine_color.png'
    shutil.move(old, new)
    print('rename {}th color files'.format(m))
    m+=1

i = 0
for file in img_files:
    #import pdb;pdb.set_trace()

    old = img_path + os.sep + img_files[i]
    filename = os.path.splitext(file)[0]

    new = leftImg8bit_path+ 'part1_' + filename + '_leftImg8bit.png'
    shutil.move(old, new)
    print('rename {}th img files'.format(i))
    i+=1
j = 0
for file in TrainID_files:
    # import pdb;pdb.set_trace()

    old = TrainID_path + os.sep + TrainID_files[j]
    filename = os.path.splitext(file)[0]

    new = gtFine_path + 'part1_' + filename + '_gtFine_labelTrainIds.png'
    shutil.move(old, new)
    print('rename {}th trainid files'.format(j))
    j += 1
k = 0
for file in LabelID_files:
    # import pdb;pdb.set_trace()

    old = LabelID_path + os.sep + LabelID_files[k]
    filename = os.path.splitext(file)[0]

    new = gtFine_path + 'part1_' + filename + '_gtFine_labelIds.png'
    shutil.move(old, new)
    print('rename {}th labelid files'.format(k))
    k += 1
  • color
    inserte la descripción de la imagen aquí

  • etiquetaentrenid
    inserte la descripción de la imagen aquí

  • etiquetado
    inserte la descripción de la imagen aquí

3.3 xml a json

import json
import xmltodict
import glob
import os

xml_list = glob.glob('./20210222_TJP_freespace_ss_xml/*.xml') #xml文件的路径

 
'''json to xml'''
def json_to_xml(json_str):
    # xmltodict库的unparse()json转xml
    # 参数pretty 是格式化xml
    xml_str = xmltodict.unparse(json_str, pretty=1, root='shapes')
    return xml_str
 
'''xml to json'''
def xml_to_json(xml_str):
    # parse是的xml解析器
    xml_parse = xmltodict.parse(xml_str)
    # json库dumps()是将dict转化成json格式,loads()是将json转化成dict格式。
    # dumps()方法的ident=1,格式化json
    json_str = json.dumps(xml_parse, indent=1)
    return json_str
 

for xml_path in xml_list:

    if os.path.exists(xml_path):
        with open(xml_path, 'r') as f1:
            xmlfile = f1.read()
            print('---------xml文件-----------')
            print(xmlfile)
            print('---------json文件----------')
            print(xml_to_json(xmlfile))
        with open(xml_path[:-3]+'json','w') as newfile:
            newfile.write(xml_to_json(xmlfile))
            print('--------写入json文件--------')
            print('写入xml.json文件成功')

4. Entrenamiento y pruebas

inserte la descripción de la imagen aquí

4.1 Cambiar el nombre de la ruta del conjunto de datos, etc.

Nota: primero compila la ruta python setup.py develop

1. Modificar la ruta del conjunto de datos

mmseg/datasets/cityscapes.py

inserte la descripción de la imagen aquí

2. Modificar el número de categorías.

config/_base_/ocrnet_hr18.py  

inserte la descripción de la imagen aquí

mmseg/datasets/cityscapes.py

inserte la descripción de la imagen aquí

mmseg/core/class_names.py

inserte la descripción de la imagen aquí
3. Utilice labelid.png como etiqueta para el entrenamiento, es decir, entrene dos categorías: espacio libre y fondo:

Si solo entrena la clase de espacio libre y usa el fondo como ignorado de la manera habitual y no participa en el entrenamiento, encontrará que el fondo ha aprendido toda la clase de espacio libre cuando lo visualiza, porque la negligencia en la segmentación es en realidad son los píxeles a los que no prestas atención, es decir, a nadie le importa qué categoría se predice que serán, pero si hay un área grande que se ignora en la imagen, es decir, hay un Un área grande de categoría incorrecta, dicho modelo no se puede usar, por lo que aquí use labelid.png como etiqueta de entrenamiento, es decir, entrenando dos categorías: espacio libre y fondo, por lo que el efecto de entrenamiento será muy bueno.

mmseg/datasets/cityscapes.py

inserte la descripción de la imagen aquí

4. Modificar los datos de capacitación, pruebas y verificación.

mmseg/datasets/cityscapes.py

inserte la descripción de la imagen aquí

5. Modifique la cantidad de muestras (lotes) para cada GPU

configs/_base_/datasets/cityscapes.py

inserte la descripción de la imagen aquí

6. Modificación BN de formación distribuida/formación no distribuida

config/_base_/models/ocrnet_hr18.py

inserte la descripción de la imagen aquí
7. Modificar el número de iteraciones y el intervalo para guardar el modelo.

config/_base_/schedules/schedule_160k.py

inserte la descripción de la imagen aquí

8. Dónde consultar los datos de cada entrenamiento:

mmseg/datasets/builder.py

inserte la descripción de la imagen aquí
inserte la descripción de la imagen aquí

datasets[0].keys()
>>>
dict_keys(['img_metas', 'img', 'gt_semantic_seg'])

4.2 Formación

1. Entrenamiento con tarjeta única

python tools/train.py ${
    
    CONFIG_FILE} [optional arguments]

2. Entrenamiento Doka

./tools/dist_train.sh ${
    
    CONFIG_FILE} ${
    
    GPU_NUM} [optional arguments]
python -m torch.distributed.launch --nproc_per_node=2 --master_port=29003  tools/train.py --config configs/ocrnet/ocrnet_hr18s_512x1024_40k_cityscapes.py --launcher pytorch --work_dir work_dir

Si se reporta el siguiente error, la solución es master_port modificar el valor de

subprocess.CalledProcessError: Command '[xxx]' returned non-zero exit status 1.
Optional arguments are:
--no-validate (not suggested): By default, the codebase will perform evaluation at every k iterations during the training. To disable this behavior, use --no-validate.
--work-dir ${
    
    WORK_DIR}: Override the working directory specified in the config file.
--resume-from ${
    
    CHECKPOINT_FILE}: Resume from a previous checkpoint file (to continue the training process).
--load-from ${
    
    CHECKPOINT_FILE}: Load weights from a checkpoint file (to start finetuning for another task).
Difference between resume-from and load-from:
resume-from loads both the model weights and optimizer state including the iteration number.
load-from loads only the model weights, starts the training from iteration 0.

4.3 Pruebas

# single-gpu testing
python tools/test.py ${
    
    CONFIG_FILE} ${
    
    CHECKPOINT_FILE} [--out ${
    
    RESULT_FILE}] [--eval ${
    
    EVAL_METRICS}] [--show]

# save test result at dir
python tools/test.py ${
    
    CONFIG_FILE} ${
    
    CHECKPOINT_FILE} [--out ${
    
    RESULT_FILE}] [--show-dir result]

# multi-gpu testing
./tools/dist_test.sh ${
    
    CONFIG_FILE} ${
    
    CHECKPOINT_FILE} ${
    
    GPU_NUM} [--out ${
    
    RESULT_FILE}] [--eval ${
    
    EVAL_METRICS}]
Optional arguments:

RESULT_FILE: Filename of the output results in pickle format. If not specified, the results will not be saved to a file.
EVAL_METRICS: Items to be evaluated on the results. Allowed values depend on the dataset, e.g., mIoU is available for all dataset. Cityscapes could be evaluated by cityscapes as well as standard mIoU metrics.
--show: If specified, segmentation results will be plotted on the images and shown in a new window. It is only applicable to single GPU testing and used for debugging and visualization. Please make sure that GUI is available in your environment, otherwise you may encounter the error like cannot connect to X server.
--show-dir: If specified, segmentation results will be plotted on the images and saved to the specified directory. It is only applicable to single GPU testing and used for debugging and visualization. You do NOT need a GUI available in your environment for using this option.

Ejemplos:

Supongamos que ya ha descargado los puntos de control en el directorio checkpoints/.

  • Pruebe PSPNet y visualice los resultados. Presione cualquier tecla para la siguiente imagen.
python tools/test.py configs/pspnet/pspnet_r50-d8_512x1024_40k_cityscapes.py \
    checkpoints/pspnet_r50-d8_512x1024_40k_cityscapes_20200605_003338-2966598c.pth \
    --show
  • Pruebe PSPNet y guarde las imágenes pintadas para su última visualización.
python tools/test.py configs/pspnet/pspnet_r50-d8_512x1024_40k_cityscapes.py \
    checkpoints/pspnet_r50-d8_512x1024_40k_cityscapes_20200605_003338-2966598c.pth \
    --show-dir psp_r50_512x1024_40ki_cityscapes_results
  • Pruebe PSPNet en PASCAL VOC (sin guardar los resultados de la prueba) y evalúe el mIoU.
python tools/test.py configs/pspnet/pspnet_r50-d8_512x1024_20k_voc12aug.py \
    checkpoints/pspnet_r50-d8_512x1024_20k_voc12aug_20200605_003338-c57ef100.pth \
    --eval mAP
  • Pruebe PSPNet con 4 GPU y evalúe la métrica estándar mIoU y paisajes urbanos.
./tools/dist_test.sh configs/pspnet/pspnet_r50-d8_512x1024_40k_cityscapes.py \
    checkpoints/pspnet_r50-d8_512x1024_40k_cityscapes_20200605_003338-2966598c.pth \
    4 --out results.pkl --eval mIoU cityscapes

Nota: Existe una brecha (~0,1 %) entre los paisajes urbanos mIoU y nuestro mIoU. La razón es que los paisajes urbanos promedian cada clase con el tamaño de clase de forma predeterminada. Usamos la versión simple sin promedio para todos los conjuntos de datos.

  • Pruebe PSPNet en paisajes urbanos divididos con 4 GPU y genere los archivos png para enviarlos al servidor de evaluación oficial.

Primero, agregue lo siguiente al archivo de configuración configs/pspnet/pspnet_r50-d8_512x1024_40k_cityscapes.py,

data = dict(
    test=dict(
        img_dir='leftImg8bit/test',
        ann_dir='gtFine/test'))

Luego ejecute la prueba.

./tools/dist_test.sh configs/pspnet/pspnet_r50-d8_512x1024_40k_cityscapes.py \
    checkpoints/pspnet_r50-d8_512x1024_40k_cityscapes_20200605_003338-2966598c.pth \
    4 --format-only --eval-options "imgfile_prefix=./pspnet_test_results"

Obtendrá archivos png en el directorio ./pspnet_test_results. Puede ejecutar zip -r results.zip pspnet_test_results/ y enviar el archivo zip al servidor de evaluación.

demostración 4.4

python demo/image_demo.py ${
    
    IMAGE_FILE} ${
    
    CONFIG_FILE} ${
    
    CHECKPOINT_FILE} [--device ${
    
    DEVICE_NAME}] [--palette-thr ${
    
    PALETTE}]
# example
python demo/image_demo.py demo/demo.jpg configs/pspnet/pspnet_r50-d8_512x1024_40k_cityscapes.py \
    checkpoints/pspnet_r50-d8_512x1024_40k_cityscapes_20200605_003338-2966598c.pth --device cuda:0 --palette cityscapes

Nota: Si usa la misma configuración para entrenar varias veces, puede colocar los resultados en diferentes directorios de trabajo cambiando el nombre de config/hr.py.

5. Habilidades de entrenamiento

5.1 Configuraciones de pérdida de peso para diferentes categorías

/configs/_base_/models/ocrnet_hr18.py

inserte la descripción de la imagen aquí

Supongo que te gusta

Origin blog.csdn.net/jiaoyangwm/article/details/114373269#comments_28426349
Recomendado
Clasificación