Python realiza la extracción de cuadros de video, la operación por lotes de archivos y el procesamiento por lotes de archivos (caja de herramientas para la producción de conjuntos de datos)

Caja de herramientas para la creación de conjuntos de datos

Tabla de contenido

  1. Preparación ambiental
  2. Producción de conjuntos de datos
  3. Cambio de nombre de archivos por lotes
  4. Movimiento de archivos por lotes
  5. Cambiar el nombre de los archivos en lotes según un formato determinado
  6. Cómo modificar el contenido del archivo xml
  7. Interfaces comunes de la biblioteca Pathlib

introducción

En proyectos de visión por computadora, las operaciones por lotes de archivos y el preprocesamiento por lotes de archivos son pasos esenciales. Implican el manejo de una gran cantidad de archivos de imágenes, incluida la lectura, el procesamiento, el almacenamiento y el preprocesamiento. Este artículo presentará algunas técnicas y métodos comunes que lo ayudarán a realizar de manera eficiente operaciones por lotes de archivos y preprocesamiento por lotes de archivos en proyectos de visión por computadora.

Preparación ambiental

from pathlib import Path, PurePath
import xml.etree.ElementTree as ET
from typing import Union
import numpy as np
from tqdm import tqdm
import time
import cv2
import os

Producción de conjuntos de datos

La inteligencia artificial es cuánta inteligencia genera la inteligencia artificial. En los proyectos de visión por computadora, los conjuntos de datos son muy importantes. A continuación se explica cómo utilizar la tecnología de extracción de cuadros de video para realizar la producción de conjuntos de datos. La función en el siguiente código _videoPlayes mostrar el video importado en tiempo real. CutVideoLa función realiza la extracción manual de fotogramas. Al visualizar, cpresione el botón para extraer el fotograma actual y Escpresione el botón para cerrar el video. ExtractAllLa función es una función de extracción automática de fotogramas y frameGapel parámetro es cuántos fotogramas se extraen automáticamente.

class ExtractImg(object):
    def __init__(self, videopath: Path, savepath: Path, delay: int = 1) -> None:
        self.spath = savepath
        self.vpath = videopath
        self.delay = delay
        cv2.namedWindow("cv", cv2.WINDOW_NORMAL)
        cv2.resizeWindow("cv", 640, 480)
        self.cap = cv2.VideoCapture(str(self.vpath))
        self._timeflag = 0
        if not savepath.exists():
            os.mkdir(Path(savepath))

    def _videoPlay(self, size: list) -> None:
        self.cap.set(3, size[0])
        self.cap.set(4, size[1])
        while self.cap.isOpened():
            ret, frame = self.cap.read()
            # frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
            cv2.imshow("cv", frame)
            if cv2.waitKey(self.delay) & 0xFF == ord('c'):
                cv2.imwrite(str(PurePath.joinpath(self.spath,
                                                  "{}.jpg".format(str(time.time())))), frame)
                print("保存成功")
                time.sleep(1)
            elif cv2.waitKey(self.delay) & 0xFF == 27:
                break

    def ExtractAll(self, frameGap: int = 3) -> None:
        """
        这是将视频流中的帧全部抽出
        :frame: 跳帧
        :return:
        """
        while self.cap.isOpened():
            self._timeflag += 1

            ret, frame = self.cap.read()
            if ret:
                cv2.imshow("cv", frame)
                if self._timeflag % frameGap == 0:
                    cv2.imwrite(str(PurePath.joinpath(self.spath,
                                                      "{}.jpg".format(str(time.time())))), frame)
                    print("保存成功")
            if (cv2.waitKey(self.delay) & 0xFF == 27) or not ret:
                break
        cv2.destroyAllWindows()
        self.cap.release()
        self._timeflag = 0

    def CutVideo(self) -> None:
        """
        这是手动抽帧
        :return:
        """
        ifm = input("文件中已经存在{}张图片,是否有继续添加"
                    "(Y or N):".format(len(os.listdir(self.spath))))
        if self.spath.exists() and ifm == 'Y':
            self._videoPlay(size=[640, 480])
        elif self.spath.exists() and ifm == 'N':
            return None
        else:
            print("\n请输入Y(yes)或者N(no)")
        cv2.destroyAllWindows()
        self.cap.release()

