Added gamma correction
[kaka/cakelight.git] / src / kaka / cakelight / Frame.java
... / ...
CommitLineData
1package kaka.cakelight;
2
3import org.opencv.core.CvType;
4import org.opencv.core.Mat;
5import org.opencv.core.Size;
6import org.opencv.imgproc.Imgproc;
7
8import static kaka.cakelight.Main.saveFile;
9import static kaka.cakelight.Main.timeIt;
10
11public 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 getLedColor(ListPosition listPosition, int xy) {
105 // TODO: maybe use highest value from pixels? 100 % from 1st, 66 % from 2nd, 33 % from 3rd. colors might be strange.
106 switch (listPosition) {
107 case LEFT:
108 return interpolatedRowColor(images[0], xy, 0, 1, 2);
109// return interpolatedRowColor(xy, 0, 1, 2);
110 case RIGHT:
111 return interpolatedRowColor(images[1], xy, 2, 1, 0);
112// return interpolatedRowColor(xy, 15, 14, 13);
113 case TOP:
114 return interpolatedColColor(images[2], xy, 0, 1, 2);
115// return interpolatedColColor(xy, 0, 1, 2);
116 case BOTTOM:
117 return interpolatedColColor(images[3], xy, 2, 1, 0);
118// return interpolatedColColor(xy, 8, 7, 6);
119 }
120 return null;
121 }
122
123 // private Color interpolatedRowColor(int y, int x1, int x2, int x3) {
124 private Color interpolatedRowColor(Mat rowImage, int y, int x1, int x2, int x3) {
125 return pixelToColor(rowImage, x3, y).interpolate(pixelToColor(rowImage, x2, y), 0.65).interpolate(pixelToColor(rowImage, x1, y), 0.65);
126 }
127
128// private Color interpolatedColColor(int x, int y1, int y2, int y3) {
129 private Color interpolatedColColor(Mat colImage, int x, int y1, int y2, int y3) {
130 return pixelToColor(colImage, x, y3).interpolate(pixelToColor(colImage, x, y2), 0.65).interpolate(pixelToColor(colImage, x, y1), 0.65);
131 }
132
133 private Color pixelToColor(Mat image, int x, int y) {
134 byte[] rgb = new byte[3];
135 image.get(y, x, rgb);
136 return Color.rgb(rgb[0] & 0xff, rgb[1] & 0xff, rgb[2] & 0xff);
137 }
138
139 private void save(Mat mat, String filepath) {
140 byte[] data = new byte[mat.cols() * mat.rows() * mat.channels()];
141 mat.get(0, 0, data);
142 saveFile(data, filepath);
143 }
144
145 public byte[] getData() {
146 byte[] buff = new byte[(int) (converted.total() * converted.channels())];
147 converted.get(0, 0, buff);
148 return buff;
149 }
150
151// public Mat getColImage() {
152// return colImage;
153// }
154
155// public Mat getRowImage() {
156// return rowImage;
157// }
158
159 public Mat getConvertedImage() {
160 return converted;
161 }
162
163 /**
164 * Creates a LED frame going counter-clockwise from the bottom-left corner, sans the corners.
165 */
166 public LedFrame getLedFrame() {
167 LedFrame frame = LedFrame.from(config);
168 int led = 0;
169 for (int i = 0; i < config.leds.cols; i++) frame.setLedColor(led++, getLedColor(ListPosition.BOTTOM, i));
170 for (int i = config.leds.rows - 1; i >= 0; i--) frame.setLedColor(led++, getLedColor(ListPosition.RIGHT, i));
171 for (int i = config.leds.cols - 1; i >= 0; i--) frame.setLedColor(led++, getLedColor(ListPosition.TOP, i));
172 for (int i = 0; i < config.leds.rows; i++) frame.setLedColor(led++, getLedColor(ListPosition.LEFT, i));
173 return frame;
174 }
175}