Diff of /src/compute_target.py [000000] .. [c1b1c5]

Switch to unified view

a b/src/compute_target.py
1
# Given HPE results and ratios, compute the final target point
2
3
import cv2
4
import matplotlib.pyplot as plt
5
from utils.trajectory_io import *
6
import pickle
7
from utils.triangulation import *
8
import open3d as o3d
9
from scipy.linalg import null_space
10
11
from subject_info import SUBJECT_NAME, SCAN_POSE
12
import argparse
13
import time
14
15
parser = argparse.ArgumentParser(description='Compute target')
16
parser.add_argument('--pose_model', type=str, default='ViTPose_large', help='pose model')
17
parser.add_argument('--subject_name', type=str, default='John Doe', help='subject name')
18
parser.add_argument('--scan_pose', type=str, default='none', help='scan pose')
19
parser.add_argument('--target1_r1', type=float, default=0.3, help='target1_r1')
20
parser.add_argument('--target1_r2', type=float, default=0.1, help='target1_r2')
21
parser.add_argument('--target2_r1', type=float, default=0.3, help='target2_r1')
22
parser.add_argument('--target2_r2', type=float, default=0.55, help='target2_r2')
23
parser.add_argument('--target4_r1', type=float, default=0.35, help='target4_r1')
24
parser.add_argument('--target4_r2', type=float, default=0.1, help='target4_r2')
25
26
args = parser.parse_args()
27
POSE_MODEL = args.pose_model
28
if args.subject_name != 'John Doe':
29
    SUBJECT_NAME = args.subject_name
30
if args.scan_pose != 'none':
31
    SCAN_POSE = args.scan_pose
32
33
34
def target12_3D(l_shoulder_cam1, l_shoulder_cam2, r_shoulder_cam1, r_shoulder_cam2,
35
                cam1_intr, cam2_intr, T_base_cam1, T_base_cam2, ratio_1=0.3, ratio_2=0.1):
36
    '''
37
    A 3D method to determine the 1st and 2nd target 3D coordinate. Didn't use nipples.
38
    :X1, X2 --3D coordinates of the right, left shoulder
39
    :X3 -- 3D coordinate of the point determined by ratio_1
40
    :t1 -- the direction vector of the line connecting X1 and X2
41
    :t2 -- the direction vector of the line connecting X3 and target
42
    :param ratio_1: ratio on the line connecting X1 and X2
43
    :param ratio_2: ratio between X3 to target and X1 to X2
44
    '''
45
    X1 = reconstruct(r_shoulder_cam1, r_shoulder_cam2, cam1_intr, cam2_intr, T_base_cam1, T_base_cam2)
46
    X1 = triangulation_post_process(X1)
47
    X2 = reconstruct(l_shoulder_cam1, l_shoulder_cam2, cam1_intr, cam2_intr, T_base_cam1, T_base_cam2)
48
    X2 = triangulation_post_process(X2)
49
50
    X3 = X1 + ratio_1 * (X2 - X1)
51
52
    t1 = ((X2 - X1) / np.linalg.norm(X2 - X1)).squeeze()
53
    n = np.array([0, 0, 1])  # z-axis of the base frame / normal vector of the base frame's x-y plane
54
    A = np.vstack([t1, n])
55
    t2 = null_space(A)  # t2 is perpendicular to both t1 and n
56
    t2 = t2 / np.linalg.norm(t2)  # normalize t2
57
58
    if t2[0] < 0:
59
        t2 = -t2
60
61
    target = X3 + ratio_2 * np.linalg.norm(X2 - X1) * t2
62
    target = triangulation_post_process(target, verbose=False)
63
    position_data['front'] = [X1, X2, t2]
64
65
    return target
66
67
68
def triangulation_post_process(original_3d, verbose=False):
69
    '''
70
    Use the original x&y values to find a closest point in the point cloud.
71
    Use the new point's z value as the new z, combined with the original x&y.
72
    :param original_3d: The original 3D coordinate from triangulation
73
    '''
74
    if verbose:
75
        print("original_3d: ", original_3d)
76
    idx = np.argmin(np.square(coordinates[:, 0:2] - original_3d.squeeze()[0:2]).sum(axis=1))
77
    if verbose:
78
        new_3d = coordinates[idx]
