22import os
33import sys
44os .getcwd ()
5+ VIS = False
56
7+ #%% things to extract
8+ tx ,ty ,tz ,rx ,ry ,rz = [None ] * 6
69#%%
7- pbg = '/mount/wp/GEMstack/data/lidar76.npz'
10+ pbg = '/mount/wp/GEMstack/data/lidar76.npz' #null scene
811pbgAndLine = '/mount/wp/GEMstack/data/lidar78.npz'
912
1013#%% load and wash data
1114import numpy as np
12- #load null scene -- no added objects
13- bg = np .load (pbg )['arr_0' ]
14- #load data pc -- with added object
15- bgAndLine = np .load (pbgAndLine )['arr_0' ]
16- #remove (0,0,0)'s
17- bg = bg [~ np .all (bg == 0 , axis = 1 )]
18- bgAndLine = bgAndLine [~ np .all (bgAndLine == 0 , axis = 1 )]
1915
20- #%% crop to only keep a frontal box area
21- def crop (pc ,ix = None ,iy = None ,iz = None ):
22- mask = True
23- for dim ,intervel in zip ([0 ,1 ,2 ],[ix ,iy ,iz ]):
24- if not intervel : continue
25- d ,u = intervel
26- mask &= pc [:,dim ] >= d
27- mask &= pc [:,dim ] <= u
28- print (f'points left after cropping { dim } \' th dim' ,mask .sum ())
29- return pc [mask ]
30- #
31- area = (- 0 ,5 ),(- 1 ,1 )
32- bg = crop (bg ,* area )
33- bgAndLine = crop (bgAndLine ,* area )
34- print (bg .shape )
35- print (bgAndLine .shape )
16+ bg = np .load (pbg )['arr_0' ] # load null scene -- no added objects
17+ bgAndLine = np .load (pbgAndLine )['arr_0' ] # load data pc -- with added object
18+ bg = bg [~ np .all (bg == 0 , axis = 1 )] # remove (0,0,0)'s
19+ bgAndLine = bgAndLine [~ np .all (bgAndLine == 0 , axis = 1 )]
3620
3721#%% visualize cropped null scene and data scene
3822import pyvista as pv
@@ -45,23 +29,121 @@ def vispc(pc):
4529 plotter .add_mesh (cloud , render_points_as_spheres = True , point_size = 2 , color = 'blue' )
4630 plotter .camera .position = (- 20 ,0 ,20 )
4731 plotter .camera .focal_point = (0 ,0 ,0 )
32+ plotter .show_axes ()
4833 plotter .show ()
34+ if VIS :
35+ vispc (bg )
36+ vispc (bgAndLine )
37+
38+
39+ #%%==============================================================
40+ #======================= util functions =========================
41+ #================================================================
42+ def crop (pc ,ix = None ,iy = None ,iz = None ):
43+ # crop a subrectangle in a pointcloud
44+ # usage: crop(pc, ix = (x_min,x_max), ...)
45+ # return: array(N,3)
46+ mask = True
47+ for dim ,intervel in zip ([0 ,1 ,2 ],[ix ,iy ,iz ]):
48+ if not intervel : continue
49+ d ,u = intervel
50+ mask &= pc [:,dim ] >= d
51+ mask &= pc [:,dim ] <= u
52+ print (f'points left after cropping { dim } \' th dim' ,mask .sum ())
53+ return pc [mask ]
54+
55+ from sklearn .linear_model import RANSACRegressor
56+ from sklearn .linear_model import LinearRegression
57+ def fit_plane_ransac (pc ,tol = 0.01 ):
58+ # fit a plane in a pointcloud
59+ # and visualize inliers
60+ # pc: np array (N,3). the pointcloud
61+ # tol: the tolerance. default 0.01
62+ # return: float, float, float, array(N,3)
63+ # ^: coeff1, coeff2, intercept toward the plane, inliers of the fit
64+ model = RANSACRegressor (LinearRegression (), residual_threshold = tol )
65+ model .fit (pc [:,:2 ], pc [:,2 ])
66+ a , b = model .estimator_ .coef_
67+ inter = model .estimator_ .intercept_
68+ return a ,b ,inter ,cropped_bg [model .inlier_mask_ ]
4969
50- vispc (bg )
51- vispc (bgAndLine )
52- #%% take difference to only keep added object
5370from scipy .spatial import cKDTree
54- tree = cKDTree (bg )
55-
56- tolerance = 0.08
57- # Find points in pc1 that do not have a match in pc2 within the tolerance
58- idiff = []
59- for i , point in enumerate (bgAndLine ):
60- _ , idx = tree .query (point ) # Find the nearest neighbor in pc2
61- distance = np .linalg .norm (point - bg [idx ]) # Compute the distance
62- if distance > tolerance : # If the nearest neighbor is outside the tolerance
63- idiff .append (i )
64-
65- diff = np .array (bgAndLine [diff ])
66- #visualize
67- vispc (diff )
71+ def pc_diff (pc0 ,pc1 ,tol = 0.1 ):
72+ # remove points in pc0 from pc1 s.t. euclidian distance is within tolerance
73+ # return: array(N,3)
74+ tree = cKDTree (pc0 )
75+ diff = []
76+ for i , point in enumerate (pc1 ):
77+ _ , idx = tree .query (point )
78+ distance = np .linalg .norm (point - pc0 [idx ]) # Compute the distance
79+ if distance > tol :
80+ diff .append (point )
81+ # tree = cKDTree(pc1)
82+ # for i, point in enumerate(pc0):
83+ # _, idx = tree.query(point)
84+ # distance = np.linalg.norm(point - pc1[idx]) # Compute the distance
85+ # if distance > tol:
86+ # diff.append(point)
87+ return np .array (diff )
88+
89+
90+
91+ #%%==============================================================
92+ #========================== tx ty rz ============================
93+ #================================================================
94+ #%% crop to only keep a frontal box area
95+ area = (- 0 ,10 ),(- 2 ,2 ),(- 3 ,1 )
96+ cropped_bg = crop (bg ,* area )
97+ cropped_bgAndLine = crop (bgAndLine ,* area )
98+ print (cropped_bg .shape )
99+ print (cropped_bgAndLine .shape )
100+
101+ #%% Take difference to only keep added object
102+ diff = pc_diff (cropped_bg ,cropped_bgAndLine )
103+ if VIS :
104+ vispc (diff ) #visualize diff, hopefully the added objects
105+
106+ # %% use the added objects to find rz.
107+ # TODO after dataset retake
108+ # right now we assume tx = ty = 0 and \
109+ # just use median to find a headding direction
110+ tx = ty = 0
111+ hx ,hy = np .median (diff ,axis = 0 )[:2 ]
112+ rz = - np .arctan2 (hy ,hx )
113+
114+
115+
116+
117+ #%%==============================================================
118+ #========================= tz rx ry =============================
119+ #================================================================
120+ # %% this time we crop to keep the ground
121+ cropped_bg = crop (bg ,iz = (- 3 ,- 2 ))
122+ if VIS :
123+ print (cropped_bg .shape )
124+ vispc (cropped_bg )
125+
126+ #%%
127+ from math import sqrt
128+ a , b , tz , inliers = fit_plane_ransac (cropped_bg ,tol = 0.001 )
129+ if VIS :
130+ vispc (inliers )
131+ normv = np .array ([a , b , - 1 ])
132+ normv /= np .linalg .norm (normv )
133+ nx ,ny ,nz = normv
134+ ry = np .arctan2 (- nx , sqrt (ny ** 2 + nz ** 2 ))
135+ rx = np .arctan2 (ny ,sqrt (nx ** 2 + nz ** 2 ))
136+
137+
138+
139+
140+
141+ #%% visualize calibrated pointcloud
142+ if VIS :
143+ from scipy .spatial .transform import Rotation as R
144+ rot = R .from_euler ('xyz' ,[rx ,ry ,rz ]).as_matrix ()
145+
146+ calibrated_bgAndLine = bgAndLine @ rot .T + [tx ,ty ,tz ]
147+
148+ vispc (calibrated_bgAndLine )
149+ # %%
0 commit comments