@webarkit/jsfeat-next
Version:
Typescript version of jsfeat for WebARKit
1,501 lines (1,306 loc) • 131 kB
text/typescript
import { IData_Type, data_type } from "./data_type/data_type";
import { cache } from "./cache/cache";
import { imgproc } from "./imgproc/imgproc";
import { _resample, _resample_u8 } from "./imgproc/resample";
import { _convol, _convol_u8 } from "./imgproc/convol";
import { linalg } from "./linalg/linalg";
import { swap, hypot } from "./linalg/linalg_base";
import { fast_corners } from "./fast_corners/fast_corners";
import { _cmp_score_16 } from "./fast_corners/fast_private";
import { math } from "./math/math";
import matmath from "./matmath/matmath";
import { matrix_t } from "./matrix_t/matrix_t";
import { pyramid_t } from "./pyramid_t/pyramid_t";
import { point_t } from "./point_t/point_t";
import { transform } from "./transform/transform";
import { keypoint_t } from "./keypoint_t/keypoint_t";
import { orb } from "./orb/orb";
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 { yape06 } from "./yape06/yape06";
import { ransac_params_t } from "./motion_estimator/ransac_params_t";
import { motion_estimator } from "./motion_estimator/motion_estimator";
import { optical_flow_lk } from "./optical_flow_lk/optical_flow_lk";
import { JSFEAT_CONSTANTS } from "./constants/constants";
import pkg from "../package.json";
export default class jsfeatNext {
private dt: IData_Type;
protected cache: cache;
static cache: typeof cache;
static fast_corners: typeof fast_corners;
static imgproc: typeof imgproc;
static linalg: typeof linalg;
static math: typeof math;
static matmath: typeof matmath;
static matrix_t: typeof matrix_t;
static pyramid_t: typeof pyramid_t;
static transform: typeof transform;
static keypoint_t: typeof keypoint_t;
static yape: typeof yape;
static yape06: typeof yape06;
static ransac_params_t: typeof ransac_params_t;
static affine2d: typeof affine2d;
static homography2d: typeof homography2d;
static motion_estimator: typeof motion_estimator;
static optical_flow_lk: typeof optical_flow_lk;
static orb: typeof orb;
constructor() {
this.dt = new data_type();
this.cache = new cache();
this.cache.allocate(30, 640 * 4);
}
// VERSION
static VERSION: string = pkg.version;
// CONSTANTS
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;
// color conversion
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;
// box blur option
static BOX_BLUR_NOSCALE = JSFEAT_CONSTANTS.BOX_BLUR_NOSCALE;
// svd options
static SVD_U_T = JSFEAT_CONSTANTS.SVD_U_T;
static SVD_V_T = JSFEAT_CONSTANTS.SVD_V_T;
// popular formats
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: number): number {
return this.dt._get_data_type(type);
}
get_channel(type: number): number {
return this.dt._get_channel(type);
}
get_data_type_size(type: number): number {
return this.dt._get_data_type_size(type);
}
}
class motion_model extends jsfeatNext {
public T0: matrix_t;
public T1: matrix_t;
public AtA: matrix_t;
public AtB: matrix_t;
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: number): number {
return x * x;
}
// does isotropic normalization
iso_normalize_points(from: point_t[], to: point_t[], T0: number[], T1: number[], count: number): void {
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: point_t[], count: number): boolean {
let j = 0,
k = 0,
i = (count - 1) | 0;
let dx1 = 0.0,
dy1 = 0.0,
dx2 = 0.0,
dy2 = 0.0;
// check that the i-th selected point does not belong
// to a line connecting some previously selected points
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: point_t[], to: point_t[], model: matrix_t, count: number): number {
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); // fill last row
// denormalize
_matmath.invert_3x3(this.T1, this.T1);
_matmath.multiply_3x3(model, this.T1, model);
_matmath.multiply_3x3(model, model, this.T0);
// free buffer
this.cache.put_buffer(a_buff);
this.cache.put_buffer(b_buff);
return 1;
}
}
class homography2d extends motion_model {
public mLtL: matrix_t;
public Evec: matrix_t;
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: point_t[], to: point_t[], model: matrix_t, count: number): number {
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();
// norm
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;
//
// construct system
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;
}
//
// symmetry
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]);
// denormalize
_matmath.multiply_3x3(model, this.T1, model);
_matmath.multiply_3x3(model, model, this.T0);
// set bottom right to 1.0
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: point_t[], to: point_t[], model: matrix_t, err: Int32Array | Float32Array, count: number): void {
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: point_t[], to: point_t[], count: number): boolean {
// seems to reject good subsets actually
//if( have_collinear_points(from, count) || have_collinear_points(to, count) ) {
//return false;
//}
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];
// set1
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++;
// set2
(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++;
// set3
(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++;
// set4
(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; // all good
}
}
jsfeatNext.cache = cache;
jsfeatNext.pyramid_t = class pyramid_t extends jsfeatNext {
public levels: number;
public data: any;
private pyrdown: any;
constructor(levels: number) {
super();
this.levels = levels | 0;
this.data = new Array(levels);
const _imgproc = new jsfeatNext.imgproc();
this.pyrdown = _imgproc.pyrdown;
}
allocate(start_w: number, start_h: number, data_type: number): void {
let i = this.levels;
while (--i >= 0) {
this.data[i] = new matrix_t(start_w >> i, start_h >> i, data_type);
}
}
build(input: matrix_t, skip_first_level: boolean): void {
if (typeof skip_first_level === "undefined") {
skip_first_level = true;
}
// just copy data to first level
let i = 2,
a = input,
b: any = 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 {
private offsets16: Int32Array;
public _threshold: number;
public threshold_tab: Uint8Array;
public pixel_off: Int32Array;
public score_diff: Int32Array;
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: number): number {
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: matrix_t, corners: point_t[], border: number): number {
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);
// local vars are faster?
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]
) {
// save corner
pt = corners[corners_cnt];
(pt.x = j), (pt.y = i - 1), (pt.score = score);
corners_cnt++;
}
}
} // y loop
this.cache.put_buffer(buf_node);
this.cache.put_buffer(cpbuf_node);
return corners_cnt;
}
private _cmp_offsets(pixel: Uint8Array | Int32Array, step: number, pattern_size: number): void {
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: Uint8Array | Uint8ClampedArray, w: number, h: number, dst: matrix_t, code?: number): void {
// this is default image data representation in browser
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;
}
}
}
// derived from CCV library
resample(src: matrix_t, dst: matrix_t, nw: number, nh: number): void {
const h = src.rows,
w = src.cols;
if (h > nh && w > nw) {
dst.resize(nw, nh, src.channel);
// using the fast alternative (fix point scale, 0x100 to avoid overflow)
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: matrix_t, dst: matrix_t, radius: number, options: number): void {
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; // to prevent overflow
let data_u8 = src.data;
let hold = 0;
dst.resize(w, h, src.channel);
// first pass
// no need to scale
//data_u8 = src.data;
//data_i32 = tmp;
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;
}
//
// second pass
srcIndex = 0;
//data_i32 = tmp; // this is a transpose
data_u8 = dst.data;
// dont scale result
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: matrix_t, dst: matrix_t, kernel_size: number, sigma: number): void {
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: matrix_t, rho_res: number, theta_res: number, threshold: number): number[] {
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)); //typed arrays are initialized to 0
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;
}
// stage 1. fill accumulator
for (i = 0; i < height; i++) {
for (let j = 0; j < width; j++) {
if (image[i * step + j] != 0) {
//console.log(r, (n+1) * (numrho+2) + r+1, tabCos[n], tabSin[n]);
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;
}
}
}
}
// stage 2. find local maximums
//TODO: Consider making a vector class that uses typed arrays
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);
}
}
}
// stage 3. sort the detected lines by accumulator value
_sort_buf.sort(function (l1, l2) {
return <number>(<unknown>(accum[l1] > accum[l2] || (accum[l1] == accum[l2] && l1 < l2)));
});
// stage 4. store the first min(total,linesMax) lines to the output buffer
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: matrix_t, dst: matrix_t, sx?: number, sy?: number): void {
// this is needed for bbf
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;
}
}
// dst: [gx,gy,...]
scharr_derivatives(src: matrix_t, dst: matrix_t): void {
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); // 2 channel output gx, gy
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;
// do vertical convolution
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;
}
// make border
x = (w + 1) | 0;
trow0[0] = trow0[1];
trow0[x] = trow0[w];
trow1[0] = trow1[1];
trow1[x] = trow1[w];
// do horizontal convolution, interleave the results and store them
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);
}
// compute gradient using Sobel kernel [1 2 1] * [-1 0 1]^T
// dst: [gx,gy,...]
sobel_derivatives(src: matrix_t, dst: matrix_t): void {
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); // 2 channel output gx, gy
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;
// do vertical convolution
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;
}
// make border
x = (w + 1) | 0;
trow0[0] = trow0[1];
trow0[x] = trow0[w];
trow1[0] = trow1[1];
trow1[x] = trow1[w];
// do horizontal convolution, interleave the results and store them
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);
}
// please note:
// dst_(type) size should be cols = src.cols+1, rows = src.rows+1
compute_integral_image(src: matrix_t, dst_sum: number[], dst_sqsum: number[], dst_tilted: any[]): void {
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) {
// fill first row with zeros
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) {
// fill first row with zeros
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) {
// fill first row with zeros
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;
fo