79
        print("new_3d: ", new_3d)
80
    new_z = coordinates[idx, 2]
81
    new_3d = np.vstack((original_3d[0:2], [new_z]))
82
    return new_3d
83
84
85
def target4_3D(r_shoulder_cam1, r_shoulder_cam2, r_hip_cam1, r_hip_cam2,
86
               cam1_intr, cam2_intr, T_base_cam1, T_base_cam2, ratio_1=0.35, ratio_2=0.1):
87
    '''
88
    Similar to 1st&2nd target but ratio 2 operated on X1 to X3, instead of X1 to X2
89
    :X1, X2 --3D coordinates of the right shoulder and the right hip
90
    :X3 -- 3D coordinate of the point determined by ratio_1
91
    :t1 -- the direction vector of the line connecting X1 and X2
92
    :t2 -- the direction vector of the line connecting X3 and target
93
    :param ratio_1: ratio on the line connecting X1 and X2
94
    :param ratio_2: ratio between X3 to target and X1 to X3
95
    '''
96
    X1 = reconstruct(r_shoulder_cam1, r_shoulder_cam2, cam1_intr, cam2_intr, T_base_cam1, T_base_cam2)
97
    X1 = triangulation_post_process(X1)
98
    X2 = reconstruct(r_hip_cam1, r_hip_cam2, cam1_intr, cam2_intr, T_base_cam1, T_base_cam2)
99
    X2 = triangulation_post_process(X2)
100
101
    X3 = X1 + ratio_1 * (X2 - X1)
102
103
    t1 = ((X2 - X1) / np.linalg.norm(X2 - X1)).squeeze()
104
    n = np.array([0, 0, 1])
105
    A = np.vstack([t1, n])
106
    t2 = null_space(A)
107
    t2 = t2 / np.linalg.norm(t2)
108
109
    if t2[1] < 0:
110
        t2 = -t2
111
112
    target = X3 + ratio_2 * np.linalg.norm(X1 - X3) * t2
113
    target = triangulation_post_process(target)
114
    position_data['side'] = [X1, X2, t2]
115
116
    return target
117
118
119
def draw_front_target_point(target_point1, target_point2, l_shoulder1, r_shoulder1, l_shoulder2, r_shoulder2, target):
120
    image1 = plt.imread(folder_path + 'color_images/cam_1.jpg')
121
    cv2.circle(image1, (int(target_point1[0]), int(target_point1[1])), 2, (36, 255, 12), 2, -1)
122
    cv2.circle(image1, (int(l_shoulder1[0]), int(l_shoulder1[1])), 2, (36, 255, 12), 2, -1)
123
    cv2.circle(image1, (int(r_shoulder1[0]), int(r_shoulder1[1])), 2, (36, 255, 12), 2, -1)
124
    cv2.line(image1, (int(l_shoulder1[0]), int(l_shoulder1[1])), (int(r_shoulder1[0]), int(r_shoulder1[1])),
125
             color=(255, 0, 0), thickness=2)
126
127
    plt.subplot(121)
128
    plt.imshow(image1)
129
130
    image2 = plt.imread(folder_path + 'color_images/cam_2.jpg')
131
    cv2.circle(image2, (int(target_point2[0]), int(target_point2[1])), 2, (36, 255, 12), 2, -1)
132
    cv2.circle(image2, (int(l_shoulder2[0]), int(l_shoulder2[1])), 2, (36, 255, 12), 2, -1)
133
    cv2.circle(image2, (int(r_shoulder2[0]), int(r_shoulder2[1])), 2, (36, 255, 12), 2, -1)
134
    cv2.line(image2, (int(l_shoulder2[0]), int(l_shoulder2[1])), (int(r_shoulder2[0]), int(r_shoulder2[1])),
135
             color=(255, 0, 0), thickness=2)
136
137
    plt.subplot(122)
138
    plt.imshow(image2)
139
140
    plt.show()
141
142
    if target == 1:
143
        plt.imsave(folder_path + 'target1_highlight/cam_1.jpg', image1)
144
        plt.imsave(folder_path + 'target1_highlight/cam_2.jpg', image2)
145
    else:
146
        plt.imsave(folder_path + 'target2_highlight/cam_1.jpg', image1)
