Spaces:
Runtime error
Runtime error
/** | |
* 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<number>) { | |
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); | |
} | |