Added a LED controller that consumes LED frames from the modes
[kaka/cakelight.git] / src / kaka / cakelight / Frame.java
... / ...
CommitLineData
1package kaka.cakelight;
2
3import javafx.scene.paint.Color;
4import org.opencv.core.CvType;
5import org.opencv.core.Mat;
6import org.opencv.core.Size;
7import org.opencv.imgproc.Imgproc;
8
9import static kaka.cakelight.Main.saveFile;
10import static kaka.cakelight.Main.timeIt;
11
12public class Frame {
13 private byte[] data;
14 private Configuration config;
15 private Mat colImage;
16 private Mat rowImage;
17 private Mat converted;
18
19 private Frame(byte[] data) {
20 this.data = data;
21 }
22
23 public static Frame of(byte[] data, Configuration config) {
24 Frame frame = new Frame(data);
25 frame.config = config;
26 frame.convert();
27 return frame;
28 }
29
30 private void convert() {
31 /* TODO: how to do this?
32 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.
33 2) Resize to 16x9 and use 2 pixels of depth (or maybe 3) and interpolate for each led.
34 3) Resize to 2 images where each led uses 2 pixels:
35 vertical - 16 x <#leds>
36 horizontal - <#leds> x 9
37 */
38 Mat src = new Mat(config.video.height, config.video.width, CvType.CV_8UC2); // 8-bit, unsigned, 2 channels
39 src.put(0, 0, data);
40
41// Mat converted = new Mat();
42// Mat resized = new Mat();
43//
44// timeIt("total", () -> {
45// timeIt("yuyv2rgb", () -> Imgproc.cvtColor(src, converted, Imgproc.COLOR_YUV2RGB_YUYV)); // 3.5 - 4.0 ms
46// 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)
47// });
48
49 Mat cropped = src.submat(
50 config.video.crop.top,
51 config.video.height - config.video.crop.bottom,
52 config.video.crop.left,
53 config.video.width - config.video.crop.right
54 );
55 converted = new Mat();
56 Imgproc.cvtColor(cropped, converted, Imgproc.COLOR_YUV2RGB_YUYV);
57// timeIt("model 1", () -> model1(converted, Imgproc.INTER_AREA));
58// timeIt("model 2", () -> model2(converted, Imgproc.INTER_AREA));
59 timeIt("model 3", () -> model3(converted, Imgproc.INTER_AREA));
60// save(converted, "/home/kaka/test-converted.data");
61// save(resized, "/home/kaka/test-resized.data");
62 src.release();
63 cropped.release();
64 }
65
66 private void model1(Mat src, int interpolation) {
67 Mat resized = new Mat();
68 Imgproc.resize(src, resized, new Size(config.leds.cols, config.leds.rows), 0, 0, interpolation);
69 }
70
71 private void model2(Mat src, int interpolation) {
72 Mat resized = new Mat();
73 Imgproc.resize(src, resized, new Size(16, 9), 0, 0, interpolation);
74 }
75
76 private void model3(Mat src, int interpolation) {
77 colImage = new Mat();
78 rowImage = new Mat();
79 Imgproc.resize(src, colImage, new Size(config.leds.cols, 9), 0, 0, interpolation);
80 Imgproc.resize(src, rowImage, new Size(16, config.leds.rows), 0, 0, interpolation);
81 }
82
83 public Color getLedColor(ListPosition listPosition, int xy) {
84 switch (listPosition) {
85 case LEFT:
86 return interpolatedRowColor(xy, 0, 1, 2);
87 case RIGHT:
88 return interpolatedRowColor(xy, 15, 14, 13);
89 case TOP:
90 return interpolatedColColor(xy, 0, 1, 2);
91 case BOTTOM:
92 return interpolatedColColor(xy, 8, 7, 6);
93 }
94 return null;
95 }
96
97 private Color interpolatedRowColor(int y, int x1, int x2, int x3) {
98 return pixelToColor(rowImage, x3, y).interpolate(pixelToColor(rowImage, x2, y), 0.65).interpolate(pixelToColor(rowImage, x1, y), 0.65);
99 }
100
101 private Color interpolatedColColor(int x, int y1, int y2, int y3) {
102 return pixelToColor(colImage, x, y3).interpolate(pixelToColor(colImage, x, y2), 0.65).interpolate(pixelToColor(colImage, x, y1), 0.65);
103 }
104
105 private Color pixelToColor(Mat image, int x, int y) {
106 byte[] rgb = new byte[3];
107 image.get(y, x, rgb);
108 return Color.rgb(rgb[0] & 0xff, rgb[1] & 0xff, rgb[2] & 0xff);
109 }
110
111 private void save(Mat mat, String filepath) {
112 byte[] data = new byte[mat.cols() * mat.rows() * mat.channels()];
113 mat.get(0, 0, data);
114 saveFile(data, filepath);
115 }
116
117 public byte[] getData() {
118 byte[] buff = new byte[(int) (converted.total() * converted.channels())];
119 converted.get(0, 0, buff);
120 return buff;
121 }
122
123 public Mat getColImage() {
124 return colImage;
125 }
126
127 public Mat getRowImage() {
128 return rowImage;
129 }
130
131 public Mat getConvertedImage() {
132 return converted;
133 }
134
135 /**
136 * Creates a LED frame going clockwise from the bottom-left corner, sans the corners.
137 */
138 public LedFrame getLedFrame() {
139 LedFrame frame = LedFrame.from(config);
140 int led = 0;
141 for (int i = config.leds.rows - 1; i >= 0; i--) frame.setLedColor(led++, getLedColor(ListPosition.LEFT, i));
142 for (int i = 0; i < config.leds.cols; i++) frame.setLedColor(led++, getLedColor(ListPosition.TOP, i));
143 for (int i = 0; i < config.leds.rows; i++) frame.setLedColor(led++, getLedColor(ListPosition.RIGHT, i));
144 for (int i = config.leds.cols - 1; i >= 0; i--) frame.setLedColor(led++, getLedColor(ListPosition.BOTTOM, i));
145 return frame;
146 }
147}