File size: 3,594 Bytes
92189dd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
/**
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
export function convertVideoFrameToImageData(
  videoFrame: VideoFrame,
): ImageData | undefined {
  const canvas = new OffscreenCanvas(
    videoFrame.displayWidth,
    videoFrame.displayHeight,
  );
  const ctx = canvas.getContext('2d');
  ctx?.drawImage(videoFrame, 0, 0);
  return ctx?.getImageData(0, 0, canvas.width, canvas.height);
}

/**
 * This utility provides two functions:
 * `process`: to find the bounding box of non-empty pixels from an ImageData, when looping through all its pixels
 * `crop` to cut out the subsection found in `process`
 * @returns
 */
export function findBoundingBox() {
  let xMin = Number.MAX_VALUE;
  let yMin = Number.MAX_VALUE;
  let xMax = Number.MIN_VALUE;
  let yMax = Number.MIN_VALUE;
  return {
    process: function (x: number, y: number, hasData: boolean) {
      if (hasData) {
        xMin = Math.min(x, xMin);
        xMax = Math.max(x, xMax);
        yMin = Math.min(y, yMin);
        yMax = Math.max(y, yMax);
      }
      return [xMin, xMax, yMin, yMax];
    },
    crop(imageData: ImageData): ImageData | null {
      const canvas = new OffscreenCanvas(imageData.width, imageData.height);
      const ctx = canvas.getContext('2d');

      const boundingBoxWidth = xMax - xMin;
      const boundingBoxHeight = yMax - yMin;
      if (ctx && boundingBoxWidth > 0 && boundingBoxHeight > 0) {
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        ctx.putImageData(imageData, 0, 0);
        return ctx.getImageData(
          xMin,
          yMin,
          boundingBoxWidth,
          boundingBoxHeight,
        );
      } else {
        return null;
      }
    },
    getBox(): [[number, number], [number, number]] {
      return [
        [xMin, yMin],
        [xMax, yMax],
      ];
    },
  };
}

export function magnifyImageRegion(
  canvas: HTMLCanvasElement | null,
  x: number,
  y: number,
  radius: number = 25,
  scale: number = 2,
): string {
  if (canvas == null) {
    return '';
  }
  const ctx = canvas.getContext('2d');
  if (ctx) {
    const minX = x - radius < 0 ? radius - x : 0;
    const minY = y - radius < 0 ? radius - y : 0;
    const region = ctx.getImageData(
      Math.max(x - radius, 0),
      Math.max(y - radius, 0),
      radius * 2,
      radius * 2,
    );

    // ImageData doesn't scale-transform correctly on canvas
    // So we first draw the original size on an offscreen canvas, and then scale it
    const regionCanvas = new OffscreenCanvas(region.width, region.height);
    const regionCtx = regionCanvas.getContext('2d');
    regionCtx?.putImageData(region, minX > 0 ? minX : 0, minY > 0 ? minY : 0);

    const scaleCanvas = document.createElement('canvas');
    scaleCanvas.width = Math.round(region.width * scale);
    scaleCanvas.height = Math.round(region.height * scale);
    const scaleCtx = scaleCanvas.getContext('2d');
    scaleCtx?.scale(scale, scale);
    scaleCtx?.drawImage(regionCanvas, 0, 0);

    return scaleCanvas.toDataURL();
  }
  return '';
}