blob: 802e448d61e3afdc510fb45324d44a7a19a96368 [file] [log] [blame]
Alex Perryd5e13572020-02-22 15:15:08 -08001import {CameraImage} from 'y2020/vision/vision_generated';
Alex Perry5f474f22020-02-01 12:14:24 -08002
3export class ImageHandler {
4 private canvas = document.createElement('canvas');
Alex Perryb41d5782020-02-09 17:06:40 -08005 private imageBuffer: Uint8ClampedArray|null = null;
6 private imageTimestamp: flatbuffers.Long|null = null;
7 private result: fr971.vision.ImageMatchResult|null = null;
8 private resultTimestamp: flatbuffers.Long|null = null;
Alex Perry5f474f22020-02-01 12:14:24 -08009
10 constructor() {
11 document.body.appendChild(this.canvas);
12 }
13
Alex Perryb41d5782020-02-09 17:06:40 -080014 handleImage(data: Uint8Array): void {
Alex Perry5f474f22020-02-01 12:14:24 -080015 const fbBuffer = new flatbuffers.ByteBuffer(data);
Alex Perryd5e13572020-02-22 15:15:08 -080016 const image = CameraImage.getRootAsCameraImage(fbBuffer);
Alex Perryb41d5782020-02-09 17:06:40 -080017 this.imageTimestamp = image.monotonicTimestampNs();
Alex Perry5f474f22020-02-01 12:14:24 -080018
19 const width = image.cols();
20 const height = image.rows();
21 if (width === 0 || height === 0) {
22 return;
23 }
Alex Perryb41d5782020-02-09 17:06:40 -080024 this.imageBuffer = new Uint8ClampedArray(width * height * 4); // RGBA
Alex Perry5f474f22020-02-01 12:14:24 -080025
26 // Read four bytes (YUYV) from the data and transform into two pixels of
27 // RGBA for canvas
28 for (const j = 0; j < height; j++) {
29 for (const i = 0; i < width; i += 2) {
30 const y1 = image.data((j * width + i) * 2);
31 const u = image.data((j * width + i) * 2 + 1);
32 const y2 = image.data((j * width + i + 1) * 2);
33 const v = image.data((j * width + i + 1) * 2 + 1);
34
35 // Based on https://en.wikipedia.org/wiki/YUV#Converting_between_Y%E2%80%B2UV_and_RGB
36 const c1 = y1 - 16;
37 const c2 = y2 - 16;
38 const d = u - 128;
39 const e = v - 128;
40
41 imageBuffer[(j * width + i) * 4 + 0] = (298 * c1 + 409 * e + 128) >> 8;
42 imageBuffer[(j * width + i) * 4 + 1] =
43 (298 * c1 - 100 * d - 208 * e + 128) >> 8;
44 imageBuffer[(j * width + i) * 4 + 2] = (298 * c1 + 516 * d + 128) >> 8;
45 imageBuffer[(j * width + i) * 4 + 3] = 255;
46 imageBuffer[(j * width + i) * 4 + 4] = (298 * c2 + 409 * e + 128) >> 8;
47 imageBuffer[(j * width + i) * 4 + 5] =
48 (298 * c2 - 100 * d - 208 * e + 128) >> 8;
49 imageBuffer[(j * width + i) * 4 + 6] = (298 * c2 + 516 * d + 128) >> 8;
50 imageBuffer[(j * width + i) * 4 + 7] = 255;
51 }
52 }
53
Alex Perryb41d5782020-02-09 17:06:40 -080054 draw();
55 }
56
57 handleImageMetadata(data: Uint8Array): void {
58 const fbBuffer = new flatbuffers.ByteBuffer(data);
59 this.result = frc971.vision.ImageMatchResult.getRootAsImageMatchResult(fbBuffer);
60 this.resultTimestamp = result.imageMonotonicTimestampNs();
61 draw();
62 }
63
64 draw(): void {
65 if (imageTimestamp.low !== resultTimestamp.low ||
66 imageTimestamp.high !== resultTimestamp.high) {
67 return;
68 }
Alex Perry5f474f22020-02-01 12:14:24 -080069 const ctx = this.canvas.getContext('2d');
70
71 this.canvas.width = width;
72 this.canvas.height = height;
73 const idata = ctx.createImageData(width, height);
Alex Perryb41d5782020-02-09 17:06:40 -080074 idata.data.set(this.imageBuffer);
Alex Perry5f474f22020-02-01 12:14:24 -080075 ctx.putImageData(idata, 0, 0);
Alex Perryb41d5782020-02-09 17:06:40 -080076 ctx.beginPath();
77 for (const feature of this.result.getFeatures()) {
78 // Based on OpenCV drawKeypoint.
79 ctx.arc(feature.x, feature.y, feature.size, 0, 2 * Math.PI);
80 ctx.moveTo(feature.x, feature.y);
81 // TODO(alex): check that angle is correct (0?, direction?)
82 const angle = feature.angle * Math.PI / 180;
83 ctx.lineTo(
84 feature.x + feature.radius * cos(angle),
85 feature.y + feature.radius * sin(angle));
86 }
87 ctx.stroke();
Alex Perry5f474f22020-02-01 12:14:24 -080088 }
89
Alex Perryb41d5782020-02-09 17:06:40 -080090 getId(): string {
Alex Perryd5e13572020-02-22 15:15:08 -080091 return CameraImage.getFullyQualifiedName();
Alex Perry5f474f22020-02-01 12:14:24 -080092 }
Alex Perryb41d5782020-02-09 17:06:40 -080093
94 getResultId(): string {
95 return frc971.vision.ImageMatchResult.getFullyQualifiedName();
96 }
Alex Perry5f474f22020-02-01 12:14:24 -080097}