Anotación automática basada en yolov5


1. Causa

El etiquetado de imágenes requiere mucho tiempo y es laborioso. Sería genial si pudiera usar el modelo que entrenó antes para etiquetar las imágenes restantes sin etiquetar. (El primer conjunto de datos de entrenamiento es demasiado pequeño, el efecto no es ideal)

2. Programa de etiquetado automático

1. Convierta el video en una imagen (si se reconoce la imagen, este paso no es necesario)

Tomar fotografías es demasiado lento y agotador, y hacer videos será más rápido. Si necesita identificar un video, debe guardar cada fotograma del video o guardar un fotograma cada pocos segundos y luego identificarlo.

El procedimiento para convertir un video en un cuadro de imágenes es el siguiente:

import cv2

vc = cv2.VideoCapture('data/mp4/02.mp4')  # 读入视频文件
c = 1
d = 0
if vc.isOpened():  # 判断是否正常打开
    rval, frame = vc.read()
else:
    rval = False

timeF = 10  # 视频帧计数间隔频率
# try:

while rval:  # 循环读取视频帧
    rval, frame = vc.read()
    if (c % timeF == 0):  # 每隔timeF帧进行存储操作
        d = d+1
        cv2.imwrite('data/images/' + str(d) + '.jpg', frame)  # 存储为图像
        print(d)
    c = c + 1
    cv2.waitKey(1)
vc.release()

Guardo las imágenes convertidas en la carpeta de datos / imágenes

2. Reconocimiento de imágenes

El programa marcado semiautomáticamente utiliza el programa detect.py de
yolov5 para analizar el programa detect.py:

import argparse                     #这个库可以让我们直接在命令行中就可以向程序中传入参数并让程序运行
import time
from pathlib import Path

import cv2
import torch
import torch.backends.cudnn as cudnn
from numpy import random

from models.experimental import attempt_load
from utils.datasets import LoadStreams, LoadImages
from utils.general import check_img_size, check_requirements, check_imshow, non_max_suppression, apply_classifier, \
    scale_coords, xyxy2xywh, strip_optimizer, set_logging, increment_path
from utils.plots import plot_one_box
from utils.torch_utils import select_device, load_classifier, time_synchronized


