diff --git a/.gitignore b/.gitignore index 766874b000..8eb27213b4 100644 --- a/.gitignore +++ b/.gitignore @@ -162,3 +162,5 @@ photonlib-cpp-examples/*/networktables.json.bck photonlib-java-examples/*/networktables.json.bck *.sqlite photon-server/src/main/resources/web/index.html + +venv diff --git a/devTools/calibrationUtils.py b/devTools/calibrationUtils.py new file mode 100644 index 0000000000..97ba70c425 --- /dev/null +++ b/devTools/calibrationUtils.py @@ -0,0 +1,134 @@ +import base64 +from dataclasses import dataclass +import json +import os +import cv2 +import numpy as np + + +@dataclass +class Resolution: + width: int + height: int + + +@dataclass +class JsonMatOfDoubles: + rows: int + cols: int + type: int + data: list[float] + + +@dataclass +class JsonMat: + rows: int + cols: int + type: int + data: str # Base64-encoded PNG data + + +@dataclass +class Point2: + x: float + y: float + + +@dataclass +class Translation3d: + x: float + y: float + z: float + + +@dataclass +class Quaternion: + X: float + Y: float + Z: float + W: float + + +@dataclass +class Rotation3d: + quaternion: Quaternion + + +@dataclass +class Pose3d: + translation: Translation3d + rotation: Rotation3d + + +@dataclass +class Point3: + x: float + y: float + z: float + + +@dataclass +class Observation: + # Expected feature 3d location in the camera frame + locationInObjectSpace: list[Point3] + # Observed location in pixel space + locationInImageSpace: list[Point2] + # (measured location in pixels) - (expected from FK) + reprojectionErrors: list[Point2] + # Solver optimized board poses + optimisedCameraToObject: Pose3d + # If we should use this observation when re-calculating camera calibration + includeObservationInCalibration: bool + snapshotName: str + # The actual image the snapshot is from + snapshotData: JsonMat + + +@dataclass +class CameraCalibration: + resolution: Resolution + cameraIntrinsics: JsonMatOfDoubles + distCoeffs: JsonMatOfDoubles + observations: list[Observation] + + +def convert_photon_to_mrcal(photon_cal_json_path: str, output_folder: str): + """ + Unpack a Photon calibration JSON (eg, photon_calibration_Microsoft_LifeCam_HD-3000_800x600.json) into + the output_folder directory with images and corners.vnl file for use with mrcal. + """ + with open(photon_cal_json_path, "r") as cal_json: + # Convert to nested objects instead of nameddicts on json-loads + class Generic: + @classmethod + def from_dict(cls, dict): + obj = cls() + obj.__dict__.update(dict) + return obj + + camera_cal_data: CameraCalibration = json.loads( + cal_json.read(), object_hook=Generic.from_dict + ) + + # Create output_folder if not exists + if not os.path.exists(output_folder): + os.makedirs(output_folder) + + # Decode each image and save it as a png + for obs in camera_cal_data.observations: + image = obs.snapshotData.data + decoded_data = base64.b64decode(image) + np_data = np.frombuffer(decoded_data, np.uint8) + img = cv2.imdecode(np_data, cv2.IMREAD_UNCHANGED) + cv2.imwrite(f"{output_folder}/{obs.snapshotName}", img) + + # And create a VNL file for use with mrcal + with open(f"{output_folder}/corners.vnl", "w+") as vnl_file: + vnl_file.write("# filename x y level\n") + + for obs in camera_cal_data.observations: + for corner in obs.locationInImageSpace: + # Always level zero + vnl_file.write(f"{obs.snapshotName} {corner.x} {corner.y} 0\n") + + vnl_file.flush() diff --git a/photon-client/src/components/cameras/CameraCalibrationCard.vue b/photon-client/src/components/cameras/CameraCalibrationCard.vue index e2d5e7d1f1..0b5163dcb8 100644 --- a/photon-client/src/components/cameras/CameraCalibrationCard.vue +++ b/photon-client/src/components/cameras/CameraCalibrationCard.vue @@ -1,7 +1,7 @@ @@ -494,6 +512,7 @@ const endCalibration = () => { tbody :hover td { background-color: #005281 !important; + cursor: pointer; } ::-webkit-scrollbar { diff --git a/photon-client/src/components/cameras/CameraCalibrationInfoCard.vue b/photon-client/src/components/cameras/CameraCalibrationInfoCard.vue new file mode 100644 index 0000000000..4177ca5c09 --- /dev/null +++ b/photon-client/src/components/cameras/CameraCalibrationInfoCard.vue @@ -0,0 +1,251 @@ + + + + + diff --git a/photon-client/src/components/cameras/CamerasView.vue b/photon-client/src/components/cameras/CamerasView.vue index f1dbfef7d8..4b4530f985 100644 --- a/photon-client/src/components/cameras/CamerasView.vue +++ b/photon-client/src/components/cameras/CamerasView.vue @@ -41,7 +41,12 @@ const fpsTooLow = computed(() => {