192f01301b2b480f172002bec92d1c0d8a24960c
[kaka/cakelight.git] / src / kaka / cakelight / Frame.java
1 package kaka.cakelight;
2
3 import javafx.scene.paint.Color;
4 import org.opencv.core.CvType;
5 import org.opencv.core.Mat;
6 import org.opencv.core.Size;
7 import org.opencv.imgproc.Imgproc;
8
9 import static kaka.cakelight.Main.saveFile;
10 import static kaka.cakelight.Main.timeIt;
11
12 public 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         4) Resize to cols x rows first, then resize to a vertical and a horizontal like in (3).
38          */
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
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 //        });
49
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();
57         Imgproc.cvtColor(cropped, converted, config.video.format);
58 //        timeIt("model 1", () -> model1(converted, Imgproc.INTER_AREA));
59 //        timeIt("model 2", () -> model2(converted, Imgproc.INTER_AREA));
60         timeIt("model 3", () -> model3(converted, Imgproc.INTER_AREA));
61 //        save(converted, "/home/kaka/test-converted.data");
62 //        save(resized, "/home/kaka/test-resized.data");
63         src.release();
64         cropped.release();
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
84     public Color getLedColor(ListPosition listPosition, int xy) {
85         // TODO: maybe use highest value from pixels? 100 % from 1st, 66 % from 2nd, 33 % from 3rd. colors might be strange.
86         switch (listPosition) {
87             case LEFT:
88                 return interpolatedRowColor(xy, 0, 1, 2);
89             case RIGHT:
90                 return interpolatedRowColor(xy, 15, 14, 13);
91             case TOP:
92                 return interpolatedColColor(xy, 0, 1, 2);
93             case BOTTOM:
94                 return interpolatedColColor(xy, 8, 7, 6);
95         }
96         return null;
97     }
98
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
107     private Color pixelToColor(Mat image, int x, int y) {
108         byte[] rgb = new byte[3];
109         image.get(y, x, rgb);
110         return Color.rgb(rgb[0] & 0xff, rgb[1] & 0xff, rgb[2] & 0xff);
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() {
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;
135     }
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     }
149 }