WIP
authorTomas Wenström <tomas.wenstrom@gmail.com>
Sun, 12 Mar 2017 13:18:53 +0000 (14:18 +0100)
committerTomas Wenström <tomas.wenstrom@gmail.com>
Sun, 12 Mar 2017 13:18:53 +0000 (14:18 +0100)
src/kaka/cakelight/CakeLight.java [new file with mode: 0644]
src/kaka/cakelight/Frame.java
src/kaka/cakelight/FrameGrabber.java
src/kaka/cakelight/ListPosition.java [new file with mode: 0644]
src/kaka/cakelight/Main.java
src/kaka/cakelight/Mode.java [new file with mode: 0644]
src/kaka/cakelight/VideoMode.java [new file with mode: 0644]

diff --git a/src/kaka/cakelight/CakeLight.java b/src/kaka/cakelight/CakeLight.java
new file mode 100644 (file)
index 0000000..afc7b26
--- /dev/null
@@ -0,0 +1,39 @@
+package kaka.cakelight;
+
+import static kaka.cakelight.Main.timeIt;
+
+public class CakeLight {
+    private Configuration config;
+    private Mode mode;
+
+    public CakeLight(Configuration config) {
+        this.config = config;
+    }
+
+    public void setMode(Mode mode) {
+        cleanup();
+        this.mode = mode;
+        mode.enter(config);
+    }
+
+    public void cleanup() {
+        if (this.mode != null) {
+            this.mode.exit();
+        }
+    }
+
+    public void startLoop() {
+        // TODO
+//        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");
+    }
+}
index 0031abc..a162024 100644 (file)
@@ -5,12 +5,16 @@ import org.opencv.core.Mat;
 import org.opencv.core.Size;
 import org.opencv.imgproc.Imgproc;
 
+import java.awt.*;
+
 import static kaka.cakelight.Main.saveFile;
 import static kaka.cakelight.Main.timeIt;
 
 public class Frame {
     private byte[] data;
     private Configuration config;
+    private Mat colImage;
+    private Mat rowImage;
 
     private Frame(byte[] data) {
         this.data = data;
@@ -24,18 +28,70 @@ public class Frame {
     }
 
     private void convert() {
+        /* TODO: how to do this?
+        1) Resize to an image with the size of the number of leds and use config to define how many pixels deep into the screen to use.
+        2) Resize to 16x9 and use 2 pixels of depth (or maybe 3) and interpolate for each led.
+        3) Resize to 2 images where each led uses 2 pixels:
+            vertical   - 16 x <#leds>
+            horizontal - <#leds> x 9
+         */
         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();
+//        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)
+//        });
 
-        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)
-        });
+        Mat converted = new Mat();
+        Imgproc.cvtColor(src, converted, Imgproc.COLOR_YUV2RGB_YUYV);
+        timeIt("model 1", () -> model1(converted, Imgproc.INTER_AREA));
+        timeIt("model 2", () -> model2(converted, Imgproc.INTER_AREA));
+        timeIt("model 3", () -> model3(converted, Imgproc.INTER_AREA));
 //        save(converted, "/home/kaka/test-converted.data");
 //        save(resized, "/home/kaka/test-resized.data");
