当前位置: 首页 > news >正文

告别手动重标:基于Python脚本的Labelme数据集增强与JSON同步更新实战

1. 为什么我们需要自动化处理Labelme标注数据

做计算机视觉项目的朋友都知道,数据标注是个体力活。特别是使用Labelme这类工具进行语义分割标注时,每张图片都要手动勾勒物体轮廓,工作量巨大。更让人头疼的是,当我们对原始图片进行数据增强(比如镜像翻转、旋转)时,对应的JSON标注文件却不会自动更新。

我最近就遇到了这个痛点。项目需要增加数据多样性,打算对标注好的数据集做90°、180°、270°旋转增强。按照传统做法,每旋转一张图片,就得重新标注一次,相当于工作量直接翻三倍!这种重复劳动不仅效率低下,还容易出错。

好在Python脚本可以帮我们解决这个问题。通过编写自动化脚本,我们可以在对图片进行几何变换的同时,智能地更新对应的JSON标注文件。这样既保证了数据增强的效果,又避免了重复标注的麻烦。下面我就分享下这个实战经验,手把手教你如何用Python脚本实现Labelme数据集的智能增强。

2. 环境准备与核心思路

2.1 基础环境配置

在开始之前,我们需要准备好开发环境。这个方案主要依赖以下几个Python库:

  • Pillow:用于图像处理
  • glob:用于文件路径匹配
  • json:用于JSON文件读写
  • os:用于系统路径操作
  • math:用于旋转计算

安装命令很简单:

pip install pillow

其他库都是Python标准库,无需额外安装。建议使用Python 3.6+版本,我在3.8环境下测试通过。

2.2 核心解决思路

整个方案的核心逻辑其实很清晰:

  1. 遍历原始图片文件夹,获取所有待处理的图片
  2. 对每张图片执行几何变换(镜像、旋转等)
  3. 同时读取对应的JSON标注文件
  4. 根据相同的变换规则,更新JSON中的标注点坐标
  5. 保存变换后的图片和更新后的JSON文件

关键在于第四步——如何准确计算变换后的标注点坐标。对于镜像翻转,我们需要找到中轴线,然后对称翻转所有点坐标;对于旋转,则需要使用三角函数进行坐标变换。

3. 实战代码解析与常见坑点

3.1 文件路径处理的正确姿势

原始代码中第一个大坑就是文件路径处理。很多人在运行脚本时发现输出为空,就是因为路径匹配出了问题。

错误示范:

img_list = glob.glob(path + "*" + file_format)

这种写法有两个问题:

  1. 如果路径中包含特殊字符(如下划线_),通配符*可能匹配失败
  2. 直接用+拼接路径在不同操作系统下可能不兼容

正确做法是使用os.path.join:

img_list = glob.glob(os.path.join(path, '*.jpg'))

同样,输出文件路径也应该这样处理:

full_path = os.path.join(save_path, LR + file_name)

3.2 镜像翻转的坐标计算

对于左右镜像翻转,我们需要先找到图片的中轴线,然后将所有标注点对称翻转。具体算法如下:

  1. 获取图片宽度width,计算中轴mid_width = width / 2
  2. 对于每个标注点(temp_x, temp_y):
    • 计算该点到中轴的距离dis
    • 新x坐标 = 中轴坐标 ± 距离(根据原位置决定加减)
    • y坐标保持不变

代码实现:

if temp_x > mid_width: dis = temp_x - mid_width new_x = mid_width - dis elif temp_x < mid_width: dis = mid_width - temp_x new_x = mid_width + dis else: new_x = temp_x new_y = temp_y

3.3 旋转操作的坐标变换

旋转操作相对复杂些,需要用到三角函数计算。核心公式是旋转矩阵:

新x = (x - mid_x)*cosθ - (y - mid_y)*sinθ + mid_x 新y = (x - mid_x)*sinθ + (y - mid_y)*cosθ + mid_y

Python实现:

new_x = (temp_x - mid_width) * math.cos(math.radians(angel)) - (temp_y - mid_height) * math.sin(math.radians(angel)) + mid_width new_y = (temp_x - mid_width) * math.sin(math.radians(angel)) + (temp_y - mid_height) * math.cos(math.radians(angel)) + mid_height

