You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

OpenCV图像与标签旋转问题:仅图像旋转,标签未同步旋转

同步旋转图像与XML标签的实现方案

要解决图像旋转后标签未同步的问题,核心是对XML文件中的**目标边界框(Bounding Box)**进行对应角度的坐标变换,同时更新图像尺寸信息。以下是修改后的完整代码:

import cv2
import os
import xml.etree.ElementTree as ET

def rotate_bbox(bbox, original_size, rotated_angle):
    """根据旋转角度计算变换后的边界框坐标"""
    xmin, ymin, xmax, ymax = bbox
    original_w, original_h = original_size

    if rotated_angle == 90:  # 顺时针旋转90度
        new_w, new_h = original_h, original_w
        # 坐标转换:原(x,y) → 新(y, original_w - x)
        new_xmin = ymin
        new_ymin = original_w - xmax
        new_xmax = ymax
        new_ymax = original_w - xmin
    elif rotated_angle == 180:  # 旋转180度
        new_w, new_h = original_w, original_h
        # 坐标转换:原(x,y) → 新(original_w - x, original_h - y)
        new_xmin = original_w - xmax
        new_ymin = original_h - ymax
        new_xmax = original_w - xmin
        new_ymax = original_h - ymin
    elif rotated_angle == -90:  # 逆时针旋转90度(等价于顺时针270度)
        new_w, new_h = original_h, original_w
        # 坐标转换:原(x,y) → 新(original_h - y, x)
        new_xmin = original_h - ymax
        new_ymin = xmin
        new_xmax = original_h - ymin
        new_ymax = xmax
    else:
        new_w, new_h = original_w, original_h
        new_xmin, new_ymin, new_xmax, new_ymax = xmin, ymin, xmax, ymax

    return (int(new_xmin), int(new_ymin), int(new_xmax), int(new_ymax)), (new_w, new_h)

data_directory = "face_dataset"

# 仅筛选图像文件
image_files = [file for file in os.listdir(data_directory) 
               if file.lower().endswith((".jpg", ".jpeg", ".png"))]

for i, file in enumerate(image_files):
    image_path = os.path.join(data_directory, file)
    label_path = os.path.join(data_directory, os.path.splitext(file)[0] + ".xml")

    # 加载图像
    image = cv2.imread(image_path)
    if image is None:
        print(f"加载图像失败: {image_path}")
        continue
    original_h, original_w = image.shape[:2]

    # 确定旋转角度与对应OpenCV常量
    if i < 55:
        rotated_angle = 90
        cv2_rotate_flag = cv2.ROTATE_90_CLOCKWISE
    elif i < 110:
        rotated_angle = 180
        cv2_rotate_flag = cv2.ROTATE_180
    else:
        rotated_angle = -90
        cv2_rotate_flag = cv2.ROTATE_90_COUNTERCLOCKWISE

    # 旋转图像并保存
    rotated_image = cv2.rotate(image, cv2_rotate_flag)
    if rotated_image is None:
        print(f"旋转图像失败: {image_path}")
        continue
    cv2.imwrite(image_path, rotated_image)

    # 处理XML标签(如果存在)
    if not os.path.exists(label_path):
        print(f"标签文件不存在: {label_path}")
        continue

    # 解析XML并更新内容
    tree = ET.parse(label_path)
    root = tree.getroot()

    # 更新图像尺寸
    size_node = root.find('size')
    size_node.find('width').text = str(rotated_image.shape[1])
    size_node.find('height').text = str(rotated_image.shape[0])

    # 更新每个目标的边界框
    for obj in root.findall('object'):
        bndbox = obj.find('bndbox')
        xmin = int(bndbox.find('xmin').text)
        ymin = int(bndbox.find('ymin').text)
        xmax = int(bndbox.find('xmax').text)
        ymax = int(bndbox.find('ymax').text)

        new_bbox, _ = rotate_bbox((xmin, ymin, xmax, ymax), (original_w, original_h), rotated_angle)
        new_xmin, new_ymin, new_xmax, new_ymax = new_bbox

        bndbox.find('xmin').text = str(new_xmin)
        bndbox.find('ymin').text = str(new_ymin)
        bndbox.find('xmax').text = str(new_xmax)
        bndbox.find('ymax').text = str(new_ymax)

    # 保存修改后的XML
    tree.write(label_path)

关键说明

  • 坐标变换逻辑:针对三种旋转角度推导了边界框坐标的转换公式,确保旋转后目标位置与图像完全匹配。
  • XML处理:通过ElementTree解析XML结构,更新图像尺寸字段,并遍历所有目标节点修改边界框坐标。
  • 代码优化:使用OpenCV官方旋转常量替代数值,提升可读性;增加标签文件存在性检查,避免无意义报错。

内容的提问来源于stack exchange,提问作者Cihan Yalçın

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 05:03:18