Cambio de nombre de archivos por lotes

Cambie el nombre de las imágenes de la carpeta en orden ascendente.

  def statistics(path: Union[str, Path], dstpath: Union[Path, str], count: int = 0, random: bool = False) -> None:
        """
        这是存放图片的文件夹安升序重命名
        :param path:需要重命名的文件文件
        :param count:观察图片总数添加使用
        """
        assert isinstance(path, (Path, str)), print("请输入的路径")
        l = os.listdir(str(path))
        if not Path.exists(dstpath):
            Path.mkdir(dstpath)
        # l = sorted(l)
        print(l)
        # print(l)
        print("存在文件{}张!".format(len(l)))
        if random:
            np.random.shuffle(l)
        # print(l)
        # 将保存图片文件中的图片按照升序的方法重命名

        suffix = Path(l[0]).suffix
        for file in tqdm(l):
            src = PurePath.joinpath(path, file)
            dst = PurePath.joinpath(dstpath, Path(str(count + int(Path(file).stem))).with_suffix(suffix))
            os.rename(src, dst)

Movimiento de archivos por lotes

Lo siguiente es seleccionar archivos en lotes de acuerdo con ciertas reglas y colocarlos en la carpeta de destino.

    def choosen(src: Union[str, Path] , folder: Union[Path,str] ,dst: Union[str, Path] , suffix: str) -> None:
        """
        1.将xml/jpg文件夹中的文件名字拿出来并且在jpg/xml对应的文件夹中将名称相同的文件挑出来
        2.将文件夹中的文件随取出
        :param xmlsrc:目标xml文件
        :param imgsrc:frameImg文件
        :param dst:根据xml挑选出的img文件
        :return: None
        """
        # l = os.listdir(str(xmlsrc))
        if not isinstance(folder,Path):
            pa = Path(folder)
        if not isinstance(src,Path):
            l = Path(src)

        # parent = src.parent
        for i in l.iterdir():
            file = Path(i.name).with_suffix(suffix)
            (pa / file).rename(Path(dst) / file)

Cambiar el nombre de los archivos en lotes según un formato determinado

A continuación, cambie el nombre del archivo según el formato de 5 dígitos 1.jpg->00001.jpg.

    def batchrenames(src: Union[str, Path], dst: Union[str, Path], sorted: bool = False) -> None:
        """
        进行特定格式的重命名
        :param src:原文件
        :param dst: 存储文件
        :param sorted: 是否已经有顺序,若有学按照1.jpg ->00001.jpg
        :return: None
        """

        d = {
    
    1: "0000",  # 这是命名格式的字典
             2: "000",
             3: "00",
             4: "0",
             5: ""}
        l = os.listdir(src)
        suffix = Path(l[0]).suffix
        l.sort(key=lambda x: int(x.split('.')[0]))
        if sorted:
            for obj in tqdm(l):
                old = PurePath.joinpath(src, obj)
                new = PurePath.joinpath(dst, d[len(obj.split('.')[0])] + obj.split('.')[0] + suffix)
                os.rename(old, new)
        else:
            # for c, i in tqdm(enumerate(l)):
            pass

Cómo modificar el contenido del archivo xml

Aquí está xmlel código que modifica el contenido del archivo.

    def revampXml(xml_path: Union[Path, str], update_content: str) -> None:
        """
        这是一个修改xml文件内容的方法,将xml文件爱中的类别改称另一个类别
        :param xml_path: 存放xml文件的路径
        :param xml_dw: xpath关键字
        :param update_content: 更新的内容
        :return:None
        """
        # 打开xml文档
        if not isinstance(xml_path, Path):
            xml_path = Path(xml_path)
        for i in tqdm(xml_path.iterdir()):
            xmlfile = xml_path / f"{
      
      i}"
            doc = ET.parse(xmlfile)
            root = doc.getroot()
            # 查找修改路劲
            for obj in root.iter("object"):
                sub1 = obj.find("name")
                if sub1.text == "motorboat":
                    # 修改标签内容
                    sub1.text = update_content
                    # 保存修改
                    doc.write(xmlfile)

API de operación de archivos comunes de la biblioteca Pathlib y la biblioteca del sistema operativo

