diff --git a/photon-client/src/views/CamerasView.vue b/photon-client/src/views/CamerasView.vue
index dcc5bd4a4..4ade293da 100644
--- a/photon-client/src/views/CamerasView.vue
+++ b/photon-client/src/views/CamerasView.vue
@@ -299,6 +299,19 @@
Download Target
+
+
+
+ mdi-upload
+
+ Import From CalibDB
+
+
@@ -375,6 +388,20 @@
+
+
+
+
+
+ {{ uploadSnackData.text }}
+
+
@@ -406,6 +433,11 @@ export default {
filteredVideomodeIndex: 0,
settingsValid: true,
unfilteredStreamDivisors: [1, 2, 4],
+ uploadSnackData: {
+ color: "success",
+ text: "",
+ },
+ uploadSnack: false,
}
},
computed: {
@@ -571,6 +603,57 @@ export default {
},
},
methods: {
+
+ readImportedCalibration(event) {
+ // let formData = new FormData();
+ // formData.append("zipData", event.target.files[0]);
+ const filename = event.target.files[0].name;
+
+ event.target.files[0].text().then(fileText => {
+ const data = {
+ "cameraIndex": this.$store.getters.currentCameraIndex,
+ "payload": fileText,
+ "filename": filename,
+ };
+
+ this.axios
+ .post("http://" + this.$address + "/api/calibration/import", data, {
+ headers: { "Content-Type": "text/plain" },
+ })
+ .then(() => {
+ this.uploadSnackData = {
+ color: "success",
+ text:
+ "Calibration imported successfully! PhotonVision will restart in the background...",
+ };
+ this.uploadSnack = true;
+ })
+ .catch((err) => {
+ if (err.response) {
+ this.uploadSnackData = {
+ color: "error",
+ text:
+ "Error while uploading calibration file! Could not process provided file.",
+ };
+ } else if (err.request) {
+ this.uploadSnackData = {
+ color: "error",
+ text:
+ "Error while uploading calibration file! No respond to upload attempt.",
+ };
+ } else {
+ this.uploadSnackData = {
+ color: "error",
+ text: "Error while uploading calibration file!",
+ };
+ }
+ this.uploadSnack = true;
+ });
+
+ })
+
+ },
+
closeDialog() {
this.snack = false;
this.calibrationInProgress = false;
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 c7d29393a..7df950533 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
@@ -21,6 +21,7 @@ import com.fasterxml.jackson.annotation.JsonAlias;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.databind.JsonNode;
import org.opencv.core.Mat;
import org.opencv.core.MatOfDouble;
import org.opencv.core.Size;
@@ -82,4 +83,43 @@ public class CameraCalibrationCoefficients implements Releasable {
cameraIntrinsics.release();
distCoeffs.release();
}
+
+ public static CameraCalibrationCoefficients parseFromCalibdbJson(JsonNode json) {
+ // camera_matrix is a row major, array of arrays
+ var cam_matrix = json.get("camera_matrix");
+
+ double[] cam_arr =
+ new double[] {
+ cam_matrix.get(0).get(0).doubleValue(),
+ cam_matrix.get(0).get(1).doubleValue(),
+ cam_matrix.get(0).get(2).doubleValue(),
+ cam_matrix.get(1).get(0).doubleValue(),
+ cam_matrix.get(1).get(1).doubleValue(),
+ cam_matrix.get(1).get(2).doubleValue(),
+ cam_matrix.get(2).get(0).doubleValue(),
+ cam_matrix.get(2).get(1).doubleValue(),
+ cam_matrix.get(2).get(2).doubleValue()
+ };
+
+ var dist_coefs = json.get("distortion_coefficients");
+
+ double[] dist_array =
+ new double[] {
+ dist_coefs.get(0).doubleValue(),
+ dist_coefs.get(1).doubleValue(),
+ dist_coefs.get(2).doubleValue(),
+ dist_coefs.get(3).doubleValue(),
+ dist_coefs.get(4).doubleValue(),
+ };
+
+ var cam_jsonmat = new JsonMat(3, 3, cam_arr);
+ var distortion_jsonmat = new JsonMat(1, 5, dist_array);
+
+ var error = json.get("avg_reprojection_error").asDouble();
+ var width = json.get("img_size").get(0).doubleValue();
+ var height = json.get("img_size").get(1).doubleValue();
+
+ return new CameraCalibrationCoefficients(
+ new Size(width, height), cam_jsonmat, distortion_jsonmat, new double[] {error}, 0);
+ }
}
diff --git a/photon-core/src/main/java/org/photonvision/vision/processes/VisionModule.java b/photon-core/src/main/java/org/photonvision/vision/processes/VisionModule.java
index 631424e65..82dd66812 100644
--- a/photon-core/src/main/java/org/photonvision/vision/processes/VisionModule.java
+++ b/photon-core/src/main/java/org/photonvision/vision/processes/VisionModule.java
@@ -586,4 +586,15 @@ public class VisionModule {
logger.error("Cannot set target model of non-reflective pipe! Ignoring...");
}
}
+
+ public void addCalibrationToConfig(CameraCalibrationCoefficients newCalibration) {
+ if (newCalibration != null) {
+ logger.info("Got new calibration for " + newCalibration.resolution);
+ visionSource.getSettables().getConfiguration().addCalibration(newCalibration);
+ } else {
+ logger.error("Got null calibration?");
+ }
+
+ saveAndBroadcastAll();
+ }
}
diff --git a/photon-core/src/main/java/org/photonvision/vision/processes/VisionModuleChangeSubscriber.java b/photon-core/src/main/java/org/photonvision/vision/processes/VisionModuleChangeSubscriber.java
index e1d605d36..e16e4eeed 100644
--- a/photon-core/src/main/java/org/photonvision/vision/processes/VisionModuleChangeSubscriber.java
+++ b/photon-core/src/main/java/org/photonvision/vision/processes/VisionModuleChangeSubscriber.java
@@ -28,6 +28,7 @@ import org.photonvision.common.logging.LogGroup;
import org.photonvision.common.logging.Logger;
import org.photonvision.common.util.numbers.DoubleCouple;
import org.photonvision.common.util.numbers.IntegerCouple;
+import org.photonvision.vision.calibration.CameraCalibrationCoefficients;
import org.photonvision.vision.pipeline.AdvancedPipelineSettings;
import org.photonvision.vision.pipeline.PipelineType;
import org.photonvision.vision.pipeline.UICalibrationData;
@@ -114,6 +115,10 @@ public class VisionModuleChangeSubscriber extends DataChangeSubscriber {
parentModule.setPipeline(idx);
parentModule.saveAndBroadcastAll();
return;
+ case "calibrationUploaded":
+ if (newPropValue instanceof CameraCalibrationCoefficients)
+ parentModule.addCalibrationToConfig((CameraCalibrationCoefficients) newPropValue);
+ return;
case "robotOffsetPoint":
if (currentSettings instanceof AdvancedPipelineSettings) {
var curAdvSettings = (AdvancedPipelineSettings) currentSettings;
diff --git a/photon-server/src/main/java/org/photonvision/server/RequestHandler.java b/photon-server/src/main/java/org/photonvision/server/RequestHandler.java
index 930b748f2..b611fc308 100644
--- a/photon-server/src/main/java/org/photonvision/server/RequestHandler.java
+++ b/photon-server/src/main/java/org/photonvision/server/RequestHandler.java
@@ -18,6 +18,7 @@
package org.photonvision.server;
import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.javalin.http.Context;
import java.io.File;
@@ -33,6 +34,9 @@ import java.util.Map;
import org.apache.commons.io.FileUtils;
import org.photonvision.common.configuration.ConfigManager;
import org.photonvision.common.configuration.NetworkConfig;
+import org.photonvision.common.dataflow.DataChangeDestination;
+import org.photonvision.common.dataflow.DataChangeService;
+import org.photonvision.common.dataflow.events.IncomingWebSocketEvent;
import org.photonvision.common.dataflow.networktables.NetworkTablesManager;
import org.photonvision.common.hardware.HardwareManager;
import org.photonvision.common.hardware.Platform;
@@ -42,6 +46,7 @@ import org.photonvision.common.networking.NetworkManager;
import org.photonvision.common.util.ShellExec;
import org.photonvision.common.util.TimedTaskManager;
import org.photonvision.common.util.file.ProgramDirectoryUtilities;
+import org.photonvision.vision.calibration.CameraCalibrationCoefficients;
import org.photonvision.vision.processes.VisionModuleManager;
import org.photonvision.vision.target.TargetModel;
@@ -274,6 +279,44 @@ public class RequestHandler {
}
}
+ public static void importCalibrationFromCalibdb(Context ctx) {
+ var file = ctx.body();
+
+ if (file != null) {
+ // check if it's a JSON file
+ // Load using Jackson
+ try {
+ ObjectMapper mapper = new ObjectMapper();
+ JsonNode actualObj = mapper.readTree(file);
+
+ int cameraIndex = actualObj.get("cameraIndex").asInt();
+ String filename = actualObj.get("filename").asText();
+ var payload = mapper.readTree(actualObj.get("payload").asText());
+
+ var coeffs = CameraCalibrationCoefficients.parseFromCalibdbJson(payload);
+
+ var uploadCalibrationEvent =
+ new IncomingWebSocketEvent(
+ DataChangeDestination.DCD_ACTIVEMODULE,
+ "calibrationUploaded",
+ coeffs,
+ (Integer) cameraIndex,
+ null);
+ DataChangeService.getInstance().publishEvent(uploadCalibrationEvent);
+
+ ctx.status(200);
+ logger.info("Calibration added!");
+ } catch (Exception e) {
+ logger.warn("Could not parse cal metaJSON!");
+ e.printStackTrace();
+ return;
+ }
+ } else {
+ ctx.status(500);
+ return;
+ }
+ }
+
public static void setCameraNickname(Context ctx) {
try {
var data = kObjectMapper.readValue(ctx.body(), HashMap.class);
@@ -304,7 +347,8 @@ public class RequestHandler {
public static void sendMetrics(Context ctx) {
HardwareManager.getInstance().publishMetrics();
- // TimedTaskManager.getInstance().addOneShotTask(() -> RoborioFinder.getInstance().findRios(),
+ // TimedTaskManager.getInstance().addOneShotTask(() ->
+ // RoborioFinder.getInstance().findRios(),
// 0);
ctx.status(200);
}
diff --git a/photon-server/src/main/java/org/photonvision/server/Server.java b/photon-server/src/main/java/org/photonvision/server/Server.java
index b84bf6635..a67dae8a1 100644
--- a/photon-server/src/main/java/org/photonvision/server/Server.java
+++ b/photon-server/src/main/java/org/photonvision/server/Server.java
@@ -93,6 +93,7 @@ public class Server {
app.post("api/vision/pnpModel", RequestHandler::uploadPnpModel);
app.post("api/sendMetrics", RequestHandler::sendMetrics);
app.post("api/setCameraNickname", RequestHandler::setCameraNickname);
+ app.post("api/calibration/import", RequestHandler::importCalibrationFromCalibdb);
app.start(port);
}