blob: ae530ef3e081ca8be29b1cfc2502ba1605c041ed [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 Perry22824d72020-02-29 17:11:43 -080010 private width = 0;
11 private height = 0;
12 private imageSkipCount = 3;
Alex Perry5f474f22020-02-01 12:14:24 -080013
14 constructor() {
15 document.body.appendChild(this.canvas);
16 }
17
Alex Perryb41d5782020-02-09 17:06:40 -080018 handleImage(data: Uint8Array): void {
Alex Perry22824d72020-02-29 17:11:43 -080019 if (this.imageSkipCount != 0) {
20 this.imageSkipCount--;
21 return;
22 } else {
23 this.imageSkipCount = 3;
24 }
25
Alex Perry5f474f22020-02-01 12:14:24 -080026 const fbBuffer = new flatbuffers.ByteBuffer(data);
Alex Perryd5e13572020-02-22 15:15:08 -080027 const image = CameraImage.getRootAsCameraImage(fbBuffer);
Alex Perryb41d5782020-02-09 17:06:40 -080028 this.imageTimestamp = image.monotonicTimestampNs();
Alex Perry5f474f22020-02-01 12:14:24 -080029
Alex Perry22824d72020-02-29 17:11:43 -080030 this.width = image.cols();
31 this.height = image.rows();
32 if (this.width === 0 || this.height === 0) {
Alex Perry5f474f22020-02-01 12:14:24 -080033 return;
34 }
Alex Perry22824d72020-02-29 17:11:43 -080035 this.imageBuffer = new Uint8ClampedArray(this.width * this.height * 4); // RGBA
Alex Perry5f474f22020-02-01 12:14:24 -080036
37 // Read four bytes (YUYV) from the data and transform into two pixels of
38 // RGBA for canvas
Alex Perry22824d72020-02-29 17:11:43 -080039 for (const j = 0; j < this.height; j++) {
40 for (const i = 0; i < this.width; i += 2) {
41 const y1 = image.data((j * this.width + i) * 2);
42 const u = image.data((j * this.width + i) * 2 + 1);
43 const y2 = image.data((j * this.width + i + 1) * 2);
44 const v = image.data((j * this.width + i + 1) * 2 + 1);
Alex Perry5f474f22020-02-01 12:14:24 -080045
46 // Based on https://en.wikipedia.org/wiki/YUV#Converting_between_Y%E2%80%B2UV_and_RGB
47 const c1 = y1 - 16;
48 const c2 = y2 - 16;
49 const d = u - 128;
50 const e = v - 128;
51
Alex Perry22824d72020-02-29 17:11:43 -080052 this.imageBuffer[(j * this.width + i) * 4 + 0] = (298 * c1 + 409 * e + 128) >> 8;
53 this.imageBuffer[(j * this.width + i) * 4 + 1] = (298 * c1 - 100 * d - 208 * e + 128) >> 8;
54 this.imageBuffer[(j * this.width + i) * 4 + 2] = (298 * c1 + 516 * d + 128) >> 8;
55 this.imageBuffer[(j * this.width + i) * 4 + 3] = 255;
56 this.imageBuffer[(j * this.width + i) * 4 + 4] = (298 * c2 + 409 * e + 128) >> 8;
57 this.imageBuffer[(j * this.width + i) * 4 + 5] = (298 * c2 - 100 * d - 208 * e + 128) >> 8;
58 this.imageBuffer[(j * this.width + i) * 4 + 6] = (298 * c2 + 516 * d + 128) >> 8;
59 this.imageBuffer[(j * this.width + i) * 4 + 7] = 255;
Alex Perry5f474f22020-02-01 12:14:24 -080060 }
61 }
62
Alex Perry22824d72020-02-29 17:11:43 -080063 this.draw();
Alex Perryb41d5782020-02-09 17:06:40 -080064 }
65
66 handleImageMetadata(data: Uint8Array): void {
67 const fbBuffer = new flatbuffers.ByteBuffer(data);
Austin Schuhf6e71392020-02-26 23:10:15 -080068 this.result = ImageMatchResult.getRootAsImageMatchResult(fbBuffer);
Alex Perry22824d72020-02-29 17:11:43 -080069 this.resultTimestamp = this.result.imageMonotonicTimestampNs();
70 this.draw();
Alex Perryb41d5782020-02-09 17:06:40 -080071 }
72
73 draw(): void {
Alex Perry22824d72020-02-29 17:11:43 -080074 if (!this.imageTimestamp || !this.resultTimestamp ||
75 this.imageTimestamp.low !== this.resultTimestamp.low ||
76 this.imageTimestamp.high !== this.resultTimestamp.high) {
Alex Perryb41d5782020-02-09 17:06:40 -080077 return;
78 }
Alex Perry5f474f22020-02-01 12:14:24 -080079 const ctx = this.canvas.getContext('2d');
80
Alex Perry22824d72020-02-29 17:11:43 -080081 this.canvas.width = this.width;
82 this.canvas.height = this.height;
83 const idata = ctx.createImageData(this.width, this.height);
Alex Perryb41d5782020-02-09 17:06:40 -080084 idata.data.set(this.imageBuffer);
Alex Perry5f474f22020-02-01 12:14:24 -080085 ctx.putImageData(idata, 0, 0);
Alex Perry22824d72020-02-29 17:11:43 -080086 for (const i = 0; i < this.result.featuresLength(); i++) {
87 const feature = this.result.features(i);
Alex Perryb41d5782020-02-09 17:06:40 -080088 // Based on OpenCV drawKeypoint.
Alex Perry22824d72020-02-29 17:11:43 -080089 ctx.beginPath();
90 ctx.arc(feature.x(), feature.y(), feature.size(), 0, 2 * Math.PI);
91 ctx.stroke();
92
93 ctx.beginPath();
94 ctx.moveTo(feature.x(), feature.y());
95 const angle = feature.angle() * Math.PI / 180;
Alex Perryb41d5782020-02-09 17:06:40 -080096 ctx.lineTo(
Alex Perry22824d72020-02-29 17:11:43 -080097 feature.x() + feature.size() * Math.cos(angle),
98 feature.y() + feature.size() * Math.sin(angle));
99 ctx.stroke();
Alex Perryb41d5782020-02-09 17:06:40 -0800100 }
Alex Perry5f474f22020-02-01 12:14:24 -0800101 }
102
Alex Perryb41d5782020-02-09 17:06:40 -0800103 getId(): string {
Alex Perryd5e13572020-02-22 15:15:08 -0800104 return CameraImage.getFullyQualifiedName();
Alex Perry5f474f22020-02-01 12:14:24 -0800105 }
Alex Perryb41d5782020-02-09 17:06:40 -0800106
107 getResultId(): string {
Austin Schuhf6e71392020-02-26 23:10:15 -0800108 return ImageMatchResult.getFullyQualifiedName();
Alex Perryb41d5782020-02-09 17:06:40 -0800109 }
Alex Perry5f474f22020-02-01 12:14:24 -0800110}