pathlib es una de las bibliotecas estándar para manipular rutas del sistema de archivos. La biblioteca puede realizar cómodamente empalmes de rutas, creación de archivos/directorios, copiar/mover, eliminar y otras operaciones.

Descripcion funcional operación pathlib operaciones os y os.path
obtener camino absoluto Ruta.resolve() os.ruta.abspath()
Modificar permisos de archivos y marcas de tiempo ruta.chmod() os.chmod()
Crear un directorio ruta.mkdir() sistema operativo.mkdir()
Cambio de nombre de archivo o carpeta, movido y renombrado si la ruta es diferente Ruta.renombrar() sistema operativo.renombrar()
Cambiar el nombre del archivo o carpeta, mover y cambiar el nombre si la ruta es diferente, destruye el objetivo existente si existe Ruta.reemplazar() sistema operativo.reemplazar()
eliminar directorio ruta.rmdir() sistema operativo.rmdir()
eliminar un archivo Ruta.desvincular() sistema operativo.remove()
eliminar un archivo Ruta.desvincular() sistema operativo.unlink()
obtener el directorio de trabajo actual Ruta.cwd() sistema operativo.getcwd()
Determinar si existe un nombre de archivo o directorio Ruta.existe() os.ruta.existe()
Regresar al directorio de usuarios de la computadora. Ruta.casa() os.ruta.expandusuario()
Verifique que la ruta proporcionada sea un archivo ruta.is_dir() os.ruta.isdir()
Verifique que la ruta proporcionada sea un directorio Ruta.is_file() os.ruta.isfile()
Verifique que la ruta proporcionada sea un enlace simbólico Ruta.is_symlink() os.ruta.islink()
obtener propiedades del archivo ruta.stat() sistema operativo.stat()
Determinar si es un camino absoluto. PurePath.is_absolute() os.ruta.isabs()
concatenar directorio con nombre de archivo o directorio PurePath.joinpath() os.ruta.join()
devolver nombre de archivo Nombre.PurePath os.ruta.nombrebase()
ruta del archivo de retorno PurePath.padre os.ruta.dirname()
Determinar si dos caminos son iguales. Ruta.mismoarchivo() os.ruta.mismoarchivo()
Nombre de archivo y extensión separados PurePath.sufijo os.ruta.splitext()

Resumir

Este artículo presenta la operación por lotes de archivos y la tecnología de preprocesamiento por lotes de archivos en el proyecto de visión por computadora. Dominar estas técnicas le permitirá procesar de manera eficiente datos de imágenes a gran escala y brindará un sólido apoyo para la implementación exitosa de proyectos de visión por computadora.

Espero que este artículo le inspire sobre la operación de archivos por lotes y el preprocesamiento por lotes en proyectos de visión por computadora.

El siguiente es el código completo.

# -*- coding: utf-8 -*-
# @Author  : cvYouTian
# @Software: PyCharm

from pathlib import Path, PurePath
import xml.etree.ElementTree as ET
from typing import Union
import numpy as np
# import torch
from tqdm import tqdm
import time
import cv2
import os


