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

labelme标注后的json文件和原图同步按角度旋转

点击查看代码
import json
import os
import base64
import numpy as np
import cv2
from math import cos, sin, radians
import argparsedef rotate_point_opencv_style(point, rotation_matrix):"""用OpenCV旋转矩阵计算标注点,确保与图像旋转逻辑一致"""point_homo = np.array([point[0], point[1], 1], dtype=np.float32)rotated_point = np.dot(rotation_matrix, point_homo)return (round(rotated_point[0]), round(rotated_point[1]))def image_to_base64(img):"""OpenCV图像转Base64编码(LabelMe imageData字段需求)"""ret, buffer = cv2.imencode('.jpg', img)if not ret:raise ValueError("无法编码图像为JPEG格式")return base64.b64encode(buffer).decode('utf-8')def rotate_single_labelme(json_path, output_root_dir, angle):"""单文件LabelMe旋转处理(核心功能,复用原逻辑):param json_path: 单个JSON文件路径:param output_root_dir: 批量输出的根目录(会保持原目录结构):param angle: 旋转角度(度,逆时针为正)"""try:# 1. 读取LabelMe数据和原始图像with open(json_path, 'r', encoding='utf-8') as f:data = json.load(f)# 拼接原始图像路径(LabelMe的imagePath是相对JSON的路径)img_origin_rel_path = data['imagePath']img_origin_abs_path = os.path.join(os.path.dirname(json_path), img_origin_rel_path)img_origin = cv2.imread(img_origin_abs_path)if img_origin is None:raise FileNotFoundError(f"图像文件不存在:{img_origin_abs_path}")# 2. 图像维度与旋转中心(严格对齐LabelMe与OpenCV坐标)h_origin, w_origin = img_origin.shape[:2]  # OpenCV:h=行(y),w=列(x)center_origin = (w_origin // 2, h_origin // 2)  # 旋转中心:(x=列, y=行)# 3. 计算旋转后图像尺寸(避免裁剪,完整保留旋转内容)angle_rad = radians(angle)w_rot = int(h_origin * abs(sin(angle_rad)) + w_origin * abs(cos(angle_rad)))h_rot = int(w_origin * abs(sin(angle_rad)) + h_origin * abs(cos(angle_rad)))# 4. 生成OpenCV旋转矩阵(图像与标注共用,强制同步)M = cv2.getRotationMatrix2D(center=center_origin, angle=angle, scale=1.0)# 偏移量:让旋转后的图像居中显示dx = (w_rot // 2) - center_origin[0]dy = (h_rot // 2) - center_origin[1]M[0, 2] += dxM[1, 2] += dy# 5. 旋转图像img_rot = cv2.warpAffine(src=img_origin,M=M,dsize=(w_rot, h_rot),flags=cv2.INTER_LINEAR,borderMode=cv2.BORDER_CONSTANT,borderValue=(255, 255, 255)  # 白色背景(可自定义))# 6. 旋转标注点(用同一个矩阵M,无偏差)for shape in data['shapes']:rotated_points = [rotate_point_opencv_style(p, M) for p in shape['points']]shape['points'] = [list(p) for p in rotated_points]  # 转为List格式(LabelMe要求)# 7. 构建输出路径(保持原目录结构,避免文件混乱)# 例:原路径 ./data/label1.json → 输出 ./output/data/label1_rotated_90deg.jsonrelative_json_path = os.path.relpath(json_path, start=os.path.dirname(output_root_dir))output_json_dir = os.path.join(output_root_dir, os.path.dirname(relative_json_path))os.makedirs(output_json_dir, exist_ok=True)# 8. 更新LabelMe JSON字段并保存# 处理图像文件名(添加旋转标识)img_filename = os.path.basename(img_origin_rel_path)img_name, img_ext = os.path.splitext(img_filename)new_img_name = f"{img_name}_rotated_{angle}deg{img_ext}"# 处理JSON文件名json_filename = os.path.basename(json_path)json_name, json_ext = os.path.splitext(json_filename)new_json_name = f"{json_name}_rotated_{angle}deg{json_ext}"# 更新JSON内容data['imagePath'] = new_img_name  # 关联旋转后的图像data['imageWidth'] = w_rot  # 旋转后图像宽度(列数)data['imageHeight'] = h_rot  # 旋转后图像高度(行数)data['imageData'] = image_to_base64(img_rot)  # 更新Base64编码# 保存旋转后的图像和JSONoutput_img_path = os.path.join(output_json_dir, new_img_name)output_json_path = os.path.join(output_json_dir, new_json_name)cv2.imwrite(output_img_path, img_rot)with open(output_json_path, 'w', encoding='utf-8') as f:json.dump(data, f, ensure_ascii=False, indent=2)return True, f"成功:{os.path.basename(json_path)} → {new_json_name}"except Exception as e:return False, f"失败:{os.path.basename(json_path)} → 错误原因:{str(e)}"def batch_rotate_labelme(input_root_dir, output_root_dir, angle):"""批量旋转目录中所有LabelMe JSON文件:param input_root_dir: 输入根目录(会递归遍历所有子目录):param output_root_dir: 输出根目录(保持与输入一致的目录结构):param angle: 旋转角度(度,逆时针为正)"""# 校验输入目录是否存在if not os.path.isdir(input_root_dir):raise NotADirectoryError(f"输入目录不存在:{input_root_dir}")os.makedirs(output_root_dir, exist_ok=True)# 统计变量total_count = 0success_count = 0fail_list = []# 递归遍历所有子目录,寻找JSON文件for root, dirs, files in os.walk(input_root_dir):for file in files:# 只处理LabelMe生成的JSON文件(避免非标注JSON)if file.endswith('.json'):total_count += 1json_abs_path = os.path.join(root, file)# 调用单文件处理函数success, msg = rotate_single_labelme(json_abs_path, output_root_dir, angle)print(msg)  # 实时打印处理进度if success:success_count += 1else:fail_list.append(msg)# 输出批量处理总结print("\n" + "=" * 50)print(f"批量处理完成!")print(f"总文件数:{total_count}")print(f"成功数:{success_count}")print(f"失败数:{len(fail_list)}")if fail_list:print("失败详情:")for fail_msg in fail_list:print(f"  - {fail_msg}")if __name__ == "__main__":# 命令行参数配置(支持批量处理)parser = argparse.ArgumentParser(description='批量旋转LabelMe标注数据(图像与标注100%对齐)')parser.add_argument('input_dir', help='输入根目录(会递归遍历所有子目录的JSON)')parser.add_argument('output_dir', help='输出根目录(保持与输入一致的目录结构)')parser.add_argument('angle', type=float, help='旋转角度(度,逆时针为正,例:90/180/-90)')args = parser.parse_args()# 启动批量处理batch_rotate_labelme(args.input_dir, args.output_dir, args.angle)
http://www.hskmm.com/?act=detail&tid=13139

