initial
This commit is contained in:
44
.gitignore
vendored
Normal file
44
.gitignore
vendored
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
target/
|
||||||
|
!.mvn/wrapper/maven-wrapper.jar
|
||||||
|
!**/src/main/**/target/
|
||||||
|
!**/src/test/**/target/
|
||||||
|
.kotlin
|
||||||
|
|
||||||
|
### IntelliJ IDEA ###
|
||||||
|
.idea/modules.xml
|
||||||
|
.idea/jarRepositories.xml
|
||||||
|
.idea/compiler.xml
|
||||||
|
.idea/libraries/
|
||||||
|
*.iws
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
|
||||||
|
### Eclipse ###
|
||||||
|
.apt_generated
|
||||||
|
.classpath
|
||||||
|
.factorypath
|
||||||
|
.project
|
||||||
|
.settings
|
||||||
|
.springBeans
|
||||||
|
.sts4-cache
|
||||||
|
|
||||||
|
### NetBeans ###
|
||||||
|
/nbproject/private/
|
||||||
|
/nbbuild/
|
||||||
|
/dist/
|
||||||
|
/nbdist/
|
||||||
|
/.nb-gradle/
|
||||||
|
build/
|
||||||
|
!**/src/main/**/build/
|
||||||
|
!**/src/test/**/build/
|
||||||
|
|
||||||
|
### VS Code ###
|
||||||
|
.vscode/
|
||||||
|
|
||||||
|
### Mac OS ###
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
### Other stuff ###
|
||||||
|
|
||||||
|
.mvn
|
||||||
|
.idea
|
||||||
58
pom.xml
Normal file
58
pom.xml
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<groupId>io.swtc</groupId>
|
||||||
|
<artifactId>swtc</artifactId>
|
||||||
|
<version>1.0-SNAPSHOT</version>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<maven.compiler.source>17</maven.compiler.source>
|
||||||
|
<maven.compiler.target>17</maven.compiler.target>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
<javacpp.platform>windows-x86_64</javacpp.platform>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.eclipse.platform</groupId>
|
||||||
|
<artifactId>org.eclipse.swt.win32.win32.x86_64</artifactId>
|
||||||
|
<version>3.132.0</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.github.sarxos</groupId>
|
||||||
|
<artifactId>webcam-capture</artifactId>
|
||||||
|
<version>0.3.12</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- for gl we use lwjgl -->
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.lwjgl</groupId>
|
||||||
|
<artifactId>lwjgl</artifactId>
|
||||||
|
<version>3.3.3</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.lwjgl</groupId>
|
||||||
|
<artifactId>lwjgl-opengl</artifactId>
|
||||||
|
<version>3.3.3</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.lwjgl</groupId>
|
||||||
|
<artifactId>lwjgl</artifactId>
|
||||||
|
<version>3.3.3</version>
|
||||||
|
<classifier>natives-windows</classifier>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.lwjgl</groupId>
|
||||||
|
<artifactId>lwjgl-opengl</artifactId>
|
||||||
|
<version>3.3.3</version>
|
||||||
|
<classifier>natives-windows</classifier>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</project>
|
||||||
74
src/main/java/io/swtc/CCTVManager.java
Normal file
74
src/main/java/io/swtc/CCTVManager.java
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
package io.swtc;
|
||||||
|
|
||||||
|
import com.github.sarxos.webcam.Webcam;
|
||||||
|
import org.eclipse.swt.SWT;
|
||||||
|
import org.eclipse.swt.graphics.Font;
|
||||||
|
import org.eclipse.swt.layout.GridData;
|
||||||
|
import org.eclipse.swt.layout.GridLayout;
|
||||||
|
import org.eclipse.swt.widgets.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class CCTVManager {
|
||||||
|
public static void main(String[] args) {
|
||||||
|
Display display = new Display();
|
||||||
|
Shell shell = new Shell(display, SWT.SHELL_TRIM | SWT.ON_TOP);
|
||||||
|
shell.setText("Dashboard");
|
||||||
|
|
||||||
|
List<Webcam> webcams = Webcam.getWebcams();
|
||||||
|
int columnCount = webcams.size() > 3 ? 3 : Math.max(1, webcams.size());
|
||||||
|
|
||||||
|
GridLayout mainLayout = new GridLayout(columnCount, true);
|
||||||
|
mainLayout.marginWidth = 20;
|
||||||
|
mainLayout.marginHeight = 20;
|
||||||
|
mainLayout.horizontalSpacing = 15;
|
||||||
|
mainLayout.verticalSpacing = 15;
|
||||||
|
shell.setLayout(mainLayout);
|
||||||
|
|
||||||
|
// Header (Spans across all columns)
|
||||||
|
Label title = new Label(shell, SWT.NONE);
|
||||||
|
title.setText("Connected Devices");
|
||||||
|
title.setFont(new Font(display, "Segoe UI", 14, SWT.BOLD));
|
||||||
|
GridData titleData = new GridData(SWT.FILL, SWT.CENTER, true, false, columnCount, 1);
|
||||||
|
title.setLayoutData(titleData);
|
||||||
|
|
||||||
|
if (webcams.isEmpty()) {
|
||||||
|
Label error = new Label(shell, SWT.NONE);
|
||||||
|
error.setText("no available cameras!");
|
||||||
|
} else {
|
||||||
|
for (Webcam webcam : webcams) {
|
||||||
|
createCameraCard(shell, display, webcam);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
shell.pack();
|
||||||
|
shell.open();
|
||||||
|
|
||||||
|
while (!shell.isDisposed()) {
|
||||||
|
if (!display.readAndDispatch()) {
|
||||||
|
display.sleep();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
display.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void createCameraCard(Composite parent, Display display, Webcam webcam) {
|
||||||
|
Group card = new Group(parent, SWT.NONE);
|
||||||
|
card.setText(webcam.getName());
|
||||||
|
card.setLayout(new GridLayout(1, false));
|
||||||
|
card.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
|
||||||
|
|
||||||
|
Button btn = new Button(card, SWT.PUSH);
|
||||||
|
btn.setText("View");
|
||||||
|
btn.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
|
||||||
|
|
||||||
|
btn.addListener(SWT.Selection, e -> {
|
||||||
|
/**
|
||||||
|
* This is where magic happens! We start the Window Here! As arguments, we give our display and webcam index!
|
||||||
|
* */
|
||||||
|
System.out.println("Starting: " + webcam.getName());
|
||||||
|
CameraWindow window = new CameraWindow(display,webcam);
|
||||||
|
window.open();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
62
src/main/java/io/swtc/CameraWindow.java
Normal file
62
src/main/java/io/swtc/CameraWindow.java
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
package io.swtc;
|
||||||
|
|
||||||
|
import com.github.sarxos.webcam.Webcam;
|
||||||
|
import io.swtc.proccessing.CameraRenderer;
|
||||||
|
import io.swtc.proccessing.WebcamCaptureLoop;
|
||||||
|
import org.eclipse.swt.SWT;
|
||||||
|
import org.eclipse.swt.layout.FillLayout;
|
||||||
|
import org.eclipse.swt.opengl.GLData;
|
||||||
|
import org.eclipse.swt.widgets.Display;
|
||||||
|
import org.eclipse.swt.widgets.Shell;
|
||||||
|
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
|
||||||
|
|
||||||
|
public class CameraWindow {
|
||||||
|
private final Shell shell;
|
||||||
|
private final CameraRenderer renderer;
|
||||||
|
private final WebcamCaptureLoop captureLoop;
|
||||||
|
|
||||||
|
public CameraWindow(Display display, Webcam webcam) {
|
||||||
|
this.shell = new Shell(display);
|
||||||
|
this.shell.setText("swt-cctv@" + webcam.getName());
|
||||||
|
this.shell.setLayout(new FillLayout());
|
||||||
|
this.shell.setSize(640, 480);
|
||||||
|
|
||||||
|
GLData data = new GLData();
|
||||||
|
data.doubleBuffer = true;
|
||||||
|
this.renderer = new CameraRenderer(shell, data);
|
||||||
|
|
||||||
|
this.captureLoop = new WebcamCaptureLoop(webcam, (BufferedImage img) -> {
|
||||||
|
if (!display.isDisposed() && !shell.isDisposed()) {
|
||||||
|
// terribly uneficcient. very stupid tbh
|
||||||
|
display.syncExec(() -> renderer.render(img));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.shell.addListener(SWT.Close, event -> {
|
||||||
|
captureLoop.stop();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void open() {
|
||||||
|
shell.open();
|
||||||
|
renderer.getCanvas().setCurrent();
|
||||||
|
captureLoop.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
Display display = new Display();
|
||||||
|
Webcam webcam = Webcam.getDefault();
|
||||||
|
|
||||||
|
CameraWindow window = new CameraWindow(display, webcam);
|
||||||
|
window.open();
|
||||||
|
|
||||||
|
while (!window.shell.isDisposed()) {
|
||||||
|
if (!display.readAndDispatch()) {
|
||||||
|
display.sleep();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
display.dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
7
src/main/java/io/swtc/Main.java
Normal file
7
src/main/java/io/swtc/Main.java
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
package io.swtc;
|
||||||
|
public class Main {
|
||||||
|
public static void main(String[] args) {
|
||||||
|
// very simple main, so that we can start the manager!
|
||||||
|
CCTVManager.main(args);
|
||||||
|
}
|
||||||
|
}
|
||||||
35
src/main/java/io/swtc/proccessing/AutoGainProcessor.java
Normal file
35
src/main/java/io/swtc/proccessing/AutoGainProcessor.java
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
package io.swtc.proccessing;
|
||||||
|
|
||||||
|
public class AutoGainProcessor {
|
||||||
|
|
||||||
|
public float[] calculateAutoGains(int[] pixels) {
|
||||||
|
long rSum = 0, gSum = 0, bSum = 0;
|
||||||
|
|
||||||
|
int step = 8;
|
||||||
|
int sampledCount = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < pixels.length; i += step) {
|
||||||
|
int pixel = pixels[i];
|
||||||
|
rSum += (pixel >> 16) & 0xFF;
|
||||||
|
gSum += (pixel >> 8) & 0xFF;
|
||||||
|
bSum += pixel & 0xFF;
|
||||||
|
sampledCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sampledCount == 0) return new float[]{1f, 1f, 1f};
|
||||||
|
|
||||||
|
float rAvg = (float) rSum / sampledCount;
|
||||||
|
float gAvg = (float) gSum / sampledCount;
|
||||||
|
float bAvg = (float) bSum / sampledCount;
|
||||||
|
|
||||||
|
float grayAvg = (rAvg + gAvg + bAvg) / 3.0f;
|
||||||
|
|
||||||
|
if (grayAvg < 1.0f) return new float[]{1.0f, 1.0f, 1.0f};
|
||||||
|
|
||||||
|
return new float[]{
|
||||||
|
Math.min(2.0f, grayAvg / rAvg),
|
||||||
|
Math.min(2.0f, grayAvg / gAvg),
|
||||||
|
Math.min(2.0f, grayAvg / bAvg)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
96
src/main/java/io/swtc/proccessing/CameraRenderer.java
Normal file
96
src/main/java/io/swtc/proccessing/CameraRenderer.java
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
package io.swtc.proccessing;
|
||||||
|
|
||||||
|
import org.eclipse.swt.opengl.GLCanvas;
|
||||||
|
import org.eclipse.swt.widgets.Composite;
|
||||||
|
import org.lwjgl.opengl.GL;
|
||||||
|
import org.lwjgl.opengl.GL11;
|
||||||
|
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.ByteOrder;
|
||||||
|
|
||||||
|
public class CameraRenderer {
|
||||||
|
private final GLCanvas canvas;
|
||||||
|
private int textureId = -1;
|
||||||
|
private ByteBuffer pixelBuffer;
|
||||||
|
private final AutoGainProcessor gainProcessor;
|
||||||
|
|
||||||
|
public CameraRenderer(Composite parent, org.eclipse.swt.opengl.GLData data) {
|
||||||
|
this.canvas = new GLCanvas(parent, 0, data);
|
||||||
|
this.gainProcessor = new AutoGainProcessor();
|
||||||
|
|
||||||
|
// Initialize OpenGL context immediately
|
||||||
|
this.canvas.setCurrent();
|
||||||
|
GL.createCapabilities();
|
||||||
|
initGL();
|
||||||
|
}
|
||||||
|
|
||||||
|
public GLCanvas getCanvas() {
|
||||||
|
return canvas;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initGL() {
|
||||||
|
GL11.glEnable(GL11.GL_TEXTURE_2D);
|
||||||
|
textureId = GL11.glGenTextures();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void render(BufferedImage img) {
|
||||||
|
if (canvas.isDisposed()) return;
|
||||||
|
|
||||||
|
canvas.setCurrent();
|
||||||
|
|
||||||
|
int width = img.getWidth();
|
||||||
|
int height = img.getHeight();
|
||||||
|
|
||||||
|
int[] rgbArray = new int[width * height];
|
||||||
|
// this is hellishly unefficcient but who cares.
|
||||||
|
img.getRGB(0, 0, width, height, rgbArray, 0, width);
|
||||||
|
|
||||||
|
float[] gains = gainProcessor.calculateAutoGains(rgbArray);
|
||||||
|
|
||||||
|
int bufferSize = width * height * 3;
|
||||||
|
if (pixelBuffer == null || pixelBuffer.capacity() < bufferSize) {
|
||||||
|
pixelBuffer = ByteBuffer.allocateDirect(bufferSize);
|
||||||
|
pixelBuffer.order(ByteOrder.nativeOrder());
|
||||||
|
}
|
||||||
|
pixelBuffer.clear();
|
||||||
|
|
||||||
|
for (int pixel : rgbArray) {
|
||||||
|
int r = (pixel >> 16) & 0xFF;
|
||||||
|
int g = (pixel >> 8) & 0xFF;
|
||||||
|
int b = pixel & 0xFF;
|
||||||
|
|
||||||
|
r = Math.min(255, (int)(r * gains[0]));
|
||||||
|
g = Math.min(255, (int)(g * gains[1]));
|
||||||
|
b = Math.min(255, (int)(b * gains[2]));
|
||||||
|
|
||||||
|
pixelBuffer.put((byte) r);
|
||||||
|
pixelBuffer.put((byte) g);
|
||||||
|
pixelBuffer.put((byte) b);
|
||||||
|
}
|
||||||
|
pixelBuffer.flip();
|
||||||
|
|
||||||
|
// this is just gl stuff
|
||||||
|
GL11.glClear(GL11.GL_COLOR_BUFFER_BIT);
|
||||||
|
GL11.glViewport(0, 0, canvas.getClientArea().width, canvas.getClientArea().height);
|
||||||
|
|
||||||
|
GL11.glBindTexture(GL11.GL_TEXTURE_2D, textureId);
|
||||||
|
GL11.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 1);
|
||||||
|
|
||||||
|
GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGB, width, height,
|
||||||
|
0, GL11.GL_RGB, GL11.GL_UNSIGNED_BYTE, pixelBuffer);
|
||||||
|
|
||||||
|
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR);
|
||||||
|
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR);
|
||||||
|
|
||||||
|
GL11.glColor3f(1.0f, 1.0f, 1.0f);
|
||||||
|
GL11.glBegin(GL11.GL_QUADS);
|
||||||
|
GL11.glTexCoord2f(0, 0); GL11.glVertex2f(-1, 1);
|
||||||
|
GL11.glTexCoord2f(1, 0); GL11.glVertex2f(1, 1);
|
||||||
|
GL11.glTexCoord2f(1, 1); GL11.glVertex2f(1, -1);
|
||||||
|
GL11.glTexCoord2f(0, 1); GL11.glVertex2f(-1, -1);
|
||||||
|
GL11.glEnd();
|
||||||
|
|
||||||
|
canvas.swapBuffers();
|
||||||
|
}
|
||||||
|
}
|
||||||
50
src/main/java/io/swtc/proccessing/WebcamCaptureLoop.java
Normal file
50
src/main/java/io/swtc/proccessing/WebcamCaptureLoop.java
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
package io.swtc.proccessing;
|
||||||
|
|
||||||
|
import com.github.sarxos.webcam.Webcam;
|
||||||
|
import java.awt.Dimension;
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
public class WebcamCaptureLoop {
|
||||||
|
private final Webcam webcam;
|
||||||
|
private final Consumer<BufferedImage> onFrameCaptured;
|
||||||
|
private volatile boolean running = false;
|
||||||
|
|
||||||
|
public WebcamCaptureLoop(Webcam webcam, Consumer<BufferedImage> onFrameCaptured) {
|
||||||
|
this.webcam = webcam;
|
||||||
|
this.onFrameCaptured = onFrameCaptured;
|
||||||
|
|
||||||
|
// Configure webcam
|
||||||
|
Dimension[] sizes = webcam.getViewSizes();
|
||||||
|
webcam.setViewSize(sizes[sizes.length - 1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void start() {
|
||||||
|
if (running) return;
|
||||||
|
running = true;
|
||||||
|
|
||||||
|
Thread captureThread = new Thread(() -> {
|
||||||
|
webcam.open();
|
||||||
|
|
||||||
|
while (running) {
|
||||||
|
BufferedImage img = webcam.getImage();
|
||||||
|
if (img != null) {
|
||||||
|
onFrameCaptured.accept(img);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
Thread.sleep(15);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
webcam.close();
|
||||||
|
});
|
||||||
|
captureThread.setName("cam_cap_thread");
|
||||||
|
captureThread.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stop() {
|
||||||
|
running = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user