当前位置: 首页 > 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.cnnetsun.cn/news/1576885.html

相关文章:

  • CSCAN磁盘调度算法实战:从真题解析到避坑指南
  • 导师认可的AI写作辅助软件星级排名(2026 权威发布)
  • 告别Vetur!Vue 3项目从VSCode插件、TS配置到Vite构建的完整避坑指南
  • 告别OOM崩溃!Python 3.9+智能体内存调度策略全解,含一键安装脚本与内存占用下降67%实测数据
  • VITA57.1标准实战:手把手教你设计兼容FMC接口的FPGA载板
  • 从合并果子到修篱笆:用C++优先队列(priority_queue)搞定两道经典贪心题
  • 3步掌握B站视频下载:BilibiliDown跨平台解决方案完全指南
  • FLUX.1-dev-fp8-dit文生图开源大模型部署:支持LoRA微调的ComfyUI环境配置
  • 实测Nanbeige 4.1-3B Streamlit UI:二次元风格聊天机器人搭建
  • Boss-Key终极指南:如何用一键隐藏技术保护你的办公隐私
  • OpenCascade避坑指南:TopoDS_Shape共享机制与常见错误排查
  • Notepad4:高效编辑全能工具从入门到精通
  • ScanTailor Advanced:开源扫描文档处理的高效解决方案
  • 从Flamingo到FocusLLaVA:视觉token压缩如何从‘硬编码’走向‘自适应’?
  • 2024最新Bypass Paywalls Clean全流程使用指南:从原理到实战的浏览器扩展技术手册
  • 视频渲染引擎技术指南:HDR画质增强与开源实现方案
  • Stable Yogi Leather-Dress-Collection基础教程:SD1.5底座模型float16加载详解
  • ChanlunX缠论工具:重构技术分析的自动化引擎
  • BMS充电管理避坑指南:从国标原理到AutoSar SWC设计的5个关键点
  • Lite-Avatar模型压缩技术:从理论到实践
  • OpenClaw+Qwen3-VL:30B:多模态AI助手案例展示
  • ASMR下载器终极指南:一键获取25619+音频资源的完整解决方案
  • Bongo Cat模型选型指南:场景适配与性能优化实战
  • EasyExcel实战:如何让@ExcelProperty支持多语言表头匹配(附完整代码)
  • Fluent滑移网格实战:螺旋桨瞬态水动力性能仿真解析
  • coze-loop惊艳案例:看AI如何将混乱代码重构为优雅解决方案
  • AI编程实战:使用DAMOYOLO-S构建智能视觉检测应用
  • 告别龟速下载!手把手教你用VMware+ISO镜像给UOS 20/CentOS 8配置离线本地源
  • 终极Windows 11优化指南:一键清理系统垃圾,让电脑焕然一新
  • 从倒立摆到无人机:雅可比矩阵线性化如何让‘不稳定’系统变得可控?