使用相同的宽高比裁剪旋转后的图像

4

当输入图像旋转给定角度并裁剪以避免任何非图像区域同时保持原始图像宽高比时,如何找出最终图像的宽度和高度。

例子: example


1
这是一个很好的问题,我也很想知道答案,但这是否更适合于数学堆栈交换? - mhlester
也许有人知道Python PIL中的窍门,所以我在这里发帖。 - miki725
祈求好运。我正在尝试... - mhlester
我刚刚想出了如何做到这一点。原来很简单。现在正在编写解决方案。 - miki725
哦,谢天谢地。我已经准备放弃了,但当我意识到我的方程式依赖于我正在尝试解决的变量时,我意识到这个变量是必须知道的! - mhlester
需要花一点时间来输入答案。我正在尝试制作所有的图表... - miki725
3个回答

7

在此输入图片描述

  • 红色矩形是带原始宽高比的原始图像。
  • W/t矩形(由绿色和黄色三角形围住)是带有 extend=True 的旋转图像宽高比。

下面是获取wh的方法。

image = Image.open(...)
rotated = image.rotate(degrees, Image.BICUBIC, True)

aspect_ratio = float(image.size[0]) / image.size[1]
rotated_aspect_ratio = float(rotated.size[0]) / rotated.size[1]
angle = math.fabs(degrees) * math.pi / 180

if aspect_ratio < 1:
    total_height = float(image.size[0]) / rotated_aspect_ratio
else:
    total_height = float(image.size[1])

h = total_height / (aspect_ratio * math.sin(angle) + math.cos(angle))
w = h * aspect_ratio

现在旋转后的图片可以通过宽为w,高为h的尺寸在中心进行裁剪,得到最终的图片。

如果 宽高比 = a = w/h,那么不应该是 w = a / h 而不是 w = h * a 吗? - martineau
你需要将两边都乘以h,因此ha=hw/h,但是h/h等于1,所以被消除了。 - miki725
在代码中发现了一个错误:你需要取正弦和余弦的绝对值,否则它将在旋转角度大于90°时出错。 - Daniel

1

enter image description here

定义:

  • W0和H0分别为原始矩形的宽度和高度。

  • D0为原始矩形的对角线(斜边)长度。

  • W1和H1分别为旋转后矩形的填充宽度和高度。

  • W2和H2分别为目标矩形的宽度和高度。

  • D2为目标矩形的对角线(斜边)长度。

  • E是复杂且瞬态的。

  • 你可能需要预先计算sin(A)、cos(A)和tan(A),因为它们会被重复使用。

计算 W1 和 H1:

它们由两个组成部分构成,即构成每个长度的两条三角形边。

  • W1 = W0*cos(A) + H0*sin(A)
  • H1 = H0*cos(A) + W0*sin(A)

计算 W2 和 H2:

  • E = W0 / (1 + tan(A) / W0 * H0)
  • W2 = E / tan(A)
  • H2 = W2 / W0 * H0

Python 代码:

W0 = # original rectangle width
H0 = # original rectangle height
cosA = cos(A)
sinA = sin(A)
tanA = tan(A)
W1 = W0 * cosA + H0 * sinA
H1 = H0 * cosA + W0 * sinA
E = W0 / (1 + tanA / W0 * H0)
W2 = E / tanA       # requested width
H2 = W2 / W0 * H0   # requested height

免责声明:这一切都未经测试。

编辑:按照要求,我最好的猜测是我所说的E:

r0 = w2/h2
h2 = (w0-e1) / sin(A)
h2 = e1/ cos(A) / r0
(w0-e1) / sin(A) = e1/ cos(A) / r0
x = cos(A) / sin(A)
e1 = w0 * x / (1/r0 + x)

E是w2的水平分量,延伸通过线a+b。然后计算ab,使用纵横比和正弦/余弦来计算。项被取消,声音变高,这就是我最终得到的结果。


什么是E?也许是一个维基链接。 - miki725
我自己推导出来的,E是我需要的一个维度。我会更新帖子并附上图片。它与你的想法出奇地不同! - mhlester
也许我在这里错过了一些微不足道的东西,但我仍然不明白e是什么。也许晚上我会再试一次。 - miki725
-1:这肯定是不正确的:除以tan(alpha)。tan(epsilon) = epsilon。微小的旋转会导致你除以一个微小的数,根据你的公式会导致完全错误的宽度。 - Martijn Courteaux

0
这段代码返回此方块的坐标。

返回的盒子 - 是此方块的坐标

from PIL import Image
def cropp_rotated(image, degrees):
    x, y = image.size
    cosA = abs(math.cos(math.radians(degrees)))
    sinA = abs(math.sin(math.radians(degrees)))

    a = x * cosA 
    b = x * sinA

    relation = a / (a + b)
    right_indent1 = a - x * relation * cosA

    relation = b / (a + b)
    bottom_ident1 = b - x * relation *sinA


    c = y * cosA 
    d = y * sinA

    relation = c / (c + d)
    right_indent2 = c - y * relation * cosA

    relation = d / (c + d)
    bottom_ident2 = d - y * relation *sinA

    right_indent = max(right_indent1, right_indent2)
    top_indent = max(bottom_ident1, bottom_ident2)

    #size of rotated image:
    w_rotated = x * cosA + y * sinA
    h_rotated = y * cosA + x * sinA


    box = (
    int(right_indent), 
    int(top_indent), 
    int(w_rotated - right_indent), 
    int(h_rotated - top_indent))

    return box

我只是为了获取图像的真实大小而导入PIL。 你不必导入PIL,可以通过其他方式获取图像大小。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接