From d918862d4a9d940bb0d5ccd48ba2a9b7f9872714 Mon Sep 17 00:00:00 2001 From: Sam948-byte Date: Mon, 6 Jan 2025 10:23:47 -0600 Subject: [PATCH 01/27] update references in docs to 2025 --- docs/source/docs/apriltag-pipelines/about-apriltags.md | 2 +- docs/source/docs/apriltag-pipelines/multitag.md | 2 +- docs/source/docs/examples/aimingatatarget.md | 2 +- docs/source/docs/objectDetection/about-object-detection.md | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/source/docs/apriltag-pipelines/about-apriltags.md b/docs/source/docs/apriltag-pipelines/about-apriltags.md index 0eba914f8e..f96ec5c229 100644 --- a/docs/source/docs/apriltag-pipelines/about-apriltags.md +++ b/docs/source/docs/apriltag-pipelines/about-apriltags.md @@ -10,5 +10,5 @@ AprilTags are a common type of visual fiducial marker. Visual fiducial markers a A more technical explanation can be found in the [WPILib documentation](https://docs.wpilib.org/en/latest/docs/software/vision-processing/apriltag/apriltag-intro.html). :::{note} -You can get FIRST's [official PDF of the targets used in 2024 here](https://firstfrc.blob.core.windows.net/frc2024/FieldAssets/Apriltag_Images_and_User_Guide.pdf). +You can get FIRST's [official PDF of the targets used in 2025 here](https://firstfrc.blob.core.windows.net/frc2025/FieldAssets/Apriltag_Images_and_User_Guide.pdf). ::: diff --git a/docs/source/docs/apriltag-pipelines/multitag.md b/docs/source/docs/apriltag-pipelines/multitag.md index da5169fb04..40bb4178d0 100644 --- a/docs/source/docs/apriltag-pipelines/multitag.md +++ b/docs/source/docs/apriltag-pipelines/multitag.md @@ -51,7 +51,7 @@ The returned field to camera transform is a transform from the fixed field origi ## Updating the Field Layout -PhotonVision ships by default with the [2024 field layout JSON](https://github.com/wpilibsuite/allwpilib/blob/main/apriltag/src/main/native/resources/edu/wpi/first/apriltag/2024-crescendo.json). The layout can be inspected by navigating to the settings tab and scrolling down to the "AprilTag Field Layout" card, as shown below. +PhotonVision ships by default with the [2025 field layout JSON](https://github.com/wpilibsuite/allwpilib/blob/main/apriltag/src/main/native/resources/edu/wpi/first/apriltag/2025-reefscape.json). The layout can be inspected by navigating to the settings tab and scrolling down to the "AprilTag Field Layout" card, as shown below. ```{image} images/field-layout.png :alt: The currently saved field layout in the Photon UI diff --git a/docs/source/docs/examples/aimingatatarget.md b/docs/source/docs/examples/aimingatatarget.md index 1217c2ff63..5cf50ea864 100644 --- a/docs/source/docs/examples/aimingatatarget.md +++ b/docs/source/docs/examples/aimingatatarget.md @@ -7,7 +7,7 @@ The following example is from the PhotonLib example repository ([Java](https://g - A Robot - A camera mounted rigidly to the robot's frame, cenetered and pointed forward. - A coprocessor running PhotonVision with an AprilTag or Aurco 2D Pipeline. -- [A printout of AprilTag 7](https://firstfrc.blob.core.windows.net/frc2024/FieldAssets/Apriltag_Images_and_User_Guide.pdf), mounted on a rigid and flat surface. +- [A printout of AprilTag 7](https://firstfrc.blob.core.windows.net/frc2025/FieldAssets/Apriltag_Images_and_User_Guide.pdf), mounted on a rigid and flat surface. ## Code diff --git a/docs/source/docs/objectDetection/about-object-detection.md b/docs/source/docs/objectDetection/about-object-detection.md index 25bd1e0dde..93e2feac65 100644 --- a/docs/source/docs/objectDetection/about-object-detection.md +++ b/docs/source/docs/objectDetection/about-object-detection.md @@ -10,7 +10,7 @@ For the 2024 season, PhotonVision shipped with a **pre-trained NOTE detector** ( ``` -For the 2025 season, we intend to release a new trained model once gamepiece data is released. +For the 2025 season, we are currently working on training an object detection model and will release it soon. ## Tracking Objects From e16b70065506cbcbb6b77c27bcef4597b5d9dc40 Mon Sep 17 00:00:00 2001 From: Sam948-byte Date: Mon, 6 Jan 2025 10:33:44 -0600 Subject: [PATCH 02/27] update plans for object detection model --- docs/source/docs/objectDetection/about-object-detection.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/docs/objectDetection/about-object-detection.md b/docs/source/docs/objectDetection/about-object-detection.md index 93e2feac65..2d27537795 100644 --- a/docs/source/docs/objectDetection/about-object-detection.md +++ b/docs/source/docs/objectDetection/about-object-detection.md @@ -10,7 +10,7 @@ For the 2024 season, PhotonVision shipped with a **pre-trained NOTE detector** ( ``` -For the 2025 season, we are currently working on training an object detection model and will release it soon. +For the 2025 season, we do not have plans to release an object detection model ourselves, but if someone in the community is interested in creating one, we would be happy to integrate it. ## Tracking Objects From aa5007904957eb01f83dc47651256c6c8af6eb14 Mon Sep 17 00:00:00 2001 From: Sam948-byte Date: Mon, 6 Jan 2025 14:27:15 -0600 Subject: [PATCH 03/27] update documentation for object detection in 2025 --- .../source/docs/objectDetection/about-object-detection.md | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/docs/source/docs/objectDetection/about-object-detection.md b/docs/source/docs/objectDetection/about-object-detection.md index 2d27537795..5b245e2e5c 100644 --- a/docs/source/docs/objectDetection/about-object-detection.md +++ b/docs/source/docs/objectDetection/about-object-detection.md @@ -4,13 +4,7 @@ PhotonVision supports object detection using neural network accelerator hardware built into Orange Pi 5/5+ coprocessors. The Neural Processing Unit, or NPU, is [used by PhotonVision](https://github.com/PhotonVision/rknn_jni/tree/main) to massively accelerate certain math operations like those needed for running ML-based object detection. -For the 2024 season, PhotonVision shipped with a **pre-trained NOTE detector** (shown above), as well as a mechanism for swapping in custom models. Future development will focus on enabling lower friction management of multiple custom models. - -```{image} images/notes-ui.png - -``` - -For the 2025 season, we do not have plans to release an object detection model ourselves, but if someone in the community is interested in creating one, we would be happy to integrate it. +For the 2025 season, PhotonVision does not currently ship with a pre-trained detector. If teams are interested in using object detection, they can follow the custom process outlined {ref}`below `. ## Tracking Objects From 3aac86806543b785ce51a57c7e2efae48cf2155a Mon Sep 17 00:00:00 2001 From: Sam948-byte Date: Mon, 6 Jan 2025 14:28:36 -0600 Subject: [PATCH 04/27] fix broken link --- docs/README.MD | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/README.MD b/docs/README.MD index 1e7a067ea9..017204cb8a 100644 --- a/docs/README.MD +++ b/docs/README.MD @@ -6,4 +6,4 @@ PhotonVision is a free open-source vision processing software for FRC teams. This repository is the source code for our ReadTheDocs documentation, which can be found [here](https://docs.photonvision.org). -[Contribution and formatting guidelines for this project](https://docs.photonvision.org/en/latest/docs/contributing/photonvision-docs/index.html) +[Contribution and formatting guidelines for this project](https://docs.photonvision.org/en/latest/docs/contributing/index.html) From 89bbdd304a7279accd4ca4b4a8da4a72ff8c291d Mon Sep 17 00:00:00 2001 From: Sam948-byte Date: Mon, 6 Jan 2025 16:27:13 -0600 Subject: [PATCH 05/27] update procedure for uploading custom model --- docs/source/docs/objectDetection/about-object-detection.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/docs/source/docs/objectDetection/about-object-detection.md b/docs/source/docs/objectDetection/about-object-detection.md index 5b245e2e5c..1214f98bd6 100644 --- a/docs/source/docs/objectDetection/about-object-detection.md +++ b/docs/source/docs/objectDetection/about-object-detection.md @@ -43,6 +43,4 @@ Coming soon! PhotonVision currently ONLY supports YOLOv5 models trained and converted to `.rknn` format for RK3588 CPUs! Other models require different post-processing code and will NOT work. The model conversion process is also highly particular. Proceed with care. ::: -Our [pre-trained NOTE model](https://github.com/PhotonVision/photonvision/blob/main/photon-server/src/main/resources/models/note-640-640-yolov5s.rknn) is automatically extracted from the JAR when PhotonVision starts, only if a file named “note-640-640-yolov5s.rknn” and "labels.txt" does not exist in the folder `photonvision_config/models/`. This technically allows power users to replace the model and label files with new ones without rebuilding Photon from source and uploading a new JAR. - -Use a program like WinSCP or FileZilla to access your coprocessor's filesystem, and copy the new `.rknn` model file into /home/pi. Next, SSH into the coprocessor and `sudo mv /path/to/new/model.rknn /opt/photonvision/photonvision_config/models/note-640-640-yolov5s.rknn`. Repeat this process with the labels file, which should contain one line per label the model outputs with no training newline. Next, restart PhotonVision via the web UI. +Use a program like WinSCP or FileZilla to access your coprocessor's filesystem, and copy the new `.rknn` model file into /home/pi. Next, SSH into the coprocessor and `sudo mv /path/to/new/model.rknn /opt/photonvision/photonvision_config/models/NEW-MODEL-NAME.rknn`. Repeat this process with the labels file, which should contain one line per label the model outputs with no training newline. Next, restart PhotonVision via the web UI. \ No newline at end of file From 3e881855f5b168c2910d0aaf0ca17e6d4b4aaad2 Mon Sep 17 00:00:00 2001 From: Sam948-byte Date: Mon, 6 Jan 2025 22:32:20 -0600 Subject: [PATCH 06/27] fix lint --- docs/source/docs/objectDetection/about-object-detection.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/docs/objectDetection/about-object-detection.md b/docs/source/docs/objectDetection/about-object-detection.md index 1214f98bd6..4d55ede355 100644 --- a/docs/source/docs/objectDetection/about-object-detection.md +++ b/docs/source/docs/objectDetection/about-object-detection.md @@ -43,4 +43,4 @@ Coming soon! PhotonVision currently ONLY supports YOLOv5 models trained and converted to `.rknn` format for RK3588 CPUs! Other models require different post-processing code and will NOT work. The model conversion process is also highly particular. Proceed with care. ::: -Use a program like WinSCP or FileZilla to access your coprocessor's filesystem, and copy the new `.rknn` model file into /home/pi. Next, SSH into the coprocessor and `sudo mv /path/to/new/model.rknn /opt/photonvision/photonvision_config/models/NEW-MODEL-NAME.rknn`. Repeat this process with the labels file, which should contain one line per label the model outputs with no training newline. Next, restart PhotonVision via the web UI. \ No newline at end of file +Use a program like WinSCP or FileZilla to access your coprocessor's filesystem, and copy the new `.rknn` model file into /home/pi. Next, SSH into the coprocessor and `sudo mv /path/to/new/model.rknn /opt/photonvision/photonvision_config/models/NEW-MODEL-NAME.rknn`. Repeat this process with the labels file, which should contain one line per label the model outputs with no training newline. Next, restart PhotonVision via the web UI. From 56902f3862a4bf5f07e62c9f94350daf49fadd0d Mon Sep 17 00:00:00 2001 From: Sam948-byte Date: Tue, 7 Jan 2025 11:04:06 -0600 Subject: [PATCH 07/27] GUI changes --- .../components/settings/DeviceControlCard.vue | 51 +++++++++++++++++++ .../src/main/resources/web/index.html | 15 +++++- 2 files changed, 65 insertions(+), 1 deletion(-) diff --git a/photon-client/src/components/settings/DeviceControlCard.vue b/photon-client/src/components/settings/DeviceControlCard.vue index 66930aa79d..7d25596c3d 100644 --- a/photon-client/src/components/settings/DeviceControlCard.vue +++ b/photon-client/src/components/settings/DeviceControlCard.vue @@ -202,6 +202,10 @@ const handleSettingsImport = () => { importFile.value = null; }; +const showObjectDetectionImportDialog = ref(false); +const importRKNNFile = ref(null); +const importLabelsFile = ref(null); + const showFactoryReset = ref(false); const expected = "Delete Everything"; const yesDeleteMySettingsText = ref(""); @@ -355,6 +359,53 @@ const nukePhotonConfigDirectory = () => { + + + + mdi-import + Import Object Detection Model + + + + Import Object Detection Model + + Upload a new object detection model to this device that can be used in a pipeline. + Naming convention is that the labels file ought to have the same name as the RKNN file, with -labels appended to the end. + For example, if the RKNN file is named foo.rknn, the labels file should be named foo-labels.txt. + + + + + + + + + mdi-import + Import Object Detection Model + + + + + + diff --git a/photon-server/src/main/resources/web/index.html b/photon-server/src/main/resources/web/index.html index 988f55e6a3..a0bbb476c9 100644 --- a/photon-server/src/main/resources/web/index.html +++ b/photon-server/src/main/resources/web/index.html @@ -1 +1,14 @@ -

UI has not been copied!

+ + + + + + + Photon Client + + + + +
+ + From 57cd5b9bb6f11062cf9333ad3410f643928aaea7 Mon Sep 17 00:00:00 2001 From: Sam948-byte Date: Tue, 7 Jan 2025 11:34:25 -0600 Subject: [PATCH 08/27] add object detection import handler --- .../components/settings/DeviceControlCard.vue | 47 +++++++++++++++++++ .../src/main/resources/web/index.html | 4 +- 2 files changed, 49 insertions(+), 2 deletions(-) diff --git a/photon-client/src/components/settings/DeviceControlCard.vue b/photon-client/src/components/settings/DeviceControlCard.vue index 7d25596c3d..37f167f2ee 100644 --- a/photon-client/src/components/settings/DeviceControlCard.vue +++ b/photon-client/src/components/settings/DeviceControlCard.vue @@ -206,6 +206,53 @@ const showObjectDetectionImportDialog = ref(false); const importRKNNFile = ref(null); const importLabelsFile = ref(null); +const handleObjectDetectionImport = () => { + if (importRKNNFile.value === null || importLabelsFile.value === null) return; + + const formData = new FormData(); + formData.append("rknnData", importRKNNFile.value); + formData.append("labelsData", importLabelsFile.value); + + useStateStore().showSnackbarMessage({ + message: "Importing Object Detection Model...", + color: "secondary", + timeout: -1 + }); + + axios + .post("/utils/importObjectDetectionModel", formData, { + headers: { "Content-Type": "multipart/form-data" } + }) + .then((response) => { + useStateStore().showSnackbarMessage({ + message: response.data.text || response.data, + color: "success" + }); + }) + .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." + }); + } + }); + + showObjectDetectionImportDialog.value = false; + importRKNNFile.value = null; + importLabelsFile.value = null; +}; + const showFactoryReset = ref(false); const expected = "Delete Everything"; const yesDeleteMySettingsText = ref(""); diff --git a/photon-server/src/main/resources/web/index.html b/photon-server/src/main/resources/web/index.html index a0bbb476c9..fa0f57d740 100644 --- a/photon-server/src/main/resources/web/index.html +++ b/photon-server/src/main/resources/web/index.html @@ -5,8 +5,8 @@ Photon Client - - + +
From 4be043497d890bd791b166335d0dfaf5a198ceae Mon Sep 17 00:00:00 2001 From: Sam948-byte Date: Tue, 7 Jan 2025 12:15:42 -0600 Subject: [PATCH 09/27] writing files to the models directory --- .../components/settings/DeviceControlCard.vue | 4 +- .../photonvision/server/RequestHandler.java | 143 +++++++++++------- .../java/org/photonvision/server/Server.java | 1 + 3 files changed, 91 insertions(+), 57 deletions(-) diff --git a/photon-client/src/components/settings/DeviceControlCard.vue b/photon-client/src/components/settings/DeviceControlCard.vue index 37f167f2ee..428d564a3b 100644 --- a/photon-client/src/components/settings/DeviceControlCard.vue +++ b/photon-client/src/components/settings/DeviceControlCard.vue @@ -210,8 +210,8 @@ const handleObjectDetectionImport = () => { if (importRKNNFile.value === null || importLabelsFile.value === null) return; const formData = new FormData(); - formData.append("rknnData", importRKNNFile.value); - formData.append("labelsData", importLabelsFile.value); + formData.append("rknn", importRKNNFile.value); + formData.append("labels", importLabelsFile.value); useStateStore().showSnackbarMessage({ message: "Importing Object Detection Model...", 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 c400d47a75..053d0305f6 100644 --- a/photon-server/src/main/java/org/photonvision/server/RequestHandler.java +++ b/photon-server/src/main/java/org/photonvision/server/RequestHandler.java @@ -98,7 +98,8 @@ public static void onSettingsImportRequest(Context ctx) { ConfigManager.getInstance().setWriteTaskEnabled(false); ConfigManager.getInstance().disableFlushOnShutdown(); - // We want to delete the -whole- zip file, so we need to teardown loggers for now + // We want to delete the -whole- zip file, so we need to teardown loggers for + // now logger.info("Writing new settings zip (logs may be truncated)..."); Logger.closeAllLoggers(); if (ConfigManager.saveUploadedSettingsZip(tempFilePath.get())) { @@ -326,8 +327,7 @@ public static void onOfflineUpdateRequest(Context ctx) { } try { - Path filePath = - Paths.get(ProgramDirectoryUtilities.getProgramDirectory(), "photonvision.jar"); + Path filePath = Paths.get(ProgramDirectoryUtilities.getProgramDirectory(), "photonvision.jar"); File targetFile = new File(filePath.toString()); var stream = new FileOutputStream(targetFile); @@ -389,8 +389,7 @@ public static void onCameraSettingsRequest(Context ctx) { var data = kObjectMapper.readTree(ctx.bodyInputStream()); String cameraUniqueName = data.get("cameraUniqueName").asText(); - var settings = - JacksonUtils.deserialize(data.get("settings").toString(), UICameraSettingsRequest.class); + var settings = JacksonUtils.deserialize(data.get("settings").toString(), UICameraSettingsRequest.class); var fov = settings.fov; logger.info("Changing camera FOV to: " + fov); @@ -444,7 +443,7 @@ public static void onLogExportRequest(Context ctx) { var out = Files.createTempFile("photonvision-logs", "zip").toFile(); try { - ZipUtil.packEntries(new File[] {tempPath.toFile(), tempPath2.toFile()}, out); + ZipUtil.packEntries(new File[] { tempPath.toFile(), tempPath2.toFile() }, out); } catch (Exception e) { e.printStackTrace(); } @@ -473,11 +472,9 @@ public static void onCalibrationEndRequest(Context ctx) { String cameraUniqueName; try { - cameraUniqueName = - kObjectMapper.readTree(ctx.bodyInputStream()).get("cameraUniqueName").asText(); + cameraUniqueName = kObjectMapper.readTree(ctx.bodyInputStream()).get("cameraUniqueName").asText(); - var calData = - VisionSourceManager.getInstance().vmm.getModule(cameraUniqueName).endCalibration(); + var calData = VisionSourceManager.getInstance().vmm.getModule(cameraUniqueName).endCalibration(); if (calData == null) { ctx.result("The calibration process failed"); ctx.status(500); @@ -510,16 +507,14 @@ public static void onDataCalibrationImportRequest(Context ctx) { var data = kObjectMapper.readTree(ctx.bodyInputStream()); String cameraUniqueName = data.get("cameraUniqueName").asText(); - var coeffs = - kObjectMapper.convertValue(data.get("calibration"), CameraCalibrationCoefficients.class); - - var uploadCalibrationEvent = - new IncomingWebSocketEvent<>( - DataChangeDestination.DCD_ACTIVEMODULE, - "calibrationUploaded", - coeffs, - cameraUniqueName, - null); + var coeffs = kObjectMapper.convertValue(data.get("calibration"), CameraCalibrationCoefficients.class); + + var uploadCalibrationEvent = new IncomingWebSocketEvent<>( + DataChangeDestination.DCD_ACTIVEMODULE, + "calibrationUploaded", + coeffs, + cameraUniqueName, + null); DataChangeService.getInstance().publishEvent(uploadCalibrationEvent); ctx.status(200); @@ -543,6 +538,49 @@ public static void onProgramRestartRequest(Context ctx) { restartProgram(); } + public static void onObjectDetectionModelImportRequest(Context ctx) { + try { + // Retrieve the uploaded files + var modelFile = ctx.uploadedFile("rknn"); + var labelsFile = ctx.uploadedFile("labels"); + + if (modelFile == null || labelsFile == null) { + ctx.status(400); + ctx.result( + "No File was sent with the request. Make sure that the model and labels files are sent at the keys 'rknn' and 'labels'"); + logger.error( + "No File was sent with the request. Make sure that the model and labels files are sent at the keys 'rknn' and 'labels'"); + return; + } + + if (!modelFile.extension().contains("rknn") || !labelsFile.extension().contains("txt")) { + ctx.status(400); + ctx.result( + "The uploaded files were not of type 'rknn' and 'txt'. The uploaded files should be a .rknn and .txt file."); + logger.error( + "The uploaded files were not of type 'rknn' and 'txt'. The uploaded files should be a .rknn and .txt file."); + return; + } + + // dump files into /opt/photonvision/photonvision_config/models/ + + var modelPath = Paths.get(ConfigManager.getInstance().getModelsDirectory().toString(), modelFile.filename()); + var labelsPath = Paths.get(ConfigManager.getInstance().getModelsDirectory().toString(), labelsFile.filename()); + + try (FileOutputStream out = new FileOutputStream(modelPath.toFile())) { + modelFile.content().transferTo(out); + } + + try (FileOutputStream out = new FileOutputStream(labelsPath.toFile())) { + labelsFile.content().transferTo(out); + } + + } catch (Exception e) { + ctx.status(500).result("Error processing files: " + e.getMessage()); + } + + } + public static void onDeviceRestartRequest(Context ctx) { ctx.status(HardwareManager.getInstance().restartDevice() ? 204 : 500); } @@ -583,32 +621,28 @@ public static void onCalibrationSnapshotRequest(Context ctx) { var height = Integer.parseInt(ctx.queryParam("height")); var observationIdx = Integer.parseInt(ctx.queryParam("snapshotIdx")); - CameraCalibrationCoefficients calList = - VisionSourceManager.getInstance() - .vmm - .getModule(cameraUniqueName) - .getStateAsCameraConfig() - .calibrations - .stream() - .filter( - it -> - Math.abs(it.unrotatedImageSize.width - width) < 1e-4 - && Math.abs(it.unrotatedImageSize.height - height) < 1e-4) - .findFirst() - .orElse(null); + CameraCalibrationCoefficients calList = VisionSourceManager.getInstance().vmm + .getModule(cameraUniqueName) + .getStateAsCameraConfig().calibrations + .stream() + .filter( + it -> Math.abs(it.unrotatedImageSize.width - width) < 1e-4 + && Math.abs(it.unrotatedImageSize.height - height) < 1e-4) + .findFirst() + .orElse(null); if (calList == null || calList.observations.size() < observationIdx) { ctx.status(404); return; } - // encode as jpeg to save even more space. reduces size of a 1280p image from 300k to 25k + // encode as jpeg to save even more space. reduces size of a 1280p image from + // 300k to 25k var jpegBytes = new MatOfByte(); Mat img = null; try { - img = - Imgcodecs.imread( - calList.observations.get(observationIdx).snapshotDataLocation.toString()); + img = Imgcodecs.imread( + calList.observations.get(observationIdx).snapshotDataLocation.toString()); } catch (Exception e) { ctx.status(500); ctx.result("Unable to read calibration image"); @@ -635,17 +669,14 @@ public static void onCalibrationExportRequest(Context ctx) { var width = Integer.parseInt(ctx.queryParam("width")); var height = Integer.parseInt(ctx.queryParam("height")); - var cc = - VisionSourceManager.getInstance().vmm.getModule(cameraUniqueName).getStateAsCameraConfig(); + var cc = VisionSourceManager.getInstance().vmm.getModule(cameraUniqueName).getStateAsCameraConfig(); - CameraCalibrationCoefficients calList = - cc.calibrations.stream() - .filter( - it -> - Math.abs(it.unrotatedImageSize.width - width) < 1e-4 - && Math.abs(it.unrotatedImageSize.height - height) < 1e-4) - .findFirst() - .orElse(null); + CameraCalibrationCoefficients calList = cc.calibrations.stream() + .filter( + it -> Math.abs(it.unrotatedImageSize.width - width) < 1e-4 + && Math.abs(it.unrotatedImageSize.height - height) < 1e-4) + .findFirst() + .orElse(null); if (calList == null) { ctx.status(404); @@ -668,7 +699,8 @@ public static void onImageSnapshotsRequest(Context ctx) { try { for (File cameraDir : cameraDirs) { var cameraSnapshots = cameraDir.listFiles(); - if (cameraSnapshots == null) continue; + if (cameraSnapshots == null) + continue; String cameraUniqueName = cameraDir.getName(); @@ -699,18 +731,19 @@ public static void onImageSnapshotsRequest(Context ctx) { public static void onCameraCalibImagesRequest(Context ctx) { try { - HashMap>>> snapshots = - new HashMap<>(); + HashMap>>> snapshots = new HashMap<>(); var cameraDirs = ConfigManager.getInstance().getCalibDir().toFile().listFiles(); if (cameraDirs != null) { var camData = new HashMap>>(); for (var cameraDir : cameraDirs) { var resolutionDirs = cameraDir.listFiles(); - if (resolutionDirs == null) continue; + if (resolutionDirs == null) + continue; for (var resolutionDir : resolutionDirs) { var calibImages = resolutionDir.listFiles(); - if (calibImages == null) continue; + if (calibImages == null) + continue; var resolutionImages = new ArrayList>(); for (var calibImg : calibImages) { var snapshotData = new HashMap(); @@ -748,8 +781,7 @@ public static void onCameraCalibImagesRequest(Context ctx) { * @return Temporary file. Empty if the temporary file was unable to be created. */ private static Optional handleTempFileCreation(UploadedFile file) { - var tempFilePath = - new File(Path.of(System.getProperty("java.io.tmpdir"), file.filename()).toString()); + var tempFilePath = new File(Path.of(System.getProperty("java.io.tmpdir"), file.filename()).toString()); boolean makeDirsRes = tempFilePath.getParentFile().mkdirs(); if (!makeDirsRes && !(tempFilePath.getParentFile().exists())) { @@ -776,7 +808,8 @@ private static Optional handleTempFileCreation(UploadedFile file) { } /** - * Restart the running program. Note that this doesn't actually restart the program itself, + * Restart the running program. Note that this doesn't actually restart the + * program itself, * instead, it relies on systemd or an equivalent. */ private static void restartProgram() { 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 67ccd2d756..84537ffa39 100644 --- a/photon-server/src/main/java/org/photonvision/server/Server.java +++ b/photon-server/src/main/java/org/photonvision/server/Server.java @@ -127,6 +127,7 @@ private static void start(int port) { // Utilities app.post("/api/utils/offlineUpdate", RequestHandler::onOfflineUpdateRequest); + app.post("/api/utils/importObjectDetectionModel", RequestHandler::onObjectDetectionModelImportRequest); app.get("/api/utils/photonvision-journalctl.txt", RequestHandler::onLogExportRequest); app.post("/api/utils/restartProgram", RequestHandler::onProgramRestartRequest); app.post("/api/utils/restartDevice", RequestHandler::onDeviceRestartRequest); From 93209ea4f0e5557c033b4b9f08dbfebdede8d4ee Mon Sep 17 00:00:00 2001 From: Sam948-byte Date: Tue, 7 Jan 2025 12:58:07 -0600 Subject: [PATCH 10/27] change docs to reflect modifications --- .../objectDetection/about-object-detection.md | 2 +- .../org/photonvision/server/RequestHandler.java | 1 + photon-server/src/main/resources/web/index.html | 15 +-------------- 3 files changed, 3 insertions(+), 15 deletions(-) diff --git a/docs/source/docs/objectDetection/about-object-detection.md b/docs/source/docs/objectDetection/about-object-detection.md index 4d55ede355..e731b5f539 100644 --- a/docs/source/docs/objectDetection/about-object-detection.md +++ b/docs/source/docs/objectDetection/about-object-detection.md @@ -43,4 +43,4 @@ Coming soon! PhotonVision currently ONLY supports YOLOv5 models trained and converted to `.rknn` format for RK3588 CPUs! Other models require different post-processing code and will NOT work. The model conversion process is also highly particular. Proceed with care. ::: -Use a program like WinSCP or FileZilla to access your coprocessor's filesystem, and copy the new `.rknn` model file into /home/pi. Next, SSH into the coprocessor and `sudo mv /path/to/new/model.rknn /opt/photonvision/photonvision_config/models/NEW-MODEL-NAME.rknn`. Repeat this process with the labels file, which should contain one line per label the model outputs with no training newline. Next, restart PhotonVision via the web UI. +In the settings, under ``Device Control``, there's an option to upload a new object detection model. When uploading the files, ensure that that the labels file has the same name as the RKNN file, with ``-labels`` appended to the end. For example, if the RKNN file is named ``foo.rknn``, the labels file should be named ``foo-labels.txt``. The labels file should contain one line per label the model outputs with no training newline. 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 053d0305f6..c4172c2722 100644 --- a/photon-server/src/main/java/org/photonvision/server/RequestHandler.java +++ b/photon-server/src/main/java/org/photonvision/server/RequestHandler.java @@ -575,6 +575,7 @@ public static void onObjectDetectionModelImportRequest(Context ctx) { labelsFile.content().transferTo(out); } + ctx.status(200).result("Successfully uploaded object detection model"); } catch (Exception e) { ctx.status(500).result("Error processing files: " + e.getMessage()); } diff --git a/photon-server/src/main/resources/web/index.html b/photon-server/src/main/resources/web/index.html index fa0f57d740..f008dfd128 100644 --- a/photon-server/src/main/resources/web/index.html +++ b/photon-server/src/main/resources/web/index.html @@ -1,14 +1 @@ - - - - - - - Photon Client - - - - -
- - +

UI has not been copied!

\ No newline at end of file From 798854a88f475bd6747285e064760260f5f2ffcc Mon Sep 17 00:00:00 2001 From: Sam948-byte Date: Tue, 7 Jan 2025 14:24:43 -0600 Subject: [PATCH 11/27] fix formatting --- .../components/settings/DeviceControlCard.vue | 1 - .../photonvision/server/RequestHandler.java | 105 ++++++++++-------- .../java/org/photonvision/server/Server.java | 4 +- .../src/main/resources/web/index.html | 2 +- 4 files changed, 64 insertions(+), 48 deletions(-) diff --git a/photon-client/src/components/settings/DeviceControlCard.vue b/photon-client/src/components/settings/DeviceControlCard.vue index 428d564a3b..3016cb3fcc 100644 --- a/photon-client/src/components/settings/DeviceControlCard.vue +++ b/photon-client/src/components/settings/DeviceControlCard.vue @@ -452,7 +452,6 @@ const nukePhotonConfigDirectory = () => { -
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 c4172c2722..18fbde5d70 100644 --- a/photon-server/src/main/java/org/photonvision/server/RequestHandler.java +++ b/photon-server/src/main/java/org/photonvision/server/RequestHandler.java @@ -327,7 +327,8 @@ public static void onOfflineUpdateRequest(Context ctx) { } try { - Path filePath = Paths.get(ProgramDirectoryUtilities.getProgramDirectory(), "photonvision.jar"); + Path filePath = + Paths.get(ProgramDirectoryUtilities.getProgramDirectory(), "photonvision.jar"); File targetFile = new File(filePath.toString()); var stream = new FileOutputStream(targetFile); @@ -389,7 +390,8 @@ public static void onCameraSettingsRequest(Context ctx) { var data = kObjectMapper.readTree(ctx.bodyInputStream()); String cameraUniqueName = data.get("cameraUniqueName").asText(); - var settings = JacksonUtils.deserialize(data.get("settings").toString(), UICameraSettingsRequest.class); + var settings = + JacksonUtils.deserialize(data.get("settings").toString(), UICameraSettingsRequest.class); var fov = settings.fov; logger.info("Changing camera FOV to: " + fov); @@ -443,7 +445,7 @@ public static void onLogExportRequest(Context ctx) { var out = Files.createTempFile("photonvision-logs", "zip").toFile(); try { - ZipUtil.packEntries(new File[] { tempPath.toFile(), tempPath2.toFile() }, out); + ZipUtil.packEntries(new File[] {tempPath.toFile(), tempPath2.toFile()}, out); } catch (Exception e) { e.printStackTrace(); } @@ -472,9 +474,11 @@ public static void onCalibrationEndRequest(Context ctx) { String cameraUniqueName; try { - cameraUniqueName = kObjectMapper.readTree(ctx.bodyInputStream()).get("cameraUniqueName").asText(); + cameraUniqueName = + kObjectMapper.readTree(ctx.bodyInputStream()).get("cameraUniqueName").asText(); - var calData = VisionSourceManager.getInstance().vmm.getModule(cameraUniqueName).endCalibration(); + var calData = + VisionSourceManager.getInstance().vmm.getModule(cameraUniqueName).endCalibration(); if (calData == null) { ctx.result("The calibration process failed"); ctx.status(500); @@ -507,14 +511,16 @@ public static void onDataCalibrationImportRequest(Context ctx) { var data = kObjectMapper.readTree(ctx.bodyInputStream()); String cameraUniqueName = data.get("cameraUniqueName").asText(); - var coeffs = kObjectMapper.convertValue(data.get("calibration"), CameraCalibrationCoefficients.class); - - var uploadCalibrationEvent = new IncomingWebSocketEvent<>( - DataChangeDestination.DCD_ACTIVEMODULE, - "calibrationUploaded", - coeffs, - cameraUniqueName, - null); + var coeffs = + kObjectMapper.convertValue(data.get("calibration"), CameraCalibrationCoefficients.class); + + var uploadCalibrationEvent = + new IncomingWebSocketEvent<>( + DataChangeDestination.DCD_ACTIVEMODULE, + "calibrationUploaded", + coeffs, + cameraUniqueName, + null); DataChangeService.getInstance().publishEvent(uploadCalibrationEvent); ctx.status(200); @@ -564,13 +570,17 @@ public static void onObjectDetectionModelImportRequest(Context ctx) { // dump files into /opt/photonvision/photonvision_config/models/ - var modelPath = Paths.get(ConfigManager.getInstance().getModelsDirectory().toString(), modelFile.filename()); - var labelsPath = Paths.get(ConfigManager.getInstance().getModelsDirectory().toString(), labelsFile.filename()); + var modelPath = + Paths.get( + ConfigManager.getInstance().getModelsDirectory().toString(), modelFile.filename()); + var labelsPath = + Paths.get( + ConfigManager.getInstance().getModelsDirectory().toString(), labelsFile.filename()); try (FileOutputStream out = new FileOutputStream(modelPath.toFile())) { modelFile.content().transferTo(out); } - + try (FileOutputStream out = new FileOutputStream(labelsPath.toFile())) { labelsFile.content().transferTo(out); } @@ -579,7 +589,6 @@ public static void onObjectDetectionModelImportRequest(Context ctx) { } catch (Exception e) { ctx.status(500).result("Error processing files: " + e.getMessage()); } - } public static void onDeviceRestartRequest(Context ctx) { @@ -622,15 +631,19 @@ public static void onCalibrationSnapshotRequest(Context ctx) { var height = Integer.parseInt(ctx.queryParam("height")); var observationIdx = Integer.parseInt(ctx.queryParam("snapshotIdx")); - CameraCalibrationCoefficients calList = VisionSourceManager.getInstance().vmm - .getModule(cameraUniqueName) - .getStateAsCameraConfig().calibrations - .stream() - .filter( - it -> Math.abs(it.unrotatedImageSize.width - width) < 1e-4 - && Math.abs(it.unrotatedImageSize.height - height) < 1e-4) - .findFirst() - .orElse(null); + CameraCalibrationCoefficients calList = + VisionSourceManager.getInstance() + .vmm + .getModule(cameraUniqueName) + .getStateAsCameraConfig() + .calibrations + .stream() + .filter( + it -> + Math.abs(it.unrotatedImageSize.width - width) < 1e-4 + && Math.abs(it.unrotatedImageSize.height - height) < 1e-4) + .findFirst() + .orElse(null); if (calList == null || calList.observations.size() < observationIdx) { ctx.status(404); @@ -642,8 +655,9 @@ public static void onCalibrationSnapshotRequest(Context ctx) { var jpegBytes = new MatOfByte(); Mat img = null; try { - img = Imgcodecs.imread( - calList.observations.get(observationIdx).snapshotDataLocation.toString()); + img = + Imgcodecs.imread( + calList.observations.get(observationIdx).snapshotDataLocation.toString()); } catch (Exception e) { ctx.status(500); ctx.result("Unable to read calibration image"); @@ -670,14 +684,17 @@ public static void onCalibrationExportRequest(Context ctx) { var width = Integer.parseInt(ctx.queryParam("width")); var height = Integer.parseInt(ctx.queryParam("height")); - var cc = VisionSourceManager.getInstance().vmm.getModule(cameraUniqueName).getStateAsCameraConfig(); + var cc = + VisionSourceManager.getInstance().vmm.getModule(cameraUniqueName).getStateAsCameraConfig(); - CameraCalibrationCoefficients calList = cc.calibrations.stream() - .filter( - it -> Math.abs(it.unrotatedImageSize.width - width) < 1e-4 - && Math.abs(it.unrotatedImageSize.height - height) < 1e-4) - .findFirst() - .orElse(null); + CameraCalibrationCoefficients calList = + cc.calibrations.stream() + .filter( + it -> + Math.abs(it.unrotatedImageSize.width - width) < 1e-4 + && Math.abs(it.unrotatedImageSize.height - height) < 1e-4) + .findFirst() + .orElse(null); if (calList == null) { ctx.status(404); @@ -700,8 +717,7 @@ public static void onImageSnapshotsRequest(Context ctx) { try { for (File cameraDir : cameraDirs) { var cameraSnapshots = cameraDir.listFiles(); - if (cameraSnapshots == null) - continue; + if (cameraSnapshots == null) continue; String cameraUniqueName = cameraDir.getName(); @@ -732,19 +748,18 @@ public static void onImageSnapshotsRequest(Context ctx) { public static void onCameraCalibImagesRequest(Context ctx) { try { - HashMap>>> snapshots = new HashMap<>(); + HashMap>>> snapshots = + new HashMap<>(); var cameraDirs = ConfigManager.getInstance().getCalibDir().toFile().listFiles(); if (cameraDirs != null) { var camData = new HashMap>>(); for (var cameraDir : cameraDirs) { var resolutionDirs = cameraDir.listFiles(); - if (resolutionDirs == null) - continue; + if (resolutionDirs == null) continue; for (var resolutionDir : resolutionDirs) { var calibImages = resolutionDir.listFiles(); - if (calibImages == null) - continue; + if (calibImages == null) continue; var resolutionImages = new ArrayList>(); for (var calibImg : calibImages) { var snapshotData = new HashMap(); @@ -782,7 +797,8 @@ public static void onCameraCalibImagesRequest(Context ctx) { * @return Temporary file. Empty if the temporary file was unable to be created. */ private static Optional handleTempFileCreation(UploadedFile file) { - var tempFilePath = new File(Path.of(System.getProperty("java.io.tmpdir"), file.filename()).toString()); + var tempFilePath = + new File(Path.of(System.getProperty("java.io.tmpdir"), file.filename()).toString()); boolean makeDirsRes = tempFilePath.getParentFile().mkdirs(); if (!makeDirsRes && !(tempFilePath.getParentFile().exists())) { @@ -809,8 +825,7 @@ private static Optional handleTempFileCreation(UploadedFile file) { } /** - * Restart the running program. Note that this doesn't actually restart the - * program itself, + * Restart the running program. Note that this doesn't actually restart the program itself, * instead, it relies on systemd or an equivalent. */ private static void restartProgram() { 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 84537ffa39..1d6c373161 100644 --- a/photon-server/src/main/java/org/photonvision/server/Server.java +++ b/photon-server/src/main/java/org/photonvision/server/Server.java @@ -127,7 +127,9 @@ private static void start(int port) { // Utilities app.post("/api/utils/offlineUpdate", RequestHandler::onOfflineUpdateRequest); - app.post("/api/utils/importObjectDetectionModel", RequestHandler::onObjectDetectionModelImportRequest); + app.post( + "/api/utils/importObjectDetectionModel", + RequestHandler::onObjectDetectionModelImportRequest); app.get("/api/utils/photonvision-journalctl.txt", RequestHandler::onLogExportRequest); app.post("/api/utils/restartProgram", RequestHandler::onProgramRestartRequest); app.post("/api/utils/restartDevice", RequestHandler::onDeviceRestartRequest); diff --git a/photon-server/src/main/resources/web/index.html b/photon-server/src/main/resources/web/index.html index f008dfd128..988f55e6a3 100644 --- a/photon-server/src/main/resources/web/index.html +++ b/photon-server/src/main/resources/web/index.html @@ -1 +1 @@ -

UI has not been copied!

\ No newline at end of file +

UI has not been copied!

From 1ecfaeade2eea39d9fbe0e85aa6373380903b2fb Mon Sep 17 00:00:00 2001 From: Sam948-byte Date: Tue, 7 Jan 2025 14:24:43 -0600 Subject: [PATCH 12/27] fix formatting --- .../components/settings/DeviceControlCard.vue | 1 - .../photonvision/server/RequestHandler.java | 105 ++++++++++-------- .../java/org/photonvision/server/Server.java | 4 +- .../src/main/resources/web/index.html | 2 +- 4 files changed, 64 insertions(+), 48 deletions(-) diff --git a/photon-client/src/components/settings/DeviceControlCard.vue b/photon-client/src/components/settings/DeviceControlCard.vue index 428d564a3b..3016cb3fcc 100644 --- a/photon-client/src/components/settings/DeviceControlCard.vue +++ b/photon-client/src/components/settings/DeviceControlCard.vue @@ -452,7 +452,6 @@ const nukePhotonConfigDirectory = () => { -
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 c4172c2722..18fbde5d70 100644 --- a/photon-server/src/main/java/org/photonvision/server/RequestHandler.java +++ b/photon-server/src/main/java/org/photonvision/server/RequestHandler.java @@ -327,7 +327,8 @@ public static void onOfflineUpdateRequest(Context ctx) { } try { - Path filePath = Paths.get(ProgramDirectoryUtilities.getProgramDirectory(), "photonvision.jar"); + Path filePath = + Paths.get(ProgramDirectoryUtilities.getProgramDirectory(), "photonvision.jar"); File targetFile = new File(filePath.toString()); var stream = new FileOutputStream(targetFile); @@ -389,7 +390,8 @@ public static void onCameraSettingsRequest(Context ctx) { var data = kObjectMapper.readTree(ctx.bodyInputStream()); String cameraUniqueName = data.get("cameraUniqueName").asText(); - var settings = JacksonUtils.deserialize(data.get("settings").toString(), UICameraSettingsRequest.class); + var settings = + JacksonUtils.deserialize(data.get("settings").toString(), UICameraSettingsRequest.class); var fov = settings.fov; logger.info("Changing camera FOV to: " + fov); @@ -443,7 +445,7 @@ public static void onLogExportRequest(Context ctx) { var out = Files.createTempFile("photonvision-logs", "zip").toFile(); try { - ZipUtil.packEntries(new File[] { tempPath.toFile(), tempPath2.toFile() }, out); + ZipUtil.packEntries(new File[] {tempPath.toFile(), tempPath2.toFile()}, out); } catch (Exception e) { e.printStackTrace(); } @@ -472,9 +474,11 @@ public static void onCalibrationEndRequest(Context ctx) { String cameraUniqueName; try { - cameraUniqueName = kObjectMapper.readTree(ctx.bodyInputStream()).get("cameraUniqueName").asText(); + cameraUniqueName = + kObjectMapper.readTree(ctx.bodyInputStream()).get("cameraUniqueName").asText(); - var calData = VisionSourceManager.getInstance().vmm.getModule(cameraUniqueName).endCalibration(); + var calData = + VisionSourceManager.getInstance().vmm.getModule(cameraUniqueName).endCalibration(); if (calData == null) { ctx.result("The calibration process failed"); ctx.status(500); @@ -507,14 +511,16 @@ public static void onDataCalibrationImportRequest(Context ctx) { var data = kObjectMapper.readTree(ctx.bodyInputStream()); String cameraUniqueName = data.get("cameraUniqueName").asText(); - var coeffs = kObjectMapper.convertValue(data.get("calibration"), CameraCalibrationCoefficients.class); - - var uploadCalibrationEvent = new IncomingWebSocketEvent<>( - DataChangeDestination.DCD_ACTIVEMODULE, - "calibrationUploaded", - coeffs, - cameraUniqueName, - null); + var coeffs = + kObjectMapper.convertValue(data.get("calibration"), CameraCalibrationCoefficients.class); + + var uploadCalibrationEvent = + new IncomingWebSocketEvent<>( + DataChangeDestination.DCD_ACTIVEMODULE, + "calibrationUploaded", + coeffs, + cameraUniqueName, + null); DataChangeService.getInstance().publishEvent(uploadCalibrationEvent); ctx.status(200); @@ -564,13 +570,17 @@ public static void onObjectDetectionModelImportRequest(Context ctx) { // dump files into /opt/photonvision/photonvision_config/models/ - var modelPath = Paths.get(ConfigManager.getInstance().getModelsDirectory().toString(), modelFile.filename()); - var labelsPath = Paths.get(ConfigManager.getInstance().getModelsDirectory().toString(), labelsFile.filename()); + var modelPath = + Paths.get( + ConfigManager.getInstance().getModelsDirectory().toString(), modelFile.filename()); + var labelsPath = + Paths.get( + ConfigManager.getInstance().getModelsDirectory().toString(), labelsFile.filename()); try (FileOutputStream out = new FileOutputStream(modelPath.toFile())) { modelFile.content().transferTo(out); } - + try (FileOutputStream out = new FileOutputStream(labelsPath.toFile())) { labelsFile.content().transferTo(out); } @@ -579,7 +589,6 @@ public static void onObjectDetectionModelImportRequest(Context ctx) { } catch (Exception e) { ctx.status(500).result("Error processing files: " + e.getMessage()); } - } public static void onDeviceRestartRequest(Context ctx) { @@ -622,15 +631,19 @@ public static void onCalibrationSnapshotRequest(Context ctx) { var height = Integer.parseInt(ctx.queryParam("height")); var observationIdx = Integer.parseInt(ctx.queryParam("snapshotIdx")); - CameraCalibrationCoefficients calList = VisionSourceManager.getInstance().vmm - .getModule(cameraUniqueName) - .getStateAsCameraConfig().calibrations - .stream() - .filter( - it -> Math.abs(it.unrotatedImageSize.width - width) < 1e-4 - && Math.abs(it.unrotatedImageSize.height - height) < 1e-4) - .findFirst() - .orElse(null); + CameraCalibrationCoefficients calList = + VisionSourceManager.getInstance() + .vmm + .getModule(cameraUniqueName) + .getStateAsCameraConfig() + .calibrations + .stream() + .filter( + it -> + Math.abs(it.unrotatedImageSize.width - width) < 1e-4 + && Math.abs(it.unrotatedImageSize.height - height) < 1e-4) + .findFirst() + .orElse(null); if (calList == null || calList.observations.size() < observationIdx) { ctx.status(404); @@ -642,8 +655,9 @@ public static void onCalibrationSnapshotRequest(Context ctx) { var jpegBytes = new MatOfByte(); Mat img = null; try { - img = Imgcodecs.imread( - calList.observations.get(observationIdx).snapshotDataLocation.toString()); + img = + Imgcodecs.imread( + calList.observations.get(observationIdx).snapshotDataLocation.toString()); } catch (Exception e) { ctx.status(500); ctx.result("Unable to read calibration image"); @@ -670,14 +684,17 @@ public static void onCalibrationExportRequest(Context ctx) { var width = Integer.parseInt(ctx.queryParam("width")); var height = Integer.parseInt(ctx.queryParam("height")); - var cc = VisionSourceManager.getInstance().vmm.getModule(cameraUniqueName).getStateAsCameraConfig(); + var cc = + VisionSourceManager.getInstance().vmm.getModule(cameraUniqueName).getStateAsCameraConfig(); - CameraCalibrationCoefficients calList = cc.calibrations.stream() - .filter( - it -> Math.abs(it.unrotatedImageSize.width - width) < 1e-4 - && Math.abs(it.unrotatedImageSize.height - height) < 1e-4) - .findFirst() - .orElse(null); + CameraCalibrationCoefficients calList = + cc.calibrations.stream() + .filter( + it -> + Math.abs(it.unrotatedImageSize.width - width) < 1e-4 + && Math.abs(it.unrotatedImageSize.height - height) < 1e-4) + .findFirst() + .orElse(null); if (calList == null) { ctx.status(404); @@ -700,8 +717,7 @@ public static void onImageSnapshotsRequest(Context ctx) { try { for (File cameraDir : cameraDirs) { var cameraSnapshots = cameraDir.listFiles(); - if (cameraSnapshots == null) - continue; + if (cameraSnapshots == null) continue; String cameraUniqueName = cameraDir.getName(); @@ -732,19 +748,18 @@ public static void onImageSnapshotsRequest(Context ctx) { public static void onCameraCalibImagesRequest(Context ctx) { try { - HashMap>>> snapshots = new HashMap<>(); + HashMap>>> snapshots = + new HashMap<>(); var cameraDirs = ConfigManager.getInstance().getCalibDir().toFile().listFiles(); if (cameraDirs != null) { var camData = new HashMap>>(); for (var cameraDir : cameraDirs) { var resolutionDirs = cameraDir.listFiles(); - if (resolutionDirs == null) - continue; + if (resolutionDirs == null) continue; for (var resolutionDir : resolutionDirs) { var calibImages = resolutionDir.listFiles(); - if (calibImages == null) - continue; + if (calibImages == null) continue; var resolutionImages = new ArrayList>(); for (var calibImg : calibImages) { var snapshotData = new HashMap(); @@ -782,7 +797,8 @@ public static void onCameraCalibImagesRequest(Context ctx) { * @return Temporary file. Empty if the temporary file was unable to be created. */ private static Optional handleTempFileCreation(UploadedFile file) { - var tempFilePath = new File(Path.of(System.getProperty("java.io.tmpdir"), file.filename()).toString()); + var tempFilePath = + new File(Path.of(System.getProperty("java.io.tmpdir"), file.filename()).toString()); boolean makeDirsRes = tempFilePath.getParentFile().mkdirs(); if (!makeDirsRes && !(tempFilePath.getParentFile().exists())) { @@ -809,8 +825,7 @@ private static Optional handleTempFileCreation(UploadedFile file) { } /** - * Restart the running program. Note that this doesn't actually restart the - * program itself, + * Restart the running program. Note that this doesn't actually restart the program itself, * instead, it relies on systemd or an equivalent. */ private static void restartProgram() { 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 84537ffa39..1d6c373161 100644 --- a/photon-server/src/main/java/org/photonvision/server/Server.java +++ b/photon-server/src/main/java/org/photonvision/server/Server.java @@ -127,7 +127,9 @@ private static void start(int port) { // Utilities app.post("/api/utils/offlineUpdate", RequestHandler::onOfflineUpdateRequest); - app.post("/api/utils/importObjectDetectionModel", RequestHandler::onObjectDetectionModelImportRequest); + app.post( + "/api/utils/importObjectDetectionModel", + RequestHandler::onObjectDetectionModelImportRequest); app.get("/api/utils/photonvision-journalctl.txt", RequestHandler::onLogExportRequest); app.post("/api/utils/restartProgram", RequestHandler::onProgramRestartRequest); app.post("/api/utils/restartDevice", RequestHandler::onDeviceRestartRequest); diff --git a/photon-server/src/main/resources/web/index.html b/photon-server/src/main/resources/web/index.html index f008dfd128..988f55e6a3 100644 --- a/photon-server/src/main/resources/web/index.html +++ b/photon-server/src/main/resources/web/index.html @@ -1 +1 @@ -

UI has not been copied!

\ No newline at end of file +

UI has not been copied!

From a542c03c1a4faa06d213d5331760125f3a616209 Mon Sep 17 00:00:00 2001 From: Sam948-byte Date: Tue, 7 Jan 2025 14:30:11 -0600 Subject: [PATCH 13/27] more formatting fix --- photon-client/src/components/settings/DeviceControlCard.vue | 1 - 1 file changed, 1 deletion(-) diff --git a/photon-client/src/components/settings/DeviceControlCard.vue b/photon-client/src/components/settings/DeviceControlCard.vue index 3016cb3fcc..cd7f5007dc 100644 --- a/photon-client/src/components/settings/DeviceControlCard.vue +++ b/photon-client/src/components/settings/DeviceControlCard.vue @@ -467,7 +467,6 @@ const nukePhotonConfigDirectory = () => {
- From fab9399658208ce917659e063365e84337cc3661 Mon Sep 17 00:00:00 2001 From: Sam948-byte Date: Tue, 7 Jan 2025 15:25:08 -0600 Subject: [PATCH 14/27] preliminary GUI restructuring --- .../components/settings/DeviceControlCard.vue | 207 ++++++------------ .../src/views/GeneralSettingsView.vue | 2 + .../src/main/resources/web/index.html | 15 +- 3 files changed, 87 insertions(+), 137 deletions(-) diff --git a/photon-client/src/components/settings/DeviceControlCard.vue b/photon-client/src/components/settings/DeviceControlCard.vue index cd7f5007dc..c964bce0a7 100644 --- a/photon-client/src/components/settings/DeviceControlCard.vue +++ b/photon-client/src/components/settings/DeviceControlCard.vue @@ -10,20 +10,21 @@ const restartProgram = () => { .then(() => { useStateStore().showSnackbarMessage({ message: "Successfully sent program restart request", - color: "success" + color: "success", }); }) .catch((error) => { // This endpoint always return 204 regardless of outcome if (error.request) { useStateStore().showSnackbarMessage({ - message: "Error while trying to process the request! The backend didn't respond.", - color: "error" + message: + "Error while trying to process the request! The backend didn't respond.", + color: "error", }); } else { useStateStore().showSnackbarMessage({ message: "An error occurred while trying to process the request.", - color: "error" + color: "error", }); } }); @@ -33,25 +34,27 @@ const restartDevice = () => { .post("/utils/restartDevice") .then(() => { useStateStore().showSnackbarMessage({ - message: "Successfully dispatched the restart command. It isn't confirmed if a device restart will occur.", - color: "success" + message: + "Successfully dispatched the restart command. It isn't confirmed if a device restart will occur.", + color: "success", }); }) .catch((error) => { if (error.response) { useStateStore().showSnackbarMessage({ message: "The backend is unable to fulfil the request to restart the device.", - color: "error" + color: "error", }); } else if (error.request) { useStateStore().showSnackbarMessage({ - message: "Error while trying to process the request! The backend didn't respond.", - color: "error" + message: + "Error while trying to process the request! The backend didn't respond.", + color: "error", }); } else { useStateStore().showSnackbarMessage({ message: "An error occurred while trying to process the request.", - color: "error" + color: "error", }); } }); @@ -73,7 +76,7 @@ const handleOfflineUpdate = () => { useStateStore().showSnackbarMessage({ message: "New Software Upload in Progress...", color: "secondary", - timeout: -1 + timeout: -1, }); axios @@ -83,40 +86,44 @@ const handleOfflineUpdate = () => { const uploadPercentage = (progress || 0) * 100.0; if (uploadPercentage < 99.5) { useStateStore().showSnackbarMessage({ - message: "New Software Upload in Process, " + uploadPercentage.toFixed(2) + "% complete", + message: + "New Software Upload in Process, " + + uploadPercentage.toFixed(2) + + "% complete", color: "secondary", - timeout: -1 + timeout: -1, }); } else { useStateStore().showSnackbarMessage({ message: "Installing uploaded software...", color: "secondary", - timeout: -1 + timeout: -1, }); } - } + }, }) .then((response) => { useStateStore().showSnackbarMessage({ message: response.data.text || response.data, - color: "success" + color: "success", }); }) .catch((error) => { if (error.response) { useStateStore().showSnackbarMessage({ color: "error", - message: error.response.data.text || error.response.data + 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." + 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." + message: "An error occurred while trying to process the request.", }); } }); @@ -137,7 +144,7 @@ enum ImportType { HardwareConfig, HardwareSettings, NetworkConfig, - ApriltagFieldLayout + ApriltagFieldLayout, } const showImportDialog = ref(false); const importType = ref(-1); @@ -170,29 +177,30 @@ const handleSettingsImport = () => { axios .post(`/settings${settingsEndpoint}`, formData, { - headers: { "Content-Type": "multipart/form-data" } + headers: { "Content-Type": "multipart/form-data" }, }) .then((response) => { useStateStore().showSnackbarMessage({ message: response.data.text || response.data, - color: "success" + color: "success", }); }) .catch((error) => { if (error.response) { useStateStore().showSnackbarMessage({ color: "error", - message: error.response.data.text || error.response.data + 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." + 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." + message: "An error occurred while trying to process the request.", }); } }); @@ -202,57 +210,6 @@ const handleSettingsImport = () => { importFile.value = null; }; -const showObjectDetectionImportDialog = ref(false); -const importRKNNFile = ref(null); -const importLabelsFile = ref(null); - -const handleObjectDetectionImport = () => { - if (importRKNNFile.value === null || importLabelsFile.value === null) return; - - const formData = new FormData(); - formData.append("rknn", importRKNNFile.value); - formData.append("labels", importLabelsFile.value); - - useStateStore().showSnackbarMessage({ - message: "Importing Object Detection Model...", - color: "secondary", - timeout: -1 - }); - - axios - .post("/utils/importObjectDetectionModel", formData, { - headers: { "Content-Type": "multipart/form-data" } - }) - .then((response) => { - useStateStore().showSnackbarMessage({ - message: response.data.text || response.data, - color: "success" - }); - }) - .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." - }); - } - }); - - showObjectDetectionImportDialog.value = false; - importRKNNFile.value = null; - importLabelsFile.value = null; -}; - const showFactoryReset = ref(false); const expected = "Delete Everything"; const yesDeleteMySettingsText = ref(""); @@ -261,25 +218,27 @@ const nukePhotonConfigDirectory = () => { .post("/utils/nukeConfigDirectory") .then(() => { useStateStore().showSnackbarMessage({ - message: "Successfully dispatched the reset command. Waiting for backend to start back up", - color: "success" + message: + "Successfully dispatched the reset command. Waiting for backend to start back up", + color: "success", }); }) .catch((error) => { if (error.response) { useStateStore().showSnackbarMessage({ message: "The backend is unable to fulfil the request to reset the device.", - color: "error" + color: "error", }); } else if (error.request) { useStateStore().showSnackbarMessage({ - message: "Error while trying to process the request! The backend didn't respond.", - color: "error" + message: + "Error while trying to process the request! The backend didn't respond.", + color: "error", }); } else { useStateStore().showSnackbarMessage({ message: "An error occurred while trying to process the request.", - color: "error" + color: "error", }); } }); @@ -309,7 +268,13 @@ const nukePhotonConfigDirectory = () => { mdi-upload Offline Update - + @@ -332,7 +297,8 @@ const nukePhotonConfigDirectory = () => { Import Settings - Upload and apply previously saved or exported PhotonVision settings to this device + Upload and apply previously saved or exported PhotonVision settings to + this device { 'Hardware Config', 'Hardware Settings', 'Network Config', - 'Apriltag Layout' + 'Apriltag Layout', ]" :select-cols="10" style="width: 100%" @@ -353,7 +319,9 @@ const nukePhotonConfigDirectory = () => { @@ -362,7 +330,11 @@ const nukePhotonConfigDirectory = () => { style="display: flex; align-items: center; justify-content: center" align="center" > - + mdi-import Import Settings @@ -406,52 +378,6 @@ const nukePhotonConfigDirectory = () => { - - - - mdi-import - Import Object Detection Model - - - - Import Object Detection Model - - Upload a new object detection model to this device that can be used in a pipeline. - Naming convention is that the labels file ought to have the same name as the RKNN file, with -labels appended to the end. - For example, if the RKNN file is named foo.rknn, the labels file should be named foo-labels.txt. - - - - - - - - - mdi-import - Import Object Detection Model - - - - - @@ -467,6 +393,7 @@ const nukePhotonConfigDirectory = () => { + @@ -480,10 +407,16 @@ const nukePhotonConfigDirectory = () => { - This will delete ALL OF YOUR SETTINGS and restart PhotonVision. + + This will delete ALL OF YOUR SETTINGS and restart PhotonVision. + - + mdi-export Backup Settings { mdi-trash-can-outline {{ - $vuetify.breakpoint.mdAndUp ? "Delete everything, I have backed up what I need" : "Delete Everything" + $vuetify.breakpoint.mdAndUp + ? "Delete everything, I have backed up what I need" + : "Delete Everything" }} diff --git a/photon-client/src/views/GeneralSettingsView.vue b/photon-client/src/views/GeneralSettingsView.vue index edd3e67edd..7fc83a0012 100644 --- a/photon-client/src/views/GeneralSettingsView.vue +++ b/photon-client/src/views/GeneralSettingsView.vue @@ -1,6 +1,7 @@ + + + +
+ + From eae2dbf32e0a2e8e956521fbee15d2094f50dd09 Mon Sep 17 00:00:00 2001 From: Sam948-byte Date: Tue, 7 Jan 2025 15:25:08 -0600 Subject: [PATCH 15/27] preliminary GUI restructuring --- .../components/settings/DeviceControlCard.vue | 207 ++++++------------ .../settings/ObjectDetectionCard.vue | 159 ++++++++++++++ .../src/views/GeneralSettingsView.vue | 2 + .../src/main/resources/web/index.html | 15 +- 4 files changed, 246 insertions(+), 137 deletions(-) create mode 100644 photon-client/src/components/settings/ObjectDetectionCard.vue diff --git a/photon-client/src/components/settings/DeviceControlCard.vue b/photon-client/src/components/settings/DeviceControlCard.vue index cd7f5007dc..c964bce0a7 100644 --- a/photon-client/src/components/settings/DeviceControlCard.vue +++ b/photon-client/src/components/settings/DeviceControlCard.vue @@ -10,20 +10,21 @@ const restartProgram = () => { .then(() => { useStateStore().showSnackbarMessage({ message: "Successfully sent program restart request", - color: "success" + color: "success", }); }) .catch((error) => { // This endpoint always return 204 regardless of outcome if (error.request) { useStateStore().showSnackbarMessage({ - message: "Error while trying to process the request! The backend didn't respond.", - color: "error" + message: + "Error while trying to process the request! The backend didn't respond.", + color: "error", }); } else { useStateStore().showSnackbarMessage({ message: "An error occurred while trying to process the request.", - color: "error" + color: "error", }); } }); @@ -33,25 +34,27 @@ const restartDevice = () => { .post("/utils/restartDevice") .then(() => { useStateStore().showSnackbarMessage({ - message: "Successfully dispatched the restart command. It isn't confirmed if a device restart will occur.", - color: "success" + message: + "Successfully dispatched the restart command. It isn't confirmed if a device restart will occur.", + color: "success", }); }) .catch((error) => { if (error.response) { useStateStore().showSnackbarMessage({ message: "The backend is unable to fulfil the request to restart the device.", - color: "error" + color: "error", }); } else if (error.request) { useStateStore().showSnackbarMessage({ - message: "Error while trying to process the request! The backend didn't respond.", - color: "error" + message: + "Error while trying to process the request! The backend didn't respond.", + color: "error", }); } else { useStateStore().showSnackbarMessage({ message: "An error occurred while trying to process the request.", - color: "error" + color: "error", }); } }); @@ -73,7 +76,7 @@ const handleOfflineUpdate = () => { useStateStore().showSnackbarMessage({ message: "New Software Upload in Progress...", color: "secondary", - timeout: -1 + timeout: -1, }); axios @@ -83,40 +86,44 @@ const handleOfflineUpdate = () => { const uploadPercentage = (progress || 0) * 100.0; if (uploadPercentage < 99.5) { useStateStore().showSnackbarMessage({ - message: "New Software Upload in Process, " + uploadPercentage.toFixed(2) + "% complete", + message: + "New Software Upload in Process, " + + uploadPercentage.toFixed(2) + + "% complete", color: "secondary", - timeout: -1 + timeout: -1, }); } else { useStateStore().showSnackbarMessage({ message: "Installing uploaded software...", color: "secondary", - timeout: -1 + timeout: -1, }); } - } + }, }) .then((response) => { useStateStore().showSnackbarMessage({ message: response.data.text || response.data, - color: "success" + color: "success", }); }) .catch((error) => { if (error.response) { useStateStore().showSnackbarMessage({ color: "error", - message: error.response.data.text || error.response.data + 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." + 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." + message: "An error occurred while trying to process the request.", }); } }); @@ -137,7 +144,7 @@ enum ImportType { HardwareConfig, HardwareSettings, NetworkConfig, - ApriltagFieldLayout + ApriltagFieldLayout, } const showImportDialog = ref(false); const importType = ref(-1); @@ -170,29 +177,30 @@ const handleSettingsImport = () => { axios .post(`/settings${settingsEndpoint}`, formData, { - headers: { "Content-Type": "multipart/form-data" } + headers: { "Content-Type": "multipart/form-data" }, }) .then((response) => { useStateStore().showSnackbarMessage({ message: response.data.text || response.data, - color: "success" + color: "success", }); }) .catch((error) => { if (error.response) { useStateStore().showSnackbarMessage({ color: "error", - message: error.response.data.text || error.response.data + 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." + 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." + message: "An error occurred while trying to process the request.", }); } }); @@ -202,57 +210,6 @@ const handleSettingsImport = () => { importFile.value = null; }; -const showObjectDetectionImportDialog = ref(false); -const importRKNNFile = ref(null); -const importLabelsFile = ref(null); - -const handleObjectDetectionImport = () => { - if (importRKNNFile.value === null || importLabelsFile.value === null) return; - - const formData = new FormData(); - formData.append("rknn", importRKNNFile.value); - formData.append("labels", importLabelsFile.value); - - useStateStore().showSnackbarMessage({ - message: "Importing Object Detection Model...", - color: "secondary", - timeout: -1 - }); - - axios - .post("/utils/importObjectDetectionModel", formData, { - headers: { "Content-Type": "multipart/form-data" } - }) - .then((response) => { - useStateStore().showSnackbarMessage({ - message: response.data.text || response.data, - color: "success" - }); - }) - .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." - }); - } - }); - - showObjectDetectionImportDialog.value = false; - importRKNNFile.value = null; - importLabelsFile.value = null; -}; - const showFactoryReset = ref(false); const expected = "Delete Everything"; const yesDeleteMySettingsText = ref(""); @@ -261,25 +218,27 @@ const nukePhotonConfigDirectory = () => { .post("/utils/nukeConfigDirectory") .then(() => { useStateStore().showSnackbarMessage({ - message: "Successfully dispatched the reset command. Waiting for backend to start back up", - color: "success" + message: + "Successfully dispatched the reset command. Waiting for backend to start back up", + color: "success", }); }) .catch((error) => { if (error.response) { useStateStore().showSnackbarMessage({ message: "The backend is unable to fulfil the request to reset the device.", - color: "error" + color: "error", }); } else if (error.request) { useStateStore().showSnackbarMessage({ - message: "Error while trying to process the request! The backend didn't respond.", - color: "error" + message: + "Error while trying to process the request! The backend didn't respond.", + color: "error", }); } else { useStateStore().showSnackbarMessage({ message: "An error occurred while trying to process the request.", - color: "error" + color: "error", }); } }); @@ -309,7 +268,13 @@ const nukePhotonConfigDirectory = () => { mdi-upload Offline Update
- +
@@ -332,7 +297,8 @@ const nukePhotonConfigDirectory = () => { Import Settings - Upload and apply previously saved or exported PhotonVision settings to this device + Upload and apply previously saved or exported PhotonVision settings to + this device { 'Hardware Config', 'Hardware Settings', 'Network Config', - 'Apriltag Layout' + 'Apriltag Layout', ]" :select-cols="10" style="width: 100%" @@ -353,7 +319,9 @@ const nukePhotonConfigDirectory = () => { @@ -362,7 +330,11 @@ const nukePhotonConfigDirectory = () => { style="display: flex; align-items: center; justify-content: center" align="center" > - + mdi-import Import Settings @@ -406,52 +378,6 @@ const nukePhotonConfigDirectory = () => {
- - - - mdi-import - Import Object Detection Model - - - - Import Object Detection Model - - Upload a new object detection model to this device that can be used in a pipeline. - Naming convention is that the labels file ought to have the same name as the RKNN file, with -labels appended to the end. - For example, if the RKNN file is named foo.rknn, the labels file should be named foo-labels.txt. - - - - - - - - - mdi-import - Import Object Detection Model - - - - - @@ -467,6 +393,7 @@ const nukePhotonConfigDirectory = () => { + @@ -480,10 +407,16 @@ const nukePhotonConfigDirectory = () => { - This will delete ALL OF YOUR SETTINGS and restart PhotonVision. + + This will delete ALL OF YOUR SETTINGS and restart PhotonVision. + - + mdi-export Backup Settings { mdi-trash-can-outline {{ - $vuetify.breakpoint.mdAndUp ? "Delete everything, I have backed up what I need" : "Delete Everything" + $vuetify.breakpoint.mdAndUp + ? "Delete everything, I have backed up what I need" + : "Delete Everything" }} diff --git a/photon-client/src/components/settings/ObjectDetectionCard.vue b/photon-client/src/components/settings/ObjectDetectionCard.vue new file mode 100644 index 0000000000..ee0c5a69cf --- /dev/null +++ b/photon-client/src/components/settings/ObjectDetectionCard.vue @@ -0,0 +1,159 @@ + + + + + diff --git a/photon-client/src/views/GeneralSettingsView.vue b/photon-client/src/views/GeneralSettingsView.vue index edd3e67edd..7fc83a0012 100644 --- a/photon-client/src/views/GeneralSettingsView.vue +++ b/photon-client/src/views/GeneralSettingsView.vue @@ -1,6 +1,7 @@ + + + +
+ + From 892756e265240aa9e56294aa8d57a0662f9f427b Mon Sep 17 00:00:00 2001 From: Sam948-byte Date: Tue, 7 Jan 2025 16:17:22 -0600 Subject: [PATCH 16/27] finalize GUI restructuring --- .../settings/ObjectDetectionCard.vue | 33 +++++++++++-------- .../src/main/resources/web/index.html | 15 +-------- 2 files changed, 20 insertions(+), 28 deletions(-) diff --git a/photon-client/src/components/settings/ObjectDetectionCard.vue b/photon-client/src/components/settings/ObjectDetectionCard.vue index ee0c5a69cf..4db69187cb 100644 --- a/photon-client/src/components/settings/ObjectDetectionCard.vue +++ b/photon-client/src/components/settings/ObjectDetectionCard.vue @@ -1,11 +1,8 @@ - - - -
- - +

UI has not been copied!

From dfcaa5fbd6192c0edba3f809a176e9d8fe9fc762 Mon Sep 17 00:00:00 2001 From: Sam948-byte Date: Tue, 7 Jan 2025 16:38:33 -0600 Subject: [PATCH 17/27] make it look pretty --- .../settings/ObjectDetectionCard.vue | 62 ++++++++++++++++--- .../src/views/GeneralSettingsView.vue | 2 +- 2 files changed, 55 insertions(+), 9 deletions(-) diff --git a/photon-client/src/components/settings/ObjectDetectionCard.vue b/photon-client/src/components/settings/ObjectDetectionCard.vue index 4db69187cb..776f793631 100644 --- a/photon-client/src/components/settings/ObjectDetectionCard.vue +++ b/photon-client/src/components/settings/ObjectDetectionCard.vue @@ -59,10 +59,18 @@ const handleObjectDetectionImport = () => { }; // Filters out models that are not supported by the current backend, and returns a flattened list. -const supportedModels = computed(() => { - const { availableModels, supportedBackends } = useSettingsStore().general; - return supportedBackends.flatMap((backend) => availableModels[backend] || []); -}); +// const supportedModels = computed(() => { +// const { availableModels, supportedBackends } = useSettingsStore().general; +// return supportedBackends.flatMap((backend) => availableModels[backend] || []); +// }); +//mock supportedModels for testing of GUI +const supportedModels = [ + "mobilenet_v1", + "mobilenet_v2", + "ssd_mobilenet_v1", + "ssd_mobilenet_v2", + "ssd_mobilenet_v3", +]; - diff --git a/photon-client/src/views/GeneralSettingsView.vue b/photon-client/src/views/GeneralSettingsView.vue index 7fc83a0012..17b442c0bc 100644 --- a/photon-client/src/views/GeneralSettingsView.vue +++ b/photon-client/src/views/GeneralSettingsView.vue @@ -12,8 +12,8 @@ import ApriltagControlCard from "@/components/settings/ApriltagControlCard.vue";
- +
From ce7946359e023264cf3def0a3448b44c9c9d7cee Mon Sep 17 00:00:00 2001 From: Sam948-byte Date: Tue, 7 Jan 2025 16:38:33 -0600 Subject: [PATCH 18/27] make it look pretty --- .../settings/ObjectDetectionCard.vue | 65 +++++++++++++++---- .../src/views/GeneralSettingsView.vue | 2 +- 2 files changed, 55 insertions(+), 12 deletions(-) diff --git a/photon-client/src/components/settings/ObjectDetectionCard.vue b/photon-client/src/components/settings/ObjectDetectionCard.vue index 4db69187cb..556908cee0 100644 --- a/photon-client/src/components/settings/ObjectDetectionCard.vue +++ b/photon-client/src/components/settings/ObjectDetectionCard.vue @@ -1,10 +1,7 @@ - diff --git a/photon-client/src/views/GeneralSettingsView.vue b/photon-client/src/views/GeneralSettingsView.vue index 7fc83a0012..17b442c0bc 100644 --- a/photon-client/src/views/GeneralSettingsView.vue +++ b/photon-client/src/views/GeneralSettingsView.vue @@ -12,8 +12,8 @@ import ApriltagControlCard from "@/components/settings/ApriltagControlCard.vue";
- +
From f451fb2c711f1d20c47bc6047a9683a0f4b79f48 Mon Sep 17 00:00:00 2001 From: Sam948-byte Date: Tue, 7 Jan 2025 17:42:55 -0600 Subject: [PATCH 19/27] remove mock testing models --- .../src/components/settings/ObjectDetectionCard.vue | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/photon-client/src/components/settings/ObjectDetectionCard.vue b/photon-client/src/components/settings/ObjectDetectionCard.vue index d4588dc2e7..e53e26ca26 100644 --- a/photon-client/src/components/settings/ObjectDetectionCard.vue +++ b/photon-client/src/components/settings/ObjectDetectionCard.vue @@ -55,12 +55,10 @@ const handleObjectDetectionImport = () => { }; // Filters out models that are not supported by the current backend, and returns a flattened list. -// const supportedModels = computed(() => { -// const { availableModels, supportedBackends } = useSettingsStore().general; -// return supportedBackends.flatMap((backend) => availableModels[backend] || []); -// }); -//mock supportedModels for testing of GUI -const supportedModels = ["mobilenet_v1", "mobilenet_v2", "ssd_mobilenet_v1", "ssd_mobilenet_v2", "ssd_mobilenet_v3"]; +const supportedModels = computed(() => { + const { availableModels, supportedBackends } = useSettingsStore().general; + return supportedBackends.flatMap((backend) => availableModels[backend] || []); +});