+        System.out.println("color: " + getPixel(ListPosition.BOTTOM, 0));
+    }
+
+    private void model1(Mat src, int interpolation) {
+        Mat resized = new Mat();
+        Imgproc.resize(src, resized, new Size(config.leds.cols, config.leds.rows), 0, 0, interpolation);
+    }
+
+    private void model2(Mat src, int interpolation) {
+        Mat resized = new Mat();
+        Imgproc.resize(src, resized, new Size(16, 9), 0, 0, interpolation);
+    }
+
+    private void model3(Mat src, int interpolation) {
+        colImage = new Mat();
+        rowImage = new Mat();
+        Imgproc.resize(src, colImage, new Size(config.leds.cols, 9), 0, 0, interpolation);
+        Imgproc.resize(src, rowImage, new Size(16, config.leds.rows), 0, 0, interpolation);
+    }
+
+    public Color getPixel(ListPosition listPosition, int xy) {
+        switch (listPosition) {
+            case LEFT:
+                return pixelToColor(rowImage, 0, xy);
+            case RIGHT:
+                return pixelToColor(rowImage, config.leds.cols - 1, xy);
+            case TOP:
+                return pixelToColor(colImage, xy, 0);
+            case BOTTOM:
+                return pixelToColor(colImage, xy, config.leds.cols - 1);
+        }
+        return null;
+    }
+
+    private Color pixelToColor(Mat image, int x, int y) {
+        byte[] rgb = new byte[3];
+        image.get(y, x, rgb);
+        System.out.println("r = " + rgb[0] + ", g = " + rgb[1] + ", b = " + rgb[2]);
+        return new Color(rgb[0], rgb[1], rgb[2]);
     }
 
     private void save(Mat mat, String filepath) {
index d228b7b..8fbb16f 100644 (file)
@@ -1,8 +1,11 @@
 package kaka.cakelight;
 
 import java.io.*;
+import java.util.Optional;
 
-public class FrameGrabber {
+import static kaka.cakelight.Main.log;
+
+public class FrameGrabber implements Closeable {
     private Configuration config;
     private File file;
     private int bytesPerFrame;
@@ -16,10 +19,11 @@ public class FrameGrabber {
         fg.config = config;
         fg.file = new File(config.video.device);
         fg.bytesPerFrame = config.video.width * config.video.height * config.video.bpp;
+        fg.prepare();
         return fg;
     }
 
-    public boolean prepare() {
+    private boolean prepare() {
         try {
             fileStream = new FileInputStream(file);
             return true;
@@ -29,24 +33,24 @@ public class FrameGrabber {
         }
     }
 
-    public Frame grabFrame() {
+    /**
+     * Must be run in the same thread as {@link #prepare}.
+     */
+    public Optional<Frame> grabFrame() {
         try {
             byte[] data = new byte[bytesPerFrame];
             int count = fileStream.read(data);
-            System.out.println("count = " + count);
-            return Frame.of(data, config);
+            log("# of bytes read = " + count);
+            return Optional.of(Frame.of(data, config));
         } catch (IOException e) {
             e.printStackTrace();
         }
 
-        return null;
+        return Optional.empty();
     }
 
-    public void close() {
-        try {
-            fileStream.close();
-        } catch (IOException e) {
-            e.printStackTrace();
-        }
+    @Override
+    public void close() throws IOException {
+        fileStream.close();
     }
 }
diff --git a/src/kaka/cakelight/ListPosition.java b/src/kaka/cakelight/ListPosition.java
new file mode 100644 (file)
index 0000000..e4b1d1c
--- /dev/null
@@ -0,0 +1,8 @@
+package kaka.cakelight;
+
+public enum ListPosition {
+    LEFT,
+    RIGHT,
+    TOP,
+    BOTTOM
+}
index 474683e..9786aac 100644 (file)
@@ -4,26 +4,25 @@ import org.opencv.core.Core;
 
 import java.io.FileOutputStream;
 import java.io.IOException;
+import java.util.HashMap;
 
 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");
+       log("Running with config:\n" + config);
+
+       CakeLight cakelight = new CakeLight(config);
+       cakelight.setMode(new VideoMode());
+       cakelight.startLoop();
+//     try {
+//         Thread.sleep(1000);
+//     } catch (InterruptedException e) {
+//         e.printStackTrace();
+//     }
+//     cakelight.setMode(null);
+       Runtime.getRuntime().addShutdownHook(new Thread(Main::printTimeStats));
     }
 
     public static void saveFile(byte[] data, String filepath) {
@@ -36,19 +35,39 @@ public class Main {
        }
     }
 
+    public static void log(String msg, Object... args) {
+       System.out.println(String.format(msg, args));
+    }
+
+    private static HashMap<String, Double> timeDurations = new HashMap<>();
+    private static HashMap<String, Integer> timeCounts = new HashMap<>();
     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);
+//     log("duration (ms): " + tag + " = " + duration);
+
+       if (!timeDurations.containsKey(tag)) {
+           timeDurations.put(tag, 0.0);
+           timeCounts.put(tag, 0);
+       }
+       timeDurations.put(tag, timeDurations.get(tag) + duration);
+       timeCounts.put(tag, timeCounts.get(tag) + 1);
        return duration;
     }
+
+    private static void printTimeStats() {
+        log("Average times in ms:");
+        timeDurations.forEach((tag, duration) -> {
+           log("%s: %s", tag, duration / timeCounts.get(tag));
+       });
+    }
 }
 
 /*
 FrameGrabber läser frames asynkront
 skickar frame till FrameConverter
 sparas i huvudklassen
-läses av FrameProcessor/LedController
- */
\ No newline at end of file
+läses av FrameProcessor/CakeLight
+ */
diff --git a/src/kaka/cakelight/Mode.java b/src/kaka/cakelight/Mode.java
new file mode 100644 (file)
index 0000000..3a1f5d0
--- /dev/null
@@ -0,0 +1,6 @@
+package kaka.cakelight;
+
+public interface Mode {
+    void enter(Configuration config);
+    void exit();
+}
diff --git a/src/kaka/cakelight/VideoMode.java b/src/kaka/cakelight/VideoMode.java
new file mode 100644 (file)
index 0000000..be2ee41
--- /dev/null
@@ -0,0 +1,41 @@
+package kaka.cakelight;
+
+import java.io.IOException;
+import java.util.Optional;
+
+import static kaka.cakelight.Main.log;
+import static kaka.cakelight.Main.timeIt;
+
+public class VideoMode implements Mode {
+    private Configuration config;
+    private Thread thread;
+
+    @Override
+    public void enter(Configuration config) {
+        this.config = config;
+        startGrabberThread();
+    }
+
+    @Override
+    public void exit() {
+        thread.interrupt();
+    }
+
+    private void startGrabberThread() {
+        thread = new Thread() {
+            public void run() {
+                try (FrameGrabber grabber = FrameGrabber.from(config)) {
+                    while (!isInterrupted()) {
+//                        Optional<Frame> frame = grabber.grabFrame();
+                        timeIt("frame", grabber::grabFrame);
+                        // TODO: process frame
+                        // TODO: save where the LedController can access it
+                    }
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+            }
+        };
+        thread.start();
+    }
+}