You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

184 lines
6.1 KiB
Python

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import os
import shutil
import random
def get_all_dirs_with_files(root_dir):
"""递归获取 root_dir 下所有包含文件的目录"""
result = []
for dirpath, dirnames, filenames in os.walk(root_dir):
if filenames:
result.append(dirpath)
return result
def process_directory(dir_path, seq_num, all_pairs):
"""处理单个目录:修改文本文件第一列为序列号,BMP 复制对应的 txt"""
all_files = os.listdir(dir_path)
bmp_files = [f for f in all_files if f.lower().endswith('.bmp')]
txt_files = [f for f in all_files if f.lower().endswith('.txt')]
other_files = [f for f in all_files
if not f.lower().endswith('.bmp') and not f.lower().endswith('.txt')]
text_candidates = txt_files + other_files
if not text_candidates:
print(f" [{os.path.basename(dir_path)}] 未找到文本文件,跳过")
return
text_filename = text_candidates[0]
text_path = os.path.join(dir_path, text_filename)
if not os.path.isfile(text_path):
print(f" [{os.path.basename(dir_path)}] 跳过非文件: {text_filename}")
return
short_name = os.path.basename(dir_path)
print(f" 处理: {short_name} (seq={seq_num}), 文本: {text_filename}")
# 读取文件
try:
with open(text_path, 'r', encoding='utf-8') as f:
lines = f.readlines()
except (UnicodeDecodeError, PermissionError, OSError):
try:
with open(text_path, 'r', encoding='gbk') as f:
lines = f.readlines()
except Exception as e:
print(f" 读取失败: {e}")
return
modified_lines = []
for line in lines:
line = line.rstrip('\n').rstrip('\r')
if not line.strip():
modified_lines.append(line)
continue
parts = line.split()
if parts:
parts[0] = str(seq_num)
modified_lines.append(' '.join(parts))
# 写回
with open(text_path, 'w', encoding='utf-8') as f:
f.write('\n'.join(modified_lines))
print(f" 已修改 {len([l for l in modified_lines if l.strip()])} 行")
# 为每个 BMP 复制/关联文档
if bmp_files:
for bmp_filename in bmp_files:
bmp_basename = os.path.splitext(bmp_filename)[0]
copy_filename = f"{bmp_basename}.txt"
copy_path = os.path.join(dir_path, copy_filename)
if os.path.normpath(text_path) == os.path.normpath(copy_path):
all_pairs.append((os.path.join(dir_path, bmp_filename), copy_path))
else:
shutil.copy2(text_path, copy_path)
all_pairs.append((os.path.join(dir_path, bmp_filename), copy_path))
print(f" BMP 数量: {len(bmp_files)}")
else:
print(f" 无 BMP 图片")
def main():
base_dir = r"D:\xwechat_files\wxid_ttkf0xgmyihv22_2388\msg\file\2026-07\抓图(1)\抓图\新建文件夹\1\1\新建文件夹"
output_dir = os.path.join(os.path.dirname(base_dir.rstrip('\\/')), "yolo_output")
train_img_dir = os.path.join(output_dir, "train", "images")
train_lbl_dir = os.path.join(output_dir, "train", "labels")
val_img_dir = os.path.join(output_dir, "val", "images")
val_lbl_dir = os.path.join(output_dir, "val", "labels")
for d in [train_img_dir, train_lbl_dir, val_img_dir, val_lbl_dir]:
os.makedirs(d, exist_ok=True)
if not os.path.isdir(base_dir):
print(f"错误: 路径不存在: {base_dir}")
return
top_dirs = [d for d in os.listdir(base_dir) if os.path.isdir(os.path.join(base_dir, d))]
top_dirs.sort()
if not top_dirs:
print("没有找到子文件夹")
return
seq_start = 20
all_pairs = [] # (bmp_path, txt_path)
mapping = {} # 顶层文件夹名 -> 序列号
for idx, top_name in enumerate(top_dirs):
top_path = os.path.join(base_dir, top_name)
seq_num = seq_start + idx
mapping[top_name] = seq_num
print(f"\n{'='*50}")
print(f"顶层文件夹: {top_name} -> 序列号: {seq_num}")
# 递归获取该顶层文件夹下所有含文件的目录
leaf_dirs = get_all_dirs_with_files(top_path)
if not leaf_dirs:
print(f" 无文件,跳过")
continue
for leaf_dir in leaf_dirs:
process_directory(leaf_dir, seq_num, all_pairs)
# ---- 输出对应关系 ----
print("\n" + "=" * 50)
print("文件夹与序列号对应关系:")
print("-" * 40)
for name, num in mapping.items():
print(f" {num} : {name}")
print("=" * 50)
# ---- YOLO 格式 train/val 划分 ----
if not all_pairs:
print("\n没有 BMP 图片,跳过 YOLO 输出")
return
random.seed(42)
random.shuffle(all_pairs)
split_idx = int(len(all_pairs) * 0.8)
train_pairs = all_pairs[:split_idx]
val_pairs = all_pairs[split_idx:]
print(f"\n总图片数: {len(all_pairs)}")
print(f"train: {len(train_pairs)} | val: {len(val_pairs)}")
def copy_pairs(pairs, img_dir, lbl_dir):
for bmp_path, txt_path in pairs:
bmp_name = os.path.basename(bmp_path)
txt_name = os.path.basename(txt_path)
dst_bmp = bmp_name
dst_txt = txt_name
counter = 0
while os.path.exists(os.path.join(img_dir, dst_bmp)):
counter += 1
base, ext = os.path.splitext(bmp_name)
dst_bmp = f"{base}_{counter}{ext}"
dst_txt = f"{base}_{counter}.txt"
shutil.copy2(bmp_path, os.path.join(img_dir, dst_bmp))
shutil.copy2(txt_path, os.path.join(lbl_dir, dst_txt))
copy_pairs(train_pairs, train_img_dir, train_lbl_dir)
copy_pairs(val_pairs, val_img_dir, val_lbl_dir)
print(f"\nYOLO 格式输出: {output_dir}")
print(f" train/images/ ({len(train_pairs)} 张)")
print(f" train/labels/ ({len(train_pairs)} 个)")
print(f" val/images/ ({len(val_pairs)} 张)")
print(f" val/labels/ ({len(val_pairs)} 个)")
print("\n全部完成!")
if __name__ == "__main__":
main()