fuck this shit ; i wont fucking maintain this anymore
This commit is contained in:
@@ -7,6 +7,7 @@ import org.opencv.dnn.Net;
|
||||
import org.opencv.imgcodecs.Imgcodecs;
|
||||
import org.opencv.imgproc.Imgproc;
|
||||
import org.opencv.videoio.VideoCapture;
|
||||
import org.opencv.videoio.VideoWriter;
|
||||
import org.opencv.videoio.Videoio;
|
||||
|
||||
import javax.swing.*;
|
||||
@@ -17,15 +18,15 @@ import java.awt.event.WindowAdapter;
|
||||
import java.awt.event.WindowEvent;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.DataBufferByte;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.FileReader;
|
||||
import java.io.InputStream;
|
||||
import java.io.*;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
@@ -106,29 +107,6 @@ class DownloadProgressBar extends JDialog {
|
||||
}
|
||||
}
|
||||
|
||||
// Download progress handler class
|
||||
class DownloadProgressHandler {
|
||||
private DownloadProgressBar progressDialog;
|
||||
private AtomicLong currentSize = new AtomicLong(0);
|
||||
private AtomicLong totalSize = new AtomicLong(0);
|
||||
private AtomicLong lastUpdate = new AtomicLong(0);
|
||||
|
||||
public DownloadProgressHandler(DownloadProgressBar progressDialog) {
|
||||
this.progressDialog = progressDialog;
|
||||
}
|
||||
|
||||
public void handleProgress(long current, long total) {
|
||||
this.totalSize.set(total);
|
||||
this.currentSize.set(current);
|
||||
|
||||
long currentTime = System.currentTimeMillis();
|
||||
if (currentTime - lastUpdate.get() > 100) { // Update every 100ms
|
||||
progressDialog.updateProgress(current, total);
|
||||
lastUpdate.set(currentTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Detection thread class
|
||||
class DetectionThread extends Thread {
|
||||
private Net net;
|
||||
@@ -258,6 +236,10 @@ public class CameraApp extends JFrame {
|
||||
private JButton stopButton;
|
||||
private JButton snapshotButton;
|
||||
private JLabel statusLabel;
|
||||
private JPanel cameraControlPanel;
|
||||
private JSlider brightnessSlider;
|
||||
private JSlider contrastSlider;
|
||||
private JSlider saturationSlider;
|
||||
|
||||
// Camera and detection attributes
|
||||
private int cameraIndex = 0;
|
||||
@@ -303,7 +285,7 @@ public class CameraApp extends JFrame {
|
||||
|
||||
private void initUI() {
|
||||
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||
setSize(1000, 800);
|
||||
setSize(1200, 900);
|
||||
setLocationRelativeTo(null);
|
||||
|
||||
// Main panel
|
||||
@@ -314,13 +296,16 @@ public class CameraApp extends JFrame {
|
||||
videoLabel = new JLabel();
|
||||
videoLabel.setHorizontalAlignment(JLabel.CENTER);
|
||||
videoLabel.setVerticalAlignment(JLabel.CENTER);
|
||||
videoLabel.setPreferredSize(new Dimension(640, 480));
|
||||
videoLabel.setPreferredSize(new Dimension(800, 600));
|
||||
videoLabel.setBorder(BorderFactory.createLoweredBevelBorder());
|
||||
videoLabel.setText("Camera feed will appear here");
|
||||
mainPanel.add(videoLabel, BorderLayout.CENTER);
|
||||
|
||||
// Control panel
|
||||
JPanel controlPanel = new JPanel(new GridBagLayout());
|
||||
JPanel controlPanel = new JPanel(new BorderLayout());
|
||||
|
||||
// Basic controls
|
||||
JPanel basicControlPanel = new JPanel(new GridBagLayout());
|
||||
GridBagConstraints gbc = new GridBagConstraints();
|
||||
gbc.insets = new Insets(5, 5, 5, 5);
|
||||
gbc.fill = GridBagConstraints.HORIZONTAL;
|
||||
@@ -329,30 +314,36 @@ public class CameraApp extends JFrame {
|
||||
gbc.gridx = 0; gbc.gridy = 0; gbc.gridwidth = 2;
|
||||
cameraSelect = new JComboBox<>();
|
||||
cameraSelect.addActionListener(this::changeCamera);
|
||||
controlPanel.add(cameraSelect, gbc);
|
||||
basicControlPanel.add(cameraSelect, gbc);
|
||||
|
||||
// Detection checkbox
|
||||
gbc.gridx = 0; gbc.gridy = 1; gbc.gridwidth = 2;
|
||||
detectionCheckbox = new JCheckBox("Enable Object Detection", true);
|
||||
detectionCheckbox.addActionListener(this::toggleDetection);
|
||||
controlPanel.add(detectionCheckbox, gbc);
|
||||
basicControlPanel.add(detectionCheckbox, gbc);
|
||||
|
||||
// Buttons
|
||||
gbc.gridwidth = 1;
|
||||
gbc.gridx = 0; gbc.gridy = 2;
|
||||
startButton = new JButton("Start Camera");
|
||||
startButton.addActionListener(e -> startCamera());
|
||||
controlPanel.add(startButton, gbc);
|
||||
basicControlPanel.add(startButton, gbc);
|
||||
|
||||
gbc.gridx = 1; gbc.gridy = 2;
|
||||
stopButton = new JButton("Stop Camera");
|
||||
stopButton.addActionListener(e -> stopCamera());
|
||||
controlPanel.add(stopButton, gbc);
|
||||
basicControlPanel.add(stopButton, gbc);
|
||||
|
||||
gbc.gridx = 0; gbc.gridy = 3; gbc.gridwidth = 2;
|
||||
snapshotButton = new JButton("Take Snapshot");
|
||||
snapshotButton.addActionListener(e -> takeSnapshot());
|
||||
controlPanel.add(snapshotButton, gbc);
|
||||
basicControlPanel.add(snapshotButton, gbc);
|
||||
|
||||
controlPanel.add(basicControlPanel, BorderLayout.NORTH);
|
||||
|
||||
// Camera controls panel for C920
|
||||
cameraControlPanel = createCameraControlPanel();
|
||||
controlPanel.add(cameraControlPanel, BorderLayout.CENTER);
|
||||
|
||||
mainPanel.add(controlPanel, BorderLayout.EAST);
|
||||
|
||||
@@ -385,6 +376,82 @@ public class CameraApp extends JFrame {
|
||||
});
|
||||
}
|
||||
|
||||
private JPanel createCameraControlPanel() {
|
||||
JPanel panel = new JPanel(new GridBagLayout());
|
||||
panel.setBorder(BorderFactory.createTitledBorder("Camera Controls"));
|
||||
GridBagConstraints gbc = new GridBagConstraints();
|
||||
gbc.insets = new Insets(5, 5, 5, 5);
|
||||
gbc.fill = GridBagConstraints.HORIZONTAL;
|
||||
|
||||
// Brightness control
|
||||
gbc.gridx = 0; gbc.gridy = 0;
|
||||
panel.add(new JLabel("Brightness:"), gbc);
|
||||
gbc.gridx = 1; gbc.gridy = 0;
|
||||
brightnessSlider = new JSlider(-100, 100, 0);
|
||||
brightnessSlider.addChangeListener(e -> adjustCameraSettings());
|
||||
panel.add(brightnessSlider, gbc);
|
||||
|
||||
// Contrast control
|
||||
gbc.gridx = 0; gbc.gridy = 1;
|
||||
panel.add(new JLabel("Contrast:"), gbc);
|
||||
gbc.gridx = 1; gbc.gridy = 1;
|
||||
contrastSlider = new JSlider(0, 200, 100);
|
||||
contrastSlider.addChangeListener(e -> adjustCameraSettings());
|
||||
panel.add(contrastSlider, gbc);
|
||||
|
||||
// Saturation control
|
||||
gbc.gridx = 0; gbc.gridy = 2;
|
||||
panel.add(new JLabel("Saturation:"), gbc);
|
||||
gbc.gridx = 1; gbc.gridy = 2;
|
||||
saturationSlider = new JSlider(0, 200, 100);
|
||||
saturationSlider.addChangeListener(e -> adjustCameraSettings());
|
||||
panel.add(saturationSlider, gbc);
|
||||
|
||||
// Reset button
|
||||
gbc.gridx = 0; gbc.gridy = 3; gbc.gridwidth = 2;
|
||||
JButton resetButton = new JButton("Reset to Default");
|
||||
resetButton.addActionListener(e -> resetCameraSettings());
|
||||
panel.add(resetButton, gbc);
|
||||
|
||||
return panel;
|
||||
}
|
||||
|
||||
private void adjustCameraSettings() {
|
||||
if (cap == null || !cap.isOpened()) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Apply brightness (-100 to 100, normalized to 0.0-1.0)
|
||||
double brightness = (brightnessSlider.getValue() + 100) / 200.0;
|
||||
cap.set(Videoio.CAP_PROP_BRIGHTNESS, brightness);
|
||||
|
||||
// Apply contrast (0 to 200, normalized to 0.0-2.0)
|
||||
double contrast = contrastSlider.getValue() / 100.0;
|
||||
cap.set(Videoio.CAP_PROP_CONTRAST, contrast);
|
||||
|
||||
// Apply saturation (0 to 200, normalized to 0.0-2.0)
|
||||
double saturation = saturationSlider.getValue() / 100.0;
|
||||
cap.set(Videoio.CAP_PROP_SATURATION, saturation);
|
||||
|
||||
// Additional C920 specific settings
|
||||
cap.set(Videoio.CAP_PROP_AUTO_WB, 1); // Enable auto white balance
|
||||
cap.set(Videoio.CAP_PROP_EXPOSURE, -5); // Set exposure for better indoor lighting
|
||||
|
||||
statusLabel.setText("Camera settings adjusted");
|
||||
|
||||
} catch (Exception e) {
|
||||
System.out.println("Warning: Could not adjust all camera settings: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void resetCameraSettings() {
|
||||
brightnessSlider.setValue(0);
|
||||
contrastSlider.setValue(100);
|
||||
saturationSlider.setValue(100);
|
||||
adjustCameraSettings();
|
||||
}
|
||||
|
||||
private void toggleDetection(ActionEvent e) {
|
||||
detectionEnabled.set(detectionCheckbox.isSelected());
|
||||
statusLabel.setText("Detection " + (detectionEnabled.get() ? "enabled" : "disabled"));
|
||||
@@ -461,7 +528,13 @@ public class CameraApp extends JFrame {
|
||||
selected = availableCameras.get(0);
|
||||
}
|
||||
|
||||
cap = new VideoCapture(selected.index, selected.backend);
|
||||
// Try to open with default backend first
|
||||
cap = new VideoCapture(selected.index);
|
||||
if (!cap.isOpened()) {
|
||||
// Fall back to selected backend if default fails
|
||||
cap = new VideoCapture(selected.index, selected.backend);
|
||||
}
|
||||
|
||||
if (!cap.isOpened()) {
|
||||
JOptionPane.showMessageDialog(this,
|
||||
String.format("Cannot open camera %d", selected.index),
|
||||
@@ -470,14 +543,37 @@ public class CameraApp extends JFrame {
|
||||
return;
|
||||
}
|
||||
|
||||
// Set camera properties
|
||||
cap.set(Videoio.CAP_PROP_FRAME_WIDTH, 1280);
|
||||
cap.set(Videoio.CAP_PROP_FRAME_HEIGHT, 720);
|
||||
cap.set(Videoio.CAP_PROP_FPS, 60);
|
||||
cap.set(Videoio.CAP_PROP_AUTOFOCUS, 0);
|
||||
// Enhanced camera settings for C920
|
||||
try {
|
||||
// Set resolution - C920 supports up to 1920x1080
|
||||
cap.set(Videoio.CAP_PROP_FRAME_WIDTH, 1280);
|
||||
cap.set(Videoio.CAP_PROP_FRAME_HEIGHT, 720);
|
||||
cap.set(Videoio.CAP_PROP_FPS, 30);
|
||||
|
||||
// C920 specific optimizations
|
||||
cap.set(Videoio.CAP_PROP_FOURCC, VideoWriter.fourcc('M', 'J', 'P', 'G'));
|
||||
cap.set(Videoio.CAP_PROP_AUTOFOCUS, 1); // Enable autofocus
|
||||
cap.set(Videoio.CAP_PROP_AUTO_WB, 1); // Auto white balance
|
||||
cap.set(Videoio.CAP_PROP_AUTO_EXPOSURE, 0.25); // Some auto exposure
|
||||
cap.set(Videoio.CAP_PROP_EXPOSURE, -5); // Manual exposure for indoor
|
||||
cap.set(Videoio.CAP_PROP_GAIN, 0); // Minimize gain to reduce noise
|
||||
|
||||
// Apply current slider settings
|
||||
adjustCameraSettings();
|
||||
|
||||
} catch (Exception e) {
|
||||
System.out.println("Warning: Could not set all camera properties: " + e.getMessage());
|
||||
}
|
||||
|
||||
// Get actual settings that were applied
|
||||
double actualWidth = cap.get(Videoio.CAP_PROP_FRAME_WIDTH);
|
||||
double actualHeight = cap.get(Videoio.CAP_PROP_FRAME_HEIGHT);
|
||||
double actualFps = cap.get(Videoio.CAP_PROP_FPS);
|
||||
|
||||
statusLabel.setText(String.format("Camera %d started (%dx%d @ %.1f FPS)",
|
||||
selected.index, (int)actualWidth, (int)actualHeight, actualFps));
|
||||
|
||||
timer.start();
|
||||
statusLabel.setText(String.format("Camera %d started", selected.index));
|
||||
|
||||
} catch (Exception ex) {
|
||||
JOptionPane.showMessageDialog(this,
|
||||
@@ -503,11 +599,161 @@ public class CameraApp extends JFrame {
|
||||
}
|
||||
}
|
||||
|
||||
private void loadYolo() {
|
||||
String weightsUrl = "https://github.com/AlexeyAB/darknet/releases/download/darknet_yolo_v4_pre/yolov4-tiny.weights";
|
||||
String configUrl = "https://raw.githubusercontent.com/AlexeyAB/darknet/master/cfg/yolov4-tiny.cfg";
|
||||
String classesUrl = "https://raw.githubusercontent.com/AlexeyAB/darknet/master/data/coco.names";
|
||||
private void updateFrame(ActionEvent e) {
|
||||
if (cap == null || !cap.isOpened()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Mat frame = new Mat();
|
||||
if (cap.read(frame) && !frame.empty()) {
|
||||
// Update FPS counter
|
||||
frameCount++;
|
||||
long currentTime = System.currentTimeMillis();
|
||||
if (currentTime - lastFpsUpdate >= 1000) {
|
||||
fps = frameCount * 1000.0 / (currentTime - lastFpsUpdate);
|
||||
frameCount = 0;
|
||||
lastFpsUpdate = currentTime;
|
||||
}
|
||||
|
||||
// Process frame with detection if enabled
|
||||
if (detectionEnabled.get() && modelLoaded.get()) {
|
||||
processWithDetection(frame);
|
||||
} else {
|
||||
displayFrame(frame);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void processWithDetection(Mat frame) {
|
||||
// Stop previous detection if still running
|
||||
if (detectionThread != null && detectionThread.isAlive()) {
|
||||
detectionThread.stopDetection();
|
||||
}
|
||||
|
||||
// Start new detection
|
||||
detectionThread = new DetectionThread(net, classes, outputLayers, frame);
|
||||
detectionThread.start();
|
||||
|
||||
// Get result asynchronously
|
||||
detectionThread.getDetectionResult().thenAccept(this::displayFrame);
|
||||
}
|
||||
|
||||
private void displayFrame(Mat frame) {
|
||||
if (frame == null || frame.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Add FPS text to frame
|
||||
String fpsText = String.format("FPS: %.1f", fps);
|
||||
Point textPos = new Point(10, 30);
|
||||
Imgproc.putText(frame, fpsText, textPos, Imgproc.FONT_HERSHEY_SIMPLEX, 1, new Scalar(0, 255, 0), 2);
|
||||
|
||||
// Convert Mat to BufferedImage
|
||||
BufferedImage image = matToBufferedImage(frame);
|
||||
|
||||
// Scale image to fit label while maintaining aspect ratio
|
||||
Dimension labelSize = videoLabel.getSize();
|
||||
if (labelSize.width > 0 && labelSize.height > 0) {
|
||||
Image scaledImage = image.getScaledInstance(labelSize.width, labelSize.height, Image.SCALE_SMOOTH);
|
||||
ImageIcon icon = new ImageIcon(scaledImage);
|
||||
|
||||
SwingUtilities.invokeLater(() -> videoLabel.setIcon(icon));
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
System.out.println("Error displaying frame: " + ex.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private BufferedImage matToBufferedImage(Mat mat) {
|
||||
int type = BufferedImage.TYPE_BYTE_GRAY;
|
||||
if (mat.channels() > 1) {
|
||||
type = BufferedImage.TYPE_3BYTE_BGR;
|
||||
}
|
||||
|
||||
int bufferSize = mat.channels() * mat.cols() * mat.rows();
|
||||
byte[] buffer = new byte[bufferSize];
|
||||
mat.get(0, 0, buffer); // get all pixels
|
||||
|
||||
BufferedImage image = new BufferedImage(mat.cols(), mat.rows(), type);
|
||||
final byte[] targetPixels = ((DataBufferByte) image.getRaster().getDataBuffer()).getData();
|
||||
System.arraycopy(buffer, 0, targetPixels, 0, buffer.length);
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
private void takeSnapshot() {
|
||||
if (cap == null || !cap.isOpened()) {
|
||||
JOptionPane.showMessageDialog(this, "Camera is not active", "Error", JOptionPane.ERROR_MESSAGE);
|
||||
return;
|
||||
}
|
||||
|
||||
Mat frame = new Mat();
|
||||
if (cap.read(frame) && !frame.empty()) {
|
||||
try {
|
||||
// Create snapshots directory if it doesn't exist
|
||||
File snapshotDir = new File("snapshots");
|
||||
if (!snapshotDir.exists()) {
|
||||
snapshotDir.mkdirs();
|
||||
}
|
||||
|
||||
// Generate filename with timestamp
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss");
|
||||
String timestamp = sdf.format(new Date());
|
||||
String filename = String.format("snapshots/snapshot_%s.jpg", timestamp);
|
||||
|
||||
// Save image
|
||||
Imgcodecs.imwrite(filename, frame);
|
||||
statusLabel.setText("Snapshot saved: " + filename);
|
||||
|
||||
JOptionPane.showMessageDialog(this,
|
||||
"Snapshot saved successfully!\n" + filename,
|
||||
"Snapshot", JOptionPane.INFORMATION_MESSAGE);
|
||||
|
||||
} catch (Exception ex) {
|
||||
JOptionPane.showMessageDialog(this,
|
||||
"Error saving snapshot: " + ex.getMessage(),
|
||||
"Error", JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean loadYoloFromResources() {
|
||||
try {
|
||||
// Check if files exist in resources
|
||||
InputStream weightsStream = getClass().getResourceAsStream("/yolov4-tiny.weights");
|
||||
InputStream configStream = getClass().getResourceAsStream("/yolov4-tiny.cfg");
|
||||
InputStream classesStream = getClass().getResourceAsStream("/coco.names");
|
||||
|
||||
if (weightsStream != null && configStream != null && classesStream != null) {
|
||||
statusLabel.setText("Loading YOLO from resources...");
|
||||
|
||||
// Copy files from resources to working directory
|
||||
Files.copy(weightsStream, Paths.get("yolov4-tiny.weights"), StandardCopyOption.REPLACE_EXISTING);
|
||||
Files.copy(configStream, Paths.get("yolov4-tiny.cfg"), StandardCopyOption.REPLACE_EXISTING);
|
||||
Files.copy(classesStream, Paths.get("coco.names"), StandardCopyOption.REPLACE_EXISTING);
|
||||
|
||||
weightsStream.close();
|
||||
configStream.close();
|
||||
classesStream.close();
|
||||
|
||||
statusLabel.setText("YOLO files loaded from resources");
|
||||
return true;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.out.println("Could not load YOLO from resources: " + e.getMessage());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void loadYolo() {
|
||||
// First try to load from resources
|
||||
if (loadYoloFromResources()) {
|
||||
initializeYoloModel();
|
||||
return;
|
||||
}
|
||||
|
||||
// If not in resources, check existing files
|
||||
String weightsPath = "yolov4-tiny.weights";
|
||||
String configPath = "yolov4-tiny.cfg";
|
||||
String classesPath = "coco.names";
|
||||
@@ -525,28 +771,45 @@ public class CameraApp extends JFrame {
|
||||
filesValid = false;
|
||||
}
|
||||
|
||||
if (!filesValid) {
|
||||
int reply = JOptionPane.showConfirmDialog(this,
|
||||
"YOLO model files need to be downloaded (~25MB). Continue?",
|
||||
"Download Files",
|
||||
JOptionPane.YES_NO_OPTION);
|
||||
|
||||
if (reply != JOptionPane.YES_OPTION) {
|
||||
statusLabel.setText("Object detection disabled - model not loaded");
|
||||
return;
|
||||
}
|
||||
|
||||
downloadYoloFiles(weightsUrl, configUrl, classesUrl);
|
||||
if (filesValid) {
|
||||
initializeYoloModel();
|
||||
return;
|
||||
}
|
||||
|
||||
// If files don't exist, offer to download
|
||||
int reply = JOptionPane.showConfirmDialog(this,
|
||||
"YOLO model files need to be downloaded (~25MB). Continue?",
|
||||
"Download Files",
|
||||
JOptionPane.YES_NO_OPTION);
|
||||
|
||||
if (reply != JOptionPane.YES_OPTION) {
|
||||
statusLabel.setText("Object detection disabled - model not loaded");
|
||||
detectionCheckbox.setSelected(false);
|
||||
detectionEnabled.set(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Download files
|
||||
String weightsUrl = "https://github.com/AlexeyAB/darknet/releases/download/darknet_yolo_v4_pre/yolov4-tiny.weights";
|
||||
String configUrl = "https://raw.githubusercontent.com/AlexeyAB/darknet/master/cfg/yolov4-tiny.cfg";
|
||||
String classesUrl = "https://raw.githubusercontent.com/AlexeyAB/darknet/master/data/coco.names";
|
||||
|
||||
downloadYoloFiles(weightsUrl, configUrl, classesUrl);
|
||||
}
|
||||
|
||||
private void initializeYoloModel() {
|
||||
try {
|
||||
String weightsPath = "yolov4-tiny.weights";
|
||||
String configPath = "yolov4-tiny.cfg";
|
||||
String classesPath = "coco.names";
|
||||
|
||||
net = Dnn.readNetFromDarknet(configPath, weightsPath);
|
||||
|
||||
// Try to use CUDA if available (OpenCV Java bindings may not support this fully)
|
||||
// Try to use CUDA if available
|
||||
try {
|
||||
net.setPreferableBackend(Dnn.DNN_BACKEND_CUDA);
|
||||
net.setPreferableTarget(Dnn.DNN_TARGET_CUDA);
|
||||
statusLabel.setText("Attempting CUDA acceleration");
|
||||
statusLabel.setText("Using CUDA acceleration");
|
||||
} catch (Exception e) {
|
||||
net.setPreferableBackend(Dnn.DNN_BACKEND_OPENCV);
|
||||
net.setPreferableTarget(Dnn.DNN_TARGET_CPU);
|
||||
@@ -572,12 +835,16 @@ public class CameraApp extends JFrame {
|
||||
}
|
||||
|
||||
modelLoaded.set(true);
|
||||
statusLabel.setText("YOLOv4 model loaded successfully");
|
||||
statusLabel.setText("YOLOv4-Tiny model loaded successfully (" + classes.size() + " classes)");
|
||||
|
||||
} catch (Exception e) {
|
||||
modelLoaded.set(false);
|
||||
statusLabel.setText("Error loading model: " + e.getMessage());
|
||||
JOptionPane.showMessageDialog(this, "Failed to load object detection model", "Error", JOptionPane.WARNING_MESSAGE);
|
||||
JOptionPane.showMessageDialog(this,
|
||||
"Failed to load object detection model: " + e.getMessage(),
|
||||
"Error", JOptionPane.WARNING_MESSAGE);
|
||||
detectionCheckbox.setSelected(false);
|
||||
detectionEnabled.set(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -601,17 +868,19 @@ public class CameraApp extends JFrame {
|
||||
|
||||
// Download in background thread
|
||||
CompletableFuture.runAsync(() -> {
|
||||
boolean allSuccess = true;
|
||||
try {
|
||||
for (String[] file : files) {
|
||||
String fileName = file[0];
|
||||
String url = file[1];
|
||||
|
||||
// Check if file already exists and is valid
|
||||
if (Files.exists(Paths.get(fileName))) {
|
||||
try {
|
||||
long size = getFileSize(fileName);
|
||||
if ((fileName.equals("yolov4-tiny.weights") && size >= 10000000) ||
|
||||
(fileName.equals("yolov4-tiny.cfg") && size >= 10000) ||
|
||||
(fileName.equals("coco.names") && size >= 1000)) {
|
||||
(fileName.equals("yolov4-tiny.cfg") && size >= 1000) ||
|
||||
(fileName.equals("coco.names") && size >= 500)) {
|
||||
continue;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
@@ -620,6 +889,7 @@ public class CameraApp extends JFrame {
|
||||
}
|
||||
|
||||
if (progressDialog.isCancelled()) {
|
||||
allSuccess = false;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -631,14 +901,34 @@ public class CameraApp extends JFrame {
|
||||
System.out.println("Downloaded " + fileName);
|
||||
} catch (Exception e) {
|
||||
System.out.println("Error downloading " + fileName + ": " + e.getMessage());
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
JOptionPane.showMessageDialog(this, "Failed to download " + fileName, "Error", JOptionPane.ERROR_MESSAGE);
|
||||
});
|
||||
allSuccess = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
SwingUtilities.invokeLater(() -> progressDialog.dispose());
|
||||
|
||||
boolean finalAllSuccess = allSuccess;
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
progressDialog.dispose();
|
||||
if (finalAllSuccess && !progressDialog.isCancelled()) {
|
||||
statusLabel.setText("YOLO files downloaded successfully");
|
||||
initializeYoloModel();
|
||||
} else {
|
||||
statusLabel.setText("Download failed or cancelled");
|
||||
detectionCheckbox.setSelected(false);
|
||||
detectionEnabled.set(false);
|
||||
}
|
||||
});
|
||||
|
||||
} catch (Exception e) {
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
progressDialog.dispose();
|
||||
statusLabel.setText("Download error: " + e.getMessage());
|
||||
JOptionPane.showMessageDialog(this,
|
||||
"Download failed: " + e.getMessage(),
|
||||
"Error", JOptionPane.ERROR_MESSAGE);
|
||||
detectionCheckbox.setSelected(false);
|
||||
detectionEnabled.set(false);
|
||||
});
|
||||
}
|
||||
}, executorService);
|
||||
}
|
||||
@@ -647,17 +937,25 @@ public class CameraApp extends JFrame {
|
||||
URL url = new URL(urlString);
|
||||
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
||||
connection.setRequestMethod("GET");
|
||||
connection.connect();
|
||||
connection.setConnectTimeout(10000);
|
||||
connection.setReadTimeout(30000);
|
||||
|
||||
int contentLength = connection.getContentLength();
|
||||
// Set user agent to avoid blocking
|
||||
connection.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36");
|
||||
|
||||
int responseCode = connection.getResponseCode();
|
||||
if (responseCode != HttpURLConnection.HTTP_OK) {
|
||||
throw new IOException("HTTP error code: " + responseCode);
|
||||
}
|
||||
|
||||
long fileSize = connection.getContentLengthLong();
|
||||
|
||||
try (InputStream inputStream = connection.getInputStream();
|
||||
FileOutputStream outputStream = new FileOutputStream(fileName)) {
|
||||
|
||||
byte[] buffer = new byte[8192];
|
||||
int bytesRead;
|
||||
long totalBytesRead = 0;
|
||||
long lastUpdate = 0;
|
||||
int bytesRead;
|
||||
|
||||
while ((bytesRead = inputStream.read(buffer)) != -1) {
|
||||
if (progressDialog.isCancelled()) {
|
||||
@@ -667,150 +965,53 @@ public class CameraApp extends JFrame {
|
||||
outputStream.write(buffer, 0, bytesRead);
|
||||
totalBytesRead += bytesRead;
|
||||
|
||||
long currentTime = System.currentTimeMillis();
|
||||
if (currentTime - lastUpdate > 100) { // Update every 100ms
|
||||
progressDialog.updateProgress(totalBytesRead, contentLength);
|
||||
lastUpdate = currentTime;
|
||||
if (fileSize > 0) {
|
||||
progressDialog.updateProgress(totalBytesRead, fileSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void updateFrame(ActionEvent e) {
|
||||
if (cap == null || !cap.isOpened()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Mat frame = new Mat();
|
||||
boolean ret = cap.read(frame);
|
||||
if (!ret || frame.empty()) {
|
||||
statusLabel.setText("Failed to capture frame");
|
||||
return;
|
||||
}
|
||||
|
||||
frameCount++;
|
||||
long currentTime = System.currentTimeMillis();
|
||||
if (currentTime - lastFpsUpdate >= 1000) {
|
||||
fps = frameCount * 1000.0 / (currentTime - lastFpsUpdate);
|
||||
lastFpsUpdate = currentTime;
|
||||
frameCount = 0;
|
||||
statusLabel.setText(String.format("Running at %.1f FPS", fps));
|
||||
}
|
||||
|
||||
// Convert BGR to RGB
|
||||
Mat rgbFrame = new Mat();
|
||||
Imgproc.cvtColor(frame, rgbFrame, Imgproc.COLOR_BGR2RGB);
|
||||
|
||||
if (detectionEnabled.get() && modelLoaded.get()) {
|
||||
if (detectionThread == null || !detectionThread.isAlive()) {
|
||||
detectionThread = new DetectionThread(net, classes, outputLayers, rgbFrame);
|
||||
detectionThread.start();
|
||||
|
||||
// Handle detection result asynchronously
|
||||
detectionThread.getDetectionResult().thenAccept(this::displayFrame);
|
||||
} else {
|
||||
displayFrame(rgbFrame);
|
||||
}
|
||||
} else {
|
||||
displayFrame(rgbFrame);
|
||||
}
|
||||
}
|
||||
|
||||
private void displayFrame(Mat frame) {
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
BufferedImage bufferedImage = matToBufferedImage(frame);
|
||||
if (bufferedImage != null) {
|
||||
// Scale image to fit label while maintaining aspect ratio
|
||||
Dimension labelSize = videoLabel.getSize();
|
||||
ImageIcon scaledIcon = new ImageIcon(bufferedImage.getScaledInstance(
|
||||
labelSize.width, labelSize.height, Image.SCALE_SMOOTH));
|
||||
videoLabel.setIcon(scaledIcon);
|
||||
videoLabel.setText(null);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private BufferedImage matToBufferedImage(Mat mat) {
|
||||
try {
|
||||
int type = BufferedImage.TYPE_BYTE_GRAY;
|
||||
if (mat.channels() > 1) {
|
||||
type = BufferedImage.TYPE_3BYTE_BGR;
|
||||
}
|
||||
|
||||
int bufferSize = mat.channels() * mat.cols() * mat.rows();
|
||||
byte[] buffer = new byte[bufferSize];
|
||||
mat.get(0, 0, buffer); // get all pixels
|
||||
|
||||
BufferedImage image = new BufferedImage(mat.cols(), mat.rows(), type);
|
||||
final byte[] targetPixels = ((DataBufferByte) image.getRaster().getDataBuffer()).getData();
|
||||
System.arraycopy(buffer, 0, targetPixels, 0, buffer.length);
|
||||
|
||||
return image;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private void takeSnapshot() {
|
||||
if (cap == null || !cap.isOpened()) {
|
||||
JOptionPane.showMessageDialog(this, "No active camera to take snapshot from", "Warning", JOptionPane.WARNING_MESSAGE);
|
||||
return;
|
||||
}
|
||||
|
||||
Mat frame = new Mat();
|
||||
boolean ret = cap.read(frame);
|
||||
if (!ret || frame.empty()) {
|
||||
JOptionPane.showMessageDialog(this, "Failed to capture frame", "Warning", JOptionPane.WARNING_MESSAGE);
|
||||
return;
|
||||
}
|
||||
|
||||
JFileChooser fileChooser = new JFileChooser();
|
||||
fileChooser.setDialogTitle("Save Snapshot");
|
||||
fileChooser.setFileFilter(new javax.swing.filechooser.FileNameExtensionFilter("JPEG Images", "jpg", "jpeg"));
|
||||
|
||||
int result = fileChooser.showSaveDialog(this);
|
||||
if (result == JFileChooser.APPROVE_OPTION) {
|
||||
String fileName = fileChooser.getSelectedFile().getAbsolutePath();
|
||||
if (!fileName.toLowerCase().endsWith(".jpg") && !fileName.toLowerCase().endsWith(".jpeg")) {
|
||||
fileName += ".jpg";
|
||||
}
|
||||
|
||||
// Convert BGR to RGB for saving
|
||||
Mat rgbFrame = new Mat();
|
||||
Imgproc.cvtColor(frame, rgbFrame, Imgproc.COLOR_BGR2RGB);
|
||||
|
||||
boolean success = Imgcodecs.imwrite(fileName, rgbFrame);
|
||||
if (success) {
|
||||
statusLabel.setText("Snapshot saved to " + fileName);
|
||||
} else {
|
||||
JOptionPane.showMessageDialog(this, "Failed to save snapshot", "Error", JOptionPane.WARNING_MESSAGE);
|
||||
}
|
||||
}
|
||||
connection.disconnect();
|
||||
}
|
||||
|
||||
private void closeEvent() {
|
||||
stopCamera();
|
||||
if (cap != null) {
|
||||
cap.release();
|
||||
}
|
||||
|
||||
if (detectionThread != null) {
|
||||
detectionThread.stopDetection();
|
||||
}
|
||||
|
||||
if (executorService != null) {
|
||||
executorService.shutdown();
|
||||
executorService.shutdownNow();
|
||||
}
|
||||
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
// Main method
|
||||
public static void main(String[] args) {
|
||||
try {
|
||||
// Set system look and feel
|
||||
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
System.out.println("Could not set system look and feel: " + e.getMessage());
|
||||
}
|
||||
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
CameraApp app = new CameraApp();
|
||||
app.setVisible(true);
|
||||
});
|
||||
}
|
||||
}
|
||||
try {
|
||||
new CameraApp().setVisible(true);
|
||||
} catch (Exception e) {
|
||||
System.err.println("Error starting application: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
|
||||
JOptionPane.showMessageDialog(null,
|
||||
"Error starting application:\n" + e.getMessage() +
|
||||
"\n\nPlease ensure OpenCV is properly installed and configured.",
|
||||
"Startup Error",
|
||||
JOptionPane.ERROR_MESSAGE);
|
||||
System.exit(1);
|
||||
}
|
||||
});
|
||||
}}
|
||||
80
src/main/resources/coco.names
Normal file
80
src/main/resources/coco.names
Normal file
@@ -0,0 +1,80 @@
|
||||
person
|
||||
bicycle
|
||||
car
|
||||
motorbike
|
||||
aeroplane
|
||||
bus
|
||||
train
|
||||
truck
|
||||
boat
|
||||
traffic light
|
||||
fire hydrant
|
||||
stop sign
|
||||
parking meter
|
||||
bench
|
||||
bird
|
||||
cat
|
||||
dog
|
||||
horse
|
||||
sheep
|
||||
cow
|
||||
elephant
|
||||
bear
|
||||
zebra
|
||||
giraffe
|
||||
backpack
|
||||
umbrella
|
||||
handbag
|
||||
tie
|
||||
suitcase
|
||||
frisbee
|
||||
skis
|
||||
snowboard
|
||||
sports ball
|
||||
kite
|
||||
baseball bat
|
||||
baseball glove
|
||||
skateboard
|
||||
surfboard
|
||||
tennis racket
|
||||
bottle
|
||||
wine glass
|
||||
cup
|
||||
fork
|
||||
knife
|
||||
spoon
|
||||
bowl
|
||||
banana
|
||||
apple
|
||||
sandwich
|
||||
orange
|
||||
broccoli
|
||||
carrot
|
||||
hot dog
|
||||
pizza
|
||||
donut
|
||||
cake
|
||||
chair
|
||||
sofa
|
||||
pottedplant
|
||||
bed
|
||||
diningtable
|
||||
toilet
|
||||
tvmonitor
|
||||
laptop
|
||||
mouse
|
||||
remote
|
||||
keyboard
|
||||
cell phone
|
||||
microwave
|
||||
oven
|
||||
toaster
|
||||
sink
|
||||
refrigerator
|
||||
book
|
||||
clock
|
||||
vase
|
||||
scissors
|
||||
teddy bear
|
||||
hair drier
|
||||
toothbrush
|
||||
294
src/main/resources/yolov4-tiny.cfg
Normal file
294
src/main/resources/yolov4-tiny.cfg
Normal file
@@ -0,0 +1,294 @@
|
||||
[net]
|
||||
# Testing
|
||||
#batch=1
|
||||
#subdivisions=1
|
||||
# Training
|
||||
batch=64
|
||||
subdivisions=1
|
||||
width=416
|
||||
height=416
|
||||
channels=3
|
||||
momentum=0.9
|
||||
decay=0.0005
|
||||
angle=0
|
||||
saturation = 1.5
|
||||
exposure = 1.5
|
||||
hue=.1
|
||||
|
||||
learning_rate=0.00261
|
||||
burn_in=1000
|
||||
|
||||
max_batches = 2000200
|
||||
policy=steps
|
||||
steps=1600000,1800000
|
||||
scales=.1,.1
|
||||
|
||||
|
||||
#weights_reject_freq=1001
|
||||
#ema_alpha=0.9998
|
||||
#equidistant_point=1000
|
||||
#num_sigmas_reject_badlabels=3
|
||||
#badlabels_rejection_percentage=0.2
|
||||
|
||||
|
||||
[convolutional]
|
||||
batch_normalize=1
|
||||
filters=32
|
||||
size=3
|
||||
stride=2
|
||||
pad=1
|
||||
activation=leaky
|
||||
|
||||
[convolutional]
|
||||
batch_normalize=1
|
||||
filters=64
|
||||
size=3
|
||||
stride=2
|
||||
pad=1
|
||||
activation=leaky
|
||||
|
||||
[convolutional]
|
||||
batch_normalize=1
|
||||
filters=64
|
||||
size=3
|
||||
stride=1
|
||||
pad=1
|
||||
activation=leaky
|
||||
|
||||
[route]
|
||||
layers=-1
|
||||
groups=2
|
||||
group_id=1
|
||||
|
||||
[convolutional]
|
||||
batch_normalize=1
|
||||
filters=32
|
||||
size=3
|
||||
stride=1
|
||||
pad=1
|
||||
activation=leaky
|
||||
|
||||
[convolutional]
|
||||
batch_normalize=1
|
||||
filters=32
|
||||
size=3
|
||||
stride=1
|
||||
pad=1
|
||||
activation=leaky
|
||||
|
||||
[route]
|
||||
layers = -1,-2
|
||||
|
||||
[convolutional]
|
||||
batch_normalize=1
|
||||
filters=64
|
||||
size=1
|
||||
stride=1
|
||||
pad=1
|
||||
activation=leaky
|
||||
|
||||
[route]
|
||||
layers = -6,-1
|
||||
|
||||
[maxpool]
|
||||
size=2
|
||||
stride=2
|
||||
|
||||
[convolutional]
|
||||
batch_normalize=1
|
||||
filters=128
|
||||
size=3
|
||||
stride=1
|
||||
pad=1
|
||||
activation=leaky
|
||||
|
||||
[route]
|
||||
layers=-1
|
||||
groups=2
|
||||
group_id=1
|
||||
|
||||
[convolutional]
|
||||
batch_normalize=1
|
||||
filters=64
|
||||
size=3
|
||||
stride=1
|
||||
pad=1
|
||||
activation=leaky
|
||||
|
||||
[convolutional]
|
||||
batch_normalize=1
|
||||
filters=64
|
||||
size=3
|
||||
stride=1
|
||||
pad=1
|
||||
activation=leaky
|
||||
|
||||
[route]
|
||||
layers = -1,-2
|
||||
|
||||
[convolutional]
|
||||
batch_normalize=1
|
||||
filters=128
|
||||
size=1
|
||||
stride=1
|
||||
pad=1
|
||||
activation=leaky
|
||||
|
||||
[route]
|
||||
layers = -6,-1
|
||||
|
||||
[maxpool]
|
||||
size=2
|
||||
stride=2
|
||||
|
||||
[convolutional]
|
||||
batch_normalize=1
|
||||
filters=256
|
||||
size=3
|
||||
stride=1
|
||||
pad=1
|
||||
activation=leaky
|
||||
|
||||
[route]
|
||||
layers=-1
|
||||
groups=2
|
||||
group_id=1
|
||||
|
||||
[convolutional]
|
||||
batch_normalize=1
|
||||
filters=128
|
||||
size=3
|
||||
stride=1
|
||||
pad=1
|
||||
activation=leaky
|
||||
|
||||
[convolutional]
|
||||
batch_normalize=1
|
||||
filters=128
|
||||
size=3
|
||||
stride=1
|
||||
pad=1
|
||||
activation=leaky
|
||||
|
||||
[route]
|
||||
layers = -1,-2
|
||||
|
||||
[convolutional]
|
||||
batch_normalize=1
|
||||
filters=256
|
||||
size=1
|
||||
stride=1
|
||||
pad=1
|
||||
activation=leaky
|
||||
|
||||
[route]
|
||||
layers = -6,-1
|
||||
|
||||
[maxpool]
|
||||
size=2
|
||||
stride=2
|
||||
|
||||
[convolutional]
|
||||
batch_normalize=1
|
||||
filters=512
|
||||
size=3
|
||||
stride=1
|
||||
pad=1
|
||||
activation=leaky
|
||||
|
||||
##################################
|
||||
|
||||
[convolutional]
|
||||
batch_normalize=1
|
||||
filters=256
|
||||
size=1
|
||||
stride=1
|
||||
pad=1
|
||||
activation=leaky
|
||||
|
||||
[convolutional]
|
||||
batch_normalize=1
|
||||
filters=512
|
||||
size=3
|
||||
stride=1
|
||||
pad=1
|
||||
activation=leaky
|
||||
|
||||
[convolutional]
|
||||
size=1
|
||||
stride=1
|
||||
pad=1
|
||||
filters=255
|
||||
activation=linear
|
||||
|
||||
|
||||
|
||||
[yolo]
|
||||
mask = 3,4,5
|
||||
anchors = 10,14, 23,27, 37,58, 81,82, 135,169, 344,319
|
||||
classes=80
|
||||
num=6
|
||||
jitter=.3
|
||||
scale_x_y = 1.05
|
||||
cls_normalizer=1.0
|
||||
iou_normalizer=0.07
|
||||
iou_loss=ciou
|
||||
ignore_thresh = .7
|
||||
truth_thresh = 1
|
||||
random=0
|
||||
resize=1.5
|
||||
nms_kind=greedynms
|
||||
beta_nms=0.6
|
||||
#new_coords=1
|
||||
#scale_x_y = 2.0
|
||||
|
||||
[route]
|
||||
layers = -4
|
||||
|
||||
[convolutional]
|
||||
batch_normalize=1
|
||||
filters=128
|
||||
size=1
|
||||
stride=1
|
||||
pad=1
|
||||
activation=leaky
|
||||
|
||||
[upsample]
|
||||
stride=2
|
||||
|
||||
[route]
|
||||
layers = -1, 23
|
||||
|
||||
[convolutional]
|
||||
batch_normalize=1
|
||||
filters=256
|
||||
size=3
|
||||
stride=1
|
||||
pad=1
|
||||
activation=leaky
|
||||
|
||||
[convolutional]
|
||||
size=1
|
||||
stride=1
|
||||
pad=1
|
||||
filters=255
|
||||
activation=linear
|
||||
|
||||
[yolo]
|
||||
mask = 1,2,3
|
||||
anchors = 10,14, 23,27, 37,58, 81,82, 135,169, 344,319
|
||||
classes=80
|
||||
num=6
|
||||
jitter=.3
|
||||
scale_x_y = 1.05
|
||||
cls_normalizer=1.0
|
||||
iou_normalizer=0.07
|
||||
iou_loss=ciou
|
||||
ignore_thresh = .7
|
||||
truth_thresh = 1
|
||||
random=0
|
||||
resize=1.5
|
||||
nms_kind=greedynms
|
||||
beta_nms=0.6
|
||||
#new_coords=1
|
||||
#scale_x_y = 2.0
|
||||
BIN
src/main/resources/yolov4-tiny.weights
Normal file
BIN
src/main/resources/yolov4-tiny.weights
Normal file
Binary file not shown.
Reference in New Issue
Block a user