/** * 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 class DataArray { data: Uint8Array; readonly shape: number[]; constructor(data: Uint8Array, shape: Array) { this.data = data; this.shape = shape; } } export type RLEObject = { size: [h: number, w: number]; counts: string; }; type RLE = { h: number; w: number; m: number; cnts: number[]; }; type BB = number[]; function rleInit(R: RLE, h: number, w: number, m: number, cnts: number[]) { R.h = h; R.w = w; R.m = m; R.cnts = m === 0 ? [0] : cnts; } function rlesInit(R: RLE[], n: number) { let i; for (i = 0; i < n; i++) { R[i] = {h: 0, w: 0, m: 0, cnts: [0]}; rleInit(R[i], 0, 0, 0, [0]); } } class RLEs { _R: RLE[]; _n: number; constructor(n: number) { this._R = []; rlesInit(this._R, n); this._n = n; } } export class Masks { _mask: Uint8Array; _h: number; _w: number; _n: number; constructor(h: number, w: number, n: number) { this._mask = new Uint8Array(h * w * n); this._h = h; this._w = w; this._n = n; } toDataArray(): DataArray { return new DataArray(this._mask, [this._h, this._w, this._n]); } } // encode mask to RLEs objects // list of RLE string can be generated by RLEs member function export function encode(mask: DataArray): RLEObject[] { const h = mask.shape[0]; const w = mask.shape[1]; const n = mask.shape[2]; const Rs = new RLEs(n); rleEncode(Rs._R, mask.data, h, w, n); const objs = _toString(Rs); return objs; } // decode mask from compressed list of RLE string or RLEs object export function decode(rleObjs: RLEObject[]): DataArray { const Rs = _frString(rleObjs); const h = Rs._R[0].h; const w = Rs._R[0].w; const n = Rs._n; const masks = new Masks(h, w, n); rleDecode(Rs._R, masks._mask, n); return masks.toDataArray(); } export function toBbox(rleObjs: RLEObject[]): BB { const Rs = _frString(rleObjs); const n = Rs._n; const bb: BB = []; rleToBbox(Rs._R, bb, n); return bb; } function rleEncode(R: RLE[], M: Uint8Array, h: number, w: number, n: number) { let i; let j; let k; const a = w * h; let c; const cnts: number[] = []; let p; for (i = 0; i < n; i++) { const from = a * i; const to = a * (i + 1); // Slice data for current RLE object const T = M.slice(from, to); k = 0; p = 0; c = 0; for (j = 0; j < a; j++) { if (T[j] !== p) { cnts[k++] = c; c = 0; p = T[j]; } c++; } cnts[k++] = c; rleInit(R[i], h, w, k, [...cnts]); } } function rleDecode(R: RLE[], M: Uint8Array, n: number): void { let i; let j; let k; let p = 0; for (i = 0; i < n; i++) { let v = false; for (j = 0; j < R[i].m; j++) { for (k = 0; k < R[i].cnts[j]; k++) { M[p++] = v === false ? 0 : 1; } v = !v; } } } function rleToString(R: RLE): string { /* Similar to LEB128 but using 6 bits/char and ascii chars 48-111. */ let i; const m = R.m; let p = 0; let x: number; let more; const s: string[] = []; for (i = 0; i < m; i++) { x = R.cnts[i]; if (i > 2) { x -= R.cnts[i - 2]; } more = true; // 1; while (more) { let c = x & 0x1f; x >>= 5; more = c & 0x10 ? x != -1 : x != 0; if (more) { c |= 0x20; } c += 48; s[p++] = String.fromCharCode(c); } } return s.join(''); } // internal conversion from Python RLEs object to compressed RLE format function _toString(Rs: RLEs): RLEObject[] { const n = Rs._n; let py_string; let c_string; const objs: RLEObject[] = []; for (let i = 0; i < n; i++) { c_string = rleToString(Rs._R[i]); py_string = c_string; objs.push({ size: [Rs._R[i].h, Rs._R[i].w], counts: py_string, }); } return objs; } // internal conversion from compressed RLE format to Python RLEs object function _frString(rleObjs: RLEObject[]): RLEs { const n = rleObjs.length; const Rs = new RLEs(n); let py_string; let c_string; for (let i = 0; i < rleObjs.length; i++) { const obj = rleObjs[i]; py_string = obj.counts; c_string = py_string; rleFrString(Rs._R[i], c_string, obj.size[0], obj.size[1]); } return Rs; } function rleToBbox(R: RLE[], bb: BB, n: number) { for (let i = 0; i < n; i++) { const h = R[i].h; const w = R[i].w; let m = R[i].m; // The RLE structure likely contains run-length encoded data where each // element represents a count of consecutive pixels with the same value in // a binary image (black or white). Since the counts represent both black // and white pixels, this operation ((siz)(m/2)) * 2 is used to ensure that // m is always an even number. By doing so, the code can later check // whether the current pixel is black or white based on whether the index j // is even or odd. m = Math.floor(m / 2) * 2; let xs = w; let ys = h; let xe = 0; let ye = 0; let cc = 0; let t; let y; let x; let xp = 0; if (m === 0) { bb[4 * i] = bb[4 * i + 1] = bb[4 * i + 2] = bb[4 * i + 3] = 0; continue; } for (let j = 0; j < m; j++) { cc += R[i].cnts[j]; t = cc - (j % 2); y = t % h; x = Math.floor((t - y) / h); if (j % 2 === 0) { xp = x; } else if (xp < x) { ys = 0; ye = h - 1; } xs = Math.min(xs, x); xe = Math.max(xe, x); ys = Math.min(ys, y); ye = Math.max(ye, y); } bb[4 * i] = xs; bb[4 * i + 2] = xe - xs + 1; bb[4 * i + 1] = ys; bb[4 * i + 3] = ye - ys + 1; } } function rleFrString(R: RLE, s: string, h: number, w: number): void { let m = 0; let p = 0; let k; let x; let more; let cnts = []; while (s[m]) { m++; } cnts = []; m = 0; while (s[p]) { x = 0; k = 0; more = 1; while (more) { const c = s.charCodeAt(p) - 48; x |= (c & 0x1f) << (5 * k); more = c & 0x20; p++; k++; if (!more && c & 0x10) { x |= -1 << (5 * k); } } if (m > 2) { x += cnts[m - 2]; } cnts[m++] = x; } rleInit(R, h, w, m, cnts); }