147
        plt.imsave(folder_path + 'target2_highlight/cam_2.jpg', image2)
148
149
150
def draw_side_target_point(target_point1, target_point2, r_hip1, r_shoulder1, r_hip2, r_shoulder2):
151
    image1 = plt.imread(folder_path + 'color_images/cam_1.jpg')
152
    cv2.circle(image1, (int(target_point1[0]), int(target_point1[1])), 3, (36, 255, 12), 3, -1)
153
    cv2.circle(image1, (int(r_hip1[0]), int(r_hip1[1])), 2, (36, 255, 12), 2, -1)
154
    cv2.circle(image1, (int(r_shoulder1[0]), int(r_shoulder1[1])), 2, (36, 255, 12), 2, -1)
155
    cv2.line(image1, (int(r_hip1[0]), int(r_hip1[1])), (int(r_shoulder1[0]), int(r_shoulder1[1])),
156
             color=(255, 0, 0), thickness=2)
157
158
    plt.subplot(121)
159
    plt.imshow(image1)
160
161
    image2 = plt.imread(folder_path + 'color_images/cam_2.jpg')
162
    cv2.circle(image2, (int(target_point2[0]), int(target_point2[1])), 3, (36, 255, 12), 3, -1)
163
    cv2.circle(image2, (int(r_hip2[0]), int(r_hip2[1])), 2, (36, 255, 12), 2, -1)
164
    cv2.circle(image2, (int(r_shoulder2[0]), int(r_shoulder2[1])), 2, (36, 255, 12), 2, -1)
165
    cv2.line(image2, (int(r_hip2[0]), int(r_hip2[1])), (int(r_shoulder2[0]), int(r_shoulder2[1])),
166
             color=(255, 0, 0), thickness=2)
167
168
    plt.subplot(122)
169
    plt.imshow(image2)
170
171
    plt.show()
172
    plt.imsave(folder_path + 'target4_highlight/cam_1.jpg', image1)
173
    plt.imsave(folder_path + 'target4_highlight/cam_2.jpg', image2)
174
175
folder_path = 'src/data/' + SUBJECT_NAME + '/' + SCAN_POSE + '/'  # when run from src directory
176
# folder_path = '../data/' + SUBJECT_NAME + '/' + SCAN_POSE + '/'  # when run from evaluation directory
177
178
# read RGBD intrinsics
179
with open(folder_path + 'intrinsics/cam_1_intrinsics.pickle', 'rb') as f:
180
    cam1_intr = pickle.load(f)
181
182
with open(folder_path + 'intrinsics/cam_2_intrinsics.pickle', 'rb') as f:
183
    cam2_intr = pickle.load(f)
184
185
with open(folder_path + 'intrinsics/cam_1_depth_intr.pickle', 'rb') as f:
186
    depth_cam1_intr = pickle.load(f)
187
188
with open(folder_path + 'intrinsics/cam_2_depth_intr.pickle', 'rb') as f:
189
    depth_cam2_intr = pickle.load(f)
190
191
# read extrinsics
192
camera_poses = read_trajectory(folder_path + "odometry.log")
193
T_cam1_base = camera_poses[0].pose
194
T_base_cam1 = np.linalg.inv(T_cam1_base)
195
# print("T_base_cam1/extr: \n", T_base_cam1)
196
T_cam2_base = camera_poses[1].pose
197
T_base_cam2 = np.linalg.inv(T_cam2_base)
198
# print("T_base_cam2/extr: \n", T_base_cam2)
199
200
# read pose keypoints
201
with open(folder_path + POSE_MODEL + '/keypoints/cam_1_keypoints.pickle', 'rb') as f:
202
    cam1_keypoints = pickle.load(f)
203
    if POSE_MODEL == "OpenPose":
204
        l_shoulder1 = np.array(cam1_keypoints['people'][0]['pose_keypoints_2d'][15:17])
205
        r_shoulder1 = np.array(cam1_keypoints['people'][0]['pose_keypoints_2d'][6:8])
206
        l_hip1 = np.array(cam1_keypoints['people'][0]['pose_keypoints_2d'][36:38])
207
        r_hip1 = np.array(cam1_keypoints['people'][0]['pose_keypoints_2d'][27:29])
208
    else:
209
        l_shoulder1 = cam1_keypoints[0]['keypoints'][5][:2]