class ExtractImg(object):
    def __init__(self, videopath: Path, savepath: Path, delay: int = 1) -> None:
        self.spath = savepath
        self.vpath = videopath
        self.delay = delay
        cv2.namedWindow("cv", cv2.WINDOW_NORMAL)
        cv2.resizeWindow("cv", 640, 480)
        self.cap = cv2.VideoCapture(str(self.vpath))
        self._timeflag = 0
        if not savepath.exists():
            os.mkdir(Path(savepath))

    def _videoPlay(self, size: list) -> None:
        self.cap.set(3, size[0])
        self.cap.set(4, size[1])
        while self.cap.isOpened():
            ret, frame = self.cap.read()
            # frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
            cv2.imshow("cv", frame)
            if cv2.waitKey(self.delay) & 0xFF == ord('c'):
                cv2.imwrite(str(PurePath.joinpath(self.spath,
                                                  "{}.jpg".format(str(time.time())))), frame)
                print("保存成功")
                time.sleep(1)
            elif cv2.waitKey(self.delay) & 0xFF == 27:
                break

    def ExtractAll(self, frameGap: int = 3) -> None:
        """
        这是将视频流中的帧全部抽出
        :frame: 跳帧
        :return:
        """
        while self.cap.isOpened():
            self._timeflag += 1

            ret, frame = self.cap.read()
            if ret:
                cv2.imshow("cv", frame)
                if self._timeflag % frameGap == 0:
                    cv2.imwrite(str(PurePath.joinpath(self.spath,
                                                      "{}.jpg".format(str(time.time())))), frame)
                    print("保存成功")
            if (cv2.waitKey(self.delay) & 0xFF == 27) or not ret:
                break
        cv2.destroyAllWindows()
        self.cap.release()
        self._timeflag = 0

    def CutVideo(self) -> None:
        """
        这是手动抽帧
        :return:
        """
        ifm = input("文件中已经存在{}张图片,是否有继续添加"
                    "(Y or N):".format(len(os.listdir(self.spath))))
        if self.spath.exists() and ifm == 'Y':
            self._videoPlay(size=[640, 480])
        elif self.spath.exists() and ifm == 'N':
            return None
        else:
            print("\n请输入Y(yes)或者N(no)")
        cv2.destroyAllWindows()
        self.cap.release()

    @staticmethod
    def statistics(path: Union[str, Path], dstpath: Union[Path, str], count: int = 5305, random: bool = False) -> None:
        """
        这是存放图片的文件夹安升序重命名
        :param path:需要重命名的文件文件
        :param count:观察图片总数添加使用
        """
        assert isinstance(path, (Path, str)), print("请输入的路径")
        l = os.listdir(str(path))
        if not Path.exists(dstpath):
            Path.mkdir(dstpath)
        # l = sorted(l)
        print(l)
        # print(l)
        print("存在文件{}张!".format(len(l)))
        if random:
            np.random.shuffle(l)
        # print(l)
        # 将保存图片文件中的图片按照升序的方法重命名

        suffix = Path(l[0]).suffix
        for file in tqdm(l):
            src = PurePath.joinpath(path, file)
            dst = PurePath.joinpath(dstpath, Path(str(count + int(Path(file).stem))).with_suffix(suffix))
            os.rename(src, dst)

    @staticmethod
    def choosen(src: Union[str, Path]="/home/you/Desktop/2023海上高速目标检测/val", folder: Union[Path,str]="/home/you/Desktop/2023海上高速目标检测/annotations",dst: Union[str, Path]="/home/you/Desktop/2023海上高速目标检测/train", suffix: str=".xml") -> None:
        """
        1.将xml/jpg文件夹中的文件名字拿出来并且在jpg/xml对应的文件夹中将名称相同的文件挑出来
        2.将文件夹中的文件随取出
        :param xmlsrc:目标xml文件
        :param imgsrc:frameImg文件
        :param dst:根据xml挑选出的img文件
        :return: None
        """
        # l = os.listdir(str(xmlsrc))
        if not isinstance(folder,Path):
            pa = Path(folder)
        if not isinstance(src,Path):
            l = Path(src)

        # parent = src.parent
        for i in l.iterdir():
            file = Path(i.name).with_suffix(suffix)
            (pa / file).rename(Path(dst) / file)

    @staticmethod
    def batchrenames(src: Union[str, Path], dst: Union[str, Path], sorted: bool = False) -> None:
        """
        进行特定格式的重命名
        :param src:原文件
        :param dst: 存储文件
        :param sorted: 是否已经有顺序,若有学按照1.jpg ->00001.jpg
        :return: None
        """

        d = {
    
    1: "0000",  # 这是命名格式的字典
             2: "000",
             3: "00",
             4: "0",
             5: ""}
        l = os.listdir(src)
        suffix = Path(l[0]).suffix
        l.sort(key=lambda x: int(x.split('.')[0]))
        if sorted:
            for obj in tqdm(l):
                old = PurePath.joinpath(src, obj)
                new = PurePath.joinpath(dst, d[len(obj.split('.')[0])] + obj.split('.')[0] + suffix)
                os.rename(old, new)
        else:
            # for c, i in tqdm(enumerate(l)):
            pass

    @staticmethod
    def text(file: Union[Path, str]):
        l = []
        f = open(file)
        for i in f.readlines():
            i = i.strip()
            stem = Path(i).stem
            suffix = Path(i).suffix
            n1, n2 = int(stem) - 1, int(stem) + 1
            l.append(str(n1) + ".xml")
            l.append(str(n2) + ".xml")
        print(l)

    @staticmethod
    def revampXml(xml_path: Union[Path, str], update_content: str) -> None:
        """
        这是一个修改xml文件内容的方法,将xml文件爱中的类别改称另一个类别
        :param xml_path: 存放xml文件的路径
        :param xml_dw: xpath关键字
        :param update_content: 更新的内容
        :return:None
        """
        # 打开xml文档
        if not isinstance(xml_path, Path):
            xml_path = Path(xml_path)
        for i in tqdm(xml_path.iterdir()):
            xmlfile = xml_path / f"{
      
      i}"
            doc = ET.parse(xmlfile)
            root = doc.getroot()
            # 查找修改路劲
            for obj in root.iter("object"):
                sub1 = obj.find("name")
                if sub1.text == "motorboat":
                    # 修改标签内容
                    sub1.text = update_content
                    # 保存修改
                    doc.write(xmlfile)

    @staticmethod
    def movefile(folder_path: Union[Path, str], dst: Union[Path, str], suffix: str) -> None:
        """
        批量移动剪切文件
        :param folder_path: 原文件夹路径
        :param dst: 目标文件夹路径
        :param suffix: 移动的文件格式/后缀
        :return:
        """
        if not isinstance(folder_path, Path):
            folder_path = Path(folder_path)
        # for i in folder_path.iterdir():
        #     if i.is_dir():
        #         ExtractImg.movefile(folder_path / i, suffix, res)
        #     else:
        #         if i.suffix == suffix:
        #             res.append(str(i))
        # # return res if suffix is None or suffix == "" else list(filter(lambda x: str(x).endswith(suffix),res))
        # return res
        for i in tqdm(folder_path.rglob(f"*{
      
      suffix}")):
            i.rename(dst / i.name)
    @staticmethod
    def convert_box(size, box):
            dw, dh = 1. / size[0], 1. / size[1]
            x, y, w, h = (box[0] + box[1]) / 2.0 - 1, (box[2] + box[3]) / 2.0 - 1, box[1] - box[0], box[3] - box[2]
            return x * dw, y * dh, w * dw, h * dh

  

if __name__ == "__main__":
    # 目标视频文件
    videopath = Path("videoSet/seabird6.mp4")
    # 图片保存文件
    savepath = Path("./dataset/imgs")
    # xin = Path("./VOC6detect/imgss")
    # savepath = Path("frameSave")
    # 目标xml文件
    # xmlpath = Path("./VOC6detect/annotations")
    # old = Path("/home/you/Desktop/dateset/20(pass)/seabird5")
    # new = Path("/home/you/Desktop/dateset/11(pass)/temp")
    # pa = Path("./labels/")
    # xin = Path()
    # renamepath = Path("/home/you/Desktop/dateset/4(pass)/a-1")
    # 实例化
    a = ExtractImg(videopath=videopath, savepath=savepath)
    a.choosen()
    # VOC2YOLO
    # a.convert_label()


    # 将帧全部抽出
    # a.ExtractAll(frameGap=8)

    # 手动抽帧
    # a.CutVideo()

    # 根据xml文选出对应的文件
    # a.choosen(xmlsrc=xmlpath, imgsrc=savepath, dst=xin)

    # 将数字命名的图片按照加上一个数字的方式命名
    # a.statistics(path=Path("./DATA/xml"), dstpath=Path("./DATA/t"), count=5305)

    # 对已经有顺序或者没顺序的文件进行特定格式的重命名78.jpg -> 00078.jpg
    # a.batchrenames(src=new, dst=old, sorted=True)
    # a.text("./data1.txt")
    # 对xml文件进行修改
    # a.revampXml(xml_path= "/home/you/Desktop/tools/dataset/annotations", update_content="speedboat")

    # 批量拿到文件夹中的某格式的文件
    # a.movefile(folder_path="/home/you/Desktop/网上快艇", dst=pa, suffix=".jpg")

Supongo que te gusta

Origin blog.csdn.net/m0_46114594/article/details/131956883
Recomendado
Clasificación