
本文详解如何在 python 批量处理多文件时避免因全局/跨循环变量未清空引发的数据累积与坐标越界问题,重点解决 `coordinateslist` 持久化导致后续输出文件内容异常(行数增多、数值 >1)的核心缺陷。
在将 Dota 格式标注批量转换为 YOLO 格式时,一个常见却隐蔽的错误是:未在每次处理新文件前重置用于暂存坐标的列表变量。您提供的代码中,coordinatesList = list() 被定义在外部作用域(如 for 循环之外),导致它在处理第一个文件后仍保留历史数据;当处理第二个文件时,新解析的坐标被追加到该非空列表中,最终写入输出文件的内容既包含前一文件的残留数据,又混入当前文件数据——这直接造成输出行数膨胀、中心点或宽高值超出 [0, 1] 范围(YOLO 格式强制要求归一化坐标)。
正确做法是:确保 coordinatesList 在每次处理独立输入文件前被显式初始化为空列表。修改关键位置如下:
# ✅ 正确:在匹配图像与文本文件的 if 块内,紧邻文件读取前初始化
if imageFile[:-4] == textFile[:-4]:
imagePath = os.path.join(imageDir, imageFile)
textPath = os.path.join(textDir, textFile)
img = cv2.imread(imagePath)
coordinatesList = [] # ← 关键修复:每次处理新文件前清空列表
with open(textPath, "r") as f:
contents = f.read()
contentsSplitLine = contents.splitlines()[2:] # 跳过前两行元数据
for line in contentsSplitLine:
# 安全分割:移除类别和难度字段,提取坐标字符串
coords_str = ' '.join(line.rsplit(' ', 2)[:-2])
coordinatesList.append(coords_str.split())
# 后续坐标归一化逻辑(保持不变)
imageHeight, imageWidth = img.shape[:2] # 更健壮地获取尺寸(忽略 channels)
with open(f"./outputData/{textFile[:-4]}.txt", "w") as out_f: # ⚠️ 使用 'w' 而非 'a'
for coords in coordinatesList:
coords_int = [int(x) for x in coords] # 避免 eval 的安全风险
xs, ys = coords_int[::2], coords_int[1::2]
x_center = ((max(xs) + min(xs)) / 2) / imageWidth
y_center = ((max(ys) + min(ys)) / 2) / imageHeight
width = (max(xs) - min(xs)) / imageWidth
height = (max(ys) - min(ys)) / imageHeight
out_f.write(f"0 {x_center:.16f} {y_center:.16f} {width:.16f} {height:.16f}\n")关键改进说明:
- 作用域隔离:coordinatesList = [] 移入 if 条件块内部,确保每个文件独享干净的坐标容器;
- 文件写入模式修正:使用 "w"(覆盖写入)替代 "a"(追加写入),彻底杜绝因文件残留导致的重复写入;
- 安全性提升:用 int() 替代 eval() 解析坐标,防止恶意字符串执行;
- 鲁棒性增强:img.shape[:2] 兼容灰度/彩色图,textFile[:-4] 假设扩展名为 .txt,建议改用 os.path.splitext(textFile)[0] 更可靠。
额外注意事项:
立即学习“Python免费学习笔记(深入)”;
- 确保 imageDir 和 textDir 中文件名严格一一对应(如 P0000.jpg ↔ P0000.txt),否则匹配逻辑失效;
- 若存在编码问题(如 DOS 行尾 \r\n),splitlines() 可能引入空行,建议添加 line.strip() 过滤;
- 对于超大目录,可考虑使用 pathlib.Path 替代 os.path 提升可读性与跨平台兼容性。
遵循以上结构化重置与安全写入原则,即可稳定支持任意数量的 Dota→YOLO 批量转换,输出完全符合 YOLO 规范的归一化标注文件。










