我的应用程序:我正在尝试旋转图像(使用OpenCV和Python)
目前,我开发了以下代码,该代码旋转输入图像,并用黑色边框填充,给我A。我想要的是B-旋转图像中最大的区域裁剪窗口。我将此称为轴对齐的boundED框。
这基本上与Rotate and crop相同,但是我无法获得该问题的答案。此外,该答案显然仅对方形图像有效。我的图像是矩形的。
代码提供给A:
import cv2
import numpy as np
def getTranslationMatrix2d(dx, dy):
"""
Returns a numpy affine transformation matrix for a 2D translation of
(dx, dy)
"""
return np.matrix([[1, 0, dx], [0, 1, dy], [0, 0, 1]])
def rotateImage(image, angle):
"""
Rotates the given image about it's centre
"""
image_size = (image.shape[1], image.shape[0])
image_center = tuple(np.array(image_size) / 2)
rot_mat = np.vstack([cv2.getRotationMatrix2D(image_center, angle, 1.0), [0, 0, 1]])
trans_mat = np.identity(3)
w2 = image_size[0] * 0.5
h2 = image_size[1] * 0.5
rot_mat_notranslate = np.matrix(rot_mat[0:2, 0:2])
tl = (np.array([-w2, h2]) * rot_mat_notranslate).A[0]
tr = (np.array([w2, h2]) * rot_mat_notranslate).A[0]
bl = (np.array([-w2, -h2]) * rot_mat_notranslate).A[0]
br = (np.array([w2, -h2]) * rot_mat_notranslate).A[0]
x_coords = [pt[0] for pt in [tl, tr, bl, br]]
x_pos = [x for x in x_coords if x > 0]
x_neg = [x for x in x_coords if x < 0]
y_coords = [pt[1] for pt in [tl, tr, bl, br]]
y_pos = [y for y in y_coords if y > 0]
y_neg = [y for y in y_coords if y < 0]
right_bound = max(x_pos)
left_bound = min(x_neg)
top_bound = max(y_pos)
bot_bound = min(y_neg)
new_w = int(abs(right_bound - left_bound))
new_h = int(abs(top_bound - bot_bound))
new_image_size = (new_w, new_h)
new_midx = new_w * 0.5
new_midy = new_h * 0.5
dx = int(new_midx - w2)
dy = int(new_midy - h2)
trans_mat = getTranslationMatrix2d(dx, dy)
affine_mat = (np.matrix(trans_mat) * np.matrix(rot_mat))[0:2, :]
result = cv2.warpAffine(image, affine_mat, new_image_size, flags=cv2.INTER_LINEAR)
return result
最佳答案
此解决方案/实现背后的数学公式等于this solution of an analagous question,但是公式已简化并且避免了奇异之处。这是python代码,具有与其他解决方案中的largest_rotated_rect
相同的接口(interface),但是在几乎所有情况下(总是被证明是最佳的)都可以提供更大的区域:
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
中的[0,pi/2[
角度时,旋转图像的边界框(width w
,height h
)具有以下尺寸:w_bb = w*math.cos(angle) + h*math.sin(angle)
h_bb = w*math.sin(angle) + h*math.cos(angle)
如果
w_r
,h_r
是所计算出的裁剪图像的最佳宽度和高度,那么边界框的插图为:沿水平方向的
(w_bb-w_r)/2
(h_bb-h_r)/2
证明:
寻找具有最大面积的两条平行线之间的轴对齐矩形是具有一个参数的优化问题,例如
x
如下图所示:令
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≤lsin2α,其中s和l是旋转矩形的较短和较长的一面。
如果s> lsin2α,则参数
x
必须小于(小于s/2)和s.t。搜寻矩形的所有角均位于旋转矩形的一侧。这导致方程x * cotα+(s-x)* tanα= l
给出x = sinα*(lcosα-ssinα)/cos 2 *α。从a = x/sinα和b =(s-x)/cosα可以得到上面使用的公式。
关于python - 旋转图像并裁剪出黑色边框,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/16702966/