mirror of
https://github.com/PhotonVision/photonvision
synced 2026-06-19 00:41:41 +00:00
Add support for removing calib coefficients (#2150)
## Description Adds the ability to remove old calibrations. This might be helpful if you have a bad calibration. closes #1262 ## Meta Merge checklist: - [x] Pull Request title is [short, imperative summary](https://cbea.ms/git-commit/) of proposed changes - [x] The description documents the _what_ and _why_ - [ ] If this PR changes behavior or adds a feature, user documentation is updated - [ ] If this PR touches photon-serde, all messages have been regenerated and hashes have not changed unexpectedly - [ ] If this PR touches configuration, this is backwards compatible with settings back to v2025.3.2 - [ ] If this PR touches pipeline settings or anything related to data exchange, the frontend typing is updated - [ ] If this PR addresses a bug, a regression test for it is added --------- Co-authored-by: Gold856 <117957790+Gold856@users.noreply.github.com>
This commit is contained in:
@@ -3,7 +3,7 @@ import type { CameraCalibrationResult, VideoFormat } from "@/types/SettingTypes"
|
||||
import { useCameraSettingsStore } from "@/stores/settings/CameraSettingsStore";
|
||||
import { useStateStore } from "@/stores/StateStore";
|
||||
import { computed, inject, ref } from "vue";
|
||||
import { getResolutionString, parseJsonFile } from "@/lib/PhotonUtils";
|
||||
import { axiosPost, getResolutionString, parseJsonFile } from "@/lib/PhotonUtils";
|
||||
import { useTheme } from "vuetify";
|
||||
|
||||
const theme = useTheme();
|
||||
@@ -12,6 +12,18 @@ const props = defineProps<{
|
||||
videoFormat: VideoFormat;
|
||||
}>();
|
||||
|
||||
const confirmRemoveDialog = ref({ show: false, vf: {} as VideoFormat });
|
||||
|
||||
const removeCalibration = (vf: VideoFormat) => {
|
||||
axiosPost("/calibration/remove", "delete a camera calibration", {
|
||||
cameraUniqueName: useCameraSettingsStore().currentCameraSettings.uniqueName,
|
||||
width: vf.resolution.width,
|
||||
height: vf.resolution.height
|
||||
});
|
||||
|
||||
confirmRemoveDialog.value.show = false;
|
||||
};
|
||||
|
||||
const exportCalibration = ref();
|
||||
const openExportCalibrationPrompt = () => {
|
||||
exportCalibration.value.click();
|
||||
@@ -93,18 +105,30 @@ const calibrationImageURL = (index: number) =>
|
||||
</script>
|
||||
<template>
|
||||
<v-card color="surface" dark>
|
||||
<div class="d-flex flex-wrap pt-2 pl-2 pr-2">
|
||||
<div class="d-flex flex-wrap pt-2 pl-2 pr-2 align-center">
|
||||
<v-col cols="12" md="6">
|
||||
<v-card-title class="pa-0"> Calibration Details </v-card-title>
|
||||
</v-col>
|
||||
<v-col cols="6" md="3" class="d-flex align-center pt-0 pt-md-3 pl-6 pl-md-3">
|
||||
<v-col cols="12" md="6" class="d-flex align-center pt-0 pt-md-3">
|
||||
<v-btn
|
||||
color="error"
|
||||
:disabled="!currentCalibrationCoeffs"
|
||||
class="mr-2"
|
||||
style="flex: 1"
|
||||
:variant="theme.global.name.value === 'LightTheme' ? 'elevated' : 'outlined'"
|
||||
@click="() => (confirmRemoveDialog = { show: true, vf: props.videoFormat })"
|
||||
>
|
||||
<v-icon start size="large">mdi-delete</v-icon>
|
||||
<span>Delete</span>
|
||||
</v-btn>
|
||||
<v-btn
|
||||
color="buttonPassive"
|
||||
style="width: 100%"
|
||||
class="mr-2"
|
||||
style="flex: 1"
|
||||
:variant="theme.global.name.value === 'LightTheme' ? 'elevated' : 'outlined'"
|
||||
@click="openUploadPhotonCalibJsonPrompt"
|
||||
>
|
||||
<v-icon start size="large"> mdi-import</v-icon>
|
||||
<v-icon start size="large">mdi-import</v-icon>
|
||||
<span>Import</span>
|
||||
</v-btn>
|
||||
<input
|
||||
@@ -114,12 +138,10 @@ const calibrationImageURL = (index: number) =>
|
||||
style="display: none"
|
||||
@change="importCalibration"
|
||||
/>
|
||||
</v-col>
|
||||
<v-col cols="6" md="3" class="d-flex align-center pt-0 pt-md-3 pr-6 pr-md-3">
|
||||
<v-btn
|
||||
color="buttonPassive"
|
||||
:disabled="!currentCalibrationCoeffs"
|
||||
style="width: 100%"
|
||||
style="flex: 1"
|
||||
:variant="theme.global.name.value === 'LightTheme' ? 'elevated' : 'outlined'"
|
||||
@click="openExportCalibrationPrompt"
|
||||
>
|
||||
@@ -289,6 +311,33 @@ const calibrationImageURL = (index: number) =>
|
||||
</v-data-table>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
|
||||
<v-dialog v-model="confirmRemoveDialog.show" width="600">
|
||||
<v-card color="surface" dark>
|
||||
<v-card-title>Delete Calibration</v-card-title>
|
||||
<v-card-text class="pt-0">
|
||||
Are you sure you want to delete the calibration for {{ confirmRemoveDialog.vf.resolution.width }}x{{
|
||||
confirmRemoveDialog.vf.resolution.height
|
||||
}}? This cannot be undone.
|
||||
<v-card-actions class="pt-5 pb-0 pr-0" style="justify-content: flex-end">
|
||||
<v-btn
|
||||
:variant="theme.global.name.value === 'LightTheme' ? 'elevated' : 'outlined'"
|
||||
color="primary"
|
||||
@click="() => (confirmRemoveDialog.show = false)"
|
||||
>
|
||||
Cancel
|
||||
</v-btn>
|
||||
<v-btn
|
||||
:variant="theme.global.name.value === 'LightTheme' ? 'elevated' : 'outlined'"
|
||||
color="error"
|
||||
@click="removeCalibration(confirmRemoveDialog.vf)"
|
||||
>
|
||||
Delete
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@@ -24,6 +24,7 @@ import edu.wpi.first.cscore.UsbCameraInfo;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import org.opencv.core.Size;
|
||||
import org.photonvision.common.dataflow.websocket.UICameraConfiguration;
|
||||
import org.photonvision.common.logging.LogGroup;
|
||||
import org.photonvision.common.logging.Logger;
|
||||
@@ -189,6 +190,23 @@ public class CameraConfiguration {
|
||||
calibrations.add(calibration);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a calibration from our list.
|
||||
*
|
||||
* @param calibration The calibration to remove
|
||||
*/
|
||||
public void removeCalibration(Size unrotatedImageSize) {
|
||||
logger.info("deleting calibration " + unrotatedImageSize);
|
||||
calibrations.stream()
|
||||
.filter(it -> it.unrotatedImageSize.equals(unrotatedImageSize))
|
||||
.findAny()
|
||||
.ifPresent(
|
||||
(it) -> {
|
||||
it.release();
|
||||
calibrations.remove(it);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* cscore will auto-reconnect to the camera path we give it. v4l does not guarantee that if i swap
|
||||
* cameras around, the same /dev/videoN ID will be assigned to that camera. So instead default to
|
||||
|
||||
@@ -680,6 +680,16 @@ public class VisionModule {
|
||||
saveAndBroadcastAll();
|
||||
}
|
||||
|
||||
public void removeCalibrationFromConfig(Size unrotatedImageSize) {
|
||||
if (unrotatedImageSize != null) {
|
||||
visionSource.getSettables().removeCalibration(unrotatedImageSize);
|
||||
} else {
|
||||
logger.error("Got null size?");
|
||||
}
|
||||
|
||||
saveAndBroadcastAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add/remove quirks from the camera we're controlling
|
||||
*
|
||||
|
||||
@@ -19,6 +19,7 @@ package org.photonvision.vision.processes;
|
||||
|
||||
import edu.wpi.first.cscore.VideoMode;
|
||||
import java.util.HashMap;
|
||||
import org.opencv.core.Size;
|
||||
import org.photonvision.common.configuration.CameraConfiguration;
|
||||
import org.photonvision.common.logging.LogGroup;
|
||||
import org.photonvision.common.logging.Logger;
|
||||
@@ -120,6 +121,11 @@ public abstract class VisionSourceSettables {
|
||||
calculateFrameStaticProps();
|
||||
}
|
||||
|
||||
public void removeCalibration(Size unrotatedImageSize) {
|
||||
configuration.removeCalibration(unrotatedImageSize);
|
||||
calculateFrameStaticProps();
|
||||
}
|
||||
|
||||
protected void calculateFrameStaticProps() {
|
||||
var videoMode = getCurrentVideoMode();
|
||||
this.frameStaticProperties =
|
||||
|
||||
@@ -35,6 +35,7 @@ import org.apache.commons.io.FileUtils;
|
||||
import org.opencv.core.Mat;
|
||||
import org.opencv.core.MatOfByte;
|
||||
import org.opencv.core.MatOfInt;
|
||||
import org.opencv.core.Size;
|
||||
import org.opencv.imgcodecs.Imgcodecs;
|
||||
import org.photonvision.common.configuration.ConfigManager;
|
||||
import org.photonvision.common.configuration.NetworkConfig;
|
||||
@@ -997,6 +998,46 @@ public class RequestHandler {
|
||||
ctx.status(204);
|
||||
}
|
||||
|
||||
private record CalibrationRemoveRequest(int width, int height, String cameraUniqueName) {}
|
||||
|
||||
public static void onCalibrationRemoveRequest(Context ctx) {
|
||||
try {
|
||||
CalibrationRemoveRequest request =
|
||||
kObjectMapper.readValue(ctx.body(), CalibrationRemoveRequest.class);
|
||||
|
||||
logger.info(
|
||||
"Attempting to remove calibration for camera: "
|
||||
+ request.cameraUniqueName
|
||||
+ " with a resolution of "
|
||||
+ request.width
|
||||
+ "x"
|
||||
+ request.height);
|
||||
|
||||
VisionSourceManager.getInstance()
|
||||
.vmm
|
||||
.getModule(request.cameraUniqueName)
|
||||
.removeCalibrationFromConfig(new Size(request.width, request.height));
|
||||
|
||||
ctx.status(200);
|
||||
ctx.result(
|
||||
"Successfully removed calibration for resolution: "
|
||||
+ request.width
|
||||
+ "x"
|
||||
+ request.height);
|
||||
logger.info(
|
||||
"Successfully removed calibration for resolution: "
|
||||
+ request.width
|
||||
+ "x"
|
||||
+ request.height);
|
||||
} catch (JsonProcessingException e) {
|
||||
ctx.status(400).result("Invalid JSON format");
|
||||
logger.error("Failed to process calibration removed request", e);
|
||||
} catch (Exception e) {
|
||||
ctx.status(500).result("Failed to removed calibration");
|
||||
logger.error("Unexpected error while attempting to remove calibration", e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void onCalibrationSnapshotRequest(Context ctx) {
|
||||
String cameraUniqueName = ctx.queryParam("cameraUniqueName");
|
||||
var width = Integer.parseInt(ctx.queryParam("width"));
|
||||
|
||||
@@ -148,6 +148,7 @@ public class Server {
|
||||
// Calibration
|
||||
app.post("/api/calibration/end", RequestHandler::onCalibrationEndRequest);
|
||||
app.post("/api/calibration/importFromData", RequestHandler::onDataCalibrationImportRequest);
|
||||
app.post("/api/calibration/remove", RequestHandler::onCalibrationRemoveRequest);
|
||||
|
||||
// Object detection
|
||||
app.post("/api/objectdetection/import", RequestHandler::onImportObjectDetectionModelRequest);
|
||||
|
||||
Reference in New Issue
Block a user