diff --git a/EOCV-Sim/build.gradle b/EOCV-Sim/build.gradle index dc762e68..085973c8 100644 --- a/EOCV-Sim/build.gradle +++ b/EOCV-Sim/build.gradle @@ -1,3 +1,4 @@ +import java.nio.file.Paths import java.time.LocalDateTime import java.time.format.DateTimeFormatter @@ -34,7 +35,9 @@ apply from: '../test-logging.gradle' dependencies { implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8' + implementation 'org.openpnp:opencv:4.3.0-2' + implementation 'com.github.sarxos:webcam-capture:0.3.12' implementation 'com.google.code.gson:gson:2.8.7' implementation 'io.github.classgraph:classgraph:4.8.108' @@ -55,7 +58,7 @@ task(writeBuildClassJava) { String date = DateTimeFormatter.ofPattern("yyyy-M-d hh:mm:ss").format(LocalDateTime.now()) - File versionFile = java.nio.file.Paths.get( + File versionFile = Paths.get( projectDir.absolutePath, 'src', 'main', 'java', 'com', 'github', 'serivesmejia', 'eocvsim', 'Build.java' ).toFile() @@ -79,4 +82,4 @@ task(writeBuildClassJava) { "}" } -build.dependsOn writeBuildClassJava +build.dependsOn writeBuildClassJava \ No newline at end of file diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/Icons.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/Icons.kt index 0d64707a..6f30dbd9 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/Icons.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/Icons.kt @@ -31,7 +31,7 @@ import javax.swing.ImageIcon object Icons { - private val bufferedImages = HashMap() + private val bufferedImages = HashMap() private val icons = HashMap() private val resizedIcons = HashMap() @@ -43,7 +43,7 @@ object Icons { private const val TAG = "Icons" init { - addFutureImage("ico_eocvsim", "/images/icon/ico_eocvsim.png") + addFutureImage("ico_eocvsim", "/images/icon/ico_eocvsim.png", false) addFutureImage("ico_img", "/images/icon/ico_img.png") addFutureImage("ico_cam", "/images/icon/ico_cam.png") @@ -64,7 +64,7 @@ object Icons { for(futureIcon in futureIcons.toTypedArray()) { if(futureIcon.name == name) { Log.info(TAG, "Loading future icon $name") - addImage(futureIcon.name, futureIcon.resourcePath) + addImage(futureIcon.name, futureIcon.resourcePath, futureIcon.allowInvert) futureIcons.remove(futureIcon) } @@ -97,15 +97,17 @@ object Icons { return icon!! } - fun addFutureImage(name: String, path: String) = futureIcons.add(FutureIcon(name, path)) + fun addFutureImage(name: String, path: String, allowInvert: Boolean = true) = futureIcons.add( + FutureIcon(name, path, allowInvert) + ) - fun addImage(name: String, path: String) { + fun addImage(name: String, path: String, allowInvert: Boolean = true) { val buffImg = GuiUtil.loadBufferedImage(path) - if(colorsInverted) { + if(colorsInverted && allowInvert) { GuiUtil.invertBufferedImageColors(buffImg) } - bufferedImages[name] = buffImg + bufferedImages[name] = Image(buffImg, allowInvert) icons[name] = ImageIcon(buffImg) } @@ -124,11 +126,15 @@ object Icons { } private fun invertAll() { - for((_, img) in bufferedImages) { - GuiUtil.invertBufferedImageColors(img) + for((_, image) in bufferedImages) { + if(image.allowInvert) { + GuiUtil.invertBufferedImageColors(image.img) + } } } - data class FutureIcon(val name: String, val resourcePath: String) + data class Image(val img: BufferedImage, val allowInvert: Boolean) + + data class FutureIcon(val name: String, val resourcePath: String, val allowInvert: Boolean) -} +} \ No newline at end of file diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/Visualizer.java b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/Visualizer.java index bdc802b1..732b6f61 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/Visualizer.java +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/Visualizer.java @@ -148,26 +148,25 @@ public void init(Theme theme) { imgScrollPane.getHorizontalScrollBar().setUnitIncrement(16); imgScrollPane.getVerticalScrollBar().setUnitIncrement(16); - rightContainer.setLayout(new BorderLayout()); - + rightContainer.setLayout(new BoxLayout(rightContainer, BoxLayout.Y_AXIS)); /* * PIPELINE SELECTOR */ pipelineSelectorPanel.setBorder(new EmptyBorder(0, 20, 0, 20)); - rightContainer.add(pipelineSelectorPanel, BorderLayout.NORTH); + rightContainer.add(pipelineSelectorPanel); /* * SOURCE SELECTOR */ sourceSelectorPanel.setBorder(new EmptyBorder(0, 20, 0, 20)); - rightContainer.add(sourceSelectorPanel, BorderLayout.CENTER); + rightContainer.add(sourceSelectorPanel); /* * TELEMETRY */ telemetryPanel.setBorder(new EmptyBorder(0, 20, 20, 20)); - rightContainer.add(telemetryPanel, BorderLayout.SOUTH); + rightContainer.add(telemetryPanel); /* * SPLIT @@ -269,24 +268,17 @@ public void mouseClicked(MouseEvent e) { frame.addComponentListener(new ComponentAdapter() { @Override public void componentResized(ComponentEvent evt) { - double ratio = frame.getSize().getHeight() / 645; - - double fontSize = 15.5 * ratio; - int columns = (int) Math.round(5 * ratio); + double ratioH = frame.getSize().getHeight() / 645; + double fontSize = 17 * ratioH; Font font = pipelineSelectorPanel.getPipelineSelectorLabel().getFont().deriveFont((float)fontSize); - pipelineSelectorPanel.getPipelineSelector().setVisibleRowCount(columns); pipelineSelectorPanel.getPipelineSelectorLabel().setFont(font); pipelineSelectorPanel.revalAndRepaint(); - columns = (int) Math.round(5 * ratio); - - sourceSelectorPanel.getSourceSelector().setVisibleRowCount(columns); sourceSelectorPanel.getSourceSelectorLabel().setFont(font); sourceSelectorPanel.revalAndRepaint(); - telemetryPanel.getTelemetryList().setVisibleRowCount(columns); telemetryPanel.getTelemetryLabel().setFont(font); telemetryPanel.revalAndRepaint(); @@ -295,8 +287,9 @@ public void componentResized(ComponentEvent evt) { } }); - //stop color-picking mode when changing pipeline - //eocvSim.pipelineManager.onPipelineChange.doPersistent(() -> colorPicker.stopPicking()); + // stop color-picking mode when changing pipeline + // TODO: find out why this breaks everything????? + // eocvSim.pipelineManager.onPipelineChange.doPersistent(() -> colorPicker.stopPicking()); } public boolean hasFinishedInit() { return hasFinishedInitializing; } diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/tuner/TunableFieldPanel.java b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/tuner/TunableFieldPanel.java index 1a254208..71efcf45 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/tuner/TunableFieldPanel.java +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/tuner/TunableFieldPanel.java @@ -139,7 +139,15 @@ public void showFieldPanel() { public void setFieldValue(int index, Object value) { if(index >= fields.length) return; - fields[index].setText(value.toString()); + String text; + if(tunableField.getAllowMode() == TunableField.AllowMode.ONLY_NUMBERS) { + text = String.valueOf((int) Math.round(Double.parseDouble(value.toString()))); + } else { + text = value.toString(); + } + + fields[index].setText(text); + try { sliders[index].setScaledValue(Double.parseDouble(value.toString())); } catch(NumberFormatException ignored) {} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/Configuration.java b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/Configuration.java index 867b0751..f8883d67 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/Configuration.java +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/Configuration.java @@ -66,7 +66,7 @@ private void initConfiguration() { Config config = this.eocvSim.configManager.getConfig(); configuration.setModal(true); configuration.setTitle("Settings"); - configuration.setSize(350, 300); + configuration.setSize(350, 320); JPanel themePanel = new JPanel(new FlowLayout()); JLabel themeLabel = new JLabel("Theme: "); diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateCameraSource.java b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateCameraSource.java index 4626bd23..e946b79d 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateCameraSource.java +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateCameraSource.java @@ -23,9 +23,9 @@ package com.github.serivesmejia.eocvsim.gui.dialog.source; +import com.github.sarxos.webcam.Webcam; import com.github.serivesmejia.eocvsim.EOCVSim; import com.github.serivesmejia.eocvsim.gui.component.input.SizeFields; -import com.github.serivesmejia.eocvsim.gui.util.GuiUtil; import com.github.serivesmejia.eocvsim.input.source.CameraSource; import com.github.serivesmejia.eocvsim.util.CvUtil; import com.github.serivesmejia.eocvsim.util.Log; @@ -42,20 +42,17 @@ public class CreateCameraSource { public JDialog createCameraSource = null; - public JTextField cameraIdField = null; - public JButton createButton = null; - + public JComboBox camerasComboBox = null; public SizeFields sizeFieldsInput = null; - public JTextField nameTextField = null; + public JButton createButton = null; + public boolean wasCancelled = false; - private boolean validCameraIdNumber = true; private final EOCVSim eocvSim; private State state = State.INITIAL; - private int camId = 0; JLabel statusLabel = new JLabel(); @@ -71,25 +68,30 @@ public CreateCameraSource(JFrame parent, EOCVSim eocvSim) { } public void initCreateImageSource() { + java.util.List webcams = Webcam.getWebcams(); createCameraSource.setModal(true); createCameraSource.setTitle("Create camera source"); - createCameraSource.setSize(350, 230); + createCameraSource.setSize(350, 250); JPanel contentsPanel = new JPanel(new GridLayout(5, 1)); // Camera id part - JPanel idPanel = new JPanel(new FlowLayout()); - JLabel idLabel = new JLabel("Camera Index: "); - idLabel.setHorizontalAlignment(JLabel.CENTER); + JLabel idLabel = new JLabel("Camera: "); + idLabel.setHorizontalAlignment(JLabel.LEFT); - cameraIdField = new JTextField("0", 4); + camerasComboBox = new JComboBox<>(); + for(Webcam webcam : webcams) { + camerasComboBox.addItem(webcam.getName()); + } + + SwingUtilities.invokeLater(() -> camerasComboBox.setSelectedIndex(0)); idPanel.add(idLabel); - idPanel.add(cameraIdField); + idPanel.add(camerasComboBox); contentsPanel.add(idPanel); @@ -131,27 +133,25 @@ public void initCreateImageSource() { contentsPanel.add(buttonsPanel); //Add contents - contentsPanel.setBorder(BorderFactory.createEmptyBorder(15, 0, 0, 0)); createCameraSource.getContentPane().add(contentsPanel, BorderLayout.CENTER); // Additional stuff & events - - GuiUtil.jTextFieldOnlyNumbers(cameraIdField, -100, 0); - createButton.addActionListener(e -> { if(state == State.TEST_SUCCESSFUL) { - createSource(nameTextField.getText(), camId, sizeFieldsInput.getCurrentSize()); + createSource( + nameTextField.getText(), + camerasComboBox.getSelectedIndex(), + sizeFieldsInput.getCurrentSize() + ); close(); } else { - camId = Integer.parseInt(cameraIdField.getText()); - state = State.CLICKED_TEST; updateState(); eocvSim.onMainUpdate.doOnce(() -> { - if (testCamera(camId)) { + if (testCamera(camerasComboBox.getSelectedIndex())) { if (wasCancelled) return; SwingUtilities.invokeLater(() -> { @@ -168,32 +168,14 @@ public void initCreateImageSource() { } }); - cameraIdField.getDocument().addDocumentListener(new DocumentListener() { - public void changedUpdate(DocumentEvent e) { - changed(); - } - public void removeUpdate(DocumentEvent e) { - changed(); - } - public void insertUpdate(DocumentEvent e) { - changed(); + camerasComboBox.addActionListener((e) -> { + String sourceName = (String)camerasComboBox.getSelectedItem(); + if(!eocvSim.inputSourceManager.isNameOnUse(sourceName)) { + nameTextField.setText(sourceName); } - public void changed() { - try { - Integer.parseInt(cameraIdField.getText()); - - String sourceName = "Camera " + cameraIdField.getText(); - if(!eocvSim.inputSourceManager.isNameOnUse(sourceName)) { - nameTextField.setText(sourceName); - } - - validCameraIdNumber = true; - } catch (Exception ex) { - validCameraIdNumber = false; - } - updateCreateBtt(); - } + state = State.INITIAL; + updateCreateBtt(); }); nameTextField.getDocument().addDocumentListener(new DocumentListener() { @@ -209,8 +191,6 @@ public void changed() { } }); - SwingUtilities.invokeLater(() -> cameraIdField.setText("0")); - cancelButton.addActionListener(e -> { wasCancelled = true; close(); @@ -228,7 +208,7 @@ public void close() { public boolean testCamera(int camIndex) { VideoCapture camera = new VideoCapture(); - camera.open(camIndex); + camera.open(camerasComboBox.getSelectedIndex()); boolean wasOpened = camera.isOpened(); @@ -264,21 +244,21 @@ private void updateState() { case CLICKED_TEST: statusLabel.setText("Trying to open camera, please wait..."); - cameraIdField.setEditable(false); + camerasComboBox.setEnabled(false); createButton.setEnabled(false); break; case TEST_SUCCESSFUL: - cameraIdField.setEditable(true); + camerasComboBox.setEnabled(true); createButton.setEnabled(true); statusLabel.setText("Camera was opened successfully."); createButton.setText("Create"); break; case TEST_FAILED: - cameraIdField.setEditable(true); + camerasComboBox.setEnabled(true); createButton.setEnabled(true); - statusLabel.setText("Failed to open camera, try with another index."); + statusLabel.setText("Failed to open camera, try another one."); createButton.setText("Test"); break; } @@ -293,7 +273,6 @@ public void createSource(String sourceName, int index, Size size) { public void updateCreateBtt() { createButton.setEnabled(!nameTextField.getText().trim().equals("") - && validCameraIdNumber && sizeFieldsInput.getValid() && !eocvSim.inputSourceManager.isNameOnUse(nameTextField.getText())); diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/PipelineManager.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/PipelineManager.kt index 24fef625..e0c3b237 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/PipelineManager.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/PipelineManager.kt @@ -383,8 +383,8 @@ class PipelineManager(var eocvSim: EOCVSim) { captureSnapshot() - var nextPipeline: OpenCvPipeline? = null - var nextTelemetry: Telemetry? = null + var nextPipeline: OpenCvPipeline? + var nextTelemetry: Telemetry? val pipelineClass = pipelines[index].clazz Log.info(TAG, "Changing to pipeline " + pipelineClass.name) @@ -394,30 +394,29 @@ class PipelineManager(var eocvSim: EOCVSim) { try { nextTelemetry = Telemetry() - try { //instantiate pipeline if it has a constructor with a telemetry parameter + try { //instantiate pipeline if it has a constructor of a telemetry parameter constructor = pipelineClass.getConstructor(Telemetry::class.java) nextPipeline = constructor.newInstance(nextTelemetry) as OpenCvPipeline - } catch (ex: NoSuchMethodException) { //instantiating with a constructor with no params + } catch (ex: NoSuchMethodException) { //instantiating with a constructor of no params constructor = pipelineClass.getConstructor() nextPipeline = constructor.newInstance() as OpenCvPipeline } Log.info(TAG, "Instantiated pipeline class " + pipelineClass.name) } catch (ex: NoSuchMethodException) { - eocvSim.visualizer.asyncPleaseWaitDialog("Error while instantiating requested pipeline", "Check console for details", - "Close", Dimension(300, 150), true, true) - - Log.error(TAG, "Error while instantiating requested pipeline (" + pipelineClass.simpleName + ")", ex) - Log.info(TAG, "Make sure your pipeline implements a public constructor with no parameters or with a Telemetry parameter") + pipelineExceptionTracker.addMessage("Error while instantiating requested pipeline, \"${pipelineClass.simpleName}\". Falling back to previous one.") + pipelineExceptionTracker.addMessage("Make sure your pipeline implements a public constructor with no parameters or a Telemetry parameter.") eocvSim.visualizer.pipelineSelectorPanel.selectedIndex = currentPipelineIndex + Log.error(TAG, "Error while instantiating requested pipeline, ${pipelineClass.simpleName} (usable constructor missing)", ex) Log.blank() + return } catch (ex: Exception) { - eocvSim.visualizer.asyncPleaseWaitDialog("Error while instantiating requested pipeline", "Falling back to previous one", - "Close", Dimension(300, 150), true, true) + pipelineExceptionTracker.addMessage("Error while instantiating requested pipeline, \"${pipelineClass.simpleName}\". Falling back to previous one.") + updateExceptionTracker(ex) - Log.error(TAG, "Error while instantiating requested pipeline (" + pipelineClass.simpleName + ")", ex) + Log.error(TAG, "Error while instantiating requested pipeline, ${pipelineClass.simpleName} (unknown issue)", ex) Log.blank() eocvSim.visualizer.pipelineSelectorPanel.selectedIndex = currentPipelineIndex diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/util/PipelineExceptionTracker.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/util/PipelineExceptionTracker.kt index 1dc2c381..c763c03f 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/util/PipelineExceptionTracker.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/util/PipelineExceptionTracker.kt @@ -41,6 +41,7 @@ class PipelineExceptionTracker(private val pipelineManager: PipelineManager) { private set val exceptionsThrown = mutableMapOf() + val messages = mutableMapOf() val onPipelineException = EventHandler("OnPipelineException") val onNewPipelineException = EventHandler("OnNewPipelineException") @@ -101,6 +102,13 @@ class PipelineExceptionTracker(private val pipelineManager: PipelineManager) { } } + for((message, millisAdded) in messages.entries.toTypedArray()) { + val timeElapsed = System.currentTimeMillis() - millisAdded + if(timeElapsed >= millisExceptionExpire) { + messages.remove(message) + } + } + onUpdate.run() } @@ -116,8 +124,6 @@ class PipelineExceptionTracker(private val pipelineManager: PipelineManager) { .append("**Pipeline $pipelineName is throwing ${exceptionsThrown.size} exception(s)**") .appendLine("\n") } else { - - messageBuilder.append("**Pipeline $pipelineName ") if(pipelineManager.paused) { @@ -144,11 +150,20 @@ class PipelineExceptionTracker(private val pipelineManager: PipelineManager) { .appendLine() } + for((message, _) in messages) { + messageBuilder.appendLine(message) + } + return messageBuilder.toString().trim() } fun clear() = exceptionsThrown.clear() + fun addMessage(s: String) { + messages[s] = System.currentTimeMillis() + onNewPipelineException.run() + } + data class PipelineException(var count: Int, val stacktrace: String, var millisThrown: Long) diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/util/PipelineSnapshot.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/util/PipelineSnapshot.kt index 79608c83..c37810fe 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/util/PipelineSnapshot.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/util/PipelineSnapshot.kt @@ -121,10 +121,7 @@ class PipelineSnapshot(holdingPipeline: OpenCvPipeline) { val (otherField, otherValue) = pipelineSnapshotB.getField(field.name) ?: continue if (field.type != otherField.type) continue - println("comparing $value (${field.type}) to $otherValue (${otherField.type})") - if(otherValue != value) { - println("field $field changed") changedList.add(field) } }