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

Python图像任意角度旋转及像素坐标正反映射问题求助

解决方案:图像任意角度旋转及像素坐标双向转换

核心问题分析

PIL的image.rotate(θ, expand=True)是围绕原图中心旋转,且自动计算旋转后完整图像尺寸。你的坐标转换仅在90度倍数角度生效,问题出在未处理两个关键细节:

  • PIL坐标系以左上角为原点,y轴向下,与数学坐标系(y轴向上)存在差异
  • 旋转基于图像中心进行,必须先完成坐标的中心平移,旋转后再平移回新图像的坐标系

1. 图像旋转实现(复用你的方案,补充说明)

直接使用PIL的rotate方法,expand=True会自动计算旋转后不裁剪的完整尺寸,默认填充黑色(可通过fill参数指定填充色):

from PIL import Image

def rotate_image(image, angle):
    # 可选添加resample参数优化旋转画质,比如resample=Image.Resampling.LANCZOS
    return image.rotate(angle, expand=True)

2. 原图坐标 → 旋转后坐标(正向转换)

推导逻辑

  1. 计算原图中心坐标(cx, cy):cx = 原图宽度/2,cy = 原图高度/2
  2. 将原图像素坐标转换为中心为原点的坐标系:x0 = x - cx,y0 = y - cy
  3. 适配PIL的y轴方向,使用调整后的旋转矩阵计算旋转后的中心坐标系坐标
  4. 计算旋转后图像的中心坐标(cx_new, cy_new),将中心坐标系坐标转换回新图像的左上角坐标系

代码实现

import math

def original_to_rotated(x, y, original_size, rotated_size, angle):
    w, h = original_size
    w_new, h_new = rotated_size
    
    # 原图中心坐标
    cx, cy = w / 2, h / 2
    # 转换为中心原点坐标系
    x0 = x - cx
    y0 = y - cy
    
    rad = math.radians(angle)
    cosθ = math.cos(rad)
    sinθ = math.sin(rad)
    
    # 适配PIL y轴向下的旋转计算
    x1 = x0 * cosθ + y0 * sinθ
    y1 = -x0 * sinθ + y0 * cosθ
    
    # 旋转后图像的中心坐标
    cx_new, cy_new = w_new / 2, h_new / 2
    # 转换回旋转后图像的左上角坐标系
    x_r = x1 + cx_new
    y_r = y1 + cy_new
    
    return x_r, y_r  # 返回浮点坐标,需整数可使用round()

3. 旋转后坐标 → 原图坐标(反向转换)

推导逻辑

反向转换相当于对旋转后的坐标执行逆旋转(即旋转-θ角度),步骤与正向转换相反:

  1. 将旋转后图像的像素坐标转换为中心坐标系
  2. 用逆旋转矩阵计算原图的中心坐标系坐标
  3. 转换回原图的左上角坐标系

代码实现

def rotated_to_original(x_r, y_r, original_size, rotated_size, angle):
    w, h = original_size
    w_new, h_new = rotated_size
    
    # 旋转后图像的中心坐标
    cx_new, cy_new = w_new / 2, h_new / 2
    # 转换为中心原点坐标系
    x1 = x_r - cx_new
    y1 = y_r - cy_new
    
    rad = math.radians(angle)
    cosθ = math.cos(rad)
    sinθ = math.sin(rad)
    
    # 逆旋转计算(等价于旋转-θ角度)
    x0 = x1 * cosθ - y1 * sinθ
    y0 = x1 * sinθ + y1 * cosθ
    
    # 原图中心坐标
    cx, cy = w / 2, h / 2
    # 转换回原图的左上角坐标系
    x = x0 + cx
    y = y0 + cy
    
    return x, y

完整测试示例

# 加载测试图像
img = Image.open("test_img.png")
angle = 45

# 执行旋转
rotated_img = rotate_image(img, angle)
original_size = img.size
rotated_size = rotated_img.size

# 测试正向转换:取原图中红点的坐标
x_orig, y_orig = 200, 150
x_rot, y_rot = original_to_rotated(x_orig, y_orig, original_size, rotated_size, angle)
print(f"原图({x_orig},{y_orig}) → 旋转后({x_rot:.2f},{y_rot:.2f})")

# 测试反向转换
x_back, y_back = rotated_to_original(x_rot, y_rot, original_size, rotated_size, angle)
print(f"旋转后({x_rot:.2f},{y_rot:.2f}) → 原图({x_back:.2f},{y_back:.2f})")  # 结果应与原坐标几乎一致

关键注意事项

  • 所有计算使用浮点型避免截断误差,最终若需像素坐标可通过round()或int()转换
  • 该公式对90/180/270度等整数倍旋转同样适用,与你原有的逻辑兼容
  • 若需要更平滑的旋转画质,可在rotate_image函数中添加resample=Image.Resampling.LANCZOS参数

内容的提问来源于stack exchange,提问作者WeReign

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 05:16:33