blob: 9d43ee469a073664c027965fa5d6a2ecb6b2e6b5 [file] [log] [blame]
import copy
import glog
import json
import math
import numpy as np
import os
glog.setLevel("INFO")
# Quick fix to naming games that happen with bazel
def bazel_name_fix(filename):
ret_name = filename
try:
from bazel_tools.tools.python.runfiles import runfiles
r = runfiles.Create()
ret_name = r.Rlocation('org_frc971/y2022/vision/' + filename)
#print("Trying directory: ", ret_name)
except:
print("Failed bazel_name_fix")
pass
### TODO<Jim>: Need to figure out why this isn't working
### Hardcoding for now
if ret_name == None:
ret_name = '/home/jim/code/FRC/971-Robot-Code/y2022/vision/calib_files'
return ret_name
class CameraIntrinsics:
def __init__(self):
self.camera_matrix = []
self.dist_coeffs = []
pass
class CameraExtrinsics:
def __init__(self):
self.R = []
self.T = []
class CameraParameters:
def __init__(self):
self.camera_int = CameraIntrinsics()
self.camera_ext = CameraExtrinsics()
self.turret_ext = None
self.node_name = ""
self.team_number = -1
self.camera_id = ""
self.timestamp = 0
def compute_extrinsic(camera_roll, camera_pitch, camera_yaw, T_camera,
is_turret):
# Compute the extrinsic calibration based on pitch and translation
# Includes camera rotation from robot x,y,z to opencv (z, -x, -y)
# Also, handle extrinsics for the turret
# The basic camera pose is relative to the center, base of the turret
# TODO<Jim>: Maybe store these to .json files, like with intrinsics?
base_cam_ext = CameraExtrinsics()
turret_cam_ext = CameraExtrinsics()
camera_roll_matrix = np.array(
[[1.0, 0.0, 0.0], [0.0, np.cos(camera_roll), -np.sin(camera_roll)],
[0.0, np.sin(camera_roll),
np.cos(camera_roll)]])
camera_pitch_matrix = np.array(
[[np.cos(camera_pitch), 0.0,
np.sin(camera_pitch)], [0.0, 1.0, 0.0],
[-np.sin(camera_pitch), 0.0,
np.cos(camera_pitch)]])
camera_yaw_matrix = np.array(
[[np.cos(camera_yaw), -np.sin(camera_yaw), 0.0],
[np.sin(camera_yaw), np.cos(camera_yaw), 0.0], [0.0, 0.0, 1.0]])
robot_to_camera_rotation = np.array([[0., 0., 1.], [-1, 0, 0], [0, -1.,
0]])
if is_turret:
# Turret is just an identity matrix in the middle of the robot.
base_cam_ext.R = np.array([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0],
[0.0, 0.0, 1.0]])
base_cam_ext.T = np.array([0.0, 0.0, 0.0])
turret_cam_ext.R = camera_yaw_matrix @ camera_pitch_matrix @ camera_roll_matrix @ robot_to_camera_rotation
turret_cam_ext.T = T_camera
else:
base_cam_ext.R = camera_yaw_matrix @ camera_pitch_matrix @ camera_roll_matrix @ robot_to_camera_rotation
base_cam_ext.T = T_camera
turret_cam_ext = None
return base_cam_ext, turret_cam_ext
def compute_extrinsic_by_pi(pi_number, team_number):
# Defaults for all cameras
camera_roll = 0.0
camera_pitch = -35.0 * np.pi / 180.0
camera_yaw = 0.0
is_turret = True
# Default camera location to robot origin
T = np.array([0.0, 0.0, 0.0])
if team_number == 971:
if pi_number == "pi1":
camera_yaw = 90.0 * np.pi / 180.0
T = np.array([-11.0 * 0.0254, 5.5 * 0.0254, 29.5 * 0.0254])
elif pi_number == "pi2":
camera_yaw = 0.0
T = np.array([-9.5 * 0.0254, -3.5 * 0.0254, 34.5 * 0.0254])
elif pi_number == "pi3":
camera_yaw = 182.0 * np.pi / 180.0
T = np.array([-9.5 * 0.0254, 3.5 * 0.0254, 34.5 * 0.0254])
elif pi_number == "pi4":
camera_yaw = -90.0 * np.pi / 180.0
T = np.array([-10.25 * 0.0254, -5.0 * 0.0254, 27.5 * 0.0254])
elif team_number == 9971:
if pi_number == "pi1":
camera_yaw = 179.0 * np.pi / 180.0
T = np.array([-9.5 * 0.0254, 3.25 * 0.0254, 35.5 * 0.0254])
elif pi_number == "pi2":
camera_yaw = 0.0
T = np.array([-9.0 * 0.0254, -3.25 * 0.0254, 35.5 * 0.0254])
elif pi_number == "pi3":
camera_yaw = 90.0 * np.pi / 180.0
T = np.array([-10.5 * 0.0254, 5.0 * 0.0254, 29.5 * 0.0254])
elif pi_number == "pi4":
camera_yaw = -90.0 * np.pi / 180.0
T = np.array([-10.5 * 0.0254, -5.0 * 0.0254, 28.5 * 0.0254])
elif team_number == 7971:
is_turret = False
# Cameras are flipped upside down
camera_roll = 180.0 * np.pi / 180.0
if pi_number == "pi1":
camera_yaw = 90.0 * np.pi / 180.0
T = np.array([-6.0 * 0.0254, -8.0 * 0.0254, 0.5 * 0.0254])
elif pi_number == "pi2":
camera_yaw = 0.0
T = np.array([3.75 * 0.0254, 7.5 * 0.0254, 0.5 * 0.0254])
elif pi_number == "pi3":
camera_yaw = 0.0
T = np.array([3.75 * 0.0254, -4.25 * 0.0254, 0.5 * 0.0254])
elif pi_number == "pi4":
camera_yaw = 0.0
T = np.array([-6.0 * 0.0254, -7.0 * 0.0254, 0.5 * 0.0254])
else:
glog.fatal("Unknown team number for extrinsics")
return compute_extrinsic(camera_roll, camera_pitch, camera_yaw, T,
is_turret)
def load_camera_definitions():
### CAMERA DEFINITIONS
# We only load in cameras that have a calibration file
# These are stored in y2022/vision/calib_files
#
# Or better yet, use //y2020/vision:calibration to calibrate the camera
# using a Charuco target board
camera_list = []
dir_name = bazel_name_fix('calib_files')
if dir_name is not None:
glog.debug("Searching for calibration files in " + dir_name)
else:
glog.fatal("Failed to find calib_files directory")
for filename in sorted(os.listdir(dir_name)):
glog.debug("Inspecting %s", filename)
if ("cam-calib-int" in filename
or 'calibration' in filename) and filename.endswith(".json"):
# Extract intrinsics from file
calib_file = open(dir_name + "/" + filename, 'r')
calib_dict = json.loads(calib_file.read())
team_number = calib_dict["team_number"]
node_name = calib_dict["node_name"]
camera_id = "UNKNOWN"
if "camera_id" in calib_dict:
camera_id = calib_dict["camera_id"]
camera_matrix = np.asarray(calib_dict["intrinsics"]).reshape(
(3, 3))
dist_coeffs = np.asarray(calib_dict["dist_coeffs"]).reshape((1, 5))
glog.debug("Found calib for " + node_name + ", team #" +
str(team_number))
camera_params = CameraParameters()
# TODO: Need to add reading in extrinsic camera parameters from json
camera_params.camera_ext, camera_params.turret_ext = compute_extrinsic_by_pi(
node_name, team_number)
camera_params.node_name = node_name
camera_params.team_number = team_number
camera_params.camera_id = camera_id
camera_params.camera_int.camera_matrix = copy.copy(camera_matrix)
camera_params.camera_int.dist_coeffs = copy.copy(dist_coeffs)
camera_list.append(camera_params)
return camera_list