Visual Servoing Platform version 3.7.0
Loading...
Searching...
No Matches
camera_calibration_show_extrinsics.py
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3
4# ViSP, open source Visual Servoing Platform software.
5# Copyright (C) 2005 - 2025 by Inria. All rights reserved.
6# Redistribution and use in source and binary forms, with or without modification,
7# are permitted provided that the following conditions are met:
8#
9# * Redistributions of source code must retain the above copyright notice,
10# this list of conditions and the following disclaimer.
11#
12# * Redistributions in binary form must reproduce the above copyright notice,
13# this list of conditions and the following disclaimer in the documentation
14# and/or other materials provided with the distribution.
15#
16# * Neither the names of the copyright holders nor the names of the contributors
17# may be used to endorse or promote products derived from this software
18# without specific prior written permission.
19#
20# This software is provided by the copyright holders and contributors "as is" and
21# any express or implied warranties, including, but not limited to, the implied
22# warranties of merchantability and fitness for a particular purpose are disclaimed.
23# In no event shall copyright holders or contributors be liable for any direct,
24# indirect, incidental, special, exemplary, or consequential damages
25# (including, but not limited to, procurement of substitute goods or services;
26# loss of use, data, or profits; or business interruption) however caused
27# and on any theory of liability, whether in contract, strict liability,
28# or tort (including negligence or otherwise) arising in any way out of
29# the use of this software, even if advised of the possibility of such damage.
30
31# Python 2/3 compatibility
32from __future__ import print_function
33
34from scipy.spatial.transform import Rotation
35import numpy as np
36from numpy import linspace
37from enum import Enum
38import xml.etree.ElementTree as ET
39import re
40
41class PatternType(Enum):
42 CHESSBOARD = 'Chessboard'
43 CIRCLES_GRID = 'Circles grid'
44
46 def __init__(self, str):
47 nums = [float(n) for n in str.split()]
48 self.pose = np.array(nums)
49
50 def __str__(self):
51 return "Pose: {}".format(self.pose.transpose())
52
54 def __init__(self, name, image_width, image_height, px, py, u0, v0, \
55 pattern_type, board_width, board_height, square_size, cMo_vec):
56 self.name = name
57 self.image_width = image_width
58 self.image_height = image_height
59 self.intrinsics = np.array([[px, 0, u0], [0, py, v0], [0, 0, 1]], np.float64)
60 self.pattern_type = pattern_type
61 self.board_width = board_width
62 self.board_height = board_height
63 self.square_size = square_size
64 self.cMo_vec = cMo_vec
65
66 def __str__(self):
67 str = "CameraInfo:\n\t camera name:{}\n\timage: {:d}x{:d}\n\tcamera intrinsics:\n{}\n\t{}\n\tboard_size: {:d}x{:d}\n\tsquare_size: {:g}\n\tposes:\n".format( \
68 self.name, self.image_width, self.image_height, self.intrinsics, \
69 self.pattern_type, self.board_width, self.board_height, self.square_size)
70 for cMo in self.cMo_vec:
71 str += "\t\t" + cMo.__str__() + "\n"
72 return str
73
75 R = M[0:3, 0:3]
76 T = M[0:3, 3]
77 M_inv = np.identity(4)
78 M_inv[0:3, 0:3] = R.T
79 M_inv[0:3, 3] = -(R.T).dot(T)
80
81 return M_inv
82
83def transform_to_matplotlib_frame(cMo, X, patternCentric=False):
84 M = np.identity(4)
85 if patternCentric:
86 M[1,1] = -1
87 M[2,2] = -1
88 else:
89 M[1,1] = 0
90 M[1,2] = 1
91 M[2,1] = -1
92 M[2,2] = 0
93
94 if patternCentric:
95 return M.dot(inverse_homogeneoux_matrix(cMo).dot(X))
96 else:
97 return M.dot(cMo.dot(X))
98
99def create_camera_model(camera_matrix, width, height, scale_focal, draw_frame_axis=False):
100 fx = camera_matrix[0,0]
101 fy = camera_matrix[1,1]
102 focal = 2 / (fx + fy)
103 f_scale = scale_focal * focal
104
105 # draw image plane
106 X_img_plane = np.ones((4,5))
107 X_img_plane[0:3,0] = [-width, height, f_scale]
108 X_img_plane[0:3,1] = [width, height, f_scale]
109 X_img_plane[0:3,2] = [width, -height, f_scale]
110 X_img_plane[0:3,3] = [-width, -height, f_scale]
111 X_img_plane[0:3,4] = [-width, height, f_scale]
112
113 # draw triangle above the image plane
114 X_triangle = np.ones((4,3))
115 X_triangle[0:3,0] = [-width, -height, f_scale]
116 X_triangle[0:3,1] = [0, -2*height, f_scale]
117 X_triangle[0:3,2] = [width, -height, f_scale]
118
119 # draw camera
120 X_center1 = np.ones((4,2))
121 X_center1[0:3,0] = [0, 0, 0]
122 X_center1[0:3,1] = [-width, height, f_scale]
123
124 X_center2 = np.ones((4,2))
125 X_center2[0:3,0] = [0, 0, 0]
126 X_center2[0:3,1] = [width, height, f_scale]
127
128 X_center3 = np.ones((4,2))
129 X_center3[0:3,0] = [0, 0, 0]
130 X_center3[0:3,1] = [width, -height, f_scale]
131
132 X_center4 = np.ones((4,2))
133 X_center4[0:3,0] = [0, 0, 0]
134 X_center4[0:3,1] = [-width, -height, f_scale]
135
136 # draw camera frame axis
137 X_frame1 = np.ones((4,2))
138 X_frame1[0:3,0] = [0, 0, 0]
139 X_frame1[0:3,1] = [f_scale/2, 0, 0]
140
141 X_frame2 = np.ones((4,2))
142 X_frame2[0:3,0] = [0, 0, 0]
143 X_frame2[0:3,1] = [0, f_scale/2, 0]
144
145 X_frame3 = np.ones((4,2))
146 X_frame3[0:3,0] = [0, 0, 0]
147 X_frame3[0:3,1] = [0, 0, f_scale/2]
148
149 if draw_frame_axis:
150 return [X_img_plane, X_triangle, X_center1, X_center2, X_center3, X_center4, X_frame1, X_frame2, X_frame3]
151 else:
152 return [X_img_plane, X_triangle, X_center1, X_center2, X_center3, X_center4]
153
154def create_board_model(extrinsics, board_width, board_height, square_size, draw_frame_axis=False):
155 width = board_width*square_size
156 height = board_height*square_size
157
158 # draw calibration board
159 X_board = np.ones((4,5))
160 X_board[0:3,0] = [0,0,0]
161 X_board[0:3,1] = [width,0,0]
162 X_board[0:3,2] = [width,height,0]
163 X_board[0:3,3] = [0,height,0]
164 X_board[0:3,4] = [0,0,0]
165
166 # draw board frame axis
167 X_frame1 = np.ones((4,2))
168 X_frame1[0:3,0] = [0, 0, 0]
169 X_frame1[0:3,1] = [height/2, 0, 0]
170
171 X_frame2 = np.ones((4,2))
172 X_frame2[0:3,0] = [0, 0, 0]
173 X_frame2[0:3,1] = [0, height/2, 0]
174
175 X_frame3 = np.ones((4,2))
176 X_frame3[0:3,0] = [0, 0, 0]
177 X_frame3[0:3,1] = [0, 0, height/2]
178
179 if draw_frame_axis:
180 return [X_board, X_frame1, X_frame2, X_frame3]
181 else:
182 return [X_board]
183
184def draw_camera_boards(ax, camera_matrix, cam_width, cam_height, scale_focal,
185 extrinsics, board_width, board_height, square_size,
186 patternCentric):
187 from matplotlib import cm
188
189 min_values = np.zeros((3,1))
190 min_values = np.inf
191 max_values = np.zeros((3,1))
192 max_values = -np.inf
193
194 if patternCentric:
195 X_moving = create_camera_model(camera_matrix, cam_width, cam_height, scale_focal)
196 X_static = create_board_model(extrinsics, board_width, board_height, square_size, True)
197 else:
198 X_static = create_camera_model(camera_matrix, cam_width, cam_height, scale_focal, True)
199 X_moving = create_board_model(extrinsics, board_width, board_height, square_size, True)
200
201 cm_subsection = linspace(0.0, 1.0, extrinsics.shape[0])
202 colors = [ cm.jet(x) for x in cm_subsection ]
203
204 for i in range(len(X_static)):
205 X = np.zeros(X_static[i].shape)
206 for j in range(X_static[i].shape[1]):
207 X[:,j] = transform_to_matplotlib_frame(np.eye(4), X_static[i][:,j], patternCentric)
208 ax.plot3D(X[0,:], X[1,:], X[2,:], color='r')
209 min_values = np.minimum(min_values, X[0:3,:].min(1))
210 max_values = np.maximum(max_values, X[0:3,:].max(1))
211
212 for idx in range(extrinsics.shape[0]):
213 #R = Rotation.from_rotvec(extrinsics[idx,0:3]).as_dcm()
214 R = Rotation.from_rotvec(extrinsics[idx,0:3]).as_matrix()
215 cMo = np.eye(4,4)
216 cMo[0:3,0:3] = R
217 cMo[0:3,3] = extrinsics[idx,3:6]
218 for i in range(len(X_moving)):
219 X = np.zeros(X_moving[i].shape)
220 for j in range(X_moving[i].shape[1]):
221 X[0:4,j] = transform_to_matplotlib_frame(cMo, X_moving[i][0:4,j], patternCentric)
222 ax.plot3D(X[0,:], X[1,:], X[2,:], color=colors[idx])
223 min_values = np.minimum(min_values, X[0:3,:].min(1))
224 max_values = np.maximum(max_values, X[0:3,:].max(1))
225
226 return min_values, max_values
227
228def main():
229 import argparse
230
231 parser = argparse.ArgumentParser(description='Plot camera calibration extrinsics.',
232 formatter_class=argparse.ArgumentDefaultsHelpFormatter)
233 parser.add_argument('--calibration', type=str, default='camera.xml',
234 help='XML camera calibration file.')
235 parser.add_argument('--cam_width', type=float, default=0.064/2,
236 help='Width/2 of the displayed camera.')
237 parser.add_argument('--cam_height', type=float, default=0.048/2,
238 help='Height/2 of the displayed camera.')
239 parser.add_argument('--scale_focal', type=float, default=40,
240 help='Value to scale the focal length.')
241 parser.add_argument('--cameraCentric', action='store_true',
242 help='If argument is present, the camera is static and the calibration boards are moving.')
243 args = parser.parse_args()
244
245 tree = ET.parse(args.calibration)
246 root = tree.getroot()
247
248 def loadCameraInfo(root):
249 camera_info_vec = []
250 for camera in root.iter('camera'):
251 pattern_type = PatternType.CHESSBOARD
252 if camera.find('additional_information').find('calibration_pattern_type').text == 'Circles grid':
253 pattern_type = PatternType.CIRCLES_GRID
254
255 board_size = re.search(r"(\d+)x(\d+)", camera.find('additional_information').find('board_size').text)
256
257 cMo_vec = []
258 for cMo in camera.find('additional_information').find('camera_poses').iter('cMo'):
259 cMo_vec.append(PoseVector(cMo.text))
260
261 camera_info_vec.append(CameraInfo( \
262 camera.find('name').text, int(camera.find('image_width').text), int(camera.find('image_height').text), \
263 float(camera.find('model').find('px').text), float(camera.find('model').find('py').text), \
264 float(camera.find('model').find('u0').text), float(camera.find('model').find('v0').text), \
265 pattern_type, int(board_size.group(1)), int(board_size.group(2)), \
266 float(camera.find('additional_information').find('square_size').text), \
267 cMo_vec \
268 ))
269
270 return camera_info_vec
271
272 camera_info_vec = loadCameraInfo(root)
273
274 for camera_info in camera_info_vec:
275 print('\n', camera_info)
276
277 import matplotlib.pyplot as plt
278 from mpl_toolkits.mplot3d import Axes3D # pylint: disable=unused-variable
279
280 fig = plt.figure()
281 #ax = fig.gca(projection='3d')
282 ax = fig.add_subplot(projection='3d')
283 #ax.set_aspect("equal")
284
285 cMo_vec = camera_info.cMo_vec
286 extrinsics = np.empty((len(cMo_vec), 6), np.float64)
287 for idx, cMo in enumerate(cMo_vec):
288 extrinsics[idx, :3] = cMo.pose[3:]
289 extrinsics[idx, 3:] = cMo.pose[:3]
290
291 cam_width = args.cam_width
292 cam_height = args.cam_height
293 scale_focal = args.scale_focal
294 pattern_centric = not args.cameraCentric
295 min_values, max_values = draw_camera_boards(ax, camera_info.intrinsics, cam_width, cam_height,
296 scale_focal, extrinsics, camera_info.board_width,
297 camera_info.board_height, camera_info.square_size,
298 pattern_centric)
299
300 X_min = min_values[0]
301 X_max = max_values[0]
302 Y_min = min_values[1]
303 Y_max = max_values[1]
304 Z_min = min_values[2]
305 Z_max = max_values[2]
306 max_range = np.array([X_max-X_min, Y_max-Y_min, Z_max-Z_min]).max() / 2.0
307
308 mid_x = (X_max+X_min) * 0.5
309 mid_y = (Y_max+Y_min) * 0.5
310 mid_z = (Z_max+Z_min) * 0.5
311 ax.set_xlim(mid_x - max_range, mid_x + max_range)
312 ax.set_ylim(mid_y - max_range, mid_y + max_range)
313 ax.set_zlim(mid_z - max_range, mid_z + max_range)
314
315 if pattern_centric:
316 ax.set_xlabel('x')
317 ax.set_ylabel('-y')
318 ax.set_zlabel('-z')
319 ax.set_title('Camera Poses Visualization')
320 else:
321 ax.set_xlabel('x')
322 ax.set_ylabel('z')
323 ax.set_zlabel('-y')
324 ax.set_title('Calibration Board Poses Visualization')
325
326 plt.show()
327
328if __name__ == '__main__':
329 main()
__init__(self, name, image_width, image_height, px, py, u0, v0, pattern_type, board_width, board_height, square_size, cMo_vec)
create_board_model(extrinsics, board_width, board_height, square_size, draw_frame_axis=False)
draw_camera_boards(ax, camera_matrix, cam_width, cam_height, scale_focal, extrinsics, board_width, board_height, square_size, patternCentric)
transform_to_matplotlib_frame(cMo, X, patternCentric=False)
create_camera_model(camera_matrix, width, height, scale_focal, draw_frame_axis=False)