diff --git a/Main/src/main/java/com/chameleonvision/config/ConfigManager.java b/Main/src/main/java/com/chameleonvision/config/ConfigManager.java index 9c8329911..0802a1194 100644 --- a/Main/src/main/java/com/chameleonvision/config/ConfigManager.java +++ b/Main/src/main/java/com/chameleonvision/config/ConfigManager.java @@ -67,7 +67,7 @@ public class ConfigManager { } } - public static void saveSettings() { + public static void saveGeneralSettings() { checkSettingsFolder(); saveSettingsFile(); } diff --git a/Main/src/main/java/com/chameleonvision/vision/VisionManager.java b/Main/src/main/java/com/chameleonvision/vision/VisionManager.java index 628e3b38d..e102b697b 100644 --- a/Main/src/main/java/com/chameleonvision/vision/VisionManager.java +++ b/Main/src/main/java/com/chameleonvision/vision/VisionManager.java @@ -7,6 +7,7 @@ import com.chameleonvision.util.Helpers; import com.chameleonvision.util.Platform; import com.chameleonvision.vision.camera.CameraCapture; import com.chameleonvision.vision.camera.USBCameraCapture; +import com.chameleonvision.vision.pipeline.CVPipeline; import com.chameleonvision.vision.pipeline.CVPipelineSettings; import edu.wpi.cscore.UsbCamera; import edu.wpi.cscore.UsbCameraInfo; @@ -136,6 +137,7 @@ public class VisionManager { return currentUIVisionProcess.getPipelines().stream().map(cvPipeline -> cvPipeline.settings.nickname).collect(Collectors.toList()); } + public static void saveCameras() { visionProcesses.forEach((vpm) -> { VisionProcess process = vpm.visionProcess; @@ -149,6 +151,25 @@ public class VisionManager { }); } + private static String getCurrentCameraName() { + return currentUIVisionProcess.getCamera().getProperties().name; + } + + public static void saveCurrentCameraSettings() { + CameraJsonConfig config = CameraJsonConfig.fromUSBCameraProcess((USBCameraCapture) currentUIVisionProcess.getCamera()); + ConfigManager.saveCameraConfig(getCurrentCameraName(), config); + } + + public static void saveCurrentCameraPipelines() { + List pipelineSettings = currentUIVisionProcess.getPipelines().stream().map(pipeline -> pipeline.settings).collect(Collectors.toList()); + ConfigManager.saveCameraPipelines(getCurrentCameraName(), pipelineSettings); + } + + public static void saveCurrentCameraDriverMode() { + CVPipelineSettings driverModeSettings = currentUIVisionProcess.getDriverModeSettings(); + ConfigManager.saveCameraDriverMode(getCurrentCameraName(), driverModeSettings); + } + public static List getCurrentCameraResolutionList() { return currentUIVisionProcess.getCamera().getProperties().getVideoModes().stream().map(Helpers::VideoModeToString).collect(Collectors.toList()); } diff --git a/Main/src/main/java/com/chameleonvision/vision/VisionProcess.java b/Main/src/main/java/com/chameleonvision/vision/VisionProcess.java index f974221f8..fca45771d 100644 --- a/Main/src/main/java/com/chameleonvision/vision/VisionProcess.java +++ b/Main/src/main/java/com/chameleonvision/vision/VisionProcess.java @@ -35,7 +35,7 @@ public class VisionProcess { private volatile CVPipelineResult lastPipelineResult; - BlockingQueue streamFrameQueue = new LinkedBlockingDeque<>(1); + private BlockingQueue streamFrameQueue = new LinkedBlockingDeque<>(1); // network table stuff private final NetworkTable defaultTable; @@ -323,10 +323,9 @@ public class VisionProcess { } - private static class CameraStreamerRunnable extends LoopingRunnable { + private class CameraStreamerRunnable extends LoopingRunnable { final CameraStreamer streamer; - private Mat streamBuffer = new Mat(); private CameraStreamerRunnable(int cameraFPS, CameraStreamer streamer) { // add 2 FPS to allow for a bit of overhead @@ -336,7 +335,18 @@ public class VisionProcess { @Override protected void process() { - + try { + if (!streamFrameQueue.isEmpty()) { + Mat latestMat = streamFrameQueue.take(); + if (!latestMat.empty()) { + streamer.runStream(latestMat); + } else { + System.out.println("stream mat empty"); + } + } + } catch (InterruptedException e) { + e.printStackTrace(); + } } } } diff --git a/Main/src/main/java/com/chameleonvision/vision/pipeline/CVPipeline2d.java b/Main/src/main/java/com/chameleonvision/vision/pipeline/CVPipeline2d.java index 898c9e57a..906354328 100644 --- a/Main/src/main/java/com/chameleonvision/vision/pipeline/CVPipeline2d.java +++ b/Main/src/main/java/com/chameleonvision/vision/pipeline/CVPipeline2d.java @@ -16,7 +16,6 @@ import static com.chameleonvision.vision.pipeline.CVPipeline2d.*; public class CVPipeline2d extends CVPipeline { private Mat rawCameraMat = new Mat(); - private Mat hsvOutputMat = new Mat(); private RotateFlipPipe rotateFlipPipe; private BlurPipe blurPipe; @@ -32,13 +31,10 @@ public class CVPipeline2d extends CVPipeline hsvResult = hsvPipe.run(erodeDilateResult.getLeft()); totalPipelineTimeNanos += hsvResult.getRight(); - // Todo: move to a pipe - try { - Imgproc.cvtColor(hsvResult.getLeft(), hsvOutputMat, Imgproc.COLOR_GRAY2BGR, 3); - } catch (CvException e) { - System.err.println("(CVPipeline2d) Exception thrown by OpenCV: \n" + e.getMessage()); - } - Pair, Long> findContoursResult = findContoursPipe.run(hsvResult.getLeft()); totalPipelineTimeNanos += findContoursResult.getRight(); @@ -153,8 +142,8 @@ public class CVPipeline2d extends CVPipeline, Long> collect2dTargetsResult = collect2dTargetsPipe.run(sortContoursResult.getLeft()); totalPipelineTimeNanos += collect2dTargetsResult.getRight(); - // takes pair of (Mat of original camera image, Mat of HSV thresholded image) - Pair outputMatResult = outputMatPipe.run(Pair.of(rawCameraMat, hsvOutputMat)); + // takes pair of (Mat of original camera image (8UC3), Mat of HSV thresholded image(8UC1)) + Pair outputMatResult = outputMatPipe.run(Pair.of(rawCameraMat, hsvResult.getLeft())); totalPipelineTimeNanos += outputMatResult.getRight(); // takes pair of (Mat to draw on, List of sorted contours) @@ -175,13 +164,15 @@ public class CVPipeline2d extends CVPipeline { public final List targets; public final boolean hasTarget; - public final Mat outputMat; + public final Mat outputMat = new Mat(); public final long processTime; public long imageTimestamp = 0; public CVPipelineResult(List targets, Mat outputMat, long processTime) { this.targets = targets; hasTarget = targets != null && !targets.isEmpty(); - this.outputMat = outputMat; + outputMat.copyTo(this.outputMat); this.processTime = processTime; } diff --git a/Main/src/main/java/com/chameleonvision/vision/pipeline/pipes/ErodeDilatePipe.java b/Main/src/main/java/com/chameleonvision/vision/pipeline/pipes/ErodeDilatePipe.java index 3798856d5..215e3ecdd 100644 --- a/Main/src/main/java/com/chameleonvision/vision/pipeline/pipes/ErodeDilatePipe.java +++ b/Main/src/main/java/com/chameleonvision/vision/pipeline/pipes/ErodeDilatePipe.java @@ -38,7 +38,7 @@ public class ErodeDilatePipe implements Pipe { } if (dilate) { - Imgproc.erode(processBuffer, processBuffer, kernel); + Imgproc.dilate(processBuffer, processBuffer, kernel); } processBuffer.copyTo(outputMat); diff --git a/Main/src/main/java/com/chameleonvision/vision/pipeline/pipes/OutputMatPipe.java b/Main/src/main/java/com/chameleonvision/vision/pipeline/pipes/OutputMatPipe.java index df7972108..82d02228b 100644 --- a/Main/src/main/java/com/chameleonvision/vision/pipeline/pipes/OutputMatPipe.java +++ b/Main/src/main/java/com/chameleonvision/vision/pipeline/pipes/OutputMatPipe.java @@ -1,12 +1,15 @@ package com.chameleonvision.vision.pipeline.pipes; import org.apache.commons.lang3.tuple.Pair; +import org.opencv.core.CvException; import org.opencv.core.Mat; +import org.opencv.imgproc.Imgproc; public class OutputMatPipe implements Pipe, Mat> { private boolean showThresholded; + private Mat processBuffer = new Mat(); private Mat outputMat = new Mat(); public OutputMatPipe(boolean showThresholded) { @@ -17,15 +20,30 @@ public class OutputMatPipe implements Pipe, Mat> { this.showThresholded = showThresholded; } + /** + * + * @param input Input object for pipe + * Left is raw camera mat (8UC3), Right is HSV threshold mat (8UC1) + * @return Returns desired output Mat, and processing time in nanoseconds + */ @Override public Pair run(Pair input) { long processStartNanos = System.nanoTime(); - outputMat = showThresholded ? input.getRight() : input.getLeft(); + if (showThresholded) { + try { + input.getRight().copyTo(processBuffer); + Imgproc.cvtColor(processBuffer, processBuffer, Imgproc.COLOR_GRAY2BGR, 3); + processBuffer.copyTo(outputMat); + processBuffer.release(); + } catch (CvException e) { + System.err.println("(OutputMat) Exception thrown by OpenCV: \n" + e.getMessage()); + } + } else { + input.getLeft().copyTo(outputMat); + } long processTime = System.nanoTime() - processStartNanos; - Pair output = Pair.of(outputMat, processTime); - outputMat.release(); - return output; + return Pair.of(outputMat, processTime); } } diff --git a/Main/src/main/java/com/chameleonvision/web/Server.java b/Main/src/main/java/com/chameleonvision/web/Server.java index 7f8fa656f..121b78634 100644 --- a/Main/src/main/java/com/chameleonvision/web/Server.java +++ b/Main/src/main/java/com/chameleonvision/web/Server.java @@ -20,7 +20,7 @@ public class Server { ws.onClose(ctx -> { handler.onClose(ctx); System.out.println("Socket Disconnected"); - ConfigManager.saveSettings(); + ConfigManager.saveGeneralSettings(); }); ws.onBinaryMessage(ctx -> { handler.onBinaryMessage(ctx); diff --git a/Main/src/main/java/com/chameleonvision/web/ServerHandler.java b/Main/src/main/java/com/chameleonvision/web/ServerHandler.java index b3608b02d..ee4e984e5 100644 --- a/Main/src/main/java/com/chameleonvision/web/ServerHandler.java +++ b/Main/src/main/java/com/chameleonvision/web/ServerHandler.java @@ -58,7 +58,7 @@ public class ServerHandler { for (HashMap.Entry e : data.entrySet()) { setField(ConfigManager.settings, e.getKey(), e.getValue()); } - ConfigManager.saveSettings(); + ConfigManager.saveGeneralSettings(); sendFullSettings(); break; } @@ -68,7 +68,7 @@ public class ServerHandler { currentProcess.getDriverModeSettings().brightness = (Integer) data.get("brightness"); currentProcess.setDriverMode((Boolean) data.get("isDriver")); - VisionManager.saveCameras(); + VisionManager.saveCurrentCameraDriverMode(); break; } case "cameraSettings": { @@ -90,20 +90,20 @@ public class ServerHandler { // currentCamera.getProperties().setCamVideoMode(newResolution, true); // } - VisionManager.saveCameras(); + VisionManager.saveCurrentCameraSettings(); sendFullSettings(); break; } case "changeCameraName": { currentCamera.getProperties().setNickname((String) entry.getValue()); sendFullSettings(); - ConfigManager.saveSettings(); + VisionManager.saveCurrentCameraSettings(); break; } case "changePipelineName": { currentPipeline.settings.nickname = ((String) entry.getValue()); sendFullSettings(); - ConfigManager.saveSettings(); + VisionManager.saveCurrentCameraPipelines(); break; } case "duplicatePipeline": { @@ -121,7 +121,7 @@ public class ServerHandler { } else { currentProcess.addPipeline(origPipeline); } - ConfigManager.saveSettings(); + VisionManager.saveCurrentCameraPipelines(); break; } case "command": { @@ -129,7 +129,7 @@ public class ServerHandler { case "addNewPipeline": currentProcess.addPipeline(); sendFullSettings(); - ConfigManager.saveSettings(); + VisionManager.saveCurrentCameraPipelines(); break; // TODO: (HIGH) this never worked before, re-visit now that VisionProcess is written sanely case "deleteCurrentPipeline": @@ -143,10 +143,10 @@ public class ServerHandler { // cam.deletePipeline(); // cam.setCurrentPipelineIndex(nextIndex); // sendFullSettings(); -// ConfigManager.saveSettings(); +// VisionManager.saveCurrentCameraPipelines(); break; case "save": - ConfigManager.saveSettings(); + ConfigManager.saveGeneralSettings(); System.out.println("saved Settings"); break; } @@ -161,17 +161,10 @@ public class ServerHandler { case "currentPipeline": { currentProcess.setPipeline((Integer) entry.getValue(), true); sendFullSettings(); - try { - currentCamera.setBrightness((int) currentPipeline.settings.brightness); - currentCamera.setExposure((int) currentPipeline.settings.exposure); - } catch (Exception e) { - continue; - } break; } default: { setField(currentPipeline.settings, entry.getKey(), entry.getValue()); - switch (entry.getKey()) { case "exposure": { currentCamera.setExposure((Integer) entry.getValue());