Try using only the hue in video mode, with max saturation and value
[kaka/cakelight.git] / src / kaka / cakelight / Frame.java
1 package kaka.cakelight;
2
3 import org.opencv.core.CvType;
4 import org.opencv.core.Mat;
5 import org.opencv.core.Size;
6 import org.opencv.imgproc.Imgproc;
7
8 import static kaka.cakelight.Main.saveFile;
9 import static kaka.cakelight.Main.timeIt;
10
11 public class Frame {
12     private byte[] data;
13     private Configuration config;
14 //    private Mat colImage;
15 //    private Mat rowImage;
16     private Mat converted;
17     private Mat[] images;
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         timeIt("model 4", () -> model4(converted, Imgproc.INTER_AREA));
62 //        save(converted, "/home/kaka/test-converted.data");
63 //        save(resized, "/home/kaka/test-resized.data");
64         src.release();
65         cropped.release();
66     }
67
68     private void model1(Mat src, int interpolation) {
69         Mat resized = new Mat();
70         Imgproc.resize(src, resized, new Size(config.leds.cols, config.leds.rows), 0, 0, interpolation);
71     }
72
73     private void model2(Mat src, int interpolation) {
74         Mat resized = new Mat();
75         Imgproc.resize(src, resized, new Size(16, 9), 0, 0, interpolation);
76     }
77
78     private void model3(Mat src, int interpolation) {
79 //        colImage = new Mat();
80 //        rowImage = new Mat();
81 //        Imgproc.resize(src, colImage, new Size(config.leds.cols, 9), 0, 0, interpolation);
82 //        Imgproc.resize(src, rowImage, new Size(16, config.leds.rows), 0, 0, interpolation);
83     }
84
85     private void model4(Mat src, int interpolation) {
86         int width = 3 * src.cols() / 16;
87         int height = 3 * src.rows() / 9;
88         Mat[] cropped = new Mat[] {
89                 /* LEFT   */ src.submat(0, src.rows(), 0, width),
90                 /* RIGHT  */ src.submat(0, src.rows(), src.cols() - width, src.cols()),
91                 /* TOP    */ src.submat(0, height, 0, src.cols()),
92                 /* BOTTOM */ src.submat(src.rows() - height, src.rows(), 0, src.cols()),
93         };
94         images = new Mat[] {new Mat(), new Mat(), new Mat(), new Mat()};
95 //        Imgproc.resize(cropped[ListPosition.LEFT.ordinal()], images[ListPosition.LEFT.ordinal()], new Size(3, config.leds.rows), 0, 0, interpolation);
96         Imgproc.resize(cropped[0], images[0], new Size(3, config.leds.rows), 0, 0, interpolation);
97         Imgproc.resize(cropped[1], images[1], new Size(3, config.leds.rows), 0, 0, interpolation);
98         Imgproc.resize(cropped[2], images[2], new Size(config.leds.cols, 3), 0, 0, interpolation);
99         Imgproc.resize(cropped[3], images[3], new Size(config.leds.cols, 3), 0, 0, interpolation);
100 //        Imgproc.resize(src, colImage, new Size(config.leds.cols, 9), 0, 0, interpolation);
101 //        Imgproc.resize(src, rowImage, new Size(16, config.leds.rows), 0, 0, interpolation);
102     }
103
104     private Color wrappedGetLedColor(ListPosition listPosition, int xy) {
105         Color c = getLedColor(listPosition, xy);
106         double[] hsv = c.toHSV();
107         return Color.hsv(hsv[0], 1, 1);
108     }
109
110     private Color getLedColor(ListPosition listPosition, int xy) {
111         // TODO: maybe use highest value from pixels? 100 % from 1st, 66 % from 2nd, 33 % from 3rd. colors might be strange.
112         switch (listPosition) {
113             case LEFT:
114                 return interpolatedRowColor(images[0], xy, 0, 1, 2);
115 //                return interpolatedRowColor(xy, 0, 1, 2);
116             case RIGHT:
117                 return interpolatedRowColor(images[1], xy, 2, 1, 0);
118 //                return interpolatedRowColor(xy, 15, 14, 13);
119             case TOP:
120                 return interpolatedColColor(images[2], xy, 0, 1, 2);
121 //                return interpolatedColColor(xy, 0, 1, 2);
122             case BOTTOM:
123                 return interpolatedColColor(images[3], xy, 2, 1, 0);
124 //                return interpolatedColColor(xy, 8, 7, 6);
125         }
126         return null;
127     }
128
129     //    private Color interpolatedRowColor(int y, int x1, int x2, int x3) {
130     private Color interpolatedRowColor(Mat rowImage, int y, int x1, int x2, int x3) {
131         return pixelToColor(rowImage, x3, y).interpolate(pixelToColor(rowImage, x2, y), 0.65).interpolate(pixelToColor(rowImage, x1, y), 0.65);
132     }
133
134 //    private Color interpolatedColColor(int x, int y1, int y2, int y3) {
135     private Color interpolatedColColor(Mat colImage, int x, int y1, int y2, int y3) {
136         return pixelToColor(colImage, x, y3).interpolate(pixelToColor(colImage, x, y2), 0.65).interpolate(pixelToColor(colImage, x, y1), 0.65);
137     }
138
139     private Color pixelToColor(Mat image, int x, int y) {
140         byte[] rgb = new byte[3];
141         image.get(y, x, rgb);
142         return Color.rgb(rgb[0] & 0xff, rgb[1] & 0xff, rgb[2] & 0xff);
143     }
144
145     private void save(Mat mat, String filepath) {
146         byte[] data = new byte[mat.cols() * mat.rows() * mat.channels()];
147         mat.get(0, 0, data);
148         saveFile(data, filepath);
149     }
150
151     public byte[] getData() {
152         byte[] buff = new byte[(int) (converted.total() * converted.channels())];
153         converted.get(0, 0, buff);
154         return buff;
155     }
156
157 //    public Mat getColImage() {
158 //        return colImage;
159 //    }
160
161 //    public Mat getRowImage() {
162 //        return rowImage;
163 //    }
164
165     public Mat getConvertedImage() {
166         return converted;
167     }
168
169     /**
170      * Creates a LED frame going counter-clockwise from the bottom-left corner, sans the corners.
171      */
172     public LedFrame getLedFrame() {
173         LedFrame frame = LedFrame.from(config);
174         int led = 0;
175         for (int i = 0; i < config.leds.cols; i++)      frame.setLedColor(led++, wrappedGetLedColor(ListPosition.BOTTOM, i));
176         for (int i = config.leds.rows - 1; i >= 0; i--) frame.setLedColor(led++, wrappedGetLedColor(ListPosition.RIGHT, i));
177         for (int i = config.leds.cols - 1; i >= 0; i--) frame.setLedColor(led++, wrappedGetLedColor(ListPosition.TOP, i));
178         for (int i = 0; i < config.leds.rows; i++)      frame.setLedColor(led++, wrappedGetLedColor(ListPosition.LEFT, i));
179         return frame;
180     }
181 }