注意这里角度需要转换为弧度,且旋转是绕图片中心点进行的。

4. 完整代码实现与使用指南

4.1 完整增强脚本

下面是整合了所有功能的完整代码,包含了左右镜像、上下镜像以及旋转增强:

from PIL import Image import os import glob import json import base64 import math # 配置参数 path = "your_dataset_path" # 原始数据集路径 save_path = "your_output_path" # 输出路径 file_format = ".jpg" # 图片格式 replace_format = ".json" # JSON文件格式 # 文件名前缀 LR = "lr_" # 左右镜像 TB = "tb_" # 上下镜像 R90 = "r90_" # 旋转90度 R180 = "r180_" # 旋转180度 R270 = "r270_" # 旋转270度 # 获取图片列表 img_list = glob.glob(os.path.join(path, f'*{file_format}')) def mirror_lr(): """左右镜像增强""" print("开始左右镜像处理...") for img_path in img_list: json_path = img_path.replace(file_format, replace_format) if not os.path.exists(json_path): continue with open(json_path, encoding='utf-8') as f: setting = json.load(f) # 计算中轴并更新坐标 width = setting['imageWidth'] mid_width = width / 2 for shape in setting['shapes']: for point in shape['points']: temp_x = point[0] if temp_x > mid_width: point[0] = mid_width - (temp_x - mid_width) elif temp_x < mid_width: point[0] = mid_width + (mid_width - temp_x) # 保存结果 file_name = setting['imagePath'] setting['imagePath'] = LR + file_name img_save_path = os.path.join(save_path, LR + file_name) json_save_path = img_save_path.replace(file_format, replace_format) # 处理图片 Image.open(img_path).transpose(Image.FLIP_LEFT_RIGHT).save(img_save_path) with open(img_save_path, 'rb') as f: setting['imageData'] = base64.b64encode(f.read()).decode() # 保存JSON with open(json_save_path, 'w', encoding='utf-8') as f: json.dump(setting, f) def rotate(angle, prefix): """旋转增强""" print(f"开始旋转{angle}度处理...") rad = math.radians(angle) cos_val = math.cos(rad) sin_val = math.sin(rad) for img_path in img_list: json_path = img_path.replace(file_format, replace_format) if not os.path.exists(json_path): continue with open(json_path, encoding='utf-8') as f: setting = json.load(f) # 计算中心点并更新坐标 width = setting['imageWidth'] height = setting['imageHeight'] mid_width = width / 2 mid_height = height / 2 for shape in setting['shapes']: for point in shape['points']: x, y = point[0] - mid_width, point[1] - mid_height point[0] = x * cos_val - y * sin_val + mid_width point[1] = x * sin_val + y * cos_val + mid_height # 保存结果 file_name = setting['imagePath'] setting['imagePath'] = prefix + file_name img_save_path = os.path.join(save_path, prefix + file_name) json_save_path = img_save_path.replace(file_format, replace_format) # 处理图片 if angle == 90: Image.open(img_path).transpose(Image.ROTATE_270).save(img_save_path) elif angle == 180: Image.open(img_path).transpose(Image.ROTATE_180).save(img_save_path) elif angle == 270: Image.open(img_path).transpose(Image.ROTATE_90).save(img_save_path) with open(img_save_path, 'rb') as f: setting['imageData'] = base64.b64encode(f.read()).decode() with open(json_save_path, 'w', encoding='utf-8') as f: json.dump(setting, f) # 执行增强 mirror_lr() rotate(90, R90) rotate(180, R180) rotate(270, R270)

4.2 使用说明

  1. 修改脚本开头的路径配置:

    • path:原始数据集路径
    • save_path:增强后数据保存路径
    • file_format:图片格式(如.jpg、.png)
  2. 根据需要注释/取消注释最后的增强函数调用

  3. 运行脚本后,增强后的图片和JSON会自动保存到输出路径

  4. 可以使用Labelme打开生成的JSON文件检查标注是否正确

5. 实际应用中的优化建议

5.1 批量处理与进度显示