210
        r_shoulder1 = cam1_keypoints[0]['keypoints'][6][:2]
211
        l_hip1 = cam1_keypoints[0]['keypoints'][11][:2]
212
        r_hip1 = cam1_keypoints[0]['keypoints'][12][:2]
213
214
with open(folder_path + POSE_MODEL + '/keypoints/cam_2_keypoints.pickle', 'rb') as f:
215
    cam2_keypoints = pickle.load(f)
216
    if POSE_MODEL == "OpenPose":
217
        l_shoulder2 = np.array(cam2_keypoints['people'][0]['pose_keypoints_2d'][15:17])
218
        r_shoulder2 = np.array(cam2_keypoints['people'][0]['pose_keypoints_2d'][6:8])
219
        l_hip2 = np.array(cam2_keypoints['people'][0]['pose_keypoints_2d'][36:38])
220
        r_hip2 = np.array(cam2_keypoints['people'][0]['pose_keypoints_2d'][27:29])
221
    else:
222
        l_shoulder2 = cam2_keypoints[0]['keypoints'][5][:2]
223
        r_shoulder2 = cam2_keypoints[0]['keypoints'][6][:2]
224
        l_hip2 = cam2_keypoints[0]['keypoints'][11][:2]
225
        r_hip2 = cam2_keypoints[0]['keypoints'][12][:2]
226
227
############ TSDF volume ############
228
start_time = time.time()
229
volume = o3d.pipelines.integration.ScalableTSDFVolume(
230
    voxel_length=1 / 512.0,
231
    sdf_trunc=0.04,
232
    color_type=o3d.pipelines.integration.TSDFVolumeColorType.RGB8)
233
234
# show RGBD images from all the views
235
for i in range(len(camera_poses)):
236
    # print("Integrate {:d}-th image into the volume.".format(i))
237
    color = o3d.io.read_image(folder_path + "color_images/cam_{}.jpg".format(i+1))
238
    depth = o3d.io.read_image(folder_path + "depth_images/cam_{}.png".format(i+1))
239
240
    depth_image = np.asanyarray(depth)
241
    color_image = np.asanyarray(color)
242
243
    depth_colormap = cv2.applyColorMap(cv2.convertScaleAbs(depth_image, alpha=0.09), cv2.COLORMAP_JET)
244
    depth_colormap_dim = depth_colormap.shape
245
    color_colormap_dim = color_image.shape
246
247
    # If depth and color resolutions are different, resize color image to match depth image for display
248
    if depth_colormap_dim != color_colormap_dim:
249
        resized_color_image = cv2.resize(color_image, dsize=(depth_colormap_dim[1], depth_colormap_dim[0]),
250
                                         interpolation=cv2.INTER_AREA)
251
        images = np.hstack((resized_color_image, depth_colormap))
252
    else:
253
        images = np.hstack((color_image, depth_colormap))
254
255
    plt.imsave(folder_path + "depth_colormap/cam_{}.png".format(i+1), depth_colormap)
256
257
    # Show images
258
    cv2.namedWindow('RealSense', cv2.WINDOW_AUTOSIZE)
259
    plt.imshow(images)
260
    plt.show()
261
262
    rgbd = o3d.geometry.RGBDImage.create_from_color_and_depth(
263
        color, depth, depth_trunc=4.0, convert_rgb_to_intensity=False)
264
265
    cam_intr = depth_cam1_intr if i == 0 else depth_cam2_intr  # use depth camera's intrinsics
266
267
    intr = o3d.camera.PinholeCameraIntrinsic(
268
        width=640,
269
        height=480,
270
        fx=cam_intr[0,0],
271
        fy=cam_intr[1,1],
272
        cx=cam_intr[0,2],
273
        cy=cam_intr[1,2]
274
    )
275
276
    volume.integrate(rgbd, intr, np.linalg.inv(camera_poses[i].pose))