def detect(save_img=False):
    # 获取输出文件夹,输入源,权重,参数等参数
    source, weights, view_img, save_txt, imgsz = opt.source, opt.weights, opt.view_img, opt.save_txt, opt.img_size
    webcam = source.isnumeric() or source.endswith('.txt') or source.lower().startswith(
        ('rtsp://', 'rtmp://', 'http://'))

    # Directories 目录
    save_dir = Path(increment_path(Path(opt.project) / opt.name, exist_ok=opt.exist_ok))  # increment run增量运行
    (save_dir / 'labels' if save_txt else save_dir).mkdir(parents=True, exist_ok=True)  # make dir

    # Initialize 获取设备
    set_logging()
    device = select_device(opt.device)
    half = device.type != 'cpu'  # half precision only supported on CUDA 如果设备为gpu,使用Float16

    # Load model 加载Float32模型,确保用户设定的输入图片分辨率能整除32(如不能则调整为能整除并返回)
    model = attempt_load(weights, map_location=device)  # load FP32 model
    stride = int(model.stride.max())  # model stride 模型步幅
    imgsz = check_img_size(imgsz, s=stride)  # check img_size
    model.half()  # to FP16 设置Float16
    # Set Dataloader 通过不同的输入源来设置不同的数据加载方式
    vid_path, vid_writer = None, None
    if webcam:
        view_img = check_imshow()
        cudnn.benchmark = True  # set True to speed up constant image size inference  设置True可加速恒定图像大小推断
        dataset = LoadStreams(source, img_size=imgsz, stride=stride)
    else:
        save_img = True
        dataset = LoadImages(source, img_size=imgsz, stride=stride)

    # Get names and colors
    # 获取类别名字
    names = model.module.names if hasattr(model, 'module') else model.names
    # 设置画框的颜色
    colors = [[random.randint(0, 255) for _ in range(3)] for _ in names]

    # Run inference 运行推理
    if device.type != 'cpu':
        model(torch.zeros(1, 3, imgsz, imgsz).to(device).type_as(next(model.parameters())))  # run once 进行一次前向推理,测试程序是否正常
    t0 = time.time()
    """
      path 图片/视频路径
      img 进行resize+pad之后的图片
      img0 原size图片
      cap 当读取图片时为None,读取视频时为视频源
      """
    for path, img, im0s, vid_cap in dataset:
        img = torch.from_numpy(img).to(device)
        # 图片也设置为Float16
        img = img.half() if half else img.float()  # uint8 to fp16/32
        img /= 255.0  # 0 - 255 to 0.0 - 1.0
        # 没有batch_size的话则在最前面添加一个轴
        if img.ndimension() == 3:
            img = img.unsqueeze(0)

        # Inference
        t1 = time_synchronized()

        """
        前向传播 返回pred的shape是(1, num_boxes, 5+num_class)
        h,w为传入网络图片的长和宽,注意dataset在检测时使用了矩形推理,所以这里h不一定等于w
        num_boxes = h/32 * w/32 + h/16 * w/16 + h/8 * w/8
        pred[..., 0:4]为预测框坐标
        预测框坐标为xywh(中心点+宽长)格式
        pred[..., 4]为objectness置信度
        pred[..., 5:-1]为分类结果
        """
        pred = model(img, augment=opt.augment)[0]

        # Apply NMS
        """
                pred:前向传播的输出
                conf_thres:置信度阈值
                iou_thres:iou阈值
                classes:是否只保留特定的类别
                agnostic:进行nms是否也去除不同类别之间的框
                经过nms之后,预测框格式:xywh-->xyxy(左上角右下角)
                pred是一个列表list[torch.tensor],长度为batch_size
                每一个torch.tensor的shape为(num_boxes, 6),内容为box+conf+cls
                """
        pred = non_max_suppression(pred, opt.conf_thres, opt.iou_thres, classes=opt.classes, agnostic=opt.agnostic_nms)
        t2 = time_synchronized()

        # Process detections
        # 对每一张图片作处理
        for i, det in enumerate(pred):  # detections per image
            if webcam:  # batch_size >= 1
                p, s, im0, frame = path[i], '%g: ' % i, im0s[i].copy(), dataset.count
            else:
                p, s, im0, frame = path, '', im0s, getattr(dataset, 'frame', 0)

            p = Path(p)  # to Path
            # 设置保存图片/视频的路径
            save_path = str(save_dir / p.name)  # img.jpg
            # 设置保存框坐标txt文件的路径
            txt_path = str(save_dir / 'labels' / p.stem) + ('' if dataset.mode == 'image' else f'_{frame}')  # img.txt
            s += '%gx%g ' % img.shape[2:]  # print string
            gn = torch.tensor(im0.shape)[[1, 0, 1, 0]]  # normalization gain whwh
            if len(det):
                # Rescale boxes from img_size to im0 size
                # 调整预测框的坐标:基于resize+pad的图片的坐标-->基于原size图片的坐标
                # 此时坐标格式为xyxy
                det[:, :4] = scale_coords(img.shape[2:], det[:, :4], im0.shape).round()

                # Print results
                # 打印检测到的类别数量
                for c in det[:, -1].unique():
                    n = (det[:, -1] == c).sum()  # detections per class
                    s += f"{n} {names[int(c)]}{'s' * (n > 1)}, "  # add to string

                # Write results
                for *xyxy, conf, cls in reversed(det):
                    if save_txt:  # Write to file
                        xywh = (xyxy2xywh(torch.tensor(xyxy).view(1, 4)) / gn).view(-1).tolist()  # normalized xywh
                        # 将xyxy(左上角+右下角)格式转为xywh(中心点+宽长)格式,并除上w,h做归一化,转化为列表再保存
                        line = (cls, *xywh, conf) if opt.save_conf else (cls, *xywh)  # label format
                        with open(txt_path + '.txt', 'a') as f:
                            f.write(('%g ' * len(line)).rstrip() % line + '\n')
                            # 在原图上画框

                    if save_img or view_img:  # Add bbox to image
                        label = f'{names[int(cls)]} {conf:.2f}'
                        plot_one_box(xyxy, im0, label=label, color=colors[int(cls)], line_thickness=3)

            # Print time (inference + NMS)
            # 打印前向传播+nms时间
            print(f'{s}Done. ({t2 - t1:.3f}s)')

            # Stream results
            # 如果设置展示,则show图片/视频
            if view_img:
                cv2.imshow(str(p), im0)
                cv2.waitKey(1)  # 1 millisecond

            # Save results (image with detections)
            # 设置保存图片/视频
            if save_img:
                if dataset.mode == 'image':
                    cv2.imwrite(save_path, im0)
                else:  # 'video'
                    if vid_path != save_path:  # new video
                        vid_path = save_path
                        if isinstance(vid_writer, cv2.VideoWriter):
                            vid_writer.release()  # release previous video writer

                        fourcc = 'mp4v'  # output video codec
                        fps = vid_cap.get(cv2.CAP_PROP_FPS)
                        w = int(vid_cap.get(cv2.CAP_PROP_FRAME_WIDTH))
                        h = int(vid_cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
                        vid_writer = cv2.VideoWriter(save_path, cv2.VideoWriter_fourcc(*fourcc), fps, (w, h))
                    vid_writer.write(im0)

    if save_txt or save_img:
        # 打开保存图片和txt的路径(好像只适用于MacOS系统)
        s = f"\n{len(list(save_dir.glob('labels/*.txt')))} labels saved to {save_dir / 'labels'}" if save_txt else ''
        # 打印总时间
        print(f"Results saved to {save_dir}{s}")

    print(f'Done. ({time.time() - t0:.3f}s)')

"""
    weights:训练的权重
    source:测试数据,可以是图片/视频路径,也可以是'0'(电脑自带摄像头),也可以是rtsp等视频流
    output:网络预测之后的图片/视频的保存路径
    img-size:网络输入图片大小
    conf-thres:置信度阈值
    iou-thres:做nms的iou阈值
    device:设置设备
    view-img:是否展示预测之后的图片/视频,默认False
    save-txt:是否将预测的框坐标以txt文件形式保存,默认False
    classes:设置只保留某一部分类别,形如0或者0 2 3 
    agnostic-nms:进行nms是否也去除不同类别之间的框,默认False
    augment:推理的时候进行多尺度,翻转等操作(TTA)推理
    update:如果为True,则对所有模型进行strip_optimizer操作,去除pt文件中的优化器等信息,默认为False
    """
if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument('--weights', nargs='+', type=str, default='best.pt', help='model.pt path(s)')
    parser.add_argument('--source', type=str, default='data/images', help='source')  # file/folder, 0 for webcam
    parser.add_argument('--img-size', type=int, default=640, help='inference size (pixels)')
    parser.add_argument('--conf-thres', type=float, default=0.25, help='object confidence threshold')
    parser.add_argument('--iou-thres', type=float, default=0.45, help='IOU threshold for NMS')
    parser.add_argument('--device', default='', help='cuda device, i.e. 0 or 0,1,2,3 or cpu')
    parser.add_argument('--view-img', action='store_true', help='display results')
    parser.add_argument('--save-txt', action='store_true', help='save results to *.txt')
    parser.add_argument('--save-conf', action='store_true', help='save confidences in --save-txt labels')
    parser.add_argument('--classes', nargs='+', type=int, help='filter by class: --class 0, or --class 0 2 3')
    parser.add_argument('--agnostic-nms', action='store_true', help='class-agnostic NMS')
    parser.add_argument('--augment', action='store_true', help='augmented inference')
    parser.add_argument('--update', action='store_true', help='update all models')
    parser.add_argument('--project', default='runs/detect', help='save results to project/name')
    parser.add_argument('--name', default='exp', help='save results to project/name')
    parser.add_argument('--exist-ok', action='store_true', help='existing project/name ok, do not increment')
    opt = parser.parse_args()
    print(opt)  #输出参数
    detect()

Para comprender mejor el programa, eliminé todos los lugares donde el programa no se puede ejecutar

Cambiar la configuración de los parámetros del programa

parser.add_argument('--weights', nargs='+', type=str, default='best.pt', help='model.pt path(s)') 
 #default=改成你的模型
parser.add_argument('--source', type=str, default='data/images', help='source') 
 # file/folder, 0 for webcam  default= 改成你放图片的路径

yolov5 nos proporciona un programa para guardar archivos txt (save-txt: ya sea para guardar las coordenadas del marco predichas como archivos txt, el valor predeterminado es False) solo necesitamos habilitar este parámetro.

python detect.py --save-txt

Luego, el programa colocará el archivo txt generado en el directorio ejecuta \ detect \ exp1 \ etiquetas, y colocará la imagen reconocida en el directorio ejecuta \ detect \ exp1 (el nombre del programa exp cambiará cada vez que se ejecute el programa, exp exp1 exp2. ...)

Debe agregar un class.txt en el directorio de etiquetas (escriba su clasificación en él)
Inserte la descripción de la imagen aquí
. El contenido de mi class.txt es

fishJ
fishL
fishU

Tres. Verificación y corrección

El etiquetado automático no garantiza que se puedan reconocer todas las imágenes, en este momento debe modificarlo manualmente

Abra el software labelImg para cambiar
la descarga y el uso de Open Dir y Change Save Dir labelImg software. Referencia: modelo de entrenamiento yolov5,
Inserte la descripción de la imagen aquí
puede ver que el efecto de etiquetado semiautomático sigue siendo bueno

tiempo de estudio

2021.3.19


Supongo que te gusta

Origin blog.csdn.net/qq_44181970/article/details/114898560
Recomendado
Clasificación