mirror of
https://github.com/PhotonVision/photonvision
synced 2026-06-19 00:41:41 +00:00
* Serialize all calibration data * Run lint * typing nit * fix code * move these tables around some * Add cool formatting * add request to get snapshots by resolution and camera * re-enable all resolutions * add wip so i can change computers (SQUASH ME AND KILL ME AHHHH) * Get everything working but viewing snapshots * Update RequestHandler.java * Update CameraCalibrationInfoCard.vue * Update CameraCalibrationInfoCard.vue * add observation viewer * round * fix illiegal import * Swap to PNG and serialize insolution * move import/export buttons TO THE TOP * Update WebsocketDataTypes.ts * Add snapshotname to observation * Refactor to serialize snapshot image itself * Run lint * Use new base64 image data in info card * Update SettingTypes.ts * Create calibration json -> mrcal converter script * Update calibrationUtils.py * Fix calibrate NPEs in teest * Run lint * Always run cornersubpix * Update CameraCalibrationInfoCard.vue Update CameraCalibrationInfoCard.vue * Update OpenCVHelp.java * Update OpenCVHelp.java * Replace test mode camera JSONs * Run wpiformat * Revert intrinsics but keep other data * Remove misc comments * Rename JsonMat->JsonImageMat and add calobject_warp * Update Server.java * Rename cameraExtrinsics to distCoeffs * fix typing issues * use util methods * Formatting fixes * fix styling * move to devTools * remove unneeded or unused imports * Remove fixed-right css If its really that big of a deal, we can add it back later, kind of a drag to fix rn. * Create util method * Remove extra legacy calibration things --------- Co-authored-by: Sriman Achanta <68172138+srimanachanta@users.noreply.github.com>
135 lines
3.1 KiB
Python
135 lines
3.1 KiB
Python
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()
|