Added a LED controller that consumes LED frames from the modes
[kaka/cakelight.git] / src / kaka / cakelight / Frame.java
CommitLineData
e59e98fc
TW
1package kaka.cakelight;
2
100b82fe 3import javafx.scene.paint.Color;
e59e98fc
TW
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;
4a2d6056
TW
15 private Mat colImage;
16 private Mat rowImage;
100b82fe 17 private Mat converted;
e59e98fc
TW
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() {
4a2d6056
TW
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 */
e59e98fc
TW
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
4a2d6056
TW
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// });
e59e98fc 48
100b82fe
TW
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));
4a2d6056 59 timeIt("model 3", () -> model3(converted, Imgproc.INTER_AREA));
e59e98fc
TW
60// save(converted, "/home/kaka/test-converted.data");
61// save(resized, "/home/kaka/test-resized.data");
100b82fe
TW
62 src.release();
63 cropped.release();
4a2d6056
TW
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
100b82fe 83 public Color getLedColor(ListPosition listPosition, int xy) {
4a2d6056
TW
84 switch (listPosition) {
85 case LEFT:
100b82fe 86 return interpolatedRowColor(xy, 0, 1, 2);
4a2d6056 87 case RIGHT:
100b82fe 88 return interpolatedRowColor(xy, 15, 14, 13);
4a2d6056 89 case TOP:
100b82fe 90 return interpolatedColColor(xy, 0, 1, 2);
4a2d6056 91 case BOTTOM:
100b82fe 92 return interpolatedColColor(xy, 8, 7, 6);
4a2d6056
TW
93 }
94 return null;
95 }
96
100b82fe
TW
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
4a2d6056
TW
105 private Color pixelToColor(Mat image, int x, int y) {
106 byte[] rgb = new byte[3];
107 image.get(y, x, rgb);
100b82fe 108 return Color.rgb(rgb[0] & 0xff, rgb[1] & 0xff, rgb[2] & 0xff);
e59e98fc
TW
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() {
100b82fe
TW
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;
e59e98fc 133 }
03b67a73
TW
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 }
e59e98fc 147}