From 98e33811fe2d30754e7773ad40ecf5e37cc76216 Mon Sep 17 00:00:00 2001 From: Matt Date: Wed, 20 Dec 2023 10:50:25 -0500 Subject: [PATCH] Update cal card to new data format --- .../cameras/CameraCalibrationCard.vue | 19 ++++++---- .../stores/settings/CameraSettingsStore.ts | 1 - photon-client/src/types/SettingTypes.ts | 36 +++++++++++++++++-- photon-client/src/types/WebsocketDataTypes.ts | 4 +-- .../CameraCalibrationCoefficients.java | 6 ++++ 5 files changed, 53 insertions(+), 13 deletions(-) diff --git a/photon-client/src/components/cameras/CameraCalibrationCard.vue b/photon-client/src/components/cameras/CameraCalibrationCard.vue index 7a73fa7aeb..82f6d0248c 100644 --- a/photon-client/src/components/cameras/CameraCalibrationCard.vue +++ b/photon-client/src/components/cameras/CameraCalibrationCard.vue @@ -31,17 +31,22 @@ const getUniqueVideoResolutions = (): VideoFormat[] => { const calib = getCalibrationCoeffs(format.resolution); if (calib !== undefined) { - format.mean = calib.perViewErrors.reduce((a, b) => a + b) / calib.perViewErrors.length; - format.horizontalFOV = 2 * Math.atan2(format.resolution.width / 2, calib.intrinsics[0]) * (180 / Math.PI); - format.verticalFOV = 2 * Math.atan2(format.resolution.height / 2, calib.intrinsics[4]) * (180 / Math.PI); + console.log(calib) + + // Is this the right formula for RMS error? who knows! not me! + const perViewSumSquareReprojectionError = calib.observations.flatMap(it=>it.reprojectionErrors.flatMap(it2=>[it2.x, it2.y])) + format.mean = Math.sqrt(perViewSumSquareReprojectionError.reduce((a, b) => a + b, 0)) + + format.horizontalFOV = 2 * Math.atan2(format.resolution.width / 2, calib.cameraIntrinsics.data[0]) * (180 / Math.PI); + format.verticalFOV = 2 * Math.atan2(format.resolution.height / 2, calib.cameraIntrinsics.data[4]) * (180 / Math.PI); format.diagonalFOV = 2 * Math.atan2( Math.sqrt( format.resolution.width ** 2 + - (format.resolution.height / (calib.intrinsics[4] / calib.intrinsics[0])) ** 2 + (format.resolution.height / (calib.cameraIntrinsics.data[4] / calib.cameraIntrinsics.data[0])) ** 2 ) / 2, - calib.intrinsics[0] + calib.cameraIntrinsics.data[0] ) * (180 / Math.PI); } @@ -492,12 +497,12 @@ const reprojectionErrorSeries = () => { - + /> --> diff --git a/photon-client/src/stores/settings/CameraSettingsStore.ts b/photon-client/src/stores/settings/CameraSettingsStore.ts index bb0ee8b904..2e263bbc07 100644 --- a/photon-client/src/stores/settings/CameraSettingsStore.ts +++ b/photon-client/src/stores/settings/CameraSettingsStore.ts @@ -1,7 +1,6 @@ import { defineStore } from "pinia"; import type { CalibrationBoardTypes, - CameraCalibrationResult, CameraSettings, ConfigurableCameraSettings, RobotOffsetType, diff --git a/photon-client/src/types/SettingTypes.ts b/photon-client/src/types/SettingTypes.ts index caa7a06d09..44766033a2 100644 --- a/photon-client/src/types/SettingTypes.ts +++ b/photon-client/src/types/SettingTypes.ts @@ -76,14 +76,44 @@ export interface VideoFormat { diagonalFOV?: number; horizontalFOV?: number; verticalFOV?: number; - standardDeviation?: number; mean?: number; } +export interface JsonMat { + rows: number + cols: number + type: number + data: number[] +} + +export interface Point3 { + x: number + y: number + z: number +} +export interface Point2 { + x: number + y: number +} + +export interface Pose3d { + translation: { x: number, y: number, z: number } + rotation: { quaternion: { W: number, X: number, Y: number, Z: number } } +} + +export interface BoardObservation { + locationInObjectSpace: Point3[] + locationInImageSpace: Point2[] + reprojectionErrors: Point2[] + optimisedCameraToObject: Pose3d[] +} + export interface CameraCalibrationResult { resolution: Resolution; - distCoeffs: number[]; - intrinsics: number[]; + cameraIntrinsics: JsonMat; + // TODO rename to be Right + cameraExtrinsics: JsonMat; + observations: BoardObservation[] } export interface ConfigurableCameraSettings { diff --git a/photon-client/src/types/WebsocketDataTypes.ts b/photon-client/src/types/WebsocketDataTypes.ts index 0114e19e79..3c4cd1bc5a 100644 --- a/photon-client/src/types/WebsocketDataTypes.ts +++ b/photon-client/src/types/WebsocketDataTypes.ts @@ -1,4 +1,4 @@ -import type { GeneralSettings, LightingSettings, LogLevel, MetricData, NetworkSettings } from "@/types/SettingTypes"; +import type { CameraCalibrationResult, GeneralSettings, LightingSettings, LogLevel, MetricData, NetworkSettings } from "@/types/SettingTypes"; import type { ActivePipelineSettings } from "@/types/PipelineTypes"; import type { AprilTagFieldLayout, PipelineResult } from "@/types/PhotonTrackingTypes"; @@ -46,7 +46,7 @@ export type WebsocketVideoFormat = Record< >; export interface WebsocketCameraSettingsUpdate { - calibrations: WebsocketCompleteCalib[]; + calibrations: CameraCalibrationResult[]; currentPipelineIndex: number; currentPipelineSettings: ActivePipelineSettings; fov: number; diff --git a/photon-core/src/main/java/org/photonvision/vision/calibration/CameraCalibrationCoefficients.java b/photon-core/src/main/java/org/photonvision/vision/calibration/CameraCalibrationCoefficients.java index ffbdcbbcef..7c0de1d024 100644 --- a/photon-core/src/main/java/org/photonvision/vision/calibration/CameraCalibrationCoefficients.java +++ b/photon-core/src/main/java/org/photonvision/vision/calibration/CameraCalibrationCoefficients.java @@ -20,6 +20,7 @@ import com.fasterxml.jackson.annotation.JsonAlias; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.JsonNode; @@ -35,6 +36,7 @@ import org.opencv.core.Size; import org.photonvision.vision.opencv.Releasable; +@JsonIgnoreProperties(ignoreUnknown = true) public class CameraCalibrationCoefficients implements Releasable { public static final class BoardObservation { @JsonProperty("locationInObjectSpace") @@ -88,6 +90,10 @@ public CameraCalibrationCoefficients( this.cameraIntrinsics = cameraIntrinsics; this.distCoeffs = distCoeffs; + // Legacy migration just to make sure that observations is at worst empty and never null + if (observations == null) { + observations = List.of(); + } this.observations = observations; // do this once so gets are quick