Caja de herramientas para la creación de conjuntos de datos
Tabla de contenido
- Preparación ambiental
- Producción de conjuntos de datos
- Cambio de nombre de archivos por lotes
- Movimiento de archivos por lotes
- Cambiar el nombre de los archivos en lotes según un formato determinado
- Cómo modificar el contenido del archivo xml
- 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 _videoPlay
es mostrar el video importado en tiempo real. CutVideo
La función realiza la extracción manual de fotogramas. Al visualizar, c
presione el botón para extraer el fotograma actual y Esc
presione el botón para cerrar el video. ExtractAll
La función es una función de extracción automática de fotogramas y frameGap
el 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á xml
el 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")