a new windowing system!
add: + CameraPanel ; To make life easier for coders + SwingIFrame ; Which is now our main UI component + VideoRecorder ; A helper Class for SwingIFrame to record cameras! changed: / SwingCCTVManager ; changes for the new UI Component deprecation: /-/ AutoGainProcessor ; cause it isnt needed anymore, back then this was needed cause we opened the webcams manually (color wise) --- rattatwinko
This commit is contained in:
@@ -31,22 +31,37 @@ public class SwingCCTVManager {
|
||||
private final JFrame frame;
|
||||
private final JTable deviceTable;
|
||||
private final DefaultTableModel tableModel;
|
||||
private final SwingIFrame IFrame;
|
||||
|
||||
public SwingCCTVManager() {
|
||||
frame = new JFrame("dashboard");
|
||||
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||
frame.setSize(1000, 600);
|
||||
|
||||
this.IFrame = new SwingIFrame();
|
||||
this.IFrame.show();
|
||||
|
||||
String[] columns = {"Status", "Device Name", "Type", "Resolution", "Address"};
|
||||
tableModel = new DefaultTableModel(columns, 0) {
|
||||
@Override public boolean isCellEditable(int r, int c) { return false; }
|
||||
@Override
|
||||
public boolean isCellEditable(int r, int c) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
deviceTable = new JTable(tableModel);
|
||||
|
||||
deviceTable.setRowSelectionAllowed(true);
|
||||
deviceTable.setFocusable(true);
|
||||
deviceTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
|
||||
|
||||
setupTableAppearance();
|
||||
|
||||
deviceTable.addMouseListener(new MouseAdapter() {
|
||||
@Override
|
||||
public void mousePressed(MouseEvent e) {
|
||||
deviceTable.requestFocusInWindow();
|
||||
|
||||
if (e.getClickCount() == 2 && deviceTable.getSelectedRow() != -1) {
|
||||
launchSelected();
|
||||
}
|
||||
@@ -74,29 +89,31 @@ public class SwingCCTVManager {
|
||||
}
|
||||
|
||||
private void setupTableAppearance() {
|
||||
deviceTable.getColumnModel().getColumn(0).setMaxWidth(80); // Status column
|
||||
deviceTable.getColumnModel().getColumn(0).setMaxWidth(80);
|
||||
deviceTable.setRowHeight(30);
|
||||
|
||||
// Custom Renderer for Status Colors
|
||||
deviceTable.getColumnModel().getColumn(0).setCellRenderer(new DefaultTableCellRenderer() {
|
||||
@Override
|
||||
public Component getTableCellRendererComponent(JTable t, Object v, boolean s, boolean f, int r, int c) {
|
||||
Component comp = super.getTableCellRendererComponent(t, v, s, f, r, c);
|
||||
if ("ONLINE".equals(v)) comp.setForeground(new Color(0, 150, 0));
|
||||
else comp.setForeground(Color.RED);
|
||||
setHorizontalAlignment(JLabel.CENTER);
|
||||
return comp;
|
||||
}
|
||||
});
|
||||
deviceTable.getColumnModel().getColumn(0)
|
||||
.setCellRenderer(new DefaultTableCellRenderer() {
|
||||
@Override
|
||||
public Component getTableCellRendererComponent(
|
||||
JTable t, Object v, boolean s, boolean f, int r, int c) {
|
||||
|
||||
Component comp = super.getTableCellRendererComponent(t, v, s, f, r, c);
|
||||
setHorizontalAlignment(JLabel.CENTER);
|
||||
comp.setForeground("ONLINE".equals(v)
|
||||
? new Color(0, 150, 0)
|
||||
: Color.RED);
|
||||
return comp;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void startAutoRefresh() {
|
||||
Timer autoRefreshTimer = new Timer(5000, e -> refreshTable());
|
||||
autoRefreshTimer.start();
|
||||
new Timer(5000, e -> refreshTable()).start();
|
||||
}
|
||||
|
||||
private void refreshTable() {
|
||||
int selectedRow = deviceTable.getSelectedRow();
|
||||
int[] selectedRows = deviceTable.getSelectedRows();
|
||||
|
||||
tableModel.setRowCount(0);
|
||||
List<Webcam> webcams = Webcam.getWebcams();
|
||||
@@ -114,14 +131,18 @@ public class SwingCCTVManager {
|
||||
});
|
||||
}
|
||||
|
||||
if (selectedRow != -1 && selectedRow < tableModel.getRowCount()) {
|
||||
deviceTable.setRowSelectionInterval(selectedRow, selectedRow);
|
||||
for (int r : selectedRows) {
|
||||
if (r < tableModel.getRowCount()) {
|
||||
deviceTable.addRowSelectionInterval(r, r);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void showContextMenu(MouseEvent e) {
|
||||
int row = deviceTable.rowAtPoint(e.getPoint());
|
||||
deviceTable.setRowSelectionInterval(row, row);
|
||||
if (row >= 0) {
|
||||
deviceTable.setRowSelectionInterval(row, row);
|
||||
}
|
||||
|
||||
JPopupMenu menu = new JPopupMenu();
|
||||
JMenuItem launch = new JMenuItem("Launch Live Stream");
|
||||
@@ -143,12 +164,16 @@ public class SwingCCTVManager {
|
||||
String name = (String) tableModel.getValueAt(row, 1);
|
||||
Webcam.getWebcams().stream()
|
||||
.filter(w -> w.getName().equals(name))
|
||||
.findFirst().ifPresent(selected -> new Thread(() -> new SwingCameraWindow(selected).open()).start());
|
||||
|
||||
.findFirst()
|
||||
.ifPresent(cam ->
|
||||
new Thread(() -> IFrame.addCameraInternalFrame(cam)).start()
|
||||
);
|
||||
}
|
||||
|
||||
private void deleteSelected() {
|
||||
int row = deviceTable.getSelectedRow();
|
||||
if (row == -1) return;
|
||||
|
||||
String name = (String) tableModel.getValueAt(row, 1);
|
||||
if (name.toLowerCase().contains("usb")) return;
|
||||
|
||||
@@ -160,11 +185,15 @@ public class SwingCCTVManager {
|
||||
private static void loadSavedCameras() {
|
||||
for (CameraConfig config : CameraSettings.load()) {
|
||||
try {
|
||||
IpCamDeviceRegistry.register(config.getName(), config.getUrl(), IpCamMode.PUSH);
|
||||
IpCamDeviceRegistry.register(
|
||||
config.getName(),
|
||||
config.getUrl(),
|
||||
IpCamMode.PUSH
|
||||
);
|
||||
} catch (MalformedURLException e) {
|
||||
JOptionPane.showMessageDialog(
|
||||
null,
|
||||
"Malformed URL\n"+e.getMessage(),
|
||||
"Malformed URL\n" + e.getMessage(),
|
||||
"Malformed URL",
|
||||
JOptionPane.ERROR_MESSAGE
|
||||
);
|
||||
@@ -176,10 +205,16 @@ public class SwingCCTVManager {
|
||||
JPanel p = new JPanel(new GridLayout(2, 2, 5, 5));
|
||||
JTextField n = new JTextField();
|
||||
JTextField u = new JTextField();
|
||||
p.add(new JLabel("Name:")); p.add(n);
|
||||
p.add(new JLabel("URL:")); p.add(u);
|
||||
|
||||
int result = JOptionPane.showConfirmDialog(frame, p, "Register IP Camera", JOptionPane.OK_CANCEL_OPTION);
|
||||
p.add(new JLabel("Name:"));
|
||||
p.add(n);
|
||||
p.add(new JLabel("URL:"));
|
||||
p.add(u);
|
||||
|
||||
int result = JOptionPane.showConfirmDialog(
|
||||
frame, p, "Register IP Camera", JOptionPane.OK_CANCEL_OPTION
|
||||
);
|
||||
|
||||
if (result == JOptionPane.OK_OPTION) {
|
||||
try {
|
||||
IpCamDeviceRegistry.register(n.getText(), u.getText(), IpCamMode.PUSH);
|
||||
|
||||
311
src/main/java/io/swtc/SwingIFrame.java
Normal file
311
src/main/java/io/swtc/SwingIFrame.java
Normal file
@@ -0,0 +1,311 @@
|
||||
package io.swtc;
|
||||
|
||||
import com.github.sarxos.webcam.Webcam;
|
||||
import io.swtc.proccessing.WebcamCaptureLoop;
|
||||
import io.swtc.proccessing.CameraPanel;
|
||||
import io.swtc.recording.VideoRecorder;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.swing.*;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
import javax.swing.border.TitledBorder;
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
/*
|
||||
*
|
||||
* This file is basically just UI, its boring the interesting stuff is in the utilities section!
|
||||
*
|
||||
* */
|
||||
|
||||
public class SwingIFrame {
|
||||
private final JFrame mainFrame;
|
||||
private final JDesktopPane desktopPane;
|
||||
|
||||
public SwingIFrame() {
|
||||
mainFrame = new JFrame("viewer");
|
||||
mainFrame.setSize(1280,720);
|
||||
mainFrame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
|
||||
desktopPane = new JDesktopPane();
|
||||
desktopPane.setBackground(Color.WHITE);
|
||||
mainFrame.add(desktopPane, BorderLayout.CENTER);
|
||||
}
|
||||
|
||||
public void addCameraInternalFrame(Webcam webcam) {
|
||||
JInternalFrame iframe = new JInternalFrame(
|
||||
webcam.getName(),
|
||||
true, true, true, true
|
||||
);
|
||||
|
||||
CameraPanel cameraPanel = new CameraPanel();
|
||||
WebcamCaptureLoop captureLoop = new WebcamCaptureLoop(webcam, (BufferedImage img) ->
|
||||
SwingUtilities.invokeLater(() -> cameraPanel.setImage(img))
|
||||
);
|
||||
|
||||
JPanel contentPanel = new JPanel(new BorderLayout());
|
||||
|
||||
JTabbedPane tabbedPane = new JTabbedPane();
|
||||
tabbedPane.addTab("View", cameraPanel);
|
||||
tabbedPane.addTab("Capture", createCapturePanel(cameraPanel, webcam));
|
||||
tabbedPane.addTab("Effects", createEffectsPanel(cameraPanel));
|
||||
|
||||
contentPanel.add(tabbedPane, BorderLayout.CENTER);
|
||||
|
||||
iframe.addInternalFrameListener(new javax.swing.event.InternalFrameAdapter() {
|
||||
@Override
|
||||
public void internalFrameClosing(javax.swing.event.InternalFrameEvent e) {
|
||||
// if we dont call this the camera stays open until the procces dies.
|
||||
captureLoop.stop();
|
||||
}
|
||||
});
|
||||
|
||||
iframe.add(contentPanel);
|
||||
iframe.setSize(600, 500);
|
||||
|
||||
int offset = desktopPane.getAllFrames().length * 30;
|
||||
iframe.setLocation(offset, offset);
|
||||
|
||||
desktopPane.add(iframe);
|
||||
iframe.setVisible(true);
|
||||
captureLoop.start();
|
||||
}
|
||||
|
||||
private JPanel createCapturePanel(CameraPanel cameraPanel, Webcam webcam) {
|
||||
JPanel panel = new JPanel(new BorderLayout(10, 10));
|
||||
panel.setBorder(new EmptyBorder(15, 15, 15, 15));
|
||||
|
||||
JPanel mainPanel = new JPanel();
|
||||
mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS));
|
||||
|
||||
// Screenshot section
|
||||
JPanel screenshotPanel = new JPanel();
|
||||
screenshotPanel.setLayout(new BoxLayout(screenshotPanel, BoxLayout.Y_AXIS));
|
||||
screenshotPanel.setBorder(BorderFactory.createTitledBorder(
|
||||
BorderFactory.createEtchedBorder(), "Screenshot",
|
||||
TitledBorder.LEFT, TitledBorder.TOP));
|
||||
|
||||
JPanel screenshotPathPanel = new JPanel(new BorderLayout(5, 5));
|
||||
JTextField screenshotPath = new JTextField(System.getProperty("user.home") + File.separator + "screenshots");
|
||||
JButton browseSS = new JButton("...");
|
||||
browseSS.setPreferredSize(new Dimension(40, 25));
|
||||
browseSS.addActionListener(e -> browseDirectory(screenshotPath, panel));
|
||||
screenshotPathPanel.add(screenshotPath, BorderLayout.CENTER);
|
||||
screenshotPathPanel.add(browseSS, BorderLayout.EAST);
|
||||
|
||||
JButton takeScreenshot = new JButton("Take Screenshot (S)");
|
||||
takeScreenshot.setAlignmentX(Component.CENTER_ALIGNMENT);
|
||||
takeScreenshot.addActionListener(e -> saveSnapshot(cameraPanel, webcam, screenshotPath.getText(), panel));
|
||||
|
||||
screenshotPanel.add(screenshotPathPanel);
|
||||
screenshotPanel.add(Box.createRigidArea(new Dimension(0, 10)));
|
||||
screenshotPanel.add(takeScreenshot);
|
||||
|
||||
// Recording section
|
||||
JPanel recordPanel = new JPanel();
|
||||
recordPanel.setLayout(new BoxLayout(recordPanel, BoxLayout.Y_AXIS));
|
||||
recordPanel.setBorder(BorderFactory.createTitledBorder(
|
||||
BorderFactory.createEtchedBorder(), "Recording",
|
||||
TitledBorder.LEFT, TitledBorder.TOP));
|
||||
|
||||
JPanel recordPathPanel = new JPanel(new BorderLayout(5, 5));
|
||||
JTextField recordPath = new JTextField(System.getProperty("user.home") + File.separator + "recordings");
|
||||
JButton browseRec = new JButton("...");
|
||||
browseRec.setPreferredSize(new Dimension(40, 25));
|
||||
browseRec.addActionListener(e -> browseDirectory(recordPath, panel));
|
||||
recordPathPanel.add(recordPath, BorderLayout.CENTER);
|
||||
recordPathPanel.add(browseRec, BorderLayout.EAST);
|
||||
|
||||
VideoRecorder recorder = new VideoRecorder();
|
||||
JButton recordButton = new JButton("Start Recording (R)");
|
||||
JLabel recordingStatus = new JLabel("Ready");
|
||||
recordingStatus.setAlignmentX(Component.CENTER_ALIGNMENT);
|
||||
|
||||
recordButton.setAlignmentX(Component.CENTER_ALIGNMENT);
|
||||
recordButton.addActionListener(e -> {
|
||||
if (!recorder.isRecording()) {
|
||||
String timestamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
|
||||
String filename = "recording_" + webcam.getName().replaceAll("[^a-zA-Z0-9]", "_")
|
||||
+ "_" + timestamp + ".mp4";
|
||||
File dir = new File(recordPath.getText());
|
||||
dir.mkdirs();
|
||||
|
||||
try {
|
||||
recorder.startRecording(cameraPanel, new File(dir, filename));
|
||||
recordButton.setText("Stop Recording");
|
||||
recordingStatus.setText("Recording...");
|
||||
recordingStatus.setForeground(Color.RED);
|
||||
} catch (Exception ex) {
|
||||
JOptionPane.showMessageDialog(panel,
|
||||
"Error starting recording: " + ex.getMessage(),
|
||||
"Error", JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
File saved = recorder.stopRecording();
|
||||
recordButton.setText("Start Recording (R)");
|
||||
recordingStatus.setText("Saved: " + saved.getName());
|
||||
recordingStatus.setForeground(Color.BLACK);
|
||||
} catch (Exception ex) {
|
||||
recordButton.setText("Start Recording (R)");
|
||||
recordingStatus.setText("Error saving");
|
||||
recordingStatus.setForeground(Color.RED);
|
||||
JOptionPane.showMessageDialog(panel,
|
||||
"Error saving recording: " + ex.getMessage(),
|
||||
"Error", JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
recordPanel.add(recordPathPanel);
|
||||
recordPanel.add(Box.createRigidArea(new Dimension(0, 10)));
|
||||
recordPanel.add(recordButton);
|
||||
recordPanel.add(Box.createRigidArea(new Dimension(0, 5)));
|
||||
recordPanel.add(recordingStatus);
|
||||
|
||||
mainPanel.add(screenshotPanel);
|
||||
mainPanel.add(Box.createRigidArea(new Dimension(0, 15)));
|
||||
mainPanel.add(recordPanel);
|
||||
mainPanel.add(Box.createVerticalGlue());
|
||||
|
||||
panel.add(mainPanel, BorderLayout.NORTH);
|
||||
|
||||
return panel;
|
||||
}
|
||||
|
||||
private JPanel createEffectsPanel(CameraPanel cameraPanel) {
|
||||
JPanel panel = new JPanel();
|
||||
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
|
||||
panel.setBorder(new EmptyBorder(15, 15, 15, 15));
|
||||
|
||||
JPanel transformPanel = new JPanel(new GridLayout(0, 1, 5, 5));
|
||||
transformPanel.setBorder(BorderFactory.createTitledBorder(
|
||||
BorderFactory.createEtchedBorder(), "Transform",
|
||||
TitledBorder.LEFT, TitledBorder.TOP));
|
||||
|
||||
JCheckBox mirrorCheck = new JCheckBox("Mirror Horizontal");
|
||||
JCheckBox flipCheck = new JCheckBox("Flip Vertical");
|
||||
JCheckBox rotateCheck = new JCheckBox("Rotate 180°");
|
||||
|
||||
mirrorCheck.addActionListener(e -> cameraPanel.setMirror(mirrorCheck.isSelected()));
|
||||
flipCheck.addActionListener(e -> cameraPanel.setFlip(flipCheck.isSelected()));
|
||||
rotateCheck.addActionListener(e -> cameraPanel.setRotate(rotateCheck.isSelected()));
|
||||
|
||||
transformPanel.add(mirrorCheck);
|
||||
transformPanel.add(flipCheck);
|
||||
transformPanel.add(rotateCheck);
|
||||
|
||||
JPanel colorPanel = new JPanel();
|
||||
colorPanel.setLayout(new BoxLayout(colorPanel, BoxLayout.Y_AXIS));
|
||||
colorPanel.setBorder(BorderFactory.createTitledBorder(
|
||||
BorderFactory.createEtchedBorder(), "Color",
|
||||
TitledBorder.LEFT, TitledBorder.TOP));
|
||||
|
||||
JCheckBox grayscaleCheck = new JCheckBox("Grayscale");
|
||||
JCheckBox invertCheck = new JCheckBox("Invert Colors");
|
||||
|
||||
grayscaleCheck.addActionListener(e -> cameraPanel.setGrayscale(grayscaleCheck.isSelected()));
|
||||
invertCheck.addActionListener(e -> cameraPanel.setInvert(invertCheck.isSelected()));
|
||||
|
||||
colorPanel.add(grayscaleCheck);
|
||||
colorPanel.add(Box.createRigidArea(new Dimension(0, 5)));
|
||||
colorPanel.add(invertCheck);
|
||||
|
||||
JPanel brightnessPanel = new JPanel();
|
||||
brightnessPanel.setLayout(new BoxLayout(brightnessPanel, BoxLayout.Y_AXIS));
|
||||
brightnessPanel.setBorder(BorderFactory.createTitledBorder(
|
||||
BorderFactory.createEtchedBorder(), "Adjustments",
|
||||
TitledBorder.LEFT, TitledBorder.TOP));
|
||||
|
||||
JPanel brightnessSliderPanel = new JPanel(new BorderLayout());
|
||||
JLabel brightnessLabel = new JLabel("Brightness: 0");
|
||||
JSlider brightnessSlider = new JSlider(-100, 100, 0);
|
||||
brightnessSlider.addChangeListener(e -> {
|
||||
int value = brightnessSlider.getValue();
|
||||
brightnessLabel.setText("Brightness: " + value);
|
||||
cameraPanel.setBrightness(value);
|
||||
});
|
||||
brightnessSliderPanel.add(brightnessLabel, BorderLayout.NORTH);
|
||||
brightnessSliderPanel.add(brightnessSlider, BorderLayout.CENTER);
|
||||
|
||||
JPanel contrastSliderPanel = new JPanel(new BorderLayout());
|
||||
JLabel contrastLabel = new JLabel("Contrast: 1.0");
|
||||
JSlider contrastSlider = new JSlider(0, 200, 100);
|
||||
contrastSlider.addChangeListener(e -> {
|
||||
float value = contrastSlider.getValue() / 100f;
|
||||
contrastLabel.setText("Contrast: " + String.format("%.1f", value));
|
||||
cameraPanel.setContrast(value);
|
||||
});
|
||||
contrastSliderPanel.add(contrastLabel, BorderLayout.NORTH);
|
||||
contrastSliderPanel.add(contrastSlider, BorderLayout.CENTER);
|
||||
|
||||
brightnessPanel.add(brightnessSliderPanel);
|
||||
brightnessPanel.add(Box.createRigidArea(new Dimension(0, 10)));
|
||||
brightnessPanel.add(contrastSliderPanel);
|
||||
|
||||
JButton resetButton = new JButton("Reset All Effects");
|
||||
resetButton.setAlignmentX(Component.CENTER_ALIGNMENT);
|
||||
resetButton.addActionListener(e -> {
|
||||
mirrorCheck.setSelected(false);
|
||||
flipCheck.setSelected(false);
|
||||
rotateCheck.setSelected(false);
|
||||
grayscaleCheck.setSelected(false);
|
||||
invertCheck.setSelected(false);
|
||||
brightnessSlider.setValue(0);
|
||||
contrastSlider.setValue(100);
|
||||
cameraPanel.resetEffects();
|
||||
});
|
||||
|
||||
panel.add(transformPanel);
|
||||
panel.add(Box.createRigidArea(new Dimension(0, 10)));
|
||||
panel.add(colorPanel);
|
||||
panel.add(Box.createRigidArea(new Dimension(0, 10)));
|
||||
panel.add(brightnessPanel);
|
||||
panel.add(Box.createRigidArea(new Dimension(0, 15)));
|
||||
panel.add(resetButton);
|
||||
panel.add(Box.createVerticalGlue());
|
||||
|
||||
return panel;
|
||||
}
|
||||
|
||||
private void browseDirectory(JTextField field, Component parent) {
|
||||
JFileChooser chooser = new JFileChooser(field.getText());
|
||||
chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
|
||||
if (chooser.showOpenDialog(parent) == JFileChooser.APPROVE_OPTION) {
|
||||
field.setText(chooser.getSelectedFile().getAbsolutePath());
|
||||
}
|
||||
}
|
||||
|
||||
private void saveSnapshot(CameraPanel cameraPanel, Webcam webcam, String directory, Component parent) {
|
||||
BufferedImage img = cameraPanel.getCurrentProcessedImage();
|
||||
if (img != null) {
|
||||
try {
|
||||
File dir = new File(directory);
|
||||
dir.mkdirs();
|
||||
|
||||
String timestamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
|
||||
String filename = "screenshot_" + webcam.getName().replaceAll("[^a-zA-Z0-9]", "_")
|
||||
+ "_" + timestamp + ".png";
|
||||
File file = new File(dir, filename);
|
||||
ImageIO.write(img, "PNG", file);
|
||||
} catch (Exception ex) {
|
||||
JOptionPane.showMessageDialog(parent,
|
||||
"Error: " + ex.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void show() {
|
||||
mainFrame.setLocationRelativeTo(null);
|
||||
mainFrame.setVisible(true);
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
SwingIFrame dashboard = new SwingIFrame();
|
||||
dashboard.show();
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,12 @@
|
||||
package io.swtc.proccessing;
|
||||
|
||||
/*
|
||||
*
|
||||
* Soon to be deprecated!
|
||||
*
|
||||
* */
|
||||
|
||||
@Deprecated
|
||||
public class AutoGainProcessor {
|
||||
|
||||
public float[] calculateAutoGains(int[] pixels) {
|
||||
|
||||
204
src/main/java/io/swtc/proccessing/CameraPanel.java
Normal file
204
src/main/java/io/swtc/proccessing/CameraPanel.java
Normal file
@@ -0,0 +1,204 @@
|
||||
package io.swtc.proccessing;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
|
||||
/*
|
||||
*
|
||||
* Now here its getting rather interesting! this class processes some
|
||||
* important stuff!
|
||||
*
|
||||
* */
|
||||
|
||||
public class CameraPanel extends JPanel {
|
||||
private BufferedImage currentImage;
|
||||
private boolean mirror = false;
|
||||
private boolean flip = false;
|
||||
private boolean rotate = false;
|
||||
private boolean grayscale = false;
|
||||
private boolean invert = false;
|
||||
private int brightness = 0;
|
||||
private float contrast = 1.0f;
|
||||
|
||||
public void setImage(BufferedImage img) {
|
||||
this.currentImage = img;
|
||||
this.repaint();
|
||||
}
|
||||
|
||||
public BufferedImage getCurrentImage() {
|
||||
return currentImage;
|
||||
}
|
||||
|
||||
public BufferedImage getCurrentProcessedImage() {
|
||||
if (currentImage == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
BufferedImage processed = currentImage;
|
||||
|
||||
// apply color effects
|
||||
if (grayscale || invert || brightness != 0 || contrast != 1.0f) {
|
||||
processed = applyColorEffects(processed);
|
||||
}
|
||||
|
||||
// apply transform.
|
||||
if (mirror || flip || rotate) {
|
||||
processed = applyTransforms(processed);
|
||||
}
|
||||
|
||||
return processed;
|
||||
}
|
||||
|
||||
/* Helper Methods ... its the same boilerplate shit over and over again, im sick of this. */
|
||||
public void setMirror(boolean mirror) {
|
||||
this.mirror = mirror;
|
||||
this.repaint();
|
||||
}
|
||||
|
||||
public void setFlip(boolean flip) {
|
||||
this.flip = flip;
|
||||
this.repaint();
|
||||
}
|
||||
|
||||
public void setRotate(boolean rotate) {
|
||||
this.rotate = rotate;
|
||||
this.repaint();
|
||||
}
|
||||
|
||||
public void setGrayscale(boolean grayscale) {
|
||||
this.grayscale = grayscale;
|
||||
this.repaint();
|
||||
}
|
||||
|
||||
public void setInvert(boolean invert) {
|
||||
this.invert = invert;
|
||||
this.repaint();
|
||||
}
|
||||
|
||||
public void setBrightness(int brightness) {
|
||||
this.brightness = brightness;
|
||||
this.repaint();
|
||||
}
|
||||
|
||||
public void setContrast(float contrast) {
|
||||
this.contrast = contrast;
|
||||
this.repaint();
|
||||
}
|
||||
|
||||
public void resetEffects() {
|
||||
mirror = flip = rotate = grayscale = invert = false;
|
||||
brightness = 0;
|
||||
contrast = 1.0f;
|
||||
this.repaint();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paintComponent(Graphics g) {
|
||||
super.paintComponent(g);
|
||||
if (currentImage != null) {
|
||||
Graphics2D g2 = (Graphics2D) g;
|
||||
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
|
||||
|
||||
BufferedImage processedImage = currentImage;
|
||||
|
||||
// effects
|
||||
if (grayscale || invert || brightness != 0 || contrast != 1.0f) {
|
||||
processedImage = applyColorEffects(processedImage);
|
||||
}
|
||||
|
||||
// transforms
|
||||
int w = getWidth(), h = getHeight();
|
||||
|
||||
if (rotate) {
|
||||
g2.translate(w / 2, h / 2);
|
||||
g2.rotate(Math.PI);
|
||||
g2.translate(-w / 2, -h / 2);
|
||||
}
|
||||
|
||||
|
||||
// here we have if, cause we need to do the stuff, yk?
|
||||
if (mirror && flip) {
|
||||
g2.drawImage(processedImage, w, h, -w, -h, null);
|
||||
} else if (mirror) {
|
||||
g2.drawImage(processedImage, w, 0, -w, h, null);
|
||||
} else if (flip) {
|
||||
g2.drawImage(processedImage, 0, h, w, -h, null);
|
||||
} else {
|
||||
g2.drawImage(processedImage, 0, 0, w, h, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private BufferedImage applyColorEffects(BufferedImage img) {
|
||||
BufferedImage result = new BufferedImage(img.getWidth(), img.getHeight(), BufferedImage.TYPE_INT_RGB);
|
||||
|
||||
for (int y = 0; y < img.getHeight(); y++) {
|
||||
for (int x = 0; x < img.getWidth(); x++) {
|
||||
int rgb = img.getRGB(x, y);
|
||||
int r = (rgb >> 16) & 0xFF;
|
||||
int g = (rgb >> 8) & 0xFF;
|
||||
int b = rgb & 0xFF;
|
||||
|
||||
if (grayscale) {
|
||||
int gray = (r + g + b) / 3;
|
||||
r = g = b = gray;
|
||||
}
|
||||
|
||||
// this is fun, this regulates the brightness or the contrast!
|
||||
// this is some real java, the other stuff is just UI design, which i am bad at,
|
||||
// but this! This is some real shit
|
||||
r = (int) ((r - 128) * contrast + 128 + brightness);
|
||||
g = (int) ((g - 128) * contrast + 128 + brightness);
|
||||
b = (int) ((b - 128) * contrast + 128 + brightness);
|
||||
|
||||
// invert the colors!
|
||||
if (invert) {
|
||||
r = 255 - r;
|
||||
g = 255 - g;
|
||||
b = 255 - b;
|
||||
}
|
||||
|
||||
// clamp so we dont get into weird color grades, or any weird looking spaces
|
||||
// if we dont do this we cant really do stuff which looks bearable
|
||||
r = Math.max(0, Math.min(255, r));
|
||||
g = Math.max(0, Math.min(255, g));
|
||||
b = Math.max(0, Math.min(255, b));
|
||||
|
||||
result.setRGB(x, y, (r << 16) | (g << 8) | b);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private BufferedImage applyTransforms(BufferedImage img) {
|
||||
int width = img.getWidth();
|
||||
int height = img.getHeight();
|
||||
BufferedImage result = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
|
||||
|
||||
for (int y = 0; y < height; y++) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
int sourceX = x;
|
||||
int sourceY = y;
|
||||
|
||||
if (mirror) {
|
||||
sourceX = width - 1 - x;
|
||||
}
|
||||
if (flip) {
|
||||
sourceY = height - 1 - y;
|
||||
}
|
||||
if (rotate) {
|
||||
int tempX = width - 1 - sourceX;
|
||||
int tempY = height - 1 - sourceY;
|
||||
sourceX = tempX;
|
||||
sourceY = tempY;
|
||||
}
|
||||
|
||||
result.setRGB(x, y, img.getRGB(sourceX, sourceY));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
114
src/main/java/io/swtc/recording/VideoRecorder.java
Normal file
114
src/main/java/io/swtc/recording/VideoRecorder.java
Normal file
@@ -0,0 +1,114 @@
|
||||
package io.swtc.recording;
|
||||
|
||||
import io.swtc.proccessing.CameraPanel;
|
||||
import org.jcodec.api.awt.AWTSequenceEncoder;
|
||||
import org.jcodec.common.io.NIOUtils;
|
||||
import org.jcodec.common.io.SeekableByteChannel;
|
||||
import org.jcodec.common.model.Rational;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
/*
|
||||
*
|
||||
* This is not that interesting but surely improtant for any security based people,
|
||||
* here we record using jcodec, which is more efficient than me writing my own recorder,
|
||||
* which i am certainly not doing.
|
||||
*
|
||||
* Anyways we dont do shit properly anyway.
|
||||
*
|
||||
* */
|
||||
|
||||
public class VideoRecorder {
|
||||
private boolean recording = false;
|
||||
private Timer captureTimer;
|
||||
private File outputFile;
|
||||
private AWTSequenceEncoder encoder;
|
||||
private SeekableByteChannel channel;
|
||||
private static final int FPS = 30;
|
||||
|
||||
public void startRecording(CameraPanel panel, File output) throws IOException {
|
||||
this.outputFile = output;
|
||||
this.recording = true;
|
||||
|
||||
channel = NIOUtils.writableFileChannel(String.valueOf(outputFile));
|
||||
encoder = new AWTSequenceEncoder(channel, Rational.R(FPS, 1));
|
||||
|
||||
captureTimer = new Timer(1000 / FPS, e -> {
|
||||
try {
|
||||
BufferedImage img = panel.getCurrentProcessedImage();
|
||||
if (img != null) {
|
||||
BufferedImage rgbImage = convertToRGB(img);
|
||||
encoder.encodeImage(rgbImage);
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
JOptionPane.showMessageDialog(
|
||||
null,
|
||||
"IOException\n" + ex,
|
||||
"IOException in Recorder",
|
||||
JOptionPane.ERROR_MESSAGE
|
||||
);
|
||||
try {
|
||||
stopRecording();
|
||||
} catch (IOException stopEx) {
|
||||
JOptionPane.showMessageDialog(
|
||||
null,
|
||||
"IOException@stopEx\n" + stopEx,
|
||||
"IOException in Recorder@stopEx",
|
||||
JOptionPane.ERROR_MESSAGE
|
||||
);
|
||||
|
||||
}
|
||||
}
|
||||
});
|
||||
captureTimer.start();
|
||||
}
|
||||
|
||||
public File stopRecording() throws IOException {
|
||||
recording = false;
|
||||
|
||||
/* some helper methods, i swear its always the same? */
|
||||
if (captureTimer != null) {
|
||||
captureTimer.stop();
|
||||
}
|
||||
|
||||
if (encoder != null) {
|
||||
encoder.finish();
|
||||
}
|
||||
|
||||
if (channel != null) {
|
||||
channel.close();
|
||||
}
|
||||
|
||||
return outputFile;
|
||||
}
|
||||
|
||||
public boolean isRecording() {
|
||||
return recording;
|
||||
}
|
||||
|
||||
// maybe i should move this to a component, some useless conversion right here,
|
||||
// (performance wise)
|
||||
// yes, capitain?
|
||||
private BufferedImage convertToRGB(BufferedImage source) {
|
||||
if (source.getType() == BufferedImage.TYPE_INT_RGB) {
|
||||
return source;
|
||||
}
|
||||
|
||||
BufferedImage rgb = new BufferedImage(
|
||||
source.getWidth(),
|
||||
source.getHeight(),
|
||||
BufferedImage.TYPE_INT_RGB
|
||||
);
|
||||
|
||||
for (int y = 0; y < source.getHeight(); y++) {
|
||||
for (int x = 0; x < source.getWidth(); x++) {
|
||||
rgb.setRGB(x, y, source.getRGB(x, y) & 0xFFFFFF);
|
||||
}
|
||||
}
|
||||
|
||||
return rgb;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user