YOLO 标签工具 2025-03-25 更新
#!/usr/bin/env python
# -*- coding: utf-8 -*-
##############################################
# Home : https://www.netkiller.cn
# Author: Neo
# Upgrade: 2025-03-25
# YOLO 标签处理工具:
# (标签删除/合并/修改/复制/图片尺寸/Labelimg2yolo)
##############################################
try:
import argparse
import glob
import logging
import os
import random
import shutil
import sys
import uuid
import hashlib
import yaml
import cv2
from datetime import datetime
from PIL import Image, ImageOps
from texttable import Texttable
from tqdm import tqdm
except ImportError as err:
print("Import Error: %s" % (err))
exit()
class YoloUtils():
def __init__(self):
self.basedir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# sys.path.append(self.basedir)
# 日志记录基本设置
# logfile = os.path.join(self.basedir, 'logs', f"{os.path.splitext(os.path.basename(__file__))[0]}.{datetime.today().strftime('%Y-%m-%d.%H%M%S')}.log")
logfile = os.path.join(self.basedir, 'logs',
f"{os.path.splitext(os.path.basename(__file__))[0]}.{datetime.today().strftime('%Y-%m-%d')}.log")
logging.basicConfig(filename=logfile, level=logging.DEBUG,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
parser = argparse.ArgumentParser(description='Yolo 标签工具',
epilog='Author: netkiller - https://www.netkiller.cn')
self.subparsers = parser.add_subparsers(title='subcommands', description='valid subcommands', dest='subcommand',
help='additional help')
self.parent_parser = argparse.ArgumentParser(add_help=False)
# parent_parser.add_argument('--parent', type=int)
common = self.parent_parser.add_argument_group(title='通用参数', description=None)
common.add_argument('--source', type=str, default=None, help='图片来源地址')
common.add_argument('--target', default=None, type=str, help='图片目标地址')
common.add_argument('--clean', action="store_true", default=False, help='清理之前的数据')
self.label = self.subparsers.add_parser('label', help='标签处理工具')
self.label.add_argument('--source', type=str, default=None, help='目录', metavar="/tmp/dir1")
self.label.add_argument('--classes', action="store_true", default=False, help='查看 classes.txt 文件')
self.label.add_argument('--total', action="store_true", default=False, help='统计标签图数量')
self.label.add_argument('--index', action="store_true", default=False, help='统计标签索引数量')
self.label.add_argument('--search', nargs='+', default=None, help='搜索标签', metavar="1 2 3")
# labelimg.add_argument('--baz', choices=('X', 'Y', 'Z'), help='baz help')
self.merge = self.subparsers.add_parser('merge', help='合并两个TXT文件中的标签到新TXT文件')
# self.parser = argparse.ArgumentParser(description='合并YOLO标签工具')
self.merge.add_argument('--left', type=str, default=None, help='左侧目录', metavar="/tmp/dir1")
self.merge.add_argument('--right', default=None, type=str, help='右侧目录', metavar="/tmp/dir2")
self.merge.add_argument('--output', type=str, default=None, help='最终输出目录', metavar="/tmp/output")
self.merge.add_argument('--clean', action="store_true", default=False, help='清理之前的数据')
# subparsers = self.parser.add_subparsers(help='subcommand help')
self.copy = self.subparsers.add_parser('copy', help='从指定标签复制图片文件')
self.copy.add_argument('--source', type=str, default=None, help='图片来源地址')
self.copy.add_argument('--target', type=str, default=None, help='图片目标地址')
self.copy.add_argument('--label', type=str, default=None, help='逗号分割多个标签')
self.copy.add_argument('-u', '--uuid', action="store_true", default=False, help='UUID 文件名')
self.copy.add_argument('-c', '--clean', action="store_true", default=False, help='清理目标文件夹')
self.remove = self.subparsers.add_parser('remove', help='从YOLO TXT文件中删除指定标签',
parents=[self.parent_parser])
# self.parser = argparse.ArgumentParser(description='YOLO标签删除工具')
self.remove.add_argument('--classes', nargs='+', default=None, help='标签序号', metavar="1 2 3")
self.remove.add_argument('--label', nargs='+', default=None, help='标签名称', metavar="label1 label2")
# remove.add_argument('--output', type=str, default=None, help='输出目录', metavar="/tmp/output")
# self.remove.add_argument('--clean', action="store_true", default=False, help='清理输出目录')
# self.remove.add_argument('--show', action='store_true', help='查看 classes.txt 文件')
self.change = self.subparsers.add_parser('change', help='修改标签索引')
self.change.add_argument('--source', type=str, default=None, help='目录', metavar="/tmp/dir1")
self.change.add_argument('--search', nargs='+', default=None, help='标签序号', metavar="1 2 3")
self.change.add_argument('--replace', nargs='+', default=None, help='标签名称', metavar="4 5 6")
self.crop = self.subparsers.add_parser('crop', help='图片裁剪', parents=[self.parent_parser])
self.crop.add_argument('--imgsz', type=int, default=640, help='长边尺寸', metavar=640)
# self.change.add_argument('--classes', action="store_true", default=False, help='查看 classes.txt 文件')
# parser_b.add_argument('--baz', choices=('X', 'Y', 'Z'), help='baz help')
#
# # parse some argument lists
# parser.parse_args(['a', '12'])
# Namespace(bar=12, foo=False)
# self.parser = argparse.ArgumentParser(description='YOLO标签删除工具')
# self.parser.add_argument('--label', type=int, default=-1, help='长边尺寸',metavar=0)
# self.parser.add_argument('--output', type=str, default=None, help='输出目录', metavar="/tmp/output")
# self.args = self.parser.parse_args()
# self.parser = argparse.ArgumentParser(description='YOLO标签删除工具')
# self.parser.add_argument('--label', type=int, default=-1, help='长边尺寸',metavar=0)
# self.parser = argparse.ArgumentParser(
# description='Yolo 工具 V3.0 - Design by netkiller - https://www.netkiller.cn')
# self.parser.add_argument('--source', type=str, default=None, help='图片来源地址')
# self.parser.add_argument('--target', default=None, type=str, help='图片目标地址')
# self.parser.add_argument('--classes', type=str, default=None, help='classes.txt 文件')
# self.parser.add_argument('--val', type=int, default=10, help='检验数量', metavar=10)
# self.parser.add_argument('--crop', action="store_true", default=False, help='裁剪')
# self.args = self.parser.parse_args()
self.labelimg = self.subparsers.add_parser('labelimg', help='labelimg 格式转换为 yolo 训练数据集',
parents=[self.parent_parser])
# self.labelimg.add_argument('--source', type=str, default=None, help='图片来源地址')
# self.labelimg.add_argument('--target', default=None, type=str, help='图片目标地址')
self.labelimg.add_argument('--classes', type=str, default=None, help='classes.txt 文件')
self.labelimg.add_argument('--val', type=int, default=10, help='检验数量', metavar=10)
# self.labelimg.add_argument('--clean', action="store_true", default=False, help='清理之前的数据')
# self.labelimg.add_argument('--crop', action="store_true", default=False, help='裁剪')
self.labelimg.add_argument('--uuid', action="store_true", default=False, help='输出文件名使用UUID')
self.labelimg.add_argument('--check', action="store_true", default=False,
help='图片检查 corrupt JPEG restored and saved')
# self.labelimg.add_argument('-l', '--label', action="store_true", default=False, help='标签统计')
self.resize = self.subparsers.add_parser('resize', help='修改图片尺寸', parents=[self.parent_parser])
# self.parser = argparse.ArgumentParser(description='自动切割学习数据')
# self.resize.add_argument('--source', type=str, default=None, help='图片来源地址')
# self.resize.add_argument('--target', default=None, type=str, help='图片目标地址')
self.resize.add_argument('--imgsz', type=int, default=640, help='长边尺寸', metavar=640)
self.resize.add_argument('--output', type=str, default=None, help='输出识别图像', metavar="")
# self.resize.add_argument('--clean', action="store_true", default=False, help='清理之前的数据')
# self.resize.add_argument('--md5sum', action="store_true", default=False, help='使用md5作为文件名')
# self.resize.add_argument('--uuid', action="store_true", default=False, help='重命名图片为UUID')
# self.resize.add_argument('--crop', action="store_true", default=False, help='裁剪')
# self.args = self.parser.parse_args()
self.classify = self.subparsers.add_parser('classify', help='图像分类数据处理', parents=[self.parent_parser])
# self.classify.add_argument('--source', type=str, default=None, help='图片来源地址')
# self.classify.add_argument('--target', default=None, type=str, help='图片目标地址')
self.classify.add_argument('--output', type=str, default=None, help='输出识别图像', metavar="")
self.classify.add_argument('--checklist', type=str, default=None, help='输出识别图像', metavar="")
self.classify.add_argument('--test', type=int, default=10, help='测试数量', metavar=100)
# self.classify.add_argument('--clean', action="store_true", default=False, help='清理之前的数据')
self.classify.add_argument('--crop', action="store_true", default=False, help='裁剪')
self.classify.add_argument('--model', type=str, default=None, help='裁剪模型', metavar="")
self.classify.add_argument('--uuid', action="store_true", default=False, help='重命名图片为UUID')
self.classify.add_argument('--verbose', action="store_true", default=False, help='过程输出')
self.parser = parser
def main(self):