277
print("Volume Integration: {:.3f} s".format(time.time() - start_time))
278
279
# point cloud generation
280
pcd = volume.extract_point_cloud()
281
downpcd = pcd.voxel_down_sample(voxel_size=0.01)
282
downpcd.estimate_normals(search_param=o3d.geometry.KDTreeSearchParamHybrid(radius=0.1, max_nn=30))
283
o3d.visualization.draw_geometries([downpcd])
284
285
coordinates = np.asarray(downpcd.points)
286
normals = np.asarray(downpcd.normals)
287
288
# save data, write pcd
289
o3d.io.write_point_cloud(folder_path + 'downpcd.ply', downpcd)
290
with open(folder_path + 'pcd_coordinates.pickle', 'wb') as f:
291
    pickle.dump(coordinates, f)
292
293
with open(folder_path + 'pcd_normals.pickle', 'wb') as f:
294
    pickle.dump(normals, f)
295
296
# Read pcd coordianates
297
with open(folder_path + 'pcd_coordinates.pickle', 'rb') as f:
298
    coordinates = pickle.load(f)
299
300
final_target_list = []  # the target coordinates used to navigate the robotic arm
301
position_data = {}  # store
302
303
if SCAN_POSE == 'front':
304
    target1 = target12_3D(l_shoulder1, l_shoulder2, r_shoulder1, r_shoulder2,
305
                          cam1_intr, cam2_intr, T_base_cam1, T_base_cam2, ratio_1=args.target1_r1,
306
                          ratio_2=args.target1_r2)
307
    target2 = target12_3D(l_shoulder1, l_shoulder2, r_shoulder1, r_shoulder2,
308
                          cam1_intr, cam2_intr, T_base_cam1, T_base_cam2, ratio_1=args.target2_r1,
309
                          ratio_2=args.target2_r2)
310
311
    target1_2d_cam1 = from_homog(cam1_intr @ T_base_cam1[0:3, :] @ to_homog(target1)).squeeze()
312
    target1_2d_cam2 = from_homog(cam2_intr @ T_base_cam2[0:3, :] @ to_homog(target1)).squeeze()
313
314
    target2_2d_cam1 = from_homog(cam1_intr @ T_base_cam1[0:3, :] @ to_homog(target2)).squeeze()
315
    target2_2d_cam2 = from_homog(cam2_intr @ T_base_cam2[0:3, :] @ to_homog(target2)).squeeze()
316
317
    draw_front_target_point(target1_2d_cam1, target1_2d_cam2, l_shoulder1, r_shoulder1, l_shoulder2, r_shoulder2, target=1)
318
    draw_front_target_point(target2_2d_cam1, target2_2d_cam2, l_shoulder1, r_shoulder1, l_shoulder2, r_shoulder2, target=2)
319
320
    final_target_list.append(target1)
321
    final_target_list.append(target2)
322
323
elif SCAN_POSE == 'side':
324
    target4 = target4_3D(r_shoulder1, r_shoulder2, r_hip1, r_hip2,
325
                         cam1_intr, cam2_intr, T_base_cam1, T_base_cam2, ratio_1=args.target4_r1,
326
                         ratio_2=args.target4_r2)
327
328
    target4_2d_cam1 = from_homog(cam1_intr @ T_base_cam1[0:3, :] @ to_homog(target4)).squeeze()
329
    target4_2d_cam2 = from_homog(cam2_intr @ T_base_cam2[0:3, :] @ to_homog(target4)).squeeze()
330
331
    draw_side_target_point(target4_2d_cam1, target4_2d_cam2, r_hip1, r_shoulder1, r_hip2, r_shoulder2)
332
333
    final_target_list.append(target4)
334
335
# # save results with initial ratio parameters
336
# with open(folder_path + POSE_MODEL + '/final_target.pickle', 'wb') as f:
337
#     pickle.dump(final_target_list, f)
338
#
339
# with open(folder_path + POSE_MODEL + '/position_data.pickle', 'wb') as f:
340
#     pickle.dump(position_data, f)
341
342
# # save results with optimized ratio parameters based on planar euclidean distance
343
# with open(folder_path + POSE_MODEL + '/final_target_opt.pickle', 'wb') as f:
344
#     pickle.dump(final_target_list, f)
345
#
346
# with open(folder_path + POSE_MODEL + '/position_data_opt.pickle', 'wb') as f:
347
#     pickle.dump(position_data, f)
348
349
# # save as temporary file for evaluation
350
# with open(folder_path + POSE_MODEL + '/tmp_target_test.pickle', 'wb') as f:
351
#     pickle.dump(final_target_list, f)