--- /dev/null
+.idea
+CakeLight.iml
+out
--- /dev/null
+# default settings
+
+video.device=/dev/video0
+video.width=720
+video.height=576
+video.bpp=2
+
+leds.cols=32
+leds.rows=18
--- /dev/null
+package kaka.cakelight;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.*;
+import java.util.stream.Collectors;
+
+public class Configuration {
+ private List<Map.Entry<String, String>> settings = new ArrayList<>();
+ public VideoConfiguration video;
+ public LedConfiguration leds;
+
+ private Configuration(Properties prop) {
+ video = new VideoConfiguration(prop);
+ leds = new LedConfiguration(prop);
+ }
+
+ public static Configuration from(String propertiesFile) {
+ InputStream input = null;
+ try {
+ input = new FileInputStream(propertiesFile);
+ Properties prop = new Properties();
+ prop.load(input);
+ return new Configuration(prop);
+ } catch (IOException ex) {
+ ex.printStackTrace();
+ } finally {
+ if (input != null) {
+ try {
+ input.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ return null;
+ }
+
+ private String get(Properties prop, String name) {
+ return addSetting(name, prop.getProperty(name));
+ }
+
+ private String get(Properties prop, String name, String dflt) {
+ return addSetting(name, prop.getProperty(name, dflt));
+ }
+
+ private String addSetting(String name, String value) {
+ settings.add(new AbstractMap.SimpleEntry<>(name, value));
+ return value;
+ }
+
+ @Override
+ public String toString() {
+ return settings.stream().map(entry -> entry.getKey() + "=" + entry.getValue()).collect(Collectors.joining("\n"));
+ }
+
+ public class VideoConfiguration {
+ public String device;
+ public int width;
+ public int height;
+ public int bpp;
+
+ private VideoConfiguration(Properties prop) {
+ device = get(prop, "video.device", "/dev/video0");
+ width = Integer.parseInt(get(prop, "video.width", "720"));
+ height = Integer.parseInt(get(prop, "video.height", "576"));
+ bpp = Integer.parseInt(get(prop, "video.bpp", "2"));
+ }
+ }
+
+ public class LedConfiguration {
+ public int cols;
+ public int rows;
+
+ private LedConfiguration(Properties prop) {
+ cols = Integer.parseInt(get(prop, "leds.cols"));
+ rows = Integer.parseInt(get(prop, "leds.rows"));
+ }
+ }
+}
--- /dev/null
+package kaka.cakelight;
+
+import org.opencv.core.CvType;
+import org.opencv.core.Mat;
+import org.opencv.core.Size;
+import org.opencv.imgproc.Imgproc;
+
+import static kaka.cakelight.Main.saveFile;
+import static kaka.cakelight.Main.timeIt;
+
+public class Frame {
+ private byte[] data;
+ private Configuration config;
+
+ private Frame(byte[] data) {
+ this.data = data;
+ }
+
+ public static Frame of(byte[] data, Configuration config) {
+ Frame frame = new Frame(data);
+ frame.config = config;
+ frame.convert();
+ return frame;
+ }
+
+ private void convert() {
+ Mat src = new Mat(config.video.height, config.video.width, CvType.CV_8UC2); // 8-bit, unsigned, 2 channels
+ src.put(0, 0, data);
+
+ Mat converted = new Mat();
+ Mat resized = new Mat();
+
+ timeIt("total", () -> {
+ timeIt("yuyv2rgb", () -> Imgproc.cvtColor(src, converted, Imgproc.COLOR_YUV2RGB_YUYV)); // 3.5 - 4.0 ms
+ timeIt("resizing", () -> Imgproc.resize(converted, resized, new Size(config.leds.cols, config.leds.rows), 0, 0, Imgproc.INTER_AREA)); // INTER_AREA is the best for shrinking, but also the slowest (~1.5 ms)
+ });
+// save(converted, "/home/kaka/test-converted.data");
+// save(resized, "/home/kaka/test-resized.data");
+ }
+
+ private void save(Mat mat, String filepath) {
+ byte[] data = new byte[mat.cols() * mat.rows() * mat.channels()];
+ mat.get(0, 0, data);
+ saveFile(data, filepath);
+ }
+
+ public byte[] getData() {
+ return data;
+ }
+}
--- /dev/null
+package kaka.cakelight;
+
+import java.io.*;
+
+public class FrameGrabber {
+ private Configuration config;
+ private File file;
+ private int bytesPerFrame;
+ private InputStream fileStream;
+
+ private FrameGrabber() {
+ }
+
+ public static FrameGrabber from(Configuration config) {
+ FrameGrabber fg = new FrameGrabber();
+ fg.config = config;
+ fg.file = new File(config.video.device);
+ fg.bytesPerFrame = config.video.width * config.video.height * config.video.bpp;
+ return fg;
+ }
+
+ public boolean prepare() {
+ try {
+ fileStream = new FileInputStream(file);
+ return true;
+ } catch (FileNotFoundException e) {
+ e.printStackTrace();
+ return false;
+ }
+ }
+
+ public Frame grabFrame() {
+ try {
+ byte[] data = new byte[bytesPerFrame];
+ int count = fileStream.read(data);
+ System.out.println("count = " + count);
+ return Frame.of(data, config);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ return null;
+ }
+
+ public void close() {
+ try {
+ fileStream.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+}
--- /dev/null
+package kaka.cakelight;
+
+import org.opencv.core.Core;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+public class Main {
+
+ public static void main(String[] args) {
+ System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
+
+ Configuration config = Configuration.from("config.properties");
+ System.out.println("Running with config:\n" + config);
+
+ FrameGrabber grabber = FrameGrabber.from(config);
+ grabber.prepare();
+ Frame frame = grabber.grabFrame();
+ double time = 0;
+ for (int i = 0; i < 100; i++) {
+ time += timeIt("frame", () -> grabber.grabFrame());
+ }
+ System.out.println("time = " + time);
+ grabber.close();
+// byte[] data = frame.getData();
+// saveFile(data, "/home/kaka/test.img");
+ }
+
+ public static void saveFile(byte[] data, String filepath) {
+ try {
+ FileOutputStream fos = new FileOutputStream(filepath);
+ fos.write(data);
+ fos.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public static double timeIt(String tag, Runnable lambda) {
+ long start = System.nanoTime();
+ lambda.run();
+ long end = System.nanoTime();
+ double duration = (end - start) * 0.000001;
+ System.out.println("duration (ms): " + tag + " = " + duration);
+ return duration;
+ }
+}
+
+/*
+FrameGrabber läser frames asynkront
+skickar frame till FrameConverter
+sparas i huvudklassen
+läses av FrameProcessor/LedController
+ */
\ No newline at end of file