该解决方案/实现背后的数学相当于这个类似问题的解决方案,但公式被简化并避免了奇点。这是具有相同接口的Python代码largest_rotated_rect
来自其他解决方案,但在几乎所有情况下都给出更大的面积(始终是经过验证的最佳方案):
def rotatedRectWithMaxArea(w, h, angle):
"""
Given a rectangle of size wxh that has been rotated by 'angle' (in
radians), computes the width and height of the largest possible
axis-aligned rectangle (maximal area) within the rotated rectangle.
"""
if w <= 0 or h <= 0:
return 0,0
width_is_longer = w >= h
side_long, side_short = (w,h) if width_is_longer else (h,w)
# since the solutions for angle, -angle and 180-angle are all the same,
# if suffices to look at the first quadrant and the absolute values of sin,cos:
sin_a, cos_a = abs(math.sin(angle)), abs(math.cos(angle))
if side_short <= 2.*sin_a*cos_a*side_long or abs(sin_a-cos_a) < 1e-10:
# half constrained case: two crop corners touch the longer side,
# the other two corners are on the mid-line parallel to the longer line
x = 0.5*side_short
wr,hr = (x/sin_a,x/cos_a) if width_is_longer else (x/cos_a,x/sin_a)
else:
# fully constrained case: crop touches all 4 sides
cos_2a = cos_a*cos_a - sin_a*sin_a
wr,hr = (w*cos_a - h*sin_a)/cos_2a, (h*cos_a - w*sin_a)/cos_2a
return wr,hr
以下是该函数与其他解决方案的比较:
>>> wl,hl = largest_rotated_rect(1500,500,math.radians(20))
>>> print (wl,hl),', area=',wl*hl
(828.2888697391496, 230.61639227890998) , area= 191016.990904
>>> wm,hm = rotatedRectWithMaxArea(1500,500,math.radians(20))
>>> print (wm,hm),', area=',wm*hm
(730.9511000407718, 266.044443118978) , area= 194465.478358
带角度angle
in [0,pi/2[
旋转图像的边界框(宽度w
, 高度h
) 具有以下尺寸:
- width
w_bb = w*cos_a + h*sin_a
- height
h_bb = w*sin_a + h*cos_a
If w_r
, h_r
是计算出的裁剪图像的最佳宽度和高度,则边界框的插图为:
- 水平方向:
(w_bb-w_r)/2
- 垂直方向:
(h_bb-h_r)/2
Proof:
Looking for the axis aligned rectangle between two parallel lines that has maximal area is an optimization problem with one parameter, e.g. x
as in this figure:
Let s
表示两条平行线之间的距离(它将成为旋转矩形的短边)。然后是侧面a
, b
所追求的矩形的比例与x
, s-x
,即 x = a sin α 和 (s-x) = b cos α:
所以面积最大化a*b
意味着最大化x*(s-x)
。由于直角三角形的“高度定理”,我们知道x*(s-x) = p*q = h*h
。因此最大面积达到x = s-x = s/2
,即平行线之间的两个角E、G在中线上:
仅当最大矩形适合旋转矩形时,此解决方案才有效。因此对角线EG
不得长于另一边l
旋转后的矩形。自从
EG = AF + DH = s/2*(cot α + tan α) = s/(2sin αcos α) = s/sin 2*α
我们有条件 s ≤ lsin 2α,其中 s 和 l 是旋转矩形的短边和长边。
如果 s > lsin 2α 参数x
必须小于(小于 s/2)并且 st。所需矩形的所有角都位于旋转矩形的一侧。这导致方程
x*cot α + (s-x)*tan α = l
给出 x = sin α*(l余弦α-ssin α)/cos 2*α。从 a = x/sin α 和 b = (s-x)/cos α 我们得到上面使用的公式。