Skip to content

Commit 6ac0eee

Browse files
committed
Resolve merge conflicts
2 parents cd28d13 + 8be544d commit 6ac0eee

20 files changed

Lines changed: 628 additions & 57 deletions

File tree

.gitignore

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,3 +165,10 @@ cython_debug/
165165
# and can be added to the global gitignore or merged into this file. For a more nuclear
166166
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
167167
#.idea/
168+
GEMstack/offboard/calibration/calibration_by_segmentation/init_proj_seg.png
169+
GEMstack/offboard/calibration/calibration_by_segmentation/init_proj.png
170+
.gitignore
171+
.gitignore
172+
GEMstack/offboard/calibration/calibration_by_segmentation/refined_proj_seg.png
173+
GEMstack/offboard/calibration/calibration_by_segmentation/refined_proj.png
174+
GEMstack/offboard/calibration/calibration_by_segmentation/extrinsic.txt
Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
1-
center: [978.828508, 605.794034]
2-
focal: [1230.144096, 1230.630424]
1+
center: [980, 605]
2+
focal: [1230, 1230]
3+
distort: [-0.23751890570984993, 0.08452214195986749, -0.00035324203850054794, -0.0003762498910536819, 0.0]
4+
skew: 0.0
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
11
center: [934.859447, 607.266974]
22
focal: [1180.753804, 1177.034929]
3+
distort: [-0.2448506795091457, 0.08202383880344215, 0.0004294271518916802, -0.0012454354245869965, 0.0]
4+
skew: 0.0
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
11
center: [955.729090, 599.315043]
22
focal: [1216.836137, 1216.045721]
3+
distort: [-0.24343640079788503, 0.09125282937288573, 5.21454371097206e-06, -2.2750254391060847e-05, 0.0]
4+
skew: 0.0
Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
1-
center: [951.302956, 600.563739]
2-
focal: [1210.294187, 1211.035576]
1+
center: [950, 600]
2+
focal: [1210,1210]
3+
distort: [-0.24754745389508612, 0.09944435338953724, -3.455420573094537e-05, -0.00022029168609146265, 0.0]
4+
skew: 0.0
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
data directory looks like
2+
|-data
3+
||-fl
4+
|||-images
5+
||||-000001.png
6+
||-oak
7+
||-rl
8+
||-...
9+
||-pc
10+
|||-000001.pcd
Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
#%%
2+
import cv2
3+
import open3d as o3d
4+
import numpy as np
5+
import pyvista as pv
6+
from typing import Literal
7+
from tqdm import tqdm
8+
#%%
9+
import os
10+
os.chdir('/mnt/GEMstack/GEMstack/offboard/calibration')
11+
from tools.save_cali import *
12+
#%%
13+
N_PAIRS = 20
14+
STEP = 3
15+
cam = 'rr'
16+
17+
#%%
18+
def get_shape():
19+
return 1920,1200
20+
21+
def getK():
22+
K_path = f"/mnt/GEMstack/GEMstack/knowledge/calibration/gem_e4_{cam}_in.yaml"
23+
K, distort = load_in(K_path,mode='matrix',return_distort=True)
24+
w,h = get_shape()
25+
newK, roi = cv2.getOptimalNewCameraMatrix(K, distort, (w,h), 1, (w,h))
26+
return newK
27+
28+
def get_img_np(img_path):
29+
K_path = f"/mnt/GEMstack/GEMstack/knowledge/calibration/gem_e4_{cam}_in.yaml"
30+
K, distort = load_in(K_path,mode='matrix',return_distort=True)
31+
img = cv2.imread(img_path, cv2.IMREAD_GRAYSCALE)
32+
undistorted = cv2.undistort(img, K, distort)
33+
return np.asarray(undistorted)
34+
35+
def get_pc_np(pcd_path):
36+
pcd = o3d.io.read_point_cloud(pcd_path)
37+
pc = np.asarray(pcd.points)
38+
pc = pc[~np.all(pc == 0, axis=1)] # remove (0,0,0)'s
39+
return pc
40+
41+
def vis(pc):
42+
plotter = pv.Plotter(notebook=False)
43+
# plotter = pv.Plotter()
44+
plotter.add_mesh(
45+
pv.PolyData(pc),
46+
render_points_as_spheres=True,
47+
point_size=2,
48+
color='red'
49+
)
50+
plotter.show()
51+
52+
#%%
53+
def pcd_paths():
54+
img_root = '/mnt/GEMstack/GEMstack/offboard/calibration/calibration_by_SfM/data/pc/'
55+
img_format = '.pcd'
56+
for id in range(0,N_PAIRS*STEP+1):
57+
path = img_root + str(id).zfill(6) + img_format
58+
print(path)
59+
yield path
60+
61+
def img_paths():
62+
img_root = f'/mnt/GEMstack/GEMstack/offboard/calibration/calibration_by_SfM/data/{cam}/images/'
63+
img_format = '.png'
64+
for id in range(0,N_PAIRS*STEP+1):
65+
path = img_root + str(id).zfill(6) + img_format
66+
print(path)
67+
yield path
68+
69+
def pcs():
70+
for path in pcd_paths():
71+
yield get_pc_np(path)
72+
73+
def imgs():
74+
for path in img_paths():
75+
yield get_img_np(path)
76+
77+
def iterpair(iter,step):
78+
pre = None
79+
for i in iter:
80+
pre = i
81+
break
82+
for i,v in enumerate(iter):
83+
if i%step == step-1:
84+
yield pre,v
85+
pre = v
86+
87+
#%%
88+
def findE_img(img1,img2,K):
89+
sift = cv2.SIFT.create()
90+
kp1,des1 = sift.detectAndCompute(img1,None)
91+
kp2,des2 = sift.detectAndCompute(img2,None)
92+
93+
if des1 is None or des2 is None or len(des1) < 5 or len(des2) < 5:
94+
return None #insufficient features
95+
96+
FLANN_INDEX_KDTREE = 1
97+
index_params = dict(algorithm=FLANN_INDEX_KDTREE, trees=5)
98+
search_params = dict(checks=50)
99+
flann = cv2.FlannBasedMatcher(index_params, search_params)
100+
matches = flann.knnMatch(des1, des2, k=2)
101+
good_matches = [m for m,n in matches if m.distance < 0.8 * n.distance]
102+
103+
pts1 = np.float32([kp1[m.queryIdx].pt for m in good_matches]).reshape(-1, 2)
104+
pts2 = np.float32([kp2[m.trainIdx].pt for m in good_matches]).reshape(-1, 2)
105+
106+
E, mask = cv2.findEssentialMat(pts1, pts2, K, method=cv2.RANSAC, prob=0.999, threshold=1.0)
107+
if E is None or mask.sum() < 5:
108+
return None #Essential matrix computation failed
109+
_, R_rel, t_rel, mask = cv2.recoverPose(E, pts1, pts2, K, mask=mask)
110+
ret = np.eye(4)
111+
ret[:3, :3] = R_rel
112+
ret[:3, 3] = t_rel.flatten()
113+
return ret
114+
115+
def findE_pc(pc1,pc2,mode:Literal['icp','kp','svd']):
116+
if mode == 'icp':
117+
source = o3d.geometry.PointCloud()
118+
source.points = o3d.utility.Vector3dVector(pc1)
119+
120+
target = o3d.geometry.PointCloud()
121+
target.points = o3d.utility.Vector3dVector(pc2)
122+
123+
source.estimate_normals(
124+
search_param=o3d.geometry.KDTreeSearchParamHybrid(radius=0.1, max_nn=30)
125+
)
126+
target.estimate_normals(
127+
search_param=o3d.geometry.KDTreeSearchParamHybrid(radius=0.1, max_nn=30)
128+
)
129+
init_pose = np.eye(4) #assume continuous movement
130+
# Robust configuration
131+
reg = o3d.pipelines.registration.registration_icp(
132+
source, target,
133+
0.1,
134+
init_pose,
135+
o3d.pipelines.registration.TransformationEstimationPointToPoint(),
136+
o3d.pipelines.registration.ICPConvergenceCriteria(
137+
max_iteration=30,
138+
relative_rmse=1e-6
139+
)
140+
)
141+
return reg.transformation
142+
else:
143+
print(mode,'no imp')
144+
return None
145+
146+
#%%
147+
rcam, tcam = [],[]
148+
rlidar, tlidar = [],[]
149+
#%%
150+
for i,((img1,img2),(pc1,pc2)) in tqdm(enumerate(zip(
151+
iterpair(map(get_img_np,img_paths()),STEP),
152+
iterpair(map(get_pc_np,pcd_paths()),STEP))),total=N_PAIRS):
153+
K = getK()
154+
E_cam = findE_img(img1,img2,K)
155+
print(E_cam)
156+
E_lidar = findE_pc(pc1,pc2,mode='icp')
157+
print(E_lidar)
158+
if E_cam is None or E_lidar is None:
159+
continue #unsolved
160+
if np.isnan(E_cam).any() or np.isnan(E_lidar).any():
161+
continue #has nan
162+
if np.linalg.norm(E_lidar[:3,3]) > 0.1*STEP:
163+
continue #too large to be possible
164+
rcam.append(E_cam[:3, :3])
165+
tcam.append(E_cam[:3, 3])
166+
rlidar.append(E_lidar[:3, :3])
167+
tlidar.append(E_lidar[:3, 3])
168+
169+
#%%
170+
rt2cam = []
171+
tt2cam = []
172+
for r,t in zip(rlidar,tlidar):
173+
B = np.eye(4)
174+
B[:3,:3] = r
175+
B[:3,3] = t
176+
B = np.linalg.inv(B)
177+
rt2cam.append(B[:3,:3])
178+
tt2cam.append(B[:3,3])
179+
180+
X = cv2.calibrateHandEye(
181+
R_gripper2base=rcam, t_gripper2base=tcam,
182+
R_target2cam=rt2cam, t_target2cam=tt2cam,
183+
# R_target2cam=rlidar, t_target2cam=tlidar,
184+
# method=cv2.CALIB_HAND_EYE_TSAI
185+
method=cv2.CALIB_HAND_EYE_PARK
186+
)
187+
#%%
188+
# Result is 4x4 transformation matrix: T_cam_lidar
189+
T_cam_lidar = np.eye(4)
190+
T_cam_lidar[:3, :3] = X[0]
191+
T_cam_lidar[:3, 3] = X[1].flatten()
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
*
2+
!.gitignore
Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1-
sam_vit_h.pth
2-
1+
weights/*
2+
init_proj*
3+
refined_proj*
4+
data/*
5+
extrinsic.txt
36
!.gitignore
Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
data=$1
22

3-
wget -c -O sam_vit_h.pth https://dl.fbaipublicfiles.com/segment_anything/sam_vit_h_4b8939.pth
3+
wget -c -O weights/sam_vit_b.pth https://dl.fbaipublicfiles.com/segment_anything/sam_vit_b_01ec64.pth
4+
# wget -c -O sam_vit_h.pth https://dl.fbaipublicfiles.com/segment_anything/sam_vit_h_4b8939.pth
45

56
echo 'running segmentation...'
6-
python3 segment-anything scripts/amg.py --checkpoint sam_vit_h.pth --model-type vit_h --input $data/images --output $data/masks/ --stability-score-thresh 0.9 --box-nms-thresh 0.5 --stability-score-offset 0.9
7+
python3 segment-anything/scripts/amg.py --checkpoint weights/sam_vit_b.pth --model-type vit_b --input $data/selected --output $data/masks/ --stability-score-thresh 0.9 --box-nms-thresh 0.5 --stability-score-offset 0.9
8+
# python3 segment-anything/scripts/amg.py --checkpoint sam_vit_h.pth --model-type vit_h --input $data/images --output $data/masks/ --stability-score-thresh 0.9 --box-nms-thresh 0.5 --stability-score-offset 0.9
79

810
echo 'reprocess segmentation...'
911
python3 CalibAnything/processed_mask.py -i $data/masks -o $data/processed_masks/
1012

1113
echo 'running calibration...'
12-
CalibAnything/bin/run_lidar2camera $data/calib.json
14+
CalibAnything/bin/run_lidar2camera $data/calib.json
15+

0 commit comments

Comments
 (0)