Added configuration for pixel format and adapted config for new video grabber
[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
cc03403a 37 4) Resize to cols x rows first, then resize to a vertical and a horizontal like in (3).
4a2d6056 38 */
e59e98fc
TW
39 Mat src = new Mat(config.video.height, config.video.width, CvType.CV_8UC2); // 8-bit, unsigned, 2 channels
40 src.put(0, 0, data);
41
4a2d6056
TW
42// Mat converted = new Mat();
43// Mat resized = new Mat();
44//
45// timeIt("total", () -> {
46// timeIt("yuyv2rgb", () -> Imgproc.cvtColor(src, converted, Imgproc.COLOR_YUV2RGB_YUYV)); // 3.5 - 4.0 ms
47// 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)
48// });
e59e98fc 49
100b82fe
TW
50 Mat cropped = src.submat(
51 config.video.crop.top,
52 config.video.height - config.video.crop.bottom,
53 config.video.crop.left,
54 config.video.width - config.video.crop.right
55 );
56 converted = new Mat();
cc03403a 57 Imgproc.cvtColor(cropped, converted, config.video.format);
100b82fe
TW
58// timeIt("model 1", () -> model1(converted, Imgproc.INTER_AREA));
59// timeIt("model 2", () -> model2(converted, Imgproc.INTER_AREA));
4a2d6056 60 timeIt("model 3", () -> model3(converted, Imgproc.INTER_AREA));
e59e98fc
TW
61// save(converted, "/home/kaka/test-converted.data");
62// save(resized, "/home/kaka/test-resized.data");
100b82fe
TW
63 src.release();
64 cropped.release();
4a2d6056
TW
65 }
66
67 private void model1(Mat src, int interpolation) {
68 Mat resized = new Mat();
69 Imgproc.resize(src, resized, new Size(config.leds.cols, config.leds.rows), 0, 0, interpolation);
70 }
71
72 private void model2(Mat src, int interpolation) {
73 Mat resized = new Mat();
74 Imgproc.resize(src, resized, new Size(16, 9), 0, 0, interpolation);
75 }
76
77 private void model3(Mat src, int interpolation) {
78 colImage = new Mat();
79 rowImage = new Mat();
80 Imgproc.resize(src, colImage, new Size(config.leds.cols, 9), 0, 0, interpolation);
81 Imgproc.resize(src, rowImage, new Size(16, config.leds.rows), 0, 0, interpolation);
82 }
83
100b82fe 84 public Color getLedColor(ListPosition listPosition, int xy) {
cc03403a 85 // TODO: maybe use highest value from pixels? 100 % from 1st, 66 % from 2nd, 33 % from 3rd. colors might be strange.
4a2d6056
TW
86 switch (listPosition) {
87 case LEFT:
100b82fe 88 return interpolatedRowColor(xy, 0, 1, 2);
4a2d6056 89 case RIGHT:
100b82fe 90 return interpolatedRowColor(xy, 15, 14, 13);
4a2d6056 91 case TOP:
100b82fe 92 return interpolatedColColor(xy, 0, 1, 2);
4a2d6056 93 case BOTTOM:
100b82fe 94 return interpolatedColColor(xy, 8, 7, 6);
4a2d6056
TW
95 }
96 return null;
97 }
98
100b82fe
TW
99 private Color interpolatedRowColor(int y, int x1, int x2, int x3) {
100 return pixelToColor(rowImage, x3, y).interpolate(pixelToColor(rowImage, x2, y), 0.65).interpolate(pixelToColor(rowImage, x1, y), 0.65);
101 }
102
103 private Color interpolatedColColor(int x, int y1, int y2, int y3) {
104 return pixelToColor(colImage, x, y3).interpolate(pixelToColor(colImage, x, y2), 0.65).interpolate(pixelToColor(colImage, x, y1), 0.65);
105 }
106
4a2d6056
TW
107 private Color pixelToColor(Mat image, int x, int y) {
108 byte[] rgb = new byte[3];
109 image.get(y, x, rgb);
100b82fe 110 return Color.rgb(rgb[0] & 0xff, rgb[1] & 0xff, rgb[2] & 0xff);
e59e98fc
TW
111 }
112
113 private void save(Mat mat, String filepath) {
114 byte[] data = new byte[mat.cols() * mat.rows() * mat.channels()];
115 mat.get(0, 0, data);
116 saveFile(data, filepath);
117 }
118
119 public byte[] getData() {
100b82fe
TW
120 byte[] buff = new byte[(int) (converted.total() * converted.channels())];
121 converted.get(0, 0, buff);
122 return buff;
123 }
124
125 public Mat getColImage() {
126 return colImage;
127 }
128
129 public Mat getRowImage() {
130 return rowImage;
131 }
132
133 public Mat getConvertedImage() {
134 return converted;
e59e98fc 135 }
03b67a73
TW
136
137 /**
138 * Creates a LED frame going clockwise from the bottom-left corner, sans the corners.
139 */
140 public LedFrame getLedFrame() {
141 LedFrame frame = LedFrame.from(config);
142 int led = 0;
143 for (int i = config.leds.rows - 1; i >= 0; i--) frame.setLedColor(led++, getLedColor(ListPosition.LEFT, i));
144 for (int i = 0; i < config.leds.cols; i++) frame.setLedColor(led++, getLedColor(ListPosition.TOP, i));
145 for (int i = 0; i < config.leds.rows; i++) frame.setLedColor(led++, getLedColor(ListPosition.RIGHT, i));
146 for (int i = config.leds.cols - 1; i >= 0; i--) frame.setLedColor(led++, getLedColor(ListPosition.BOTTOM, i));
147 return frame;
148 }
e59e98fc 149}