Jim Ostrowski | 007e2ea | 2022-01-30 13:13:26 -0800 | [diff] [blame] | 1 | import copy |
| 2 | import glog |
| 3 | import json |
| 4 | import math |
| 5 | import numpy as np |
| 6 | import os |
| 7 | |
| 8 | glog.setLevel("INFO") |
| 9 | |
| 10 | |
| 11 | # Quick fix to naming games that happen with bazel |
| 12 | def bazel_name_fix(filename): |
| 13 | ret_name = filename |
| 14 | try: |
| 15 | from bazel_tools.tools.python.runfiles import runfiles |
| 16 | r = runfiles.Create() |
| 17 | ret_name = r.Rlocation('org_frc971/y2022/vision/' + filename) |
| 18 | #print("Trying directory: ", ret_name) |
| 19 | except: |
| 20 | print("Failed bazel_name_fix") |
| 21 | pass |
| 22 | |
| 23 | ### TODO<Jim>: Need to figure out why this isn't working |
| 24 | ### Hardcoding for now |
| 25 | if ret_name == None: |
| 26 | ret_name = '/home/jim/code/FRC/971-Robot-Code/y2022/vision/calib_files' |
| 27 | return ret_name |
| 28 | |
| 29 | |
| 30 | class CameraIntrinsics: |
| 31 | def __init__(self): |
| 32 | self.camera_matrix = [] |
| 33 | self.dist_coeffs = [] |
| 34 | |
| 35 | pass |
| 36 | |
| 37 | |
| 38 | class CameraExtrinsics: |
| 39 | def __init__(self): |
| 40 | self.R = [] |
| 41 | self.T = [] |
| 42 | |
| 43 | |
| 44 | class CameraParameters: |
| 45 | def __init__(self): |
| 46 | self.camera_int = CameraIntrinsics() |
| 47 | self.camera_ext = CameraExtrinsics() |
| 48 | self.turret_ext = None |
| 49 | self.node_name = "" |
| 50 | self.team_number = -1 |
Jim Ostrowski | fec0c33 | 2022-02-06 23:28:26 -0800 | [diff] [blame] | 51 | self.camera_id = "" |
Jim Ostrowski | 007e2ea | 2022-01-30 13:13:26 -0800 | [diff] [blame] | 52 | self.timestamp = 0 |
| 53 | |
| 54 | |
| 55 | def compute_extrinsic(camera_pitch, camera_yaw, T_camera, is_turret): |
| 56 | # Compute the extrinsic calibration based on pitch and translation |
| 57 | # Includes camera rotation from robot x,y,z to opencv (z, -x, -y) |
| 58 | |
| 59 | # Also, handle extrinsics for the turret |
| 60 | # The basic camera pose is relative to the center, base of the turret |
| 61 | # TODO<Jim>: Maybe store these to .json files, like with intrinsics? |
| 62 | base_cam_ext = CameraExtrinsics() |
| 63 | turret_cam_ext = CameraExtrinsics() |
| 64 | |
| 65 | camera_pitch_matrix = np.array( |
| 66 | [[np.cos(camera_pitch), 0.0, |
| 67 | np.sin(camera_pitch)], [0.0, 1.0, 0.0], |
| 68 | [-np.sin(camera_pitch), 0.0, |
| 69 | np.cos(camera_pitch)]]) |
| 70 | |
| 71 | camera_yaw_matrix = np.array( |
| 72 | [[np.cos(camera_yaw), -np.sin(camera_yaw), 0.0], |
| 73 | [np.sin(camera_yaw), np.cos(camera_yaw), 0.0], [0.0, 0.0, 1.0]]) |
| 74 | |
| 75 | robot_to_camera_rotation = np.array([[0., 0., 1.], [-1, 0, 0], [0, -1., |
| 76 | 0]]) |
| 77 | |
| 78 | if is_turret: |
James Kuszmaul | 3f3b1d9 | 2022-03-13 18:01:37 -0700 | [diff] [blame] | 79 | # Turret is just an identity matrix in the middle of the robot. |
| 80 | base_cam_ext.R = np.array([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], |
Jim Ostrowski | 007e2ea | 2022-01-30 13:13:26 -0800 | [diff] [blame] | 81 | [0.0, 0.0, 1.0]]) |
| 82 | base_cam_ext.T = np.array([0.0, 0.0, 0.0]) |
| 83 | turret_cam_ext.R = camera_yaw_matrix @ camera_pitch_matrix @ robot_to_camera_rotation |
| 84 | turret_cam_ext.T = T_camera |
| 85 | else: |
| 86 | base_cam_ext.R = camera_yaw_matrix @ camera_pitch_matrix @ robot_to_camera_rotation |
| 87 | base_cam_ext.T = T_camera |
| 88 | turret_cam_ext = None |
| 89 | |
| 90 | return base_cam_ext, turret_cam_ext |
| 91 | |
| 92 | |
Milind Upadhyay | 37f6fe8 | 2022-04-14 23:00:44 -0700 | [diff] [blame] | 93 | def compute_extrinsic_by_pi(pi_number, team_number): |
James Kuszmaul | 3f3b1d9 | 2022-03-13 18:01:37 -0700 | [diff] [blame] | 94 | # Defaults for all cameras |
| 95 | camera_pitch = -35.0 * np.pi / 180.0 |
Jim Ostrowski | 007e2ea | 2022-01-30 13:13:26 -0800 | [diff] [blame] | 96 | camera_yaw = 0.0 |
James Kuszmaul | 3f3b1d9 | 2022-03-13 18:01:37 -0700 | [diff] [blame] | 97 | is_turret = True |
Jim Ostrowski | 007e2ea | 2022-01-30 13:13:26 -0800 | [diff] [blame] | 98 | # Default camera location to robot origin |
| 99 | T = np.array([0.0, 0.0, 0.0]) |
| 100 | |
Milind Upadhyay | 37f6fe8 | 2022-04-14 23:00:44 -0700 | [diff] [blame] | 101 | if team_number == 971: |
| 102 | if pi_number == "pi1": |
| 103 | camera_yaw = 90.0 * np.pi / 180.0 |
| 104 | T = np.array([-11.0 * 0.0254, 5.5 * 0.0254, 29.5 * 0.0254]) |
| 105 | elif pi_number == "pi2": |
| 106 | camera_yaw = 0.0 |
| 107 | T = np.array([-9.5 * 0.0254, -3.5 * 0.0254, 34.5 * 0.0254]) |
| 108 | elif pi_number == "pi3": |
| 109 | camera_yaw = 179.0 * np.pi / 180.0 |
| 110 | T = np.array([-9.5 * 0.0254, 3.5 * 0.0254, 34.5 * 0.0254]) |
| 111 | elif pi_number == "pi4": |
| 112 | camera_yaw = -90.0 * np.pi / 180.0 |
| 113 | T = np.array([-10.25 * 0.0254, -5.0 * 0.0254, 27.5 * 0.0254]) |
| 114 | elif team_number == 9971: |
| 115 | if pi_number == "pi1": |
Austin Schuh | 399b184 | 2022-04-16 14:26:10 -0700 | [diff] [blame^] | 116 | camera_yaw = 178.0 * np.pi / 180.0 |
Milind Upadhyay | 37f6fe8 | 2022-04-14 23:00:44 -0700 | [diff] [blame] | 117 | T = np.array([0.0 * 0.0254, 8.5 * 0.0254, 34.0 * 0.0254]) |
| 118 | elif pi_number == "pi2": |
| 119 | camera_yaw = 0.0 |
| 120 | T = np.array([-9.0 * 0.0254, -3.5 * 0.0254, 35.5 * 0.0254]) |
| 121 | elif pi_number == "pi3": |
| 122 | camera_yaw = 90.0 * np.pi / 180.0 |
| 123 | T = np.array([-8.0 * 0.0254, 3.0 * 0.0254, 32.0 * 0.0254]) |
| 124 | else: |
| 125 | glog.fatal("Unknown team number for extrinsics") |
Jim Ostrowski | 007e2ea | 2022-01-30 13:13:26 -0800 | [diff] [blame] | 126 | |
| 127 | return compute_extrinsic(camera_pitch, camera_yaw, T, is_turret) |
| 128 | |
| 129 | |
| 130 | def load_camera_definitions(): |
| 131 | ### CAMERA DEFINITIONS |
| 132 | # We only load in cameras that have a calibration file |
| 133 | # These are stored in y2022/vision/calib_files |
| 134 | # |
| 135 | # Or better yet, use //y2020/vision:calibration to calibrate the camera |
| 136 | # using a Charuco target board |
| 137 | |
| 138 | camera_list = [] |
| 139 | |
| 140 | dir_name = bazel_name_fix('calib_files') |
| 141 | if dir_name is not None: |
| 142 | glog.debug("Searching for calibration files in " + dir_name) |
| 143 | else: |
| 144 | glog.fatal("Failed to find calib_files directory") |
| 145 | |
| 146 | for filename in sorted(os.listdir(dir_name)): |
| 147 | glog.debug("Inspecting %s", filename) |
| 148 | if ("cam-calib-int" in filename |
| 149 | or 'calibration' in filename) and filename.endswith(".json"): |
| 150 | |
| 151 | # Extract intrinsics from file |
| 152 | calib_file = open(dir_name + "/" + filename, 'r') |
| 153 | calib_dict = json.loads(calib_file.read()) |
| 154 | |
| 155 | team_number = calib_dict["team_number"] |
| 156 | node_name = calib_dict["node_name"] |
Jim Ostrowski | fec0c33 | 2022-02-06 23:28:26 -0800 | [diff] [blame] | 157 | camera_id = "UNKNOWN" |
| 158 | if "camera_id" in calib_dict: |
| 159 | camera_id = calib_dict["camera_id"] |
| 160 | |
Jim Ostrowski | 007e2ea | 2022-01-30 13:13:26 -0800 | [diff] [blame] | 161 | camera_matrix = np.asarray(calib_dict["intrinsics"]).reshape( |
| 162 | (3, 3)) |
| 163 | dist_coeffs = np.asarray(calib_dict["dist_coeffs"]).reshape((1, 5)) |
| 164 | |
| 165 | glog.debug("Found calib for " + node_name + ", team #" + |
| 166 | str(team_number)) |
| 167 | |
| 168 | camera_params = CameraParameters() |
| 169 | # TODO: Need to add reading in extrinsic camera parameters from json |
| 170 | camera_params.camera_ext, camera_params.turret_ext = compute_extrinsic_by_pi( |
Milind Upadhyay | 37f6fe8 | 2022-04-14 23:00:44 -0700 | [diff] [blame] | 171 | node_name, team_number) |
Jim Ostrowski | 007e2ea | 2022-01-30 13:13:26 -0800 | [diff] [blame] | 172 | |
| 173 | camera_params.node_name = node_name |
| 174 | camera_params.team_number = team_number |
Jim Ostrowski | fec0c33 | 2022-02-06 23:28:26 -0800 | [diff] [blame] | 175 | camera_params.camera_id = camera_id |
Jim Ostrowski | 007e2ea | 2022-01-30 13:13:26 -0800 | [diff] [blame] | 176 | camera_params.camera_int.camera_matrix = copy.copy(camera_matrix) |
| 177 | camera_params.camera_int.dist_coeffs = copy.copy(dist_coeffs) |
| 178 | camera_list.append(camera_params) |
| 179 | |
| 180 | return camera_list |