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

旋转NumPy索引实现图像旋转仅在两个角度区间有效

无裁剪旋转图像的索引计算问题

我正在开发一个Python程序,通过逐帧计算图像生成视频,需要实现无裁剪旋转图像的功能。最初参考了OpenCV无裁剪旋转的常规方法,但需要先大幅放大图像区域,现在为了优化速度,尝试仅旋转NumPy索引并尽可能缩小缩放范围。

最初的近似方案会导致轻微裁剪,于是改用cv2.boundingRectangle计算索引尺寸。这个方法在**-45° < 角度 < 45°或135° ≤ 角度 < 225°**区间表现完美,但在剩余角度区间,旋转后的图像无法匹配蓝色边界框,完全覆盖目标红色帧。

import numpy as np
import cv2

def diag_bw(x, y):
    '''生成测试图像的简单函数'''
    return (x + y)%179

def add_cnt(img, cnt, clr, thick):
    '''逐行绘制轮廓'''
    rcoos = [x[::-1] for x in cnt.points().astype(int)]
    for i in range(4):
        cv2.line(img, rcoos[i], rcoos[(i+1)%4], clr, thick)

def slice_frame(oshape, ishape): # (外框尺寸, 内框尺寸)
    '''计算内框在外框中的切片范围'''
    y0 = (oshape[0]-ishape[0])//2
    x0 = (oshape[1]-ishape[1])//2
    sy = slice(y0, y0 + ishape[0])
    sx = slice(x0, x0 + ishape[1])
    return sy, sx

def bbox_meth1(wica, hica, wifr, hifr, angle):    # 画布尺寸、帧尺寸、旋转角度
    '''利用cv2.boundingRect()旋转索引i,j'''
    canvas = np.zeros((hica, wica, 3), np.uint8)  # 创建画布用于显示帧外内容
    rotim = cv2.RotatedRect((hica//2, wica//2), (hifr, wifr), angle) # 旋转目标帧
    add_cnt(canvas, rotim, (0, 55, 0), 1)
    # 获取旋转后帧的外接矩形尺寸
    x, y, w, h = rotim.boundingRect()
    # 创建覆盖固定帧的旋转外接矩形(borec)并绘制
    borec = cv2.RotatedRect((wica//2, hica//2), (w, h), angle)
    add_cnt(canvas, borec, (255, 0, 0), 2)

    if (angle <= 45) or (135 <= angle < 225) or (angle > 315):
        # 旋转矩阵的中心
        mx, my = h//2, w//2
        M = cv2.getRotationMatrix2D((mx, my), angle, 1.)
        print(F" {M[0, 2]:6.1f}  {M[1, 2]:6.1f} ", end='')
        print(F" {w:7} {h:7} {mx:7}  {my:7} ", end='')

        i, j = np.indices(np.array([w, h])).astype(np.float32)
        i = cv2.warpAffine(i, M, (h, w))
        j = cv2.warpAffine(j, M, (h, w))
    else:   # 这些角度下旋转后的图像无法完全覆盖帧
        mx, my = w//2, h//2
        M = cv2.getRotationMatrix2D((mx, my), angle, 1.)#1.07#1.1)
        print(F" {M[0, 2]:6.1f}  {M[1, 2]:6.1f} ", end='')
        print(F" {h:7} {w:7} {mx:7}  {my:7} ", end='')

        i, j = np.indices(np.array([w, h])).astype(np.float32)
        i = cv2.warpAffine(i, M, (w, h))
        j = cv2.warpAffine(j, M, (w, h))
    print(F"{i.max():7.2f} {j.max():6.1f} {w:6} {h:6} {x:6} {y:6} ", end='')
    print(F"{borec.points()[1][0]:6.1f}{borec.points()[1][1]:6.1f}", end='')
    return [i, j], canvas

if __name__=='__main__':
    print(F"{'angle':10}{'M0':7}{'M1':7}{'w':8}{'h':11}", end='')
    print(F"{'mx':9}{'my':6}{'imax':7}{'jmax':9}{'w':9}{'h':6}", end='')
    print(F"{'x':6}{'y':7}{'b0':6}{'b1':6}{'ishape1':9}{'ishape0':9}", end='')
    print(F"{'i1':6}{'i0':6}")

    hic, wic = 1000, 1000           # 画布尺寸
    hi,wi = 270, 480                # 待显示的帧尺寸

    angle_start, angle_end, angle_delta = 0, 361, 5
    for angle in range(angle_start, angle_end, angle_delta):
        print(F"{angle:4}", end='  ')
        # 获取图像生成函数的参数列表和画布
        arglist, canvas = bbox_meth1(wic, hic, wi, hi, angle)
        img = diag_bw(*arglist[:2])

        print(F"{img.shape[1]:7} {img.shape[0]:7}", end='')
        print(F" {arglist[0].shape[1]:5} {arglist[0].shape[0]:7}")
        # 将生成的图像转换为彩色并绘制到画布的对应区域
        canvas[slice_frame((hic, wic), img.shape)] = cv2.applyColorMap(img.astype(np.uint8), 2)
        # 绘制红色矩形标记目标帧区域
        cv2.rectangle(canvas, ((canvas.shape[1]-wi)//2, (canvas.shape[0]-hi)//2),
        ((canvas.shape[1] + wi)//2, (canvas.shape[0] + hi)//2), (0, 0, 255), 2)
        cv2.imshow('can', canvas)
        cv2.waitKey(100)

问题现象截图说明

  • 0°(无旋转):计算得到的图像完全覆盖红色帧
    0度旋转效果
  • ≤45°:旋转放大后的图像与蓝色框对齐,完全覆盖红色帧
    45度旋转效果
  • >45°(以60°为例,至135°区间):计算图像无法匹配蓝色边框,无法填满红色帧
    60度旋转效果
  • 90°旋转:误差最大,渲染图像偏移到帧外右下侧,未填充完整帧;该问题在角度超过135°后对称重复,直至135°-225°区间恢复正常
    225度旋转效果

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.01 14:07:27