From 4a2d60564647052562fad28644904298ba83667b Mon Sep 17 00:00:00 2001 From: =?utf8?q?Tomas=20Wenstr=C3=B6m?= Date: Sun, 12 Mar 2017 14:18:53 +0100 Subject: [PATCH] WIP --- src/kaka/cakelight/CakeLight.java | 39 +++++++++++++++++++++ src/kaka/cakelight/Frame.java | 68 ++++++++++++++++++++++++++++++++---- src/kaka/cakelight/FrameGrabber.java | 28 ++++++++------- src/kaka/cakelight/ListPosition.java | 8 +++++ src/kaka/cakelight/Main.java | 53 +++++++++++++++++++--------- src/kaka/cakelight/Mode.java | 6 ++++ src/kaka/cakelight/VideoMode.java | 41 ++++++++++++++++++++++ 7 files changed, 208 insertions(+), 35 deletions(-) create mode 100644 src/kaka/cakelight/CakeLight.java create mode 100644 src/kaka/cakelight/ListPosition.java create mode 100644 src/kaka/cakelight/Mode.java create mode 100644 src/kaka/cakelight/VideoMode.java diff --git a/src/kaka/cakelight/CakeLight.java b/src/kaka/cakelight/CakeLight.java new file mode 100644 index 0000000..afc7b26 --- /dev/null +++ b/src/kaka/cakelight/CakeLight.java @@ -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"); + } +} diff --git a/src/kaka/cakelight/Frame.java b/src/kaka/cakelight/Frame.java index 0031abc..a162024 100644 --- a/src/kaka/cakelight/Frame.java +++ b/src/kaka/cakelight/Frame.java @@ -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) { diff --git a/src/kaka/cakelight/FrameGrabber.java b/src/kaka/cakelight/FrameGrabber.java index d228b7b..8fbb16f 100644 --- a/src/kaka/cakelight/FrameGrabber.java +++ b/src/kaka/cakelight/FrameGrabber.java @@ -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 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 index 0000000..e4b1d1c --- /dev/null +++ b/src/kaka/cakelight/ListPosition.java @@ -0,0 +1,8 @@ +package kaka.cakelight; + +public enum ListPosition { + LEFT, + RIGHT, + TOP, + BOTTOM +} diff --git a/src/kaka/cakelight/Main.java b/src/kaka/cakelight/Main.java index 474683e..9786aac 100644 --- a/src/kaka/cakelight/Main.java +++ b/src/kaka/cakelight/Main.java @@ -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 timeDurations = new HashMap<>(); + private static HashMap 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 index 0000000..3a1f5d0 --- /dev/null +++ b/src/kaka/cakelight/Mode.java @@ -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 index 0000000..be2ee41 --- /dev/null +++ b/src/kaka/cakelight/VideoMode.java @@ -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 = grabber.grabFrame(); + timeIt("frame", grabber::grabFrame); + // TODO: process frame + // TODO: save where the LedController can access it + } + } catch (IOException e) { + e.printStackTrace(); + } + } + }; + thread.start(); + } +} -- 2.11.0