Save calibration data and show preliminary GUI (#1078)

* 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>
This commit is contained in:
Matt
2024-01-03 14:32:04 -07:00
committed by GitHub
parent e685334baa
commit 7f09f9e4f5
43 changed files with 1247 additions and 396 deletions

View File

@@ -1,7 +1,7 @@
<script setup lang="ts">
import { computed, ref } from "vue";
import { useCameraSettingsStore } from "@/stores/settings/CameraSettingsStore";
import { CalibrationBoardTypes, type Resolution, type VideoFormat } from "@/types/SettingTypes";
import { CalibrationBoardTypes, type VideoFormat } from "@/types/SettingTypes";
import JsPDF from "jspdf";
import { font as PromptRegular } from "@/assets/fonts/PromptRegular";
import MonoLogo from "@/assets/images/logoMono.png";
@@ -12,37 +12,40 @@ import PvSelect from "@/components/common/pv-select.vue";
import PvNumberInput from "@/components/common/pv-number-input.vue";
import { WebsocketPipelineType } from "@/types/WebsocketDataTypes";
import { getResolutionString, resolutionsAreEqual } from "@/lib/PhotonUtils";
import CameraCalibrationInfoCard from "@/components/cameras/CameraCalibrationInfoCard.vue";
const settingsValid = ref(true);
const getCalibrationCoeffs = (resolution: Resolution) => {
return useCameraSettingsStore().currentCameraSettings.completeCalibrations.find((cal) =>
resolutionsAreEqual(cal.resolution, resolution)
);
};
const getUniqueVideoResolutions = (): VideoFormat[] => {
const getUniqueVideoFormatsByResolution = (): VideoFormat[] => {
const uniqueResolutions: VideoFormat[] = [];
useCameraSettingsStore().currentCameraSettings.validVideoFormats.forEach((format, index) => {
if (!uniqueResolutions.some((v) => resolutionsAreEqual(v.resolution, format.resolution))) {
format.index = index;
const calib = getCalibrationCoeffs(format.resolution);
const calib = useCameraSettingsStore().getCalibrationCoeffs(format.resolution);
if (calib !== undefined) {
format.standardDeviation = calib.standardDeviation;
format.mean =
calib.perViewErrors === null
? Number.NaN
: 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);
// 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])
);
// For each error, square it, sum the squares, and divide by total points N
format.mean = Math.sqrt(
perViewSumSquareReprojectionError.map((it) => Math.pow(it, 2)).reduce((a, b) => a + b, 0) /
perViewSumSquareReprojectionError.length
);
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);
}
@@ -55,7 +58,7 @@ const getUniqueVideoResolutions = (): VideoFormat[] => {
return uniqueResolutions;
};
const getUniqueVideoResolutionStrings = (): { name: string; value: number }[] =>
getUniqueVideoResolutions().map<{ name: string; value: number }>((f) => ({
getUniqueVideoFormatsByResolution().map<{ name: string; value: number }>((f) => ({
name: `${getResolutionString(f.resolution)}`,
// Index won't ever be undefined
value: f.index || 0
@@ -150,7 +153,7 @@ const importCalibrationFromCalibDB = ref();
const openCalibUploadPrompt = () => {
importCalibrationFromCalibDB.value.click();
};
const readImportedCalibration = () => {
const readImportedCalibrationFromCalibDB = () => {
const files = importCalibrationFromCalibDB.value.files;
if (files.length === 0) return;
@@ -214,6 +217,13 @@ const endCalibration = () => {
isCalibrating.value = false;
});
};
let showCalDialog = ref(false);
let selectedVideoFormat = ref<VideoFormat | undefined>(undefined);
const setSelectedVideoFormat = (format: VideoFormat) => {
selectedVideoFormat.value = format;
showCalDialog.value = true;
};
</script>
<template>
@@ -234,7 +244,12 @@ const endCalibration = () => {
</tr>
</thead>
<tbody>
<tr v-for="(value, index) in getUniqueVideoResolutions()" :key="index">
<tr
v-for="(value, index) in getUniqueVideoFormatsByResolution()"
:key="index"
title="Click to get calibration specific information"
@click="setSelectedVideoFormat(value)"
>
<td>{{ getResolutionString(value.resolution) }}</td>
<td>
{{ value.mean !== undefined ? (isNaN(value.mean) ? "NaN" : value.mean.toFixed(2) + "px") : "-" }}
@@ -429,7 +444,7 @@ const endCalibration = () => {
type="file"
accept=".json"
style="display: none"
@change="readImportedCalibration"
@change="readImportedCalibrationFromCalibDB"
/>
</v-col>
</v-row>
@@ -478,6 +493,9 @@ const endCalibration = () => {
</v-card-actions>
</v-card>
</v-dialog>
<v-dialog v-model="showCalDialog" width="80em">
<CameraCalibrationInfoCard v-if="selectedVideoFormat" :video-format="selectedVideoFormat" />
</v-dialog>
</div>
</template>
@@ -494,6 +512,7 @@ const endCalibration = () => {
tbody :hover td {
background-color: #005281 !important;
cursor: pointer;
}
::-webkit-scrollbar {

View File

@@ -0,0 +1,251 @@
<script setup lang="ts">
import type { BoardObservation, CameraCalibrationResult, VideoFormat } from "@/types/SettingTypes";
import { useCameraSettingsStore } from "@/stores/settings/CameraSettingsStore";
import { useStateStore } from "@/stores/StateStore";
import { ref } from "vue";
import loadingImage from "@/assets/images/loading.svg";
import { getResolutionString, parseJsonFile } from "@/lib/PhotonUtils";
const props = defineProps<{
videoFormat: VideoFormat;
}>();
const getMeanFromView = (o: BoardObservation) => {
// Is this the right formula for RMS error? who knows! not me!
const perViewSumSquareReprojectionError = o.reprojectionErrors.flatMap((it2) => [it2.x, it2.y]);
// For each error, square it, sum the squares, and divide by total points N
return Math.sqrt(
perViewSumSquareReprojectionError.map((it) => Math.pow(it, 2)).reduce((a, b) => a + b, 0) /
perViewSumSquareReprojectionError.length
);
};
// Import and export functions
const downloadCalibration = () => {
const calibData = useCameraSettingsStore().getCalibrationCoeffs(props.videoFormat.resolution);
if (calibData === undefined) {
useStateStore().showSnackbarMessage({
color: "error",
message:
"Calibration data isn't available for the requested resolution, please calibrate the requested resolution first"
});
return;
}
const camUniqueName = useCameraSettingsStore().currentCameraSettings.uniqueName;
const filename = `photon_calibration_${camUniqueName}_${calibData.resolution.width}x${calibData.resolution.height}.json`;
const fileData = JSON.stringify(calibData);
const element = document.createElement("a");
element.style.display = "none";
element.setAttribute("href", "data:text/plain;charset=utf-8," + encodeURIComponent(fileData));
element.setAttribute("download", filename);
document.body.appendChild(element);
element.click();
document.body.removeChild(element);
};
const importCalibrationFromPhotonJson = ref();
const openUploadPhotonCalibJsonPrompt = () => {
importCalibrationFromPhotonJson.value.click();
};
const importCalibration = async () => {
const files = importCalibrationFromPhotonJson.value.files;
if (files.length === 0) return;
const uploadedJson = files[0];
const data = await parseJsonFile<CameraCalibrationResult>(uploadedJson);
if (
data.resolution.height != props.videoFormat.resolution.height ||
data.resolution.width != props.videoFormat.resolution.width
) {
useStateStore().showSnackbarMessage({
color: "error",
message: `The resolution of the calibration export doesn't match the current resolution ${props.videoFormat.resolution.height}x${props.videoFormat.resolution.width}`
});
return;
}
useCameraSettingsStore()
.importCalibrationFromData({ calibration: data })
.then((response) => {
useStateStore().showSnackbarMessage({
color: "success",
message: response.data.text || response.data
});
})
.catch((error) => {
if (error.response) {
useStateStore().showSnackbarMessage({
color: "error",
message: error.response.data.text || error.response.data
});
} else if (error.request) {
useStateStore().showSnackbarMessage({
color: "error",
message: "Error while trying to process the request! The backend didn't respond."
});
} else {
useStateStore().showSnackbarMessage({
color: "error",
message: "An error occurred while trying to process the request."
});
}
});
};
interface ObservationDetails {
snapshotSrc: any;
mean: number;
index: number;
}
const getObservationDetails = (): ObservationDetails[] | undefined => {
return getCalibrationCoeffs()?.observations.map((o, i) => ({
index: i,
mean: parseFloat(getMeanFromView(o).toFixed(2)),
snapshotSrc: o.includeObservationInCalibration ? "data:image/png;base64," + o.snapshotData.data : loadingImage
}));
};
</script>
<template>
<v-card color="primary" class="pa-6" dark>
<v-row>
<v-col cols="12" md="5">
<v-card-title class="pl-0 ml-0"
><span class="text-no-wrap" style="white-space: pre !important">Calibration Details: </span
><span class="text-no-wrap"
>{{ useCameraSettingsStore().currentCameraName }}@{{ getResolutionString(videoFormat.resolution) }}</span
></v-card-title
>
</v-col>
<v-col>
<v-btn color="secondary" class="mt-4" style="width: 100%" @click="openUploadPhotonCalibJsonPrompt">
<v-icon left> mdi-import</v-icon>
<span>Import</span>
</v-btn>
<input
ref="importCalibrationFromPhotonJson"
type="file"
accept=".json"
style="display: none"
@change="importCalibration"
/>
</v-col>
<v-col>
<v-btn
color="secondary"
class="mt-4"
:disabled="getCalibrationCoeffs() === undefined"
style="width: 100%"
@click="downloadCalibration"
>
<v-icon left>mdi-export</v-icon>
<span>Export</span>
</v-btn>
</v-col>
</v-row>
<v-row v-if="getCalibrationCoeffs() !== undefined" class="pt-2">
<v-card-subtitle>Calibration Details</v-card-subtitle>
<v-simple-table dense style="width: 100%" class="pl-2 pr-2">
<template #default>
<thead>
<tr>
<th class="text-left">Name</th>
<th class="text-left">Value</th>
</tr>
</thead>
<tbody>
<tr>
<td>Fx</td>
<td>{{ getCalibrationCoeffs()?.cameraIntrinsics.data[0].toFixed(2) || 0.0 }} mm</td>
</tr>
<tr>
<td>Fy</td>
<td>{{ getCalibrationCoeffs()?.cameraIntrinsics.data[4].toFixed(2) || 0.0 }} mm</td>
</tr>
<tr>
<td>Cx</td>
<td>{{ getCalibrationCoeffs()?.cameraIntrinsics.data[2].toFixed(2) || 0.0 }} px</td>
</tr>
<tr>
<td>Cy</td>
<td>{{ getCalibrationCoeffs()?.cameraIntrinsics.data[5].toFixed(2) || 0.0 }} px</td>
</tr>
<tr>
<td>Distortion</td>
<td>{{ getCalibrationCoeffs()?.distCoeffs.data.map((it) => parseFloat(it.toFixed(3))) || [] }}</td>
</tr>
<tr>
<td>Mean Err</td>
<td>
{{
videoFormat.mean !== undefined
? isNaN(videoFormat.mean)
? "NaN"
: videoFormat.mean.toFixed(2) + "px"
: "-"
}}
</td>
</tr>
<tr>
<td>Horizontal FOV</td>
<td>{{ videoFormat.horizontalFOV !== undefined ? videoFormat.horizontalFOV.toFixed(2) + "°" : "-" }}</td>
</tr>
<tr>
<td>Vertical FOV</td>
<td>{{ videoFormat.verticalFOV !== undefined ? videoFormat.verticalFOV.toFixed(2) + "°" : "-" }}</td>
</tr>
<tr>
<td>Diagonal FOV</td>
<td>{{ videoFormat.diagonalFOV !== undefined ? videoFormat.diagonalFOV.toFixed(2) + "°" : "-" }}</td>
</tr>
</tbody>
</template>
</v-simple-table>
<hr style="width: 100%" class="ma-6" />
<v-card-subtitle>Per Observation Details</v-card-subtitle>
<v-data-table
dense
style="width: 100%"
class="pl-2 pr-2"
:headers="[
{ text: 'Observation Id', value: 'index' },
{ text: 'Mean Reprojection Error', value: 'mean' }
]"
:items="getObservationDetails()"
item-key="index"
show-expand
expand-icon="mdi-eye"
>
<template #expanded-item="{ headers, item }">
<td :colspan="headers.length">
<div style="display: flex; justify-content: center; width: 100%">
<img :src="item.snapshotSrc" alt="observation image" class="snapshot-preview pt-2 pb-2" />
</div>
</td>
</template>
</v-data-table>
</v-row>
<v-row v-else class="pt-2 mb-0 pb-0">
The selected video format doesn't have any additional information as it has yet to be calibrated.
</v-row>
</v-card>
</template>
<style scoped>
.v-data-table {
background-color: #006492 !important;
}
.snapshot-preview {
max-width: 55%;
}
@media only screen and (max-width: 512px) {
.snapshot-preview {
max-width: 100%;
}
}
</style>

View File

@@ -41,7 +41,12 @@ const fpsTooLow = computed<boolean>(() => {
</script>
<template>
<v-card class="mb-3 pb-3 pa-4" color="primary" dark>
<v-card
id="camera-settings-camera-view-card"
class="camera-settings-camera-view-card mb-3 pb-3 pa-4"
color="primary"
dark
>
<v-card-title
class="pb-0 mb-2 pl-4 pt-1"
style="min-height: 50px; justify-content: space-between; align-content: center"

View File

@@ -4,6 +4,7 @@ import type {
CameraCalibrationResult,
CameraSettings,
ConfigurableCameraSettings,
Resolution,
RobotOffsetType,
VideoFormat
} from "@/types/SettingTypes";
@@ -13,6 +14,7 @@ import type { WebsocketCameraSettingsUpdate } from "@/types/WebsocketDataTypes";
import { WebsocketPipelineType } from "@/types/WebsocketDataTypes";
import type { ActiveConfigurablePipelineSettings, ActivePipelineSettings, PipelineType } from "@/types/PipelineTypes";
import axios from "axios";
import { resolutionsAreEqual } from "@/lib/PhotonUtils";
interface CameraSettingsStore {
cameras: CameraSettings[];
@@ -41,18 +43,22 @@ export const useCameraSettingsStore = defineStore("cameraSettings", {
return this.currentCameraSettings.validVideoFormats[this.currentPipelineSettings.cameraVideoModeIndex];
},
isCurrentVideoFormatCalibrated(): boolean {
return this.currentCameraSettings.completeCalibrations.some(
(v) =>
v.resolution.width === this.currentVideoFormat.resolution.width &&
v.resolution.height === this.currentVideoFormat.resolution.height
return this.currentCameraSettings.completeCalibrations.some((v) =>
resolutionsAreEqual(v.resolution, this.currentVideoFormat.resolution)
);
},
cameraNames(): string[] {
return this.cameras.map((c) => c.nickname);
},
currentCameraName(): string {
return this.cameraNames[useStateStore().currentCameraIndex];
},
pipelineNames(): string[] {
return this.currentCameraSettings.pipelineNicknames;
},
currentPipelineName(): string {
return this.pipelineNames[useStateStore().currentCameraIndex];
},
isDriverMode(): boolean {
return this.currentCameraSettings.currentPipelineIndex === WebsocketPipelineType.DriverMode;
},
@@ -67,6 +73,7 @@ export const useCameraSettingsStore = defineStore("cameraSettings", {
updateCameraSettingsFromWebsocket(data: WebsocketCameraSettingsUpdate[]) {
this.cameras = data.map<CameraSettings>((d) => ({
nickname: d.nickname,
uniqueName: d.uniqueName,
fov: {
value: d.fov,
managedByVendor: !d.isFovConfigurable
@@ -92,16 +99,7 @@ export const useCameraSettingsStore = defineStore("cameraSettings", {
standardDeviation: v.standardDeviation,
mean: v.mean
})),
completeCalibrations: d.calibrations.map<CameraCalibrationResult>((calib) => ({
resolution: {
height: calib.height,
width: calib.width
},
distCoeffs: calib.distCoeffs,
standardDeviation: calib.standardDeviation,
perViewErrors: calib.perViewErrors,
intrinsics: calib.intrinsics
})),
completeCalibrations: d.calibrations,
isCSICamera: d.isCSICamera,
pipelineNicknames: d.pipelineNicknames,
currentPipelineIndex: d.currentPipelineIndex,
@@ -360,6 +358,16 @@ export const useCameraSettingsStore = defineStore("cameraSettings", {
};
return axios.post("/calibration/importFromCalibDB", payload, { headers: { "Content-Type": "text/plain" } });
},
importCalibrationFromData(
data: { calibration: CameraCalibrationResult },
cameraIndex: number = useStateStore().currentCameraIndex
) {
const payload = {
...data,
cameraIndex: cameraIndex
};
return axios.post("/calibration/importFromData", payload);
},
/**
* Take a snapshot for the calibration processes
*
@@ -408,6 +416,12 @@ export const useCameraSettingsStore = defineStore("cameraSettings", {
cameraIndex: cameraIndex
};
useStateStore().websocket?.send(payload, true);
},
getCalibrationCoeffs(
resolution: Resolution,
cameraIndex: number = useStateStore().currentCameraIndex
): CameraCalibrationResult | undefined {
return this.cameras[cameraIndex].completeCalibrations.find((v) => resolutionsAreEqual(v.resolution, resolution));
}
}
});

View File

@@ -1,3 +1,26 @@
export interface Quaternion {
X: number;
Y: number;
Z: number;
W: number;
}
export interface Translation3d {
x: number;
y: number;
z: number;
}
export interface Rotation3d {
quaternion: Quaternion;
}
export interface Pose3d {
translation: Translation3d;
rotation: Rotation3d;
}
// TODO update backend to serialize this using correct layout
export interface Transform3d {
x: number;
y: number;
@@ -11,13 +34,6 @@ export interface Transform3d {
angle_z: number;
}
export interface Quaternion {
X: number;
Y: number;
Z: number;
W: number;
}
export interface AprilTagFieldLayout {
field: {
length: number;
@@ -25,16 +41,7 @@ export interface AprilTagFieldLayout {
};
tags: {
ID: number;
pose: {
translation: {
x: number;
y: number;
z: number;
};
rotation: {
quaternion: Quaternion;
};
};
pose: Pose3d;
}[];
}

View File

@@ -1,4 +1,5 @@
import { type ActivePipelineSettings, DefaultAprilTagPipelineSettings } from "@/types/PipelineTypes";
import type { Pose3d } from "@/types/PhotonTrackingTypes";
export interface GeneralSettings {
version?: string;
@@ -77,16 +78,59 @@ export interface VideoFormat {
diagonalFOV?: number;
horizontalFOV?: number;
verticalFOV?: number;
standardDeviation?: number;
mean?: number;
}
export enum CvType {
CV_8U = 0,
CV_8S = 1,
CV_16U = 2,
CV_16S = 3,
CV_32S = 4,
CV_32F = 5,
CV_64F = 6,
CV_16F = 7
}
export interface JsonMatOfDouble {
rows: number;
cols: number;
type: CvType;
data: number[];
}
export interface JsonImageMat {
rows: number;
cols: number;
type: CvType;
data: string; // base64 encoded
}
export interface CvPoint3 {
x: number;
y: number;
z: number;
}
export interface CvPoint {
x: number;
y: number;
}
export interface BoardObservation {
locationInObjectSpace: CvPoint3[];
locationInImageSpace: CvPoint[];
reprojectionErrors: CvPoint[];
optimisedCameraToObject: Pose3d;
includeObservationInCalibration: boolean;
snapshotName: string;
snapshotData: JsonImageMat;
}
export interface CameraCalibrationResult {
resolution: Resolution;
distCoeffs: number[];
standardDeviation: number;
perViewErrors: number[] | null;
intrinsics: number[];
cameraIntrinsics: JsonMatOfDouble;
distCoeffs: JsonMatOfDouble;
observations: BoardObservation[];
}
export interface ConfigurableCameraSettings {
@@ -95,6 +139,7 @@ export interface ConfigurableCameraSettings {
export interface CameraSettings {
nickname: string;
uniqueName: string;
fov: {
value: number;
@@ -117,6 +162,7 @@ export interface CameraSettings {
export const PlaceholderCameraSettings: CameraSettings = {
nickname: "Placeholder Camera",
uniqueName: "Placeholder Name",
fov: {
value: 70,
managedByVendor: true
@@ -142,7 +188,45 @@ export const PlaceholderCameraSettings: CameraSettings = {
pixelFormat: "RGB"
}
],
completeCalibrations: [],
completeCalibrations: [
{
resolution: { width: 1920, height: 1080 },
cameraIntrinsics: {
rows: 1,
cols: 1,
type: 1,
data: [1, 2, 3, 4, 5, 6, 7, 8, 9]
},
distCoeffs: {
rows: 1,
cols: 1,
type: 1,
data: [10, 11, 12, 13]
},
observations: [
{
locationInImageSpace: [
{ x: 100, y: 100 },
{ x: 210, y: 100 },
{ x: 320, y: 101 }
],
locationInObjectSpace: [{ x: 0, y: 0, z: 0 }],
optimisedCameraToObject: {
translation: { x: 1, y: 2, z: 3 },
rotation: { quaternion: { W: 1, X: 0, Y: 0, Z: 0 } }
},
reprojectionErrors: [
{ x: 1, y: 1 },
{ x: 2, y: 1 },
{ x: 3, y: 1 }
],
includeObservationInCalibration: false,
snapshotName: "img0.png",
snapshotData: { rows: 480, cols: 640, type: CvType.CV_8U, data: "" }
}
]
}
],
pipelineNicknames: ["Placeholder Pipeline"],
lastPipelineIndex: 0,
currentPipelineIndex: 0,

View File

@@ -1,4 +1,11 @@
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";
@@ -20,16 +27,6 @@ export interface WebsocketNumberPair {
second: number;
}
export interface WebsocketCompleteCalib {
distCoeffs: number[];
height: number;
width: number;
standardDeviation: number;
// perViewErrors not set in test mode
perViewErrors: number[] | null;
intrinsics: number[];
}
export type WebsocketVideoFormat = Record<
number,
{
@@ -47,7 +44,7 @@ export type WebsocketVideoFormat = Record<
>;
export interface WebsocketCameraSettingsUpdate {
calibrations: WebsocketCompleteCalib[];
calibrations: CameraCalibrationResult[];
currentPipelineIndex: number;
currentPipelineSettings: ActivePipelineSettings;
fov: number;
@@ -55,6 +52,7 @@ export interface WebsocketCameraSettingsUpdate {
isFovConfigurable: boolean;
isCSICamera: boolean;
nickname: string;
uniqueName: string;
outputStreamPort: number;
pipelineNicknames: string[];
videoFormatList: WebsocketVideoFormat;