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