labelme进阶技巧:高效批量更新标注标签的自动化方案

张开发
2026/4/10 4:36:44 15 分钟阅读

分享文章

labelme进阶技巧:高效批量更新标注标签的自动化方案
1. 为什么需要批量修改labelme标签做过图像标注的朋友都知道labelme生成的JSON文件里保存了所有标注对象的形状和标签信息。当我们需要调整标签命名规范时比如把car统一改成vehicle手动一个个文件修改简直是噩梦。我去年处理过一个包含5000张图片的自动驾驶数据集就因为客户临时要求更改标签命名规范差点加班到天亮。批量修改标签的典型场景包括统一不同标注人员的标签命名差异比如有人标dog有人标puppy数据集合并时需要避免标签冲突标签体系升级如从red_light改为traffic_light/red修复拼写错误的标签2. 基础批量修改方案2.1 单标签替换脚本解析原始文章给出的基础脚本已经能解决80%的需求我们先拆解这个万能模板import os import json json_dir /path/to/json/files # 关键参数1JSON文件目录 old_label cat # 关键参数2旧标签名 new_label feline # 关键参数3新标签名 for filename in os.listdir(json_dir): if filename.endswith(.json): json_path os.path.join(json_dir, filename) with open(json_path, r, encodingutf-8) as f: data json.load(f) if shapes in data: for obj in data[shapes]: if obj[label] old_label: obj[label] new_label with open(json_path, w, encodingutf-8) as f: json.dump(data, f, ensure_asciiFalse, indent4)这个脚本的精髓在于遍历目录下所有.json文件加载每个文件的标注数据在shapes数组中查找匹配的标签执行替换后保存文件注意确保文件编码为utf-8否则中文标签可能会乱码2.2 常见问题排查在实际使用中我遇到过几个典型问题权限问题脚本运行后文件没变化可能是写入权限不足建议先测试单个文件路径问题Windows用户注意路径分隔符要用os.path.join处理编码问题遇到UnicodeDecodeError时可以尝试encodinggbk3. 高级批量修改技巧3.1 多标签批量替换当需要修改多个标签时可以用字典来定义替换规则label_mapping { cat: feline, dog: canine, car: vehicle } for filename in os.listdir(json_dir): if filename.endswith(.json): json_path os.path.join(json_dir, filename) with open(json_path, r) as f: data json.load(f) if shapes in data: for obj in data[shapes]: if obj[label] in label_mapping: obj[label] label_mapping[obj[label]] with open(json_path, w) as f: json.dump(data, f, ensure_asciiFalse, indent2)3.2 正则表达式匹配对于更灵活的匹配需求比如把所有包含old_前缀的标签改为new_import re pattern re.compile(r^old_) replacement new_ for obj in data[shapes]: obj[label] pattern.sub(replacement, obj[label])4. 实战案例标签体系升级最近帮一个医疗影像项目升级标签体系需要将病变类型/位置的扁平标签改为层级结构。比如把nodule_left改为nodule/lung_left这是典型的多规则替换场景def upgrade_label(label): rules [ (r^(nodule)_(.*), r\1/lung_\2), (r^(mass)_(.*), r\1/liver_\2), (r^cyst_, cyst/kidney_) ] for pattern, repl in rules: label re.sub(pattern, repl, label) return label # 在遍历shapes时调用 for obj in data[shapes]: obj[label] upgrade_label(obj[label])这个案例的特殊之处在于需要保留原始标签的部分内容不同病变类型要映射到不同器官要处理多种命名格式5. 工程化建议5.1 备份策略直接修改原文件风险很大建议先创建副本import shutil backup_dir os.path.join(json_dir, backup) os.makedirs(backup_dir, exist_okTrue) for filename in os.listdir(json_dir): if filename.endswith(.json): shutil.copy2( os.path.join(json_dir, filename), os.path.join(backup_dir, filename) )5.2 批量验证脚本修改后需要验证结果这个脚本可以统计标签变化from collections import defaultdict label_counts defaultdict(int) for filename in os.listdir(json_dir): if filename.endswith(.json): with open(os.path.join(json_dir, filename), r) as f: data json.load(f) for obj in data.get(shapes, []): label_counts[obj[label]] 1 print(修改后标签统计) for label, count in label_counts.items(): print(f{label}: {count}次)6. 性能优化技巧处理上万文件时原始脚本可能较慢。我有三个优化建议多进程处理from multiprocessing import Pool def process_file(filename): # 处理单个文件的代码 with Pool(processes4) as pool: pool.map(process_file, [f for f in os.listdir(json_dir) if f.endswith(.json)])内存映射加速import mmap with open(json_path, r) as f: mm mmap.mmap(f.fileno(), 0) data json.loads(mm.read()) # 处理数据 mm.seek(0) mm.write(json.dumps(data).encode()) mm.close()增量处理processed set() if os.path.exists(processed.log): with open(processed.log, r) as f: processed.update(f.read().splitlines()) for filename in os.listdir(json_dir): if filename.endswith(.json) and filename not in processed: # 处理文件 with open(processed.log, a) as f: f.write(filename \n)7. 异常处理机制健壮的脚本应该能处理各种异常情况for filename in os.listdir(json_dir): try: if not filename.endswith(.json): continue json_path os.path.join(json_dir, filename) try: with open(json_path, r, encodingutf-8) as f: try: data json.load(f) except json.JSONDecodeError: print(f文件 {filename} JSON格式错误) continue except IOError: print(f文件 {filename} 读取失败) continue if shapes not in data: continue modified False for obj in data[shapes]: if obj[label] old_label: obj[label] new_label modified True if modified: try: with open(json_path, w, encodingutf-8) as f: json.dump(data, f, ensure_asciiFalse, indent4) except IOError: print(f文件 {filename} 写入失败) except Exception as e: print(f处理 {filename} 时发生未知错误: {str(e)})这个增强版脚本可以跳过非JSON文件捕获JSON解析错误处理文件读写异常记录修改状态捕获未知异常8. 可视化修改效果最后推荐一个实用的可视化检查方法使用labelme自带的查看功能labelme_draw_json 修改后的json文件或者用这个Python脚本生成对比图import cv2 import numpy as np def visualize(json_path, image_dir): with open(json_path, r) as f: data json.load(f) img_path os.path.join(image_dir, data[imagePath]) img cv2.imread(img_path) for obj in data[shapes]: points np.array(obj[points], dtypenp.int32) cv2.polylines(img, [points], True, (0,255,0), 2) cv2.putText(img, obj[label], tuple(points[0]), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0,0,255), 2) cv2.imshow(Preview, img) cv2.waitKey(0)这个脚本可以直观显示每个标注对象的标签和位置特别适合检查批量修改后的结果。

更多文章