-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathpanorama_image_cropper.py
executable file
·97 lines (76 loc) · 3.93 KB
/
panorama_image_cropper.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
import cv2
import numpy as np
import argparse
import math
def crop_panorama_image(img, theta=0.0, phi=0.0, res_x=512, res_y=512, fov=60.0, debug=False):
img_x = img.shape[0]
img_y = img.shape[1]
theta = theta / 180 * math.pi
phi = phi / 180 * math.pi
fov_x = fov
aspect_ratio = res_y * 1.0 / res_x
half_len_x = math.tan(fov_x / 180 * math.pi / 2)
half_len_y = aspect_ratio * half_len_x
pixel_len_x = 2 * half_len_x / res_x
pixel_len_y = 2 * half_len_y / res_y
map_x = np.zeros((res_x, res_y), dtype=np.float32)
map_y = np.zeros((res_x, res_y), dtype=np.float32)
axis_y = math.cos(theta)
axis_z = math.sin(theta)
axis_x = 0
# theta rotation matrix
cos_theta = math.cos(theta)
sin_theta = math.sin(theta)
theta_rot_mat = np.array([[1, 0, 0], \
[0, cos_theta, -sin_theta], \
[0, sin_theta, cos_theta]], dtype=np.float32)
# phi rotation matrix
cos_phi = math.cos(phi)
sin_phi = -math.sin(phi)
phi_rot_mat = np.array([[cos_phi + axis_x**2 * (1 - cos_phi), \
axis_x * axis_y * (1 - cos_phi) - axis_z * sin_phi, \
axis_x * axis_z * (1 - cos_phi) + axis_y * sin_phi], \
[axis_y * axis_x * (1 - cos_phi) + axis_z * sin_phi, \
cos_phi + axis_y**2 * (1 - cos_phi), \
axis_y * axis_z * (1 - cos_phi) - axis_x * sin_phi], \
[axis_z * axis_x * (1 - cos_phi) - axis_y * sin_phi, \
axis_z * axis_y * (1 - cos_phi) + axis_x * sin_phi, \
cos_phi + axis_z**2 * (1 - cos_phi)]], dtype=np.float32)
map_x = np.tile(np.array(np.arange(res_x), dtype=np.float32), (res_y, 1)).T
map_y = np.tile(np.array(np.arange(res_y), dtype=np.float32), (res_x, 1))
map_x = map_x * pixel_len_x + pixel_len_x / 2 - half_len_x
map_y = map_y * pixel_len_y + pixel_len_y / 2 - half_len_y
map_z = np.ones((res_x, res_y)).astype(np.float32) * -1
ind = np.reshape(np.concatenate((np.expand_dims(map_x, 2), np.expand_dims(map_y, 2), \
np.expand_dims(map_z, 2)), axis=2), [-1, 3]).T
ind = theta_rot_mat.dot(ind)
ind = phi_rot_mat.dot(ind)
vec_len = np.sqrt(np.sum(ind**2, axis=0))
ind /= np.tile(vec_len, (3, 1))
cur_phi = np.arcsin(ind[0, :])
cur_theta = np.arctan2(ind[1, :], -ind[2, :])
map_x = (cur_phi + math.pi/2) / math.pi * img_x
map_y = cur_theta % (2 * math.pi) / (2 * math.pi) * img_y
map_x = np.reshape(map_x, [res_x, res_y])
map_y = np.reshape(map_y, [res_x, res_y])
if debug:
for x in range(res_x):
for y in range(res_y):
print '(%.2f, %.2f)\t' % (map_x[x, y], map_y[x, y]),
print
return cv2.remap(img, map_y, map_x, cv2.INTER_LINEAR, borderMode=cv2.BORDER_WRAP)
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('input_image', type=str, help='Input Panorama Image')
parser.add_argument('output_image', type=str, help='Output Panorama Image')
parser.add_argument('--theta', type=float, default=0.0, help='Theta angle (yaw) in range [-180, 180] degrees [default: 0.0]')
parser.add_argument('--phi', type=float, default=0.0, help='Phi angle (pitch) in range [-90, 90) degrees [default: 0.0]')
parser.add_argument('--resolution_x', type=int, default=256, help='Resolution of the output image width [default: 256]')
parser.add_argument('--resolution_y', type=int, default=256, help='Resolution of the output image height [default: 256]')
parser.add_argument('--fov', type=float, default=60.0, help='Field of View for image height in range [0, 180] degrees [default: 60.0]')
parser.add_argument('--debug', type=bool, default=False, help='Debug mode')
args = parser.parse_args()
img = cv2.imread(args.input_image)
out_img = crop_panorama_image(img, theta=args.theta, phi=args.phi, res_x=args.resolution_x, \
res_y=args.resolution_y, fov=args.fov, debug=args.debug)
cv2.imwrite(args.output_image, out_img)