当处理大量图片时,建议添加进度显示功能。可以这样修改:

total = len(img_list) for idx, img_path in enumerate(img_list): print(f"处理进度: {idx+1}/{total} ({((idx+1)/total)*100:.1f}%)") # 剩余处理代码...

5.2 异常处理与日志记录

为了增强脚本的健壮性,应该添加异常处理:

try: with open(json_path, encoding='utf-8') as f: setting = json.load(f) except Exception as e: print(f"处理{json_path}时出错: {str(e)}") continue

5.3 多进程加速

对于超大规模数据集,可以使用多进程加速:

from multiprocessing import Pool def process_image(img_path): # 把原来的处理逻辑封装到这里 pass if __name__ == '__main__': with Pool(4) as p: # 4个进程 p.map(process_image, img_list)

这个脚本我已经在实际项目中多次使用,效果非常稳定。特别是在处理几千张图片的数据集时,节省了数百小时的人工标注时间。最大的收获不仅是效率提升,更重要的是保证了数据增强后标注的一致性——这是手动重标很难做到的。

http://www.jsqmd.com/news/559803/

相关文章:

  • Microsoft.Extensions.Caching.Hybrid性能优化:混合缓存策略完全解析
  • 西格列他钠是什么药?2026年双洛平降糖新药深度解析 - 品牌排行榜
  • 盘点2026年电源线包装机定制厂家,性价比高的在这里 - myqiye
  • 避坑指南:STM32F103驱动L9110s时常见的5个问题及解决方法
  • Kali Linux下Nessus 10.3.0安装避坑指南:从下载到首次扫描全流程
  • 2026本地教培GEO实操:大模型软文框架设计与留资防坑指南
  • RocketMQ Topic队列配置实战指南:从原理到最佳实践
  • 2026年杭州好用的讯灵AI推荐,获取公开邮箱及投诉联系电话 - 工业品网
  • C语言memcpy踩坑实录:内存重叠问题如何让你的代码崩溃(附memmove解决方案)
  • 专业指南:如何深度调优Ryujinx Switch模拟器实现60帧畅玩
  • YOLOv8增量训练保姆级避坑指南:冻结哪几层、学习率设多少、如何防过拟合
  • 好用的蓝莓混配基质品牌有哪些,广州地区可选择的多吗 - 工业设备
  • 2026年广东实力强的蓝莓混配基质厂家排名,这些靠谱品牌别错过 - 工业品网
  • QCustomPlot避坑指南:解决OpenGL加速下的闪屏和性能问题
  • 别墅装修进阶指南:西安家用电梯怎么选?晟瑞隆电梯,本土全流程服务标杆 - 深度智识库
  • OpenClaw安全防护:限制nanobot操作范围的5道防线
  • 告别动态注册!深入理解uniapp APP端与H5的组件注册差异(附main.js正确配置示例)
  • 2026年杭州选讯灵AI怎么样?其联系方式是啥 - 工业品牌热点
  • 从合并果子到修篱笆:用C++优先队列(priority_queue)搞定两道经典贪心题
  • 2026硫化氢/氰化氢报警仪产品推荐,固定式有毒气体报警仪性能与优势分析 - 品牌推荐大师
  • springboot+vue基于web的药店药品销售采购管理系统设计与实现
  • RuoYi-Vue3框架深度定制:灵活控制导航栏显隐的两种思路与避坑指南
  • 2026年全国做青少年科普展厅设计的靠谱企业推荐 - mypinpai
  • Understat:异步Python足球数据工具包 - 从数据获取到战术分析的全流程解决方案
  • SolidWorks设计文档智能生成:Nanbeige 4.1-3B理解三维模型
  • 3大维度解析企业内容安全如何通过开源工具降低70%审核成本
  • 2026年选购蓝莓基质混配基质,推荐一下靠谱的源头厂家 - 工业推荐榜
  • VibeVoice助力内容创作:短视频配音自动化流程设计
  • 从选型到布线:手把手教你为ADAS域控制器设计车载以太网硬件连接(含PHY/Switch配置要点)
  • EasyExcel通用监听器封装实战:告别重复代码,一个类搞定所有Excel导入校验与入库