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
Directorio de artículos
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:
- *: 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.
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.
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
- 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
-
instanciaIds.png: por ejemplo, segmentación
-
labelIds.png: etiqueta, 34 categorías en total (0-33), el fondo es 0
-
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
-
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
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
-
etiquetaentrenid
-
etiquetado
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
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
2. Modificar el número de categorías.
config/_base_/ocrnet_hr18.py
mmseg/datasets/cityscapes.py
mmseg/core/class_names.py
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
4. Modificar los datos de capacitación, pruebas y verificación.
mmseg/datasets/cityscapes.py
5. Modifique la cantidad de muestras (lotes) para cada GPU
configs/_base_/datasets/cityscapes.py
6. Modificación BN de formación distribuida/formación no distribuida
config/_base_/models/ocrnet_hr18.py
7. Modificar el número de iteraciones y el intervalo para guardar el modelo.
config/_base_/schedules/schedule_160k.py
8. Dónde consultar los datos de cada entrenamiento:
mmseg/datasets/builder.py
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