相关文章:

  • rk3588的ai功能和deepseek
  • EPSON L1300打印机清零教程
  • 「线性代数」矩阵运算与初等变换
  • 移动号码线上复机
  • Uni-App 使用android studio打包最新教程
  • tomcat CPU数量和线程数的关系
  • NASA运货飞船天鹅座再次推迟,航天任务为什么总是“彩排”不断
  • Centos系统切换为光盘本地源
  • 基于Hilbert-Huang变换(HHT)的模态分解与瞬时频率计算
  • NIO
  • python处理Excel单机小程序:匹数据,增强版VLookup
  • var sql 的不同用法
  • CF623B Array GCD
  • Python爬虫实现双色球历史数据抓取
  • 酵母细胞工厂全球调控策略研究进展:从遗传编辑到智能响应
  • Avalonia 根据绑定的数据类型动态选择模板
  • PyTorch图神经网络(一)
  • Python版Sigstore稳定版发布:软件供应链签名新标准
  • 网速带宽概念
  • 跨网传输软件:打通数据孤岛,保障安全流通!
  • 「KDOI-07」能量场
  • 基于LQR控制器的柔性机械臂抑振
  • 202507_QQ_caidundun
  • 国内企业邓白氏编码免费申请流程
  • 在CodeBolcks下wxSmith的C++编程教程——wxSmith教程目录(序言)
  • 生命周期
  • CF1893D Colorful Constructive 题解
  • C#通过15位或者18位身份证判断性别年龄
  • 深入解析:​​XMedia Recode 全能视频音频转换与编辑工具
  • MySQL同步ES的 5 种方案