Commit | Line | Data |
---|---|---|
e59e98fc TW |
1 | package kaka.cakelight; |
2 | ||
100b82fe | 3 | import javafx.scene.paint.Color; |
e59e98fc TW |
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; | |
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 | } |