@webarkit/jsfeat-next
Version:
Typescript version of jsfeat for WebARKit
1,249 lines (1,248 loc) • 117 kB
JavaScript
import { data_type } from "./data_type/data_type";
import { cache } from "./cache/cache";
import { _resample, _resample_u8 } from "./imgproc/resample";
import { _convol, _convol_u8 } from "./imgproc/convol";
import { swap, hypot } from "./linalg/linalg_base";
import { _cmp_score_16 } from "./fast_corners/fast_private";
import matmath from "./matmath/matmath";
import { matrix_t } from "./matrix_t/matrix_t";
import { transform } from "./transform/transform";
import { keypoint_t } from "./keypoint_t/keypoint_t";
import { bit_pattern_31 } from "./orb/bit_pattern_31";
import { rectify_patch } from "./orb/rectify_patch";
import { yape } from "./yape/yape";
import { compute_laplacian, hessian_min_eigen_value } from "./yape06/yape06_utils";
import { ransac_params_t } from "./motion_estimator/ransac_params_t";
import { JSFEAT_CONSTANTS } from "./constants/constants";
import pkg from "../package.json";
export default class jsfeatNext {
dt;
cache;
static cache;
static fast_corners;
static imgproc;
static linalg;
static math;
static matmath;
static matrix_t;
static pyramid_t;
static transform;
static keypoint_t;
static yape;
static yape06;
static ransac_params_t;
static affine2d;
static homography2d;
static motion_estimator;
static optical_flow_lk;
static orb;
constructor() {
this.dt = new data_type();
this.cache = new cache();
this.cache.allocate(30, 640 * 4);
}
static VERSION = pkg.version;
static EPSILON = JSFEAT_CONSTANTS.EPSILON;
static FLT_MIN = JSFEAT_CONSTANTS.FLT_MIN;
static U8_t = JSFEAT_CONSTANTS.U8_t;
static S32_t = JSFEAT_CONSTANTS.S32_t;
static F32_t = JSFEAT_CONSTANTS.F32_t;
static S64_t = JSFEAT_CONSTANTS.S64_t;
static F64_t = JSFEAT_CONSTANTS.F64_t;
static C1_t = JSFEAT_CONSTANTS.C1_t;
static C2_t = JSFEAT_CONSTANTS.C2_t;
static C3_t = JSFEAT_CONSTANTS.C3_t;
static C4_t = JSFEAT_CONSTANTS.C4_t;
static COLOR_RGBA2GRAY = JSFEAT_CONSTANTS.COLOR_RGBA2GRAY;
static COLOR_RGB2GRAY = JSFEAT_CONSTANTS.COLOR_RGB2GRAY;
static COLOR_BGRA2GRAY = JSFEAT_CONSTANTS.COLOR_BGRA2GRAY;
static COLOR_BGR2GRAY = JSFEAT_CONSTANTS.COLOR_BGR2GRAY;
static BOX_BLUR_NOSCALE = JSFEAT_CONSTANTS.BOX_BLUR_NOSCALE;
static SVD_U_T = JSFEAT_CONSTANTS.SVD_U_T;
static SVD_V_T = JSFEAT_CONSTANTS.SVD_V_T;
static U8C1_t = this.U8_t | this.C1_t;
static U8C3_t = this.U8_t | this.C3_t;
static U8C4_t = this.U8_t | this.C4_t;
static F32C1_t = this.F32_t | this.C1_t;
static F32C2_t = this.F32_t | this.C2_t;
static S32C1_t = this.S32_t | this.C1_t;
static S32C2_t = this.S32_t | this.C2_t;
get_data_type(type) {
return this.dt._get_data_type(type);
}
get_channel(type) {
return this.dt._get_channel(type);
}
get_data_type_size(type) {
return this.dt._get_data_type_size(type);
}
}
class motion_model extends jsfeatNext {
T0;
T1;
AtA;
AtB;
constructor() {
super();
this.T0 = new matrix_t(3, 3, JSFEAT_CONSTANTS.F32_t | JSFEAT_CONSTANTS.C1_t);
this.T1 = new matrix_t(3, 3, JSFEAT_CONSTANTS.F32_t | JSFEAT_CONSTANTS.C1_t);
this.AtA = new matrix_t(6, 6, JSFEAT_CONSTANTS.F32_t | JSFEAT_CONSTANTS.C1_t);
this.AtB = new matrix_t(6, 1, JSFEAT_CONSTANTS.F32_t | JSFEAT_CONSTANTS.C1_t);
}
sqr(x) {
return x * x;
}
iso_normalize_points(from, to, T0, T1, count) {
let i = 0;
let cx0 = 0.0, cy0 = 0.0, d0 = 0.0, s0 = 0.0;
let cx1 = 0.0, cy1 = 0.0, d1 = 0.0, s1 = 0.0;
let dx = 0.0, dy = 0.0;
for (; i < count; ++i) {
cx0 += from[i].x;
cy0 += from[i].y;
cx1 += to[i].x;
cy1 += to[i].y;
}
cx0 /= count;
cy0 /= count;
cx1 /= count;
cy1 /= count;
for (i = 0; i < count; ++i) {
dx = from[i].x - cx0;
dy = from[i].y - cy0;
d0 += Math.sqrt(dx * dx + dy * dy);
dx = to[i].x - cx1;
dy = to[i].y - cy1;
d1 += Math.sqrt(dx * dx + dy * dy);
}
d0 /= count;
d1 /= count;
s0 = Math.SQRT2 / d0;
s1 = Math.SQRT2 / d1;
T0[0] = T0[4] = s0;
T0[2] = -cx0 * s0;
T0[5] = -cy0 * s0;
T0[1] = T0[3] = T0[6] = T0[7] = 0.0;
T0[8] = 1.0;
T1[0] = T1[4] = s1;
T1[2] = -cx1 * s1;
T1[5] = -cy1 * s1;
T1[1] = T1[3] = T1[6] = T1[7] = 0.0;
T1[8] = 1.0;
}
have_collinear_points(points, count) {
let j = 0, k = 0, i = (count - 1) | 0;
let dx1 = 0.0, dy1 = 0.0, dx2 = 0.0, dy2 = 0.0;
for (; j < i; ++j) {
dx1 = points[j].x - points[i].x;
dy1 = points[j].y - points[i].y;
for (k = 0; k < j; ++k) {
dx2 = points[k].x - points[i].x;
dy2 = points[k].y - points[i].y;
if (Math.abs(dx2 * dy1 - dy2 * dx1) <=
JSFEAT_CONSTANTS.EPSILON * (Math.abs(dx1) + Math.abs(dy1) + Math.abs(dx2) + Math.abs(dy2)))
return true;
}
}
return false;
}
}
class affine2d extends motion_model {
constructor() {
super();
}
run(from, to, model, count) {
let i = 0, j = 0;
const dt = model.type | JSFEAT_CONSTANTS.C1_t;
const md = model.data, t0d = this.T0.data, t1d = this.T1.data;
let pt0, pt1, px = 0.0, py = 0.0;
const _matmath = new matmath();
const _linalg = new jsfeatNext.linalg();
this.iso_normalize_points(from, to, t0d, t1d, count);
const a_buff = this.cache.get_buffer((2 * count * 6) << 3);
const b_buff = this.cache.get_buffer((2 * count) << 3);
const a_mt = new matrix_t(6, 2 * count, dt, a_buff.data);
const b_mt = new matrix_t(1, 2 * count, dt, b_buff.data);
const ad = a_mt.data, bd = b_mt.data;
for (; i < count; ++i) {
pt0 = from[i];
pt1 = to[i];
px = t0d[0] * pt0.x + t0d[1] * pt0.y + t0d[2];
py = t0d[3] * pt0.x + t0d[4] * pt0.y + t0d[5];
j = i * 2 * 6;
(ad[j] = px), (ad[j + 1] = py), (ad[j + 2] = 1.0), (ad[j + 3] = 0.0), (ad[j + 4] = 0.0), (ad[j + 5] = 0.0);
j += 6;
(ad[j] = 0.0), (ad[j + 1] = 0.0), (ad[j + 2] = 0.0), (ad[j + 3] = px), (ad[j + 4] = py), (ad[j + 5] = 1.0);
bd[i << 1] = t1d[0] * pt1.x + t1d[1] * pt1.y + t1d[2];
bd[(i << 1) + 1] = t1d[3] * pt1.x + t1d[4] * pt1.y + t1d[5];
}
_matmath.multiply_AtA(this.AtA, a_mt);
_matmath.multiply_AtB(this.AtB, a_mt, b_mt);
_linalg.lu_solve(this.AtA, this.AtB);
(md[0] = this.AtB.data[0]), (md[1] = this.AtB.data[1]), (md[2] = this.AtB.data[2]);
(md[3] = this.AtB.data[3]), (md[4] = this.AtB.data[4]), (md[5] = this.AtB.data[5]);
(md[6] = 0.0), (md[7] = 0.0), (md[8] = 1.0);
_matmath.invert_3x3(this.T1, this.T1);
_matmath.multiply_3x3(model, this.T1, model);
_matmath.multiply_3x3(model, model, this.T0);
this.cache.put_buffer(a_buff);
this.cache.put_buffer(b_buff);
return 1;
}
}
class homography2d extends motion_model {
mLtL;
Evec;
constructor() {
super();
this.mLtL = new matrix_t(9, 9, JSFEAT_CONSTANTS.F32_t | JSFEAT_CONSTANTS.C1_t);
this.Evec = new matrix_t(9, 9, JSFEAT_CONSTANTS.F32_t | JSFEAT_CONSTANTS.C1_t);
}
run(from, to, model, count) {
let i = 0, j = 0;
const md = model.data, t0d = this.T0.data, t1d = this.T1.data;
const LtL = this.mLtL.data, evd = this.Evec.data;
let x = 0.0, y = 0.0, X = 0.0, Y = 0.0;
const _linalg = new jsfeatNext.linalg();
const _matmath = new matmath();
let smx = 0.0, smy = 0.0, cmx = 0.0, cmy = 0.0, sMx = 0.0, sMy = 0.0, cMx = 0.0, cMy = 0.0;
for (; i < count; ++i) {
cmx += to[i].x;
cmy += to[i].y;
cMx += from[i].x;
cMy += from[i].y;
}
cmx /= count;
cmy /= count;
cMx /= count;
cMy /= count;
for (i = 0; i < count; ++i) {
smx += Math.abs(to[i].x - cmx);
smy += Math.abs(to[i].y - cmy);
sMx += Math.abs(from[i].x - cMx);
sMy += Math.abs(from[i].y - cMy);
}
if (Math.abs(smx) < JSFEAT_CONSTANTS.EPSILON ||
Math.abs(smy) < JSFEAT_CONSTANTS.EPSILON ||
Math.abs(sMx) < JSFEAT_CONSTANTS.EPSILON ||
Math.abs(sMy) < JSFEAT_CONSTANTS.EPSILON)
return 0;
smx = count / smx;
smy = count / smy;
sMx = count / sMx;
sMy = count / sMy;
t0d[0] = sMx;
t0d[1] = 0;
t0d[2] = -cMx * sMx;
t0d[3] = 0;
t0d[4] = sMy;
t0d[5] = -cMy * sMy;
t0d[6] = 0;
t0d[7] = 0;
t0d[8] = 1;
t1d[0] = 1.0 / smx;
t1d[1] = 0;
t1d[2] = cmx;
t1d[3] = 0;
t1d[4] = 1.0 / smy;
t1d[5] = cmy;
t1d[6] = 0;
t1d[7] = 0;
t1d[8] = 1;
i = 81;
while (--i >= 0) {
LtL[i] = 0.0;
}
for (i = 0; i < count; ++i) {
x = (to[i].x - cmx) * smx;
y = (to[i].y - cmy) * smy;
X = (from[i].x - cMx) * sMx;
Y = (from[i].y - cMy) * sMy;
LtL[0] += X * X;
LtL[1] += X * Y;
LtL[2] += X;
LtL[6] += X * -x * X;
LtL[7] += X * -x * Y;
LtL[8] += X * -x;
LtL[10] += Y * Y;
LtL[11] += Y;
LtL[15] += Y * -x * X;
LtL[16] += Y * -x * Y;
LtL[17] += Y * -x;
LtL[20] += 1.0;
LtL[24] += -x * X;
LtL[25] += -x * Y;
LtL[26] += -x;
LtL[30] += X * X;
LtL[31] += X * Y;
LtL[32] += X;
LtL[33] += X * -y * X;
LtL[34] += X * -y * Y;
LtL[35] += X * -y;
LtL[40] += Y * Y;
LtL[41] += Y;
LtL[42] += Y * -y * X;
LtL[43] += Y * -y * Y;
LtL[44] += Y * -y;
LtL[50] += 1.0;
LtL[51] += -y * X;
LtL[52] += -y * Y;
LtL[53] += -y;
LtL[60] += -x * X * -x * X + -y * X * -y * X;
LtL[61] += -x * X * -x * Y + -y * X * -y * Y;
LtL[62] += -x * X * -x + -y * X * -y;
LtL[70] += -x * Y * -x * Y + -y * Y * -y * Y;
LtL[71] += -x * Y * -x + -y * Y * -y;
LtL[80] += -x * -x + -y * -y;
}
for (i = 0; i < 9; ++i) {
for (j = 0; j < i; ++j)
LtL[i * 9 + j] = LtL[j * 9 + i];
}
_linalg.eigenVV(this.mLtL, this.Evec);
(md[0] = evd[72]), (md[1] = evd[73]), (md[2] = evd[74]);
(md[3] = evd[75]), (md[4] = evd[76]), (md[5] = evd[77]);
(md[6] = evd[78]), (md[7] = evd[79]), (md[8] = evd[80]);
_matmath.multiply_3x3(model, this.T1, model);
_matmath.multiply_3x3(model, model, this.T0);
x = 1.0 / md[8];
md[0] *= x;
md[1] *= x;
md[2] *= x;
md[3] *= x;
md[4] *= x;
md[5] *= x;
md[6] *= x;
md[7] *= x;
md[8] = 1.0;
return 1;
}
error(from, to, model, err, count) {
let i = 0;
let pt0, pt1, ww = 0.0, dx = 0.0, dy = 0.0;
const m = model.data;
for (; i < count; ++i) {
pt0 = from[i];
pt1 = to[i];
ww = 1.0 / (m[6] * pt0.x + m[7] * pt0.y + 1.0);
dx = (m[0] * pt0.x + m[1] * pt0.y + m[2]) * ww - pt1.x;
dy = (m[3] * pt0.x + m[4] * pt0.y + m[5]) * ww - pt1.y;
err[i] = dx * dx + dy * dy;
}
}
check_subset(from, to, count) {
const _matmath = new matmath();
if (count == 4) {
let negative = 0;
const fp0 = from[0], fp1 = from[1], fp2 = from[2], fp3 = from[3];
const tp0 = to[0], tp1 = to[1], tp2 = to[2], tp3 = to[3];
let A11 = fp0.x, A12 = fp0.y, A13 = 1.0;
let A21 = fp1.x, A22 = fp1.y, A23 = 1.0;
let A31 = fp2.x, A32 = fp2.y, A33 = 1.0;
let B11 = tp0.x, B12 = tp0.y, B13 = 1.0;
let B21 = tp1.x, B22 = tp1.y, B23 = 1.0;
let B31 = tp2.x, B32 = tp2.y, B33 = 1.0;
let detA = _matmath.determinant_3x3(A11, A12, A13, A21, A22, A23, A31, A32, A33);
let detB = _matmath.determinant_3x3(B11, B12, B13, B21, B22, B23, B31, B32, B33);
if (detA * detB < 0)
negative++;
(A11 = fp1.x), (A12 = fp1.y);
(A21 = fp2.x), (A22 = fp2.y);
(A31 = fp3.x), (A32 = fp3.y);
(B11 = tp1.x), (B12 = tp1.y);
(B21 = tp2.x), (B22 = tp2.y);
(B31 = tp3.x), (B32 = tp3.y);
detA = _matmath.determinant_3x3(A11, A12, A13, A21, A22, A23, A31, A32, A33);
detB = _matmath.determinant_3x3(B11, B12, B13, B21, B22, B23, B31, B32, B33);
if (detA * detB < 0)
negative++;
(A11 = fp0.x), (A12 = fp0.y);
(A21 = fp2.x), (A22 = fp2.y);
(A31 = fp3.x), (A32 = fp3.y);
(B11 = tp0.x), (B12 = tp0.y);
(B21 = tp2.x), (B22 = tp2.y);
(B31 = tp3.x), (B32 = tp3.y);
detA = _matmath.determinant_3x3(A11, A12, A13, A21, A22, A23, A31, A32, A33);
detB = _matmath.determinant_3x3(B11, B12, B13, B21, B22, B23, B31, B32, B33);
if (detA * detB < 0)
negative++;
(A11 = fp0.x), (A12 = fp0.y);
(A21 = fp1.x), (A22 = fp1.y);
(A31 = fp3.x), (A32 = fp3.y);
(B11 = tp0.x), (B12 = tp0.y);
(B21 = tp1.x), (B22 = tp1.y);
(B31 = tp3.x), (B32 = tp3.y);
detA = _matmath.determinant_3x3(A11, A12, A13, A21, A22, A23, A31, A32, A33);
detB = _matmath.determinant_3x3(B11, B12, B13, B21, B22, B23, B31, B32, B33);
if (detA * detB < 0)
negative++;
if (negative != 0 && negative != 4) {
return false;
}
}
return true;
}
}
jsfeatNext.cache = cache;
jsfeatNext.pyramid_t = class pyramid_t extends jsfeatNext {
levels;
data;
pyrdown;
constructor(levels) {
super();
this.levels = levels | 0;
this.data = new Array(levels);
const _imgproc = new jsfeatNext.imgproc();
this.pyrdown = _imgproc.pyrdown;
}
allocate(start_w, start_h, data_type) {
let i = this.levels;
while (--i >= 0) {
this.data[i] = new matrix_t(start_w >> i, start_h >> i, data_type);
}
}
build(input, skip_first_level) {
if (typeof skip_first_level === "undefined") {
skip_first_level = true;
}
let i = 2, a = input, b = this.data[0];
if (!skip_first_level) {
let j = input.cols * input.rows;
while (--j >= 0) {
b.data[j] = input.data[j];
}
}
b = this.data[1];
this.pyrdown(a, b);
for (; i < this.levels; ++i) {
a = b;
b = this.data[i];
this.pyrdown(a, b);
}
}
};
jsfeatNext.transform = transform;
jsfeatNext.matrix_t = matrix_t;
jsfeatNext.keypoint_t = keypoint_t;
jsfeatNext.fast_corners = class fast_corners extends jsfeatNext {
offsets16;
_threshold;
threshold_tab;
pixel_off;
score_diff;
constructor() {
super();
this.offsets16 = new Int32Array([
0, 3, 1, 3, 2, 2, 3, 1, 3, 0, 3, -1, 2, -2, 1, -3, 0, -3, -1, -3, -2, -2, -3, -1, -3, 0, -3, 1, -2, 2, -1,
3,
]);
this.threshold_tab = new Uint8Array(512);
this._threshold = 20;
this.pixel_off = new Int32Array(25);
this.score_diff = new Int32Array(25);
}
set_threshold(threshold) {
this._threshold = Math.min(Math.max(threshold, 0), 255);
for (let i = -255; i <= 255; ++i) {
this.threshold_tab[i + 255] = i < -this._threshold ? 1 : i > this._threshold ? 2 : 0;
}
return this._threshold;
}
detect(src, corners, border) {
if (typeof border === "undefined") {
border = 3;
}
const K = 8, N = 25;
const img = src.data, w = src.cols, h = src.rows;
let i = 0, j = 0, k = 0, vt = 0, x = 0, m3 = 0;
const buf_node = this.cache.get_buffer(3 * w);
const cpbuf_node = this.cache.get_buffer(((w + 1) * 3) << 2);
const buf = buf_node.u8;
const cpbuf = cpbuf_node.i32;
const pixel = this.pixel_off;
const sd = this.score_diff;
const sy = Math.max(3, border);
const ey = Math.min(h - 2, h - border);
const sx = Math.max(3, border);
const ex = Math.min(w - 3, w - border);
let _count = 0, corners_cnt = 0, pt;
const score_func = _cmp_score_16;
const thresh_tab = this.threshold_tab;
const threshold = this._threshold;
let v = 0, tab = 0, d = 0, ncorners = 0, cornerpos = 0, curr = 0, ptr = 0, prev = 0, pprev = 0;
let jp1 = 0, jm1 = 0, score = 0;
this._cmp_offsets(pixel, w, 16);
const pixel0 = pixel[0];
const pixel1 = pixel[1];
const pixel2 = pixel[2];
const pixel3 = pixel[3];
const pixel4 = pixel[4];
const pixel5 = pixel[5];
const pixel6 = pixel[6];
const pixel7 = pixel[7];
const pixel8 = pixel[8];
const pixel9 = pixel[9];
const pixel10 = pixel[10];
const pixel11 = pixel[11];
const pixel12 = pixel[12];
const pixel13 = pixel[13];
const pixel14 = pixel[14];
const pixel15 = pixel[15];
for (i = 0; i < w * 3; ++i) {
buf[i] = 0;
}
for (i = sy; i < ey; ++i) {
ptr = (i * w + sx) | 0;
m3 = (i - 3) % 3;
curr = (m3 * w) | 0;
cornerpos = (m3 * (w + 1)) | 0;
for (j = 0; j < w; ++j)
buf[curr + j] = 0;
ncorners = 0;
if (i < ey - 1) {
j = sx;
for (; j < ex; ++j, ++ptr) {
v = img[ptr];
tab = -v + 255;
d = thresh_tab[tab + img[ptr + pixel0]] | thresh_tab[tab + img[ptr + pixel8]];
if (d == 0) {
continue;
}
d &= thresh_tab[tab + img[ptr + pixel2]] | thresh_tab[tab + img[ptr + pixel10]];
d &= thresh_tab[tab + img[ptr + pixel4]] | thresh_tab[tab + img[ptr + pixel12]];
d &= thresh_tab[tab + img[ptr + pixel6]] | thresh_tab[tab + img[ptr + pixel14]];
if (d == 0) {
continue;
}
d &= thresh_tab[tab + img[ptr + pixel1]] | thresh_tab[tab + img[ptr + pixel9]];
d &= thresh_tab[tab + img[ptr + pixel3]] | thresh_tab[tab + img[ptr + pixel11]];
d &= thresh_tab[tab + img[ptr + pixel5]] | thresh_tab[tab + img[ptr + pixel13]];
d &= thresh_tab[tab + img[ptr + pixel7]] | thresh_tab[tab + img[ptr + pixel15]];
if (d & 1) {
vt = v - threshold;
_count = 0;
for (k = 0; k < N; ++k) {
x = img[ptr + pixel[k]];
if (x < vt) {
++_count;
if (_count > K) {
++ncorners;
cpbuf[cornerpos + ncorners] = j;
buf[curr + j] = score_func(img, ptr, pixel, sd, threshold);
break;
}
}
else {
_count = 0;
}
}
}
if (d & 2) {
vt = v + threshold;
_count = 0;
for (k = 0; k < N; ++k) {
x = img[ptr + pixel[k]];
if (x > vt) {
++_count;
if (_count > K) {
++ncorners;
cpbuf[cornerpos + ncorners] = j;
buf[curr + j] = score_func(img, ptr, pixel, sd, threshold);
break;
}
}
else {
_count = 0;
}
}
}
}
}
cpbuf[cornerpos + w] = ncorners;
if (i == sy) {
continue;
}
m3 = (i - 4 + 3) % 3;
prev = (m3 * w) | 0;
cornerpos = (m3 * (w + 1)) | 0;
m3 = (i - 5 + 3) % 3;
pprev = (m3 * w) | 0;
ncorners = cpbuf[cornerpos + w];
for (k = 0; k < ncorners; ++k) {
j = cpbuf[cornerpos + k];
jp1 = (j + 1) | 0;
jm1 = (j - 1) | 0;
score = buf[prev + j];
if (score > buf[prev + jp1] &&
score > buf[prev + jm1] &&
score > buf[pprev + jm1] &&
score > buf[pprev + j] &&
score > buf[pprev + jp1] &&
score > buf[curr + jm1] &&
score > buf[curr + j] &&
score > buf[curr + jp1]) {
pt = corners[corners_cnt];
(pt.x = j), (pt.y = i - 1), (pt.score = score);
corners_cnt++;
}
}
}
this.cache.put_buffer(buf_node);
this.cache.put_buffer(cpbuf_node);
return corners_cnt;
}
_cmp_offsets(pixel, step, pattern_size) {
let k = 0;
const offsets = this.offsets16;
for (; k < pattern_size; ++k) {
pixel[k] = offsets[k << 1] + offsets[(k << 1) + 1] * step;
}
for (; k < 25; ++k) {
pixel[k] = pixel[k - pattern_size];
}
}
};
jsfeatNext.imgproc = class imgproc extends jsfeatNext {
constructor() {
super();
}
grayscale(src, w, h, dst, code) {
if (typeof code === "undefined") {
code = JSFEAT_CONSTANTS.COLOR_RGBA2GRAY;
}
let x = 0, y = 0, i = 0, j = 0, ir = 0, jr = 0;
let coeff_r = 4899, coeff_g = 9617, coeff_b = 1868, cn = 4;
if (code == JSFEAT_CONSTANTS.COLOR_BGRA2GRAY || code == JSFEAT_CONSTANTS.COLOR_BGR2GRAY) {
coeff_r = 1868;
coeff_b = 4899;
}
if (code == JSFEAT_CONSTANTS.COLOR_RGB2GRAY || code == JSFEAT_CONSTANTS.COLOR_BGR2GRAY) {
cn = 3;
}
const cn2 = cn << 1, cn3 = (cn * 3) | 0;
dst.resize(w, h, 1);
const dst_u8 = dst.data;
for (y = 0; y < h; ++y, j += w, i += w * cn) {
for (x = 0, ir = i, jr = j; x <= w - 4; x += 4, ir += cn << 2, jr += 4) {
dst_u8[jr] = (src[ir] * coeff_r + src[ir + 1] * coeff_g + src[ir + 2] * coeff_b + 8192) >> 14;
dst_u8[jr + 1] =
(src[ir + cn] * coeff_r + src[ir + cn + 1] * coeff_g + src[ir + cn + 2] * coeff_b + 8192) >> 14;
dst_u8[jr + 2] =
(src[ir + cn2] * coeff_r + src[ir + cn2 + 1] * coeff_g + src[ir + cn2 + 2] * coeff_b + 8192) >> 14;
dst_u8[jr + 3] =
(src[ir + cn3] * coeff_r + src[ir + cn3 + 1] * coeff_g + src[ir + cn3 + 2] * coeff_b + 8192) >> 14;
}
for (; x < w; ++x, ++jr, ir += cn) {
dst_u8[jr] = (src[ir] * coeff_r + src[ir + 1] * coeff_g + src[ir + 2] * coeff_b + 8192) >> 14;
}
}
}
resample(src, dst, nw, nh) {
const h = src.rows, w = src.cols;
if (h > nh && w > nw) {
dst.resize(nw, nh, src.channel);
if (src.type & JSFEAT_CONSTANTS.U8_t && dst.type & JSFEAT_CONSTANTS.U8_t && (h * w) / (nh * nw) < 0x100) {
_resample_u8(src, dst, this.cache, nw, nh);
}
else {
_resample(src, dst, this.cache, nw, nh);
}
}
}
box_blur_gray(src, dst, radius, options) {
if (typeof options === "undefined") {
options = 0;
}
const w = src.cols, h = src.rows, h2 = h << 1, w2 = w << 1;
let i = 0, x = 0, y = 0, end = 0;
const windowSize = ((radius << 1) + 1) | 0;
const radiusPlusOne = (radius + 1) | 0, radiusPlus2 = (radiusPlusOne + 1) | 0;
const scale = options & JSFEAT_CONSTANTS.BOX_BLUR_NOSCALE ? 1 : 1.0 / (windowSize * windowSize);
const tmp_buff = this.cache.get_buffer((w * h) << 2);
let sum = 0, dstIndex = 0, srcIndex = 0, nextPixelIndex = 0, previousPixelIndex = 0;
const data_i32 = tmp_buff.i32;
let data_u8 = src.data;
let hold = 0;
dst.resize(w, h, src.channel);
for (y = 0; y < h; ++y) {
dstIndex = y;
sum = radiusPlusOne * data_u8[srcIndex];
for (i = (srcIndex + 1) | 0, end = (srcIndex + radius) | 0; i <= end; ++i) {
sum += data_u8[i];
}
nextPixelIndex = (srcIndex + radiusPlusOne) | 0;
previousPixelIndex = srcIndex;
hold = data_u8[previousPixelIndex];
for (x = 0; x < radius; ++x, dstIndex += h) {
data_i32[dstIndex] = sum;
sum += data_u8[nextPixelIndex] - hold;
nextPixelIndex++;
}
for (; x < w - radiusPlus2; x += 2, dstIndex += h2) {
data_i32[dstIndex] = sum;
sum += data_u8[nextPixelIndex] - data_u8[previousPixelIndex];
data_i32[dstIndex + h] = sum;
sum += data_u8[nextPixelIndex + 1] - data_u8[previousPixelIndex + 1];
nextPixelIndex += 2;
previousPixelIndex += 2;
}
for (; x < w - radiusPlusOne; ++x, dstIndex += h) {
data_i32[dstIndex] = sum;
sum += data_u8[nextPixelIndex] - data_u8[previousPixelIndex];
nextPixelIndex++;
previousPixelIndex++;
}
hold = data_u8[nextPixelIndex - 1];
for (; x < w; ++x, dstIndex += h) {
data_i32[dstIndex] = sum;
sum += hold - data_u8[previousPixelIndex];
previousPixelIndex++;
}
srcIndex += w;
}
srcIndex = 0;
data_u8 = dst.data;
if (scale == 1) {
for (y = 0; y < w; ++y) {
dstIndex = y;
sum = radiusPlusOne * data_i32[srcIndex];
for (i = (srcIndex + 1) | 0, end = (srcIndex + radius) | 0; i <= end; ++i) {
sum += data_i32[i];
}
nextPixelIndex = srcIndex + radiusPlusOne;
previousPixelIndex = srcIndex;
hold = data_i32[previousPixelIndex];
for (x = 0; x < radius; ++x, dstIndex += w) {
data_u8[dstIndex] = sum;
sum += data_i32[nextPixelIndex] - hold;
nextPixelIndex++;
}
for (; x < h - radiusPlus2; x += 2, dstIndex += w2) {
data_u8[dstIndex] = sum;
sum += data_i32[nextPixelIndex] - data_i32[previousPixelIndex];
data_u8[dstIndex + w] = sum;
sum += data_i32[nextPixelIndex + 1] - data_i32[previousPixelIndex + 1];
nextPixelIndex += 2;
previousPixelIndex += 2;
}
for (; x < h - radiusPlusOne; ++x, dstIndex += w) {
data_u8[dstIndex] = sum;
sum += data_i32[nextPixelIndex] - data_i32[previousPixelIndex];
nextPixelIndex++;
previousPixelIndex++;
}
hold = data_i32[nextPixelIndex - 1];
for (; x < h; ++x, dstIndex += w) {
data_u8[dstIndex] = sum;
sum += hold - data_i32[previousPixelIndex];
previousPixelIndex++;
}
srcIndex += h;
}
}
else {
for (y = 0; y < w; ++y) {
dstIndex = y;
sum = radiusPlusOne * data_i32[srcIndex];
for (i = (srcIndex + 1) | 0, end = (srcIndex + radius) | 0; i <= end; ++i) {
sum += data_i32[i];
}
nextPixelIndex = srcIndex + radiusPlusOne;
previousPixelIndex = srcIndex;
hold = data_i32[previousPixelIndex];
for (x = 0; x < radius; ++x, dstIndex += w) {
data_u8[dstIndex] = sum * scale;
sum += data_i32[nextPixelIndex] - hold;
nextPixelIndex++;
}
for (; x < h - radiusPlus2; x += 2, dstIndex += w2) {
data_u8[dstIndex] = sum * scale;
sum += data_i32[nextPixelIndex] - data_i32[previousPixelIndex];
data_u8[dstIndex + w] = sum * scale;
sum += data_i32[nextPixelIndex + 1] - data_i32[previousPixelIndex + 1];
nextPixelIndex += 2;
previousPixelIndex += 2;
}
for (; x < h - radiusPlusOne; ++x, dstIndex += w) {
data_u8[dstIndex] = sum * scale;
sum += data_i32[nextPixelIndex] - data_i32[previousPixelIndex];
nextPixelIndex++;
previousPixelIndex++;
}
hold = data_i32[nextPixelIndex - 1];
for (; x < h; ++x, dstIndex += w) {
data_u8[dstIndex] = sum * scale;
sum += hold - data_i32[previousPixelIndex];
previousPixelIndex++;
}
srcIndex += h;
}
}
this.cache.put_buffer(tmp_buff);
}
gaussian_blur(src, dst, kernel_size, sigma) {
const jsfeatmath = new jsfeatNext.math();
if (typeof sigma === "undefined") {
sigma = 0.0;
}
if (typeof kernel_size === "undefined") {
kernel_size = 0;
}
kernel_size = kernel_size == 0 ? (Math.max(1, 4.0 * sigma + 1.0 - 1e-8) * 2 + 1) | 0 : kernel_size;
const half_kernel = kernel_size >> 1;
const w = src.cols, h = src.rows;
const data_type = src.type, is_u8 = data_type & JSFEAT_CONSTANTS.U8_t;
dst.resize(w, h, src.channel);
const src_d = src.data, dst_d = dst.data;
let buf, filter, buf_sz = (kernel_size + Math.max(h, w)) | 0;
const buf_node = this.cache.get_buffer(buf_sz << 2);
const filt_node = this.cache.get_buffer(kernel_size << 2);
if (is_u8) {
buf = buf_node.i32;
filter = filt_node.i32;
}
else if (data_type & JSFEAT_CONSTANTS.S32_t) {
buf = buf_node.i32;
filter = filt_node.f32;
}
else {
buf = buf_node.f32;
filter = filt_node.f32;
}
jsfeatmath.get_gaussian_kernel(kernel_size, sigma, filter, data_type);
if (is_u8) {
_convol_u8(buf, src_d, dst_d, w, h, filter, kernel_size, half_kernel);
}
else {
_convol(buf, src_d, dst_d, w, h, filter, kernel_size, half_kernel);
}
this.cache.put_buffer(buf_node);
this.cache.put_buffer(filt_node);
}
hough_transform(img, rho_res, theta_res, threshold) {
let r;
let i;
const image = img.data;
const width = img.cols;
const height = img.rows;
const step = width;
const min_theta = 0.0;
const max_theta = Math.PI;
const numangle = Math.round((max_theta - min_theta) / theta_res);
const numrho = Math.round(((width + height) * 2 + 1) / rho_res);
const irho = 1.0 / rho_res;
const accum = new Int32Array((numangle + 2) * (numrho + 2));
const tabSin = new Float32Array(numangle);
const tabCos = new Float32Array(numangle);
let n = 0;
let ang = min_theta;
for (; n < numangle; n++) {
tabSin[n] = Math.sin(ang) * irho;
tabCos[n] = Math.cos(ang) * irho;
ang += theta_res;
}
for (i = 0; i < height; i++) {
for (let j = 0; j < width; j++) {
if (image[i * step + j] != 0) {
for (n = 0; n < numangle; n++) {
r = Math.round(j * tabCos[n] + i * tabSin[n]);
r += (numrho - 1) / 2;
accum[(n + 1) * (numrho + 2) + r + 1] += 1;
}
}
}
}
const _sort_buf = [];
for (r = 0; r < numrho; r++) {
for (n = 0; n < numangle; n++) {
const base = (n + 1) * (numrho + 2) + r + 1;
if (accum[base] > threshold &&
accum[base] > accum[base - 1] &&
accum[base] >= accum[base + 1] &&
accum[base] > accum[base - numrho - 2] &&
accum[base] >= accum[base + numrho + 2]) {
_sort_buf.push(base);
}
}
}
_sort_buf.sort(function (l1, l2) {
return (accum[l1] > accum[l2] || (accum[l1] == accum[l2] && l1 < l2));
});
const linesMax = Math.min(numangle * numrho, _sort_buf.length);
const scale = 1.0 / (numrho + 2);
const lines = new Array();
for (i = 0; i < linesMax; i++) {
const idx = _sort_buf[i];
n = Math.floor(idx * scale) - 1;
r = idx - (n + 1) * (numrho + 2) - 1;
const lrho = (r - (numrho - 1) * 0.5) * rho_res;
const langle = n * theta_res;
lines.push([lrho, langle]);
}
return lines;
}
pyrdown(src, dst, sx, sy) {
if (typeof sx === "undefined") {
sx = 0;
}
if (typeof sy === "undefined") {
sy = 0;
}
const w = src.cols, h = src.rows;
const w2 = w >> 1, h2 = h >> 1;
const _w2 = w2 - (sx << 1), _h2 = h2 - (sy << 1);
let x = 0, y = 0, sptr = sx + sy * w, sline = 0, dptr = 0, dline = 0;
dst.resize(w2, h2, src.channel);
const src_d = src.data, dst_d = dst.data;
for (y = 0; y < _h2; ++y) {
sline = sptr;
dline = dptr;
for (x = 0; x <= _w2 - 2; x += 2, dline += 2, sline += 4) {
dst_d[dline] = (src_d[sline] + src_d[sline + 1] + src_d[sline + w] + src_d[sline + w + 1] + 2) >> 2;
dst_d[dline + 1] =
(src_d[sline + 2] + src_d[sline + 3] + src_d[sline + w + 2] + src_d[sline + w + 3] + 2) >> 2;
}
for (; x < _w2; ++x, ++dline, sline += 2) {
dst_d[dline] = (src_d[sline] + src_d[sline + 1] + src_d[sline + w] + src_d[sline + w + 1] + 2) >> 2;
}
sptr += w << 1;
dptr += w2;
}
}
scharr_derivatives(src, dst) {
const w = src.cols, h = src.rows;
let dstep = w << 1, x = 0, y = 0, x1 = 0, a, b, c, d, e, f;
let srow0 = 0, srow1 = 0, srow2 = 0, drow = 0;
let trow0, trow1;
dst.resize(w, h, 2);
const img = src.data, gxgy = dst.data;
const buf0_node = this.cache.get_buffer((w + 2) << 2);
const buf1_node = this.cache.get_buffer((w + 2) << 2);
if (src.type & JSFEAT_CONSTANTS.U8_t || src.type & JSFEAT_CONSTANTS.S32_t) {
trow0 = buf0_node.i32;
trow1 = buf1_node.i32;
}
else {
trow0 = buf0_node.f32;
trow1 = buf1_node.f32;
}
for (; y < h; ++y, srow1 += w) {
srow0 = ((y > 0 ? y - 1 : 1) * w) | 0;
srow2 = ((y < h - 1 ? y + 1 : h - 2) * w) | 0;
drow = (y * dstep) | 0;
for (x = 0, x1 = 1; x <= w - 2; x += 2, x1 += 2) {
(a = img[srow0 + x]), (b = img[srow2 + x]);
trow0[x1] = (a + b) * 3 + img[srow1 + x] * 10;
trow1[x1] = b - a;
(a = img[srow0 + x + 1]), (b = img[srow2 + x + 1]);
trow0[x1 + 1] = (a + b) * 3 + img[srow1 + x + 1] * 10;
trow1[x1 + 1] = b - a;
}
for (; x < w; ++x, ++x1) {
(a = img[srow0 + x]), (b = img[srow2 + x]);
trow0[x1] = (a + b) * 3 + img[srow1 + x] * 10;
trow1[x1] = b - a;
}
x = (w + 1) | 0;
trow0[0] = trow0[1];
trow0[x] = trow0[w];
trow1[0] = trow1[1];
trow1[x] = trow1[w];
for (x = 0; x <= w - 4; x += 4) {
(a = trow1[x + 2]),
(b = trow1[x + 1]),
(c = trow1[x + 3]),
(d = trow1[x + 4]),
(e = trow0[x + 2]),
(f = trow0[x + 3]);
gxgy[drow++] = e - trow0[x];
gxgy[drow++] = (a + trow1[x]) * 3 + b * 10;
gxgy[drow++] = f - trow0[x + 1];
gxgy[drow++] = (c + b) * 3 + a * 10;
gxgy[drow++] = trow0[x + 4] - e;
gxgy[drow++] = (d + a) * 3 + c * 10;
gxgy[drow++] = trow0[x + 5] - f;
gxgy[drow++] = (trow1[x + 5] + c) * 3 + d * 10;
}
for (; x < w; ++x) {
gxgy[drow++] = trow0[x + 2] - trow0[x];
gxgy[drow++] = (trow1[x + 2] + trow1[x]) * 3 + trow1[x + 1] * 10;
}
}
this.cache.put_buffer(buf0_node);
this.cache.put_buffer(buf1_node);
}
sobel_derivatives(src, dst) {
const w = src.cols, h = src.rows;
let dstep = w << 1, x = 0, y = 0, x1 = 0, a, b, c, d, e, f;
let srow0 = 0, srow1 = 0, srow2 = 0, drow = 0;
let trow0, trow1;
dst.resize(w, h, 2);
const img = src.data, gxgy = dst.data;
const buf0_node = this.cache.get_buffer((w + 2) << 2);
const buf1_node = this.cache.get_buffer((w + 2) << 2);
if (src.type & JSFEAT_CONSTANTS.U8_t || src.type & JSFEAT_CONSTANTS.S32_t) {
trow0 = buf0_node.i32;
trow1 = buf1_node.i32;
}
else {
trow0 = buf0_node.f32;
trow1 = buf1_node.f32;
}
for (; y < h; ++y, srow1 += w) {
srow0 = ((y > 0 ? y - 1 : 1) * w) | 0;
srow2 = ((y < h - 1 ? y + 1 : h - 2) * w) | 0;
drow = (y * dstep) | 0;
for (x = 0, x1 = 1; x <= w - 2; x += 2, x1 += 2) {
(a = img[srow0 + x]), (b = img[srow2 + x]);
trow0[x1] = a + b + img[srow1 + x] * 2;
trow1[x1] = b - a;
(a = img[srow0 + x + 1]), (b = img[srow2 + x + 1]);
trow0[x1 + 1] = a + b + img[srow1 + x + 1] * 2;
trow1[x1 + 1] = b - a;
}
for (; x < w; ++x, ++x1) {
(a = img[srow0 + x]), (b = img[srow2 + x]);
trow0[x1] = a + b + img[srow1 + x] * 2;
trow1[x1] = b - a;
}
x = (w + 1) | 0;
trow0[0] = trow0[1];
trow0[x] = trow0[w];
trow1[0] = trow1[1];
trow1[x] = trow1[w];
for (x = 0; x <= w - 4; x += 4) {
(a = trow1[x + 2]),
(b = trow1[x + 1]),
(c = trow1[x + 3]),
(d = trow1[x + 4]),
(e = trow0[x + 2]),
(f = trow0[x + 3]);
gxgy[drow++] = e - trow0[x];
gxgy[drow++] = a + trow1[x] + b * 2;
gxgy[drow++] = f - trow0[x + 1];
gxgy[drow++] = c + b + a * 2;
gxgy[drow++] = trow0[x + 4] - e;
gxgy[drow++] = d + a + c * 2;
gxgy[drow++] = trow0[x + 5] - f;
gxgy[drow++] = trow1[x + 5] + c + d * 2;
}
for (; x < w; ++x) {
gxgy[drow++] = trow0[x + 2] - trow0[x];
gxgy[drow++] = trow1[x + 2] + trow1[x] + trow1[x + 1] * 2;
}
}
this.cache.put_buffer(buf0_node);
this.cache.put_buffer(buf1_node);
}
compute_integral_image(src, dst_sum, dst_sqsum, dst_tilted) {
const w0 = src.cols | 0, h0 = src.rows | 0, src_d = src.data;
const w1 = (w0 + 1) | 0;
let s = 0, s2 = 0, p = 0, pup = 0, i = 0, j = 0, v = 0, k = 0;
if (dst_sum && dst_sqsum) {
for (; i < w1; ++i) {
(dst_sum[i] = 0), (dst_sqsum[i] = 0);
}
(p = (w1 + 1) | 0), (pup = 1);
for (i = 0, k = 0; i < h0; ++i, ++p, ++pup) {
s = s2 = 0;
for (j = 0; j <= w0 - 2; j += 2, k += 2, p += 2, pup += 2) {
v = src_d[k];
(s += v), (s2 += v * v);
dst_sum[p] = dst_sum[pup] + s;
dst_sqsum[p] = dst_sqsum[pup] + s2;
v = src_d[k + 1];
(s += v), (s2 += v * v);
dst_sum[p + 1] = dst_sum[pup + 1] + s;
dst_sqsum[p + 1] = dst_sqsum[pup + 1] + s2;
}
for (; j < w0; ++j, ++k, ++p, ++pup) {
v = src_d[k];
(s += v), (s2 += v * v);
dst_sum[p] = dst_sum[pup] + s;
dst_sqsum[p] = dst_sqsum[pup] + s2;
}
}
}
else if (dst_sum) {
for (; i < w1; ++i) {
dst_sum[i] = 0;
}
(p = (w1 + 1) | 0), (pup = 1);
for (i = 0, k = 0; i < h0; ++i, ++p, ++pup) {
s = 0;
for (j = 0; j <= w0 - 2; j += 2, k += 2, p += 2, pup += 2) {
s += src_d[k];
dst_sum[p] = dst_sum[pup] + s;
s += src_d[k + 1];
dst_sum[p + 1] = dst_sum[pup + 1] + s;
}
for (; j < w0; ++j, ++k, ++p, ++pup) {
s += src_d[k];
dst_sum[p] = dst_sum[pup] + s;
}
}
}
else if (dst_sqsum) {
for (; i < w1; ++i) {
dst_sqsum[i] = 0;
}
(p = (w1 + 1) | 0), (pup = 1);
for (i = 0, k = 0; i < h0; ++i, ++p, ++pup) {
s2 = 0;
for (j = 0; j <= w0 - 2; j += 2, k += 2, p += 2, pup += 2) {
v = src_d[k];
s2 += v * v;
dst_sqsum[p] = dst_sqsum[pup] + s2;
v = src_d[k + 1];
s2 += v * v;
dst_sqsum[p + 1] = dst_sqsum[pup + 1] + s2;
}
for (; j < w0; ++j, ++k, ++p, ++pup) {
v = src_d[k];
s2 += v * v;
dst_sqsum[p] = dst_sqsum[pup] + s2;
}
}
}
if (dst_tilted) {
for (i = 0; i < w1; ++i) {
dst_tilted[i] = 0;
}
(p = (w1 + 1) | 0), (pup = 0);
for (i = 0, k = 0; i < h0; ++i, ++p, ++pup) {
for (j = 0; j <= w0 - 2; j += 2, k += 2, p += 2, pup += 2) {
dst_tilted[p] = src_d[k] + dst_tilted[pup];
dst_tilted[p + 1] = src_d[k + 1] + dst_tilted[pup + 1];
}
for (; j < w0; ++j, ++k, ++p, ++pup) {
dst_tilted[p] = src_d[k] + dst_tilted[pup];
}
}
(p = (w1 + w0) | 0), (pup = w0);
for (i = 0; i < h0; ++i, p += w1, pup += w1) {
dst_tilted[p] += dst_tilted[pup];
}
for (j = w0 - 1; j > 0; --j) {
(p = j + h0 * w1), (pup = p - w1);
for (i = h0; i > 0; --i, p -= w1, pup -= w1) {
dst_tilted[p] += dst_tilted[pup] + dst_tilted[pup + 1];
}
}
}
}
equalize_histogram(src, dst) {
const w = src.cols, h = src.rows, src_d = src.data;
dst.resize(w, h, src.channel);
const dst_d = dst.data, size = w * h;
let i = 0, prev = 0, hist0, norm;
const hist0_node = this.cache.get_buffer(256 << 2);
hist0 = hist0_node.i32;
for (; i < 256; ++i)
hist0[i] = 0;
for (i = 0; i < size; ++i) {
++hist0[src_d[i]];
}
prev = hist0[0];
for (i = 1; i < 256; ++i) {
prev = hist0[i] += prev;
}
norm = 255 / size;
for (i = 0; i < size; ++i) {
dst_d[i] = (hist0[src_d[i]] * norm + 0.5) | 0;
}
this.cache.put_buffer(hist0_node);
}
canny(src, dst, low_thresh, high_thresh) {
const w = src.cols, h = src.rows, src_d = src.data;
dst.resize(w, h, src.channel);
const dst_d = dst.data;
let i = 0, j = 0, grad = 0, w2 = w << 1, _grad = 0, suppress = 0, f = 0, x = 0, y = 0, s = 0;
let tg22x = 0, tg67x = 0;
const dxdy_node = this.cache.get_buffer((h * w2) << 2);
const buf_node = this.cache.get_buffer((3 * (w + 2)) << 2);
const map_node = this.cache.get_buffer(((h + 2) * (w + 2)) << 2);
const stack_node = this.cache.get_buffer((h * w) << 2);
const buf = buf_node.i32;
const map = map_node.i32;
const stack = stack_node.i32;
const dxdy = dxdy_node.i32;
const dxdy_m = new matrix_t(w, h, JSFEAT_CONSTANTS.S32C2_t, dxdy_node.data);
let row0 = 1, row1 = (w + 2 + 1) | 0, row2 = (2 * (w + 2) + 1) | 0, map_w = (w + 2) | 0, map_i = (map_w + 1) | 0, stack_i = 0;
this.sobel_derivatives(src, dxdy_m);
if (low_thresh > high_thresh) {
i = low_thresh;
low_thresh = high_thresh;
high_thresh = i;
}
i = (3 * (w + 2)) | 0;
while (--i >= 0) {
buf[i] = 0;
}
i = ((h + 2) * (w + 2)) | 0;
while (--i >= 0) {
map[i] = 0;
}
for (; j < w; ++j, grad += 2) {
(x = dxdy[grad]), (y = dxdy[grad + 1]);
buf[row1 + j] = (x ^ (x >> 31)) - (x >> 31) + ((y ^ (y >> 31)) - (y >> 31));
}
for (i = 1; i <= h; ++i, grad += w2) {
if (i == h) {
j = row2 + w;
while (--j >= row2) {
buf[j] = 0;
}
}
else {
for (j = 0; j < w; j++) {
(x = dxdy[grad + (j << 1)]), (y = dxdy[grad + (j << 1) + 1]);
buf[row2 + j] = (x ^ (x >> 31)) - (x >> 31) + ((y ^ (y >> 31)) - (y >> 31));
}
}
_grad = (grad - w2) | 0;
map[map_i - 1] = 0;
suppress = 0;
for (j = 0; j < w; ++j, _grad += 2) {
f = buf[row1 + j];
if (f > low_thresh) {
x = dxdy[_grad];
y = dxdy[_grad + 1];
s = x ^ y;
x = ((x ^ (x >> 31)) - (x >> 31)) | 0;
y = ((y ^ (y >> 31)) - (y >> 31)) | 0;
tg22x = x * 13573;
tg67x = tg22x + ((x + x) << 15);
y <<= 15;
if (y < tg22x) {
if (f > buf[row1 + j - 1] && f >= buf[row1 + j + 1]) {
if (f > high_thresh && !suppress && map[map_i + j - map_w] != 2) {
map[map_i + j] = 2;
suppress = 1;
stack[stack_i++] = map_i + j;
}
else {
map[map_i + j] = 1;
}
continue;
}
}
else if (y > tg67x) {
if (f > buf[row0 + j] && f >= buf[row2 + j]) {
if (f > high_thresh && !suppress && map[map_i + j - map_w] != 2) {
map[map_i + j] = 2;
suppress = 1;
stack[stack_i++] = map_i + j;
}
else {
map[map_i + j] = 1;
}
continue;
}
}
else {
s = s < 0 ? -1 : 1;
if (f > buf[row0 + j - s] && f > buf[row2 + j + s]) {
if (f > high_thresh && !suppress && map[map_i + j - map_w] != 2) {
map[map_i + j] = 2;
suppress = 1;