From 03ee4b235117de8f295f01b8315f19bb76a2c9d6 Mon Sep 17 00:00:00 2001 From: Walter Perdan Date: Sat, 11 Jul 2026 10:49:04 +0200 Subject: [PATCH] feat(api)!: singleton modules, shared cache, direct namespace export (#41) BREAKING CHANGE: restores original jsfeat's calling convention (ships as 0.9.0). Designed via /brainstorming with every decision user-confirmed; full migration guide in docs/migration-0.9.md. What changed: - The 14 algorithm modules (imgproc, math, matmath, linalg, transform, fast_corners, yape, yape06, orb, optical_flow_lk, motion_estimator, affine2d, homography2d, cache) are now SINGLETON INSTANCES on the namespace: jsfeatNext.imgproc.grayscale(...) -- no `new`. The classes themselves are untouched (kept verbatim so the parity suite still guards unmodified algorithm code); the aggregator instantiates them once at load. - ONE shared buffer pool: src/core/core.ts now creates a module-level shared_cache that every module binds to, replacing the per-instance 30-buffer/76.8KB pools. jsfeatNext.cache is that pool instance (get_buffer/put_buffer), matching original jsfeat's global jsfeat.cache -- which was never a constructor. - The jsfeatNext.jsfeatNext double namespace is gone: src/index.ts default-exports the namespace directly (UMD global and ESM default are now the namespace itself). - Data-structure constructors unchanged: matrix_t, keypoint_t, pyramid_t, ransac_params_t are still `new`ed, as in original jsfeat. Ground truth for the design (verified against inspirit/jsfeat source, not assumed): jsfeat's own modules are singletons even when stateful (fast_corners keeps closure-level _threshold and is configured once via set_threshold(20) at load); imgproc borrows from the single global jsfeat.cache -- per-instance pools defeated the cache's purpose and made new-in-a-frame-loop silently reallocate pools every frame. Also in this change: - tests: parity suite converted to the new convention (63 tests total, including a new tests/api-shape.test.ts pinning the namespace shape, singleton identity, shared-cache identity and constructor surface) - examples: all 22 converted and validated against the new bundle (the 5 non-camera ones executed in Node, all 22 static-scanned, and browser-tested by the user); browser.html's cache usage rewritten (it used new jsfeat.cache()+allocate(), which original jsfeat never supported either); grayscale example simplified to direct jsfeatNext.imgproc.grayscale() calls - docs: docs/migration-0.9.md (full old->new mapping + motivation), README quick start, AGENTS.md architecture/gotchas (also refreshed for the post-#47 layout), CLAUDE.md notes (were stale re: monolith and 'no tests'), copilot-instructions, audit doc Axis 2 marked resolved with a status column on the severity table - dist/ + types/ rebuilt and committed IN THIS PR (deviation from the source-only convention, at user request: an API-breaking change with a stale committed bundle made local example testing confusing) Verified: tsc --noEmit clean; npm test 63/63; prettier clean; UMD and ESM bundle shapes checked (no double namespace, singletons callable, shared pool public); examples validated against the new bundle and manually tested in a browser by the user. Closes #41. Co-Authored-By: Claude Fable 5 --- .github/copilot-instructions.md | 12 +- AGENTS.md | 44 ++-- CLAUDE.md | 7 +- README.md | 23 +- dist/jsfeatNext.js | 2 +- dist/jsfeatNext.mjs | 232 +++++++++--------- docs/jsfeat-parity-and-refactor-audit.md | 28 ++- docs/migration-0.9.md | 134 ++++++++++ examples/browser.html | 15 +- examples/grayscale.html | 8 +- examples/linalg_example.html | 10 +- examples/mat_math_example.html | 8 +- examples/matrix_t_example.html | 5 +- examples/orb_test.html | 11 +- examples/sample_boxblur.html | 8 +- examples/sample_canny_edge.html | 10 +- examples/sample_equalize_hist.html | 8 +- examples/sample_fast_corners.html | 15 +- examples/sample_gaussblur.html | 8 +- examples/sample_oflow_lk.html | 15 +- examples/sample_orb.html | 16 +- examples/sample_orb_pinball.html | 16 +- examples/sample_pyrdown.html | 12 +- examples/sample_scharr.html | 10 +- examples/sample_sobel.html | 10 +- examples/sample_sobel_edge.html | 16 +- examples/sample_warp_affine.html | 12 +- examples/sample_warp_perspective.html | 18 +- examples/sample_yape.html | 15 +- examples/sample_yape06.html | 17 +- src/cache/cache.ts | 7 +- src/core/core.ts | 58 +++-- src/index.ts | 18 +- src/jsfeatNext.ts | 52 ++-- tests/api-shape.test.ts | 76 ++++++ tests/parity/detectors.test.ts | 12 +- tests/parity/imgproc.test.ts | 4 +- tests/parity/linalg.test.ts | 2 +- tests/parity/math.get_gaussian_kernel.test.ts | 2 +- tests/parity/math.test.ts | 2 +- tests/parity/motion_estimator.test.ts | 12 +- tests/parity/structs.test.ts | 2 +- types/src/cache/cache.d.ts | 7 +- types/src/core/core.d.ts | 53 ++-- types/src/index.d.ts | 19 +- 45 files changed, 633 insertions(+), 438 deletions(-) create mode 100644 docs/migration-0.9.md create mode 100644 tests/api-shape.test.ts diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 9292adf..477587a 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -3,9 +3,9 @@ The full guidance is in **[`AGENTS.md`](../AGENTS.md)** (source of truth) and the roadmap in [`docs/jsfeat-parity-and-refactor-audit.md`](../docs/jsfeat-parity-and-refactor-audit.md). Critical points, inlined because Copilot injects this file directly: - **TypeScript port of [jsfeat](https://github.com/inspirit/jsfeat)** for WebARKit. npm: `@webarkit/jsfeat-next`. Node v24.18.0 (npm 11). -- Build: `npm run build-ts` → `dist/jsfeatNext.js` (UMD) + `types/`. Watch: `npm run dev-ts`. Format: `npm run format` (Prettier). **Tests:** `npm test` (Vitest, parity vs original jsfeat); also verify via `examples/*.html`. -- **Architecture:** most algorithms live **inline in `src/jsfeatNext.ts` (~3,900 lines)**, attached as `jsfeatNext.X = class X extends jsfeatNext`. Edit *there* for `imgproc, fast_corners, math, linalg, orb, yape06, motion_estimator, optical_flow_lk, pyramid_t`. -- **⚠️ Trap:** several `src//.ts` files are **type-only stubs** that `throw new Error("Method not implemented.")` (e.g. `src/imgproc/imgproc.ts`). Do not treat them as the implementation and never instantiate them. -- **API notes:** consumers use `jsfeatNext.jsfeatNext` (double namespace); algorithm modules require `new` (instance methods), unlike jsfeat's static namespace; each `new` allocates its own cache. `haar` and `bbf` are not ported. -- **Conventions:** keep numeric/behavioral parity with jsfeat (typed-array/bitwise hot loops); preserve the public `jsfeatNext.` API; avoid `any` in new code; LGPL-3.0-or-later. -- **Git workflow:** open PRs against **`dev`**, never `main` (`dev` = integration, `main` = release). Commit messages follow **Conventional Commits** (`feat:`, `fix:`, `chore:`, `docs:`, `test:`, `refactor:`, `ci:` …). +- Build: `npm run build-ts` (Vite) → `dist/jsfeatNext.js` (UMD) + `dist/jsfeatNext.mjs` (ESM) + `types/`. Watch: `npm run dev-ts`. Format: `npm run format` (Prettier). **Tests:** `npm test` (Vitest, parity vs original jsfeat); also verify via `examples/*.html`. +- **Architecture:** one real module per algorithm under `src//`, extending the base in `src/core/core.ts`; `src/jsfeatNext.ts` is a thin aggregator. +- **Calling convention (0.9.0+, #41):** algorithm modules are **singletons** — `jsfeatNext.imgproc.grayscale(...)`, **no `new`**. Only the data structures (`matrix_t`, `keypoint_t`, `pyramid_t`, `ransac_params_t`) are constructors. All modules share ONE buffer pool (`jsfeatNext.cache`); balance `get_buffer`/`put_buffer`. See `docs/migration-0.9.md`. +- **Missing vs jsfeat:** `haar` and `bbf` are not ported (#43/#44). +- **Conventions:** keep numeric/behavioral parity with jsfeat (typed-array/bitwise hot loops; the Vitest suite pins outputs against a vendored jsfeat oracle); preserve the public `jsfeatNext.` API; avoid `any` in new code; LGPL-3.0-or-later. +- **Git workflow:** open PRs against **`dev`**, never `main` (`dev` = integration, `main` = release). Commit messages follow **Conventional Commits** (`feat:`, `fix:`, `chore:`, `docs:`, `test:`, `refactor:`, `ci:` …) — the release changelog is generated from them. Release tags are bare `X.Y.Z`. diff --git a/AGENTS.md b/AGENTS.md index 3e0f95c..83979f9 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -5,34 +5,31 @@ ## What this project is -**jsfeatNext** is a TypeScript port of [jsfeat](https://github.com/inspirit/jsfeat) (a JS computer-vision library) for the **WebARKit** project. Published to npm as `@webarkit/jsfeat-next`. It ships a UMD bundle for browsers and TypeScript type declarations. +**jsfeatNext** is a TypeScript port of [jsfeat](https://github.com/inspirit/jsfeat) (a JS computer-vision library) for the **WebARKit** project. Published to npm as `@webarkit/jsfeat-next`. It ships UMD + ESM bundles and TypeScript type declarations. ## Environment & commands - **Node:** v24.18.0 (see `.nvmrc`; npm 11). **Package manager:** npm. -- Install: `npm install` -- Build (prod): `npm run build-ts` → runs `tsc` (emits `.d.ts` to `types/`) then webpack → `dist/jsfeatNext.js` +- Install: `npm install` (regenerate the lockfile only with npm 11, e.g. `npx npm@11 install` — older npm writes incomplete cross-platform lockfiles that break CI's `npm ci`) +- Build: `npm run build-ts` → Vite library mode → `dist/jsfeatNext.js` (UMD) + `dist/jsfeatNext.mjs` (ESM) + `types/` (via vite-plugin-dts) - Watch/dev: `npm run dev-ts` -- Format: `npm run format` (write) · `npm run format-check` (verify) — Prettier, config in `.prettierrc.json` -- **Test:** `npm test` (Vitest) runs characterization tests asserting parity against the original `jsfeat` (see `tests/`). Also verify visually via `examples/*.html` after building. Do **not** claim behavior is verified without a real check. +- Format: `npm run format` (write) · `npm run format-check` (verify) — Prettier, config in `.prettierrc.json`. On Windows, verify with `node_modules/.bin/prettier` directly, not bare `npx prettier` (which can silently resolve a different version). +- API docs: `npm run docs` (TypeDoc → `docs/api/`, gitignored) +- **Test:** `npm test` (Vitest) runs characterization tests asserting parity against the original `jsfeat` (see `tests/`, oracle vendored in `tests/vendor/`). Also verify visually via `examples/*.html` after building. Do **not** claim behavior is verified without a real check. ## Architecture — read this before editing -The runtime is centered on one large file: **`src/jsfeatNext.ts` (~3,900 lines)**. +- **One real module per algorithm** under `src//.ts`, each extending the base class in **`src/core/core.ts`** (constants, data-type helpers, the shared cache). `src/jsfeatNext.ts` is a thin aggregator that only attaches modules to the namespace; `src/index.ts` default-exports the namespace directly. +- **Calling convention (since 0.9.0, issue #41):** the 14 algorithm modules (`imgproc`, `math`, `matmath`, `linalg`, `transform`, `fast_corners`, `yape`, `yape06`, `orb`, `optical_flow_lk`, `motion_estimator`, `affine2d`, `homography2d`, plus the `cache` pool) are **singleton instances** on the namespace — `jsfeatNext.imgproc.grayscale(...)`, no `new` — matching original jsfeat. The data-structure classes (`matrix_t`, `keypoint_t`, `pyramid_t`, `ransac_params_t`) remain constructors. +- **One shared buffer pool:** all modules borrow scratch buffers from the single `shared_cache` exported by `src/core/core.ts` (public as `jsfeatNext.cache`), exactly like jsfeat's global `jsfeat.cache`. Balance every `get_buffer` with a `put_buffer`. +- Full background: `docs/jsfeat-parity-and-refactor-audit.md` (the plan) and `docs/migration-0.9.md` (the 0.9.0 API break and its motivation). -- `src/index.ts` default-exports `{ jsfeatNext }`. The UMD global is `jsfeatNext`, so **consumers write `jsfeatNext.jsfeatNext`** (double namespace — known wart). -- The base `class jsfeatNext` holds `cache`, `data_type`, and all constants. Algorithm modules are attached as **static members**. -- **Two conflicting patterns coexist:** - - **Implemented INLINE in `src/jsfeatNext.ts`** (attached via `jsfeatNext.X = class X extends jsfeatNext {…}`): `imgproc, fast_corners, pyramid_t, math, linalg, orb, yape06, motion_estimator, optical_flow_lk` (plus `motion_model, affine2d, homography2d`). - - **Assigned from a REAL module file**: `cache, transform, matrix_t, keypoint_t, matmath, yape, ransac_params_t`. +### ⚠️ Gotchas -### ⚠️ Critical gotchas (these will bite you) - -1. **Type-only stub files.** Several `src//.ts` files (confirmed: `src/imgproc/imgproc.ts`) are **stubs** whose every method is `throw new Error("Method not implemented.")`. They exist only to type `static X: typeof X`. **The real code is inline in `src/jsfeatNext.ts`.** → When editing an inline-implemented algorithm, **edit `src/jsfeatNext.ts`**, not the stub. Never instantiate a stub class. -2. **Latent trap:** `src/orb/rectify_patch.ts` imports the *stub* `imgproc`. -3. **Instantiation required.** Unlike jsfeat's static namespace (`jsfeat.imgproc.grayscale()`), jsfeatNext modules are instance classes: `new jsfeatNext.jsfeatNext.imgproc().grayscale(...)`. Not drop-in compatible with jsfeat. -4. **Per-instance cache.** Every `new` of a module runs the base ctor `this.cache.allocate(30, 640*4)` — each instance gets its own buffer pool (jsfeat shares one global cache). -5. **Missing vs jsfeat:** `haar` and `bbf` (object/face detection) are **not ported**. +1. **Don't reintroduce `new jsfeatNext.()`** in examples, docs or tests — the slots hold instances, not classes. The classes still exist in their module files (importable for isolated instances if truly needed) and bind to the shared pool. +2. **Missing vs jsfeat:** `haar` and `bbf` (object/face detection) are **not ported** (#43/#44). +3. **`transform` signature divergence:** jsfeatNext's `transform` methods take `matrix_t`; original jsfeat's (never actually shipped in any jsfeat build) took raw arrays. Same math. +4. **The parity suite is the safety net.** Any change to algorithm code must keep `npm test` green — the tests pin outputs bit-for-bit/close-to against a vendored original-jsfeat oracle. ## Conventions @@ -44,17 +41,18 @@ The runtime is centered on one large file: **`src/jsfeatNext.ts` (~3,900 lines)* ## Git & contribution workflow - **Open PRs against the `dev` branch — never `main`.** `dev` is the integration branch; `main` is stable/release. Branch your work off `dev`. -- **Commit messages follow [Conventional Commits](https://www.conventionalcommits.org/):** `type(scope): summary`, e.g. `feat:`, `fix:`, `chore:`, `docs:`, `test:`, `refactor:`, `perf:`, `ci:`. Keep the subject imperative and concise. +- **Commit messages follow [Conventional Commits](https://www.conventionalcommits.org/):** `type(scope): summary`, e.g. `feat:`, `fix:`, `chore:`, `docs:`, `test:`, `refactor:`, `perf:`, `ci:`. Keep the subject imperative and concise. The release changelog is generated from these (git-cliff) — non-conforming commits are silently dropped from release notes. - One feature branch per issue; reference the issue in the PR body. +- Release tags are bare `X.Y.Z` (never `vX.Y.Z`); releases are automated from the tag (see `MAINTAINERS.md`). - Never commit `.idea/` (JetBrains IDE files). ## Before you make changes - Small, incremental, reviewable diffs. Match surrounding code style. -- If a change could alter algorithm output, note that there is **no test net** and flag it for manual example verification. -- There is a full audit + refactoring roadmap in **[`docs/jsfeat-parity-and-refactor-audit.md`](docs/jsfeat-parity-and-refactor-audit.md)** — read it before proposing structural refactors, the webpack→Vite migration, or porting `haar`/`bbf`. +- Keep `npm test` green; add parity tests for new algorithm code. +- The audit + roadmap lives in **[`docs/jsfeat-parity-and-refactor-audit.md`](docs/jsfeat-parity-and-refactor-audit.md)**; the release runbook in **[`MAINTAINERS.md`](MAINTAINERS.md)**. ## Roadmap pointers -- **Refactor direction:** de-duplicate stub/monolith → one real class per module, `jsfeatNext.ts` becomes a thin aggregator, shared singleton cache, static-facade API for jsfeat parity. (See audit doc §4.) -- **Build direction:** webpack → **Vite library mode** + `vite-plugin-dts`; ship ESM+UMD; fix the double namespace. (See audit doc §5.) +- **Remaining vs jsfeat:** port `haar` (#43) and `bbf` (#44); exhaustive per-symbol parity audit (#45). +- **Toward 1.0:** prerelease-tag support in the release pipeline (#81); examples modernization (#79); new descriptors like FREAK (#80). diff --git a/CLAUDE.md b/CLAUDE.md index 062557b..3751503 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -6,7 +6,8 @@ The canonical, tool-agnostic guidance for this repo lives in **AGENTS.md**. It i ## Claude-specific notes -- When editing an algorithm module (`imgproc`, `fast_corners`, `math`, `linalg`, `orb`, `yape06`, `motion_estimator`, `optical_flow_lk`, `pyramid_t`), remember the implementation is **inline in `src/jsfeatNext.ts`**, not in the same-named `src//.ts` stub. Grep `jsfeatNext. = class` to jump to it. -- There are **no automated tests**. Do not report a change as "verified" unless you actually built (`npm run build-ts`) and checked the relevant `examples/*.html`, or the user confirms. -- The audit/roadmap is in [`docs/jsfeat-parity-and-refactor-audit.md`](docs/jsfeat-parity-and-refactor-audit.md). Consult it before structural refactors, the Vite migration, or porting `haar`/`bbf`. +- Every algorithm lives in its own module under `src//.ts`, extending the base in `src/core/core.ts`; `src/jsfeatNext.ts` is only the aggregator that attaches the singletons (the old inline monolith and its type-only stubs are gone since #47). +- **Always run `npm test`** (Vitest parity suite vs a vendored original-jsfeat oracle) before claiming an algorithm change is verified; for consumer-facing changes also build (`npm run build-ts`) and check the relevant `examples/*.html`, or ask the user to confirm. +- On this Windows machine: verify formatting with `node_modules/.bin/prettier` directly (bare `npx prettier` can resolve the wrong version) and regenerate `package-lock.json` only with `npx npm@11`. +- The audit/roadmap is in [`docs/jsfeat-parity-and-refactor-audit.md`](docs/jsfeat-parity-and-refactor-audit.md); the 0.9.0 API break is documented in [`docs/migration-0.9.md`](docs/migration-0.9.md); the release runbook is in [`MAINTAINERS.md`](MAINTAINERS.md). - Keep numeric/behavioral parity with the original jsfeat; preserve the public `jsfeatNext.` API. diff --git a/README.md b/README.md index 455ac16..1f18758 100644 --- a/README.md +++ b/README.md @@ -18,29 +18,26 @@ npm install @webarkit/jsfeat-next ``` ```js -import pkg from "@webarkit/jsfeat-next"; +import jsfeatNext from "@webarkit/jsfeat-next"; -// consumers unwrap the double namespace: pkg.jsfeatNext, not pkg directly -// (a known wart, tracked for a future breaking-change cleanup — see #41) -const jsfeatNext = pkg.jsfeatNext; - -const imgproc = new jsfeatNext.imgproc(); +// algorithm modules are singletons — call them directly, no `new` (since 0.9.0) const src = new jsfeatNext.matrix_t(width, height, jsfeatNext.U8_t | jsfeatNext.C1_t); -imgproc.grayscale(rgbaPixelData, width, height, src); +jsfeatNext.imgproc.grayscale(rgbaPixelData, width, height, src); ``` -In the browser (UMD build), the same shape applies via the global: +In the browser (UMD build), the global is the namespace directly: ```html ``` +> **Upgrading from ≤ 0.8.x?** The `jsfeatNext.jsfeatNext` double namespace and the +> `new jsfeatNext.imgproc()` calling convention were removed in 0.9.0 — see the +> [migration guide](docs/migration-0.9.md). + ## List of features ✨ - TypeScript definitions, with full TSDoc on every public class/method (`npm run docs` to generate a browsable API reference locally) @@ -73,7 +70,7 @@ npm install @webarkit/jsfeat-next ## Known limitations 🔍 - Not every original jsfeat class is ported yet — `haar` and `bbf` (Haar-cascade / BBF object detection) are not implemented. Tracked in [#43](https://github.com/webarkit/jsfeatNext/issues/43) and [#44](https://github.com/webarkit/jsfeatNext/issues/44). -- jsfeatNext is **not a drop-in replacement** for jsfeat: algorithm modules are instantiated (`new jsfeatNext.imgproc()`) rather than called as static namespace functions, and consumers must unwrap the `jsfeatNext.jsfeatNext` double namespace. Tracked in [#41](https://github.com/webarkit/jsfeatNext/issues/41). +- The `transform` module takes `matrix_t` arguments where original jsfeat's (never-shipped) `transform` module used raw arrays — same math, slightly different calling convention (see the parity audit, Axis 2). ## Examples 🧪 diff --git a/dist/jsfeatNext.js b/dist/jsfeatNext.js index bf23a82..1299c8f 100644 --- a/dist/jsfeatNext.js +++ b/dist/jsfeatNext.js @@ -1 +1 @@ -(function(e,t){typeof exports==`object`&&typeof module<`u`?module.exports=t():typeof define==`function`&&define.amd?define([],t):(e=typeof globalThis<`u`?globalThis:e||self,e.jsfeatNext=t())})(this,function(){var e=class{constructor(){this._data_type_size=new Int32Array([-1,1,4,-1,4,-1,-1,-1,8,-1,-1,-1,-1,-1,-1,-1,8])}_get_data_type(e){return e&65280}_get_channel(e){return e&255}_get_data_type_size(e){return this._data_type_size[(e&65280)>>8]}},t=class{constructor(e,t){this.size=(e+7|0)&-8,t===void 0?this.buffer=new ArrayBuffer(this.size):(this.buffer=t,this.size=t.length),this.u8=new Uint8Array(this.buffer),this.i32=new Int32Array(this.buffer),this.f32=new Float32Array(this.buffer),this.f64=new Float64Array(this.buffer)}},n=class{constructor(e){this.next=null,this.data=new t(e),this.size=this.data.size,this.buffer=this.data.buffer,this.u8=this.data.u8,this.i32=this.data.i32,this.f32=this.data.f32,this.f64=this.data.f64}resize(e){delete this.data,this.data=new t(e),this.size=this.data.size,this.buffer=this.data.buffer,this.u8=this.data.u8,this.i32=this.data.i32,this.f32=this.data.f32,this.f64=this.data.f64}},r=class{constructor(){this._pool_head,this._pool_tail,this._pool_size=0}allocate(e,t){this._pool_head=this._pool_tail=new n(t);for(let r=0;rt.size&&t.resize(e),t}put_buffer(e){this._pool_tail=this._pool_tail.next=e,this._pool_size++}},i={EPSILON:1.192092896e-7,FLT_MIN:1e-37,U8_t:256,S32_t:512,F32_t:1024,S64_t:2048,F64_t:4096,C1_t:1,C2_t:2,C3_t:3,C4_t:4,COLOR_RGBA2GRAY:0,COLOR_RGB2GRAY:1,COLOR_BGRA2GRAY:2,COLOR_BGR2GRAY:3,BOX_BLUR_NOSCALE:1,SVD_U_T:1,SVD_V_T:2,U8C1_t:257,U8C3_t:259,U8C4_t:260,F32C1_t:1025,F32C2_t:1026,S32C1_t:513,S32C2_t:514},a={name:`@webarkit/jsfeat-next`,version:`0.8.0`,description:`Typescript version of jsfeat for WebARKit`,main:`dist/jsfeatNext.js`,module:`dist/jsfeatNext.mjs`,types:`types/src/index.d.ts`,unpkg:`dist/jsfeatNext.js`,jsdelivr:`dist/jsfeatNext.js`,exports:{".":{types:`./types/src/index.d.ts`,import:`./dist/jsfeatNext.mjs`,require:`./dist/jsfeatNext.js`},"./package.json":`./package.json`},files:[`dist`,`types`],scripts:{"build-ts":`vite build`,"dev-ts":`vite build --watch`,"format-check":`prettier --check .`,format:`prettier --write .`,test:`vitest run`,"test:watch":`vitest`,docs:`typedoc`},repository:{type:`git`,url:`git+https://github.com/webarkit/jsfeatNext.git`},keywords:[`jsfeat`,`jsfeatNext`,`WebAR`,`WebARKit`,`AugmentedReality`,`computer`,`vision`],author:`Walter Perdan @kalwalt`,license:`LGPL-3.0-or-later`,bugs:{url:`https://github.com/webarkit/jsfeatNext/issues`},homepage:`https://github.com/webarkit/jsfeatNext#readme`,devDependencies:{prettier:`~3.9.4`,typedoc:`^0.28.20`,typescript:`^6.0.3`,vite:`^8.1.4`,"vite-plugin-dts":`^5.0.3`,vitest:`^4.1.10`}},o,s=class{constructor(){this.dt=new e,this.cache=new r,this.cache.allocate(30,640*4)}get_data_type(e){return this.dt._get_data_type(e)}get_channel(e){return this.dt._get_channel(e)}get_data_type_size(e){return this.dt._get_data_type_size(e)}};o=s,o.VERSION=a.version,o.EPSILON=i.EPSILON,o.FLT_MIN=i.FLT_MIN,o.U8_t=i.U8_t,o.S32_t=i.S32_t,o.F32_t=i.F32_t,o.S64_t=i.S64_t,o.F64_t=i.F64_t,o.C1_t=i.C1_t,o.C2_t=i.C2_t,o.C3_t=i.C3_t,o.C4_t=i.C4_t,o.COLOR_RGBA2GRAY=i.COLOR_RGBA2GRAY,o.COLOR_RGB2GRAY=i.COLOR_RGB2GRAY,o.COLOR_BGRA2GRAY=i.COLOR_BGRA2GRAY,o.COLOR_BGR2GRAY=i.COLOR_BGR2GRAY,o.BOX_BLUR_NOSCALE=i.BOX_BLUR_NOSCALE,o.SVD_U_T=i.SVD_U_T,o.SVD_V_T=i.SVD_V_T,o.U8C1_t=o.U8_t|o.C1_t,o.U8C3_t=o.U8_t|o.C3_t,o.U8C4_t=o.U8_t|o.C4_t,o.F32C1_t=o.F32_t|o.C1_t,o.F32C2_t=o.F32_t|o.C2_t,o.S32C1_t=o.S32_t|o.C1_t,o.S32C2_t=o.S32_t|o.C2_t;var c=class{constructor(t,n,r,a){this.dt=new e,this.type=this.dt._get_data_type(r)|0,this.channel=this.dt._get_channel(r)|0,this.cols=t|0,this.rows=n|0,a===void 0?this.allocate():(this.buffer=a,this.data=this.type&i.U8_t?this.buffer.u8:this.type&i.S32_t?this.buffer.i32:this.type&i.F32_t?this.buffer.f32:this.buffer.f64)}allocate(){delete this.data,delete this.buffer,this.buffer=new t(this.cols*this.dt._get_data_type_size(this.type)*this.channel*this.rows),this.data=this.type&i.U8_t?this.buffer.u8:this.type&i.S32_t?this.buffer.i32:this.type&i.F32_t?this.buffer.f32:this.buffer.f64}copy_to(e){let t=e.data,n=this.data,r=0,i=this.cols*this.rows*this.channel|0;for(;rthis.buffer.size?(this.cols=e,this.rows=t,this.channel=n,this.allocate()):(this.cols=e,this.rows=t,this.channel=n)}};function l(e,t,n,r,i){let a=0,o=e.channel,s=e.cols,c=e.rows,l=e.data,u=t.data,d=s/r,f=c/i,p=d*f*65536|0,m=0,h=0,g=0,_=0,v=0,y=0,b=0,x=0,S=0,C=0,w=0,T=0,E=0,D=0,O=0,k=0,A=n.get_buffer(r*o<<2),j=n.get_buffer(r*o<<2),M=n.get_buffer(s*2*3<<2),N=A.i32,P=j.i32,F=M.i32;for(;mS&&(F[x++]=m*o|0,F[x++]=(v-1)*o|0,F[x++]=(v-S)*256|0,a++),g=v;g.001&&(a++,F[x++]=m*o|0,F[x++]=y*o|0,F[x++]=(C-y)*256|0)}for(m=0;mS&&(a++,F[x++]=(v-1)*o|0,F[x++]=m*o|0,F[x++]=(v-S)*p),g=v;g.001&&(a++,F[x++]=y*o|0,F[x++]=m*o|0,F[x++]=(C-y)*p)}for(m=0;m>8,255),n[f+l+1]=Math.min(m>>8,255),n[f+l+2]=Math.min(h>>8,255),n[f+l+3]=Math.min(g>>8,255)}for(;l>8,255)}d+=r,f+=r}for(c=0;c>8,255),n[f+r]=Math.min(m>>8,255),n[f+y]=Math.min(h>>8,255),n[f+b]=Math.min(g>>8,255)}for(;l>8,255)}}}function f(e,t,n,r,i,a,o,s){let c=0,l=0,u=0,d=0,f=0,p=0,m=0,h=0,g=0,_=a[0],v=0,y=r<<1,b=r*3,x=r<<2;for(;c>1){case 0:f[0]=1,u=1;break;case 1:f[0]=.25,f[1]=.5,f[2]=.25,u=1;break;case 2:f[0]=.0625,f[1]=.25,f[2]=.375,f[3]=.25,f[4]=.0625,u=1;break;case 3:f[0]=.03125,f[1]=.109375,f[2]=.21875,f[3]=.28125,f[4]=.21875,f[5]=.109375,f[6]=.03125,u=1;break}else for(c=t>0?t:((e-1)*.5-1)*.3+.8,l=-.5/(c*c);a=0;)for(l=E[c<<1],u=E[(c<<1)+1],c--;;)if(f=u-l+1,f<=7){for(m=l+1;m<=u;m++)for(h=m;h>l&&r(e[h],e[h-1]);h--)i=e[h],e[h]=e[h-1],e[h-1]=i;break}else{for(T=0,_=l,y=u,x=l+(f>>1),f>40&&(g=f>>3,S=l,C=l+g,w=l+(g<<1),a=e[S],o=e[C],s=e[w],l=r(a,o)?r(o,s)?C:r(a,s)?w:S:r(s,o)?C:r(a,s)?S:w,S=x-g,C=x,w=x+g,a=e[S],o=e[C],s=e[w],x=r(a,o)?r(o,s)?C:r(a,s)?w:S:r(s,o)?C:r(a,s)?S:w,S=u-(g<<1),C=u-g,w=u,a=e[S],o=e[C],s=e[w],u=r(a,o)?r(o,s)?C:r(a,s)?w:S:r(s,o)?C:r(a,s)?S:w),S=l,C=x,w=u,a=e[S],o=e[C],s=e[w],x=r(a,o)?r(o,s)?C:r(a,s)?w:S:r(s,o)?C:r(a,s)?S:w,x!=_&&(i=e[x],e[x]=e[_],e[_]=i,x=_),l=v=_+1,u=b=y,a=e[x];;){for(;l<=u&&!r(a,e[l]);)r(e[l],a)||(l>v&&(i=e[v],e[v]=e[l],e[l]=i),T=1,v++),l++;for(;l<=u&&!r(e[u],a);)r(a,e[u])||(uu)break;i=e[l],e[l]=e[u],e[u]=i,T=1,l++,u--}if(T==0){for(l=_,u=y,m=l+1;m<=u;m++)for(h=m;h>l&&r(e[h],e[h-1]);h--)i=e[h],e[h]=e[h-1],e[h-1]=i;break}for(f=Math.min(v-_,l-v),p=l-f|0,d=0;d1)p>1?f>p?(++c,E[c<<1]=_,E[(c<<1)+1]=_+f-1,l=y-p+1,u=y):(++c,E[c<<1]=y-p+1,E[(c<<1)+1]=y,l=_,u=_+f-1):(l=_,u=_+f-1);else if(p>1)l=y-p+1,u=y;else break}}median(e,t,n){let r,i=0,a=0,o=0,s=t+n>>1;for(;;){if(n<=t)return e[s];if(n==t+1)return e[t]>e[n]&&(r=e[t],e[t]=e[n],e[n]=r),e[s];for(i=t+n>>1,e[i]>e[n]&&(r=e[i],e[i]=e[n],e[n]=r),e[t]>e[n]&&(r=e[t],e[t]=e[n],e[n]=r),e[i]>e[t]&&(r=e[i],e[i]=e[t],e[t]=r),a=t+1,r=e[i],e[i]=e[a],e[a]=r,o=n;;){do++a;while(e[t]>e[a]);do--o;while(e[o]>e[t]);if(o=s&&(n=o-1)}return 0}},m=class extends s{constructor(){super()}grayscale(e,t,n,r,a){a===void 0&&(a=i.COLOR_RGBA2GRAY);let o=0,s=0,c=0,l=0,u=0,d=0,f=4899,p=9617,m=1868,h=4;(a==i.COLOR_BGRA2GRAY||a==i.COLOR_BGR2GRAY)&&(f=1868,m=4899),(a==i.COLOR_RGB2GRAY||a==i.COLOR_BGR2GRAY)&&(h=3);let g=h<<1,_=h*3|0;r.resize(t,n,1);let v=r.data;for(s=0;s>14,v[d+1]=e[u+h]*f+e[u+h+1]*p+e[u+h+2]*m+8192>>14,v[d+2]=e[u+g]*f+e[u+g+1]*p+e[u+g+2]*m+8192>>14,v[d+3]=e[u+_]*f+e[u+_+1]*p+e[u+_+2]*m+8192>>14;for(;o>14}}resample(e,t,n,r){let a=e.rows,o=e.cols;a>r&&o>n&&(t.resize(n,r,e.channel),e.type&i.U8_t&&t.type&i.U8_t&&a*o/(r*n)<256?l(e,t,this.cache,n,r):u(e,t,this.cache,n,r))}box_blur_gray(e,t,n,r){r===void 0&&(r=0);let a=e.cols,o=e.rows,s=o<<1,c=a<<1,l=0,u=0,d=0,f=0,p=(n<<1)+1|0,m=n+1|0,h=m+1|0,g=r&i.BOX_BLUR_NOSCALE?1:1/(p*p),_=this.cache.get_buffer(a*o<<2),v=0,y=0,b=0,x=0,S=0,C=_.i32,w=e.data,T=0;for(t.resize(a,o,e.channel),d=0;d>1,s=e.cols,c=e.rows,l=e.type,u=l&i.U8_t;t.resize(s,c,e.channel);let m=e.data,h=t.data,g,_,v=n+Math.max(c,s)|0,y=this.cache.get_buffer(v<<2),b=this.cache.get_buffer(n<<2);u?(g=y.i32,_=b.i32):l&i.S32_t?(g=y.i32,_=b.f32):(g=y.f32,_=b.f32),a.get_gaussian_kernel(n,r,_,l),u?d(g,m,h,s,c,_,n,o):f(g,m,h,s,c,_,n,o),this.cache.put_buffer(y),this.cache.put_buffer(b)}hough_transform(e,t,n,r){let i,a,o=e.data,s=e.cols,c=e.rows,l=s,u=Math.round((Math.PI-0)/n),d=Math.round(((s+c)*2+1)/t),f=1/t,p=new Int32Array((u+2)*(d+2)),m=new Float32Array(u),h=new Float32Array(u),g=0,_=0;for(;gr&&p[e]>p[e-1]&&p[e]>=p[e+1]&&p[e]>p[e-d-2]&&p[e]>=p[e+d+2]&&v.push(e)}v.sort(function(e,t){return p[e]>p[t]||p[e]==p[t]&&e>1,s=a>>1,c=o-(n<<1),l=s-(r<<1),u=0,d=0,f=n+r*i,p=0,m=0,h=0;t.resize(o,s,e.channel);let g=e.data,_=t.data;for(d=0;d>2,_[h+1]=g[p+2]+g[p+3]+g[p+i+2]+g[p+i+3]+2>>2;for(;u>2;f+=i<<1,m+=o}}scharr_derivatives(e,t){let n=e.cols,r=e.rows,a=n<<1,o=0,s=0,c=0,l,u,d,f,p,m,h=0,g=0,_=0,v=0,y,b;t.resize(n,r,2);let x=e.data,S=t.data,C=this.cache.get_buffer(n+2<<2),w=this.cache.get_buffer(n+2<<2);for(e.type&i.U8_t||e.type&i.S32_t?(y=C.i32,b=w.i32):(y=C.f32,b=w.f32);s0?s-1:1)*n|0,_=(s0?s-1:1)*n|0,_=(s0;--p)for(u=p+a*s,d=u-s,f=a;f>0;--f,u-=s,d-=s)r[u]+=r[d]+r[d+1]}}equalize_histogram(e,t){let n=e.cols,r=e.rows,i=e.data;t.resize(n,r,e.channel);let a=t.data,o=n*r,s=0,c=0,l,u,d=this.cache.get_buffer(1024);for(l=d.i32;s<256;++s)l[s]=0;for(s=0;sr&&(l=n,n=r,r=l),l=3*(a+2)|0;--l>=0;)T[l]=0;for(l=(o+2)*(a+2)|0;--l>=0;)E[l]=0;for(;u>31)-(g>>31)+((_^_>>31)-(_>>31));for(l=1;l<=o;++l,d+=f){if(l==o)for(u=M+a;--u>=M;)T[u]=0;else for(u=0;u>31)-(g>>31)+((_^_>>31)-(_>>31));for(p=d-f|0,E[P-1]=0,m=0,u=0;un){if(g=O[p],_=O[p+1],v=g^_,g=(g^g>>31)-(g>>31)|0,_=(_^_>>31)-(_>>31)|0,y=g*13573,b=y+(g+g<<15),_<<=15,_T[j+u-1]&&h>=T[j+u+1]){h>r&&!m&&E[P+u-N]!=2?(E[P+u]=2,m=1,D[F++]=P+u):E[P+u]=1;continue}}else if(_>b){if(h>T[A+u]&&h>=T[M+u]){h>r&&!m&&E[P+u-N]!=2?(E[P+u]=2,m=1,D[F++]=P+u):E[P+u]=1;continue}}else if(v=v<0?-1:1,h>T[A+u-v]&&h>T[M+u+v]){h>r&&!m&&E[P+u-N]!=2?(E[P+u]=2,m=1,D[F++]=P+u):E[P+u]=1;continue}}E[P+u]=0,m=0}E[P+a]=0,P+=N,u=A,A=j,j=M,M=u}for(u=P-N-1,l=0;l0;)P=D[--F],P-=N+1,E[P]==1&&(E[P]=2,D[F++]=P),P+=1,E[P]==1&&(E[P]=2,D[F++]=P),P+=1,E[P]==1&&(E[P]=2,D[F++]=P),P+=N,E[P]==1&&(E[P]=2,D[F++]=P),P-=2,E[P]==1&&(E[P]=2,D[F++]=P),P+=N,E[P]==1&&(E[P]=2,D[F++]=P),P+=1,E[P]==1&&(E[P]=2,D[F++]=P),P+=1,E[P]==1&&(E[P]=2,D[F++]=P);for(P=N+1,A=0,l=0;l0&&g>0&&p=0&&m>=0&&p95&&r>40&&i>20&&n>r&&n>i&&n-Math.min(r,i)>15&&Math.abs(n-r)>15?t[o]=255:t[o]=0}};function h(e,t,n,r){r=e[t],e[t]=e[n],e[n]=r}function g(e,t){return e=Math.abs(e),t=Math.abs(t),e>t?(t/=e,e*Math.sqrt(1+t*t)):t>0?(e/=t,t*Math.sqrt(1+e*e)):0}var _=class{constructor(){}identity(e,t){t===void 0&&(t=1);let n=e.data,r=e.rows,i=e.cols,a=i+1|0,o=r*i,s=o;for(;--o>=0;)n[o]=0;for(o=s,s=0;s0){for(d=0,b=Math.abs(e[u]),c=1;c1)for(;v0){for(d=0,b=Math.abs(e[p]),c=1;c>16&256?L:-L,e[d*t+p]=I;for(m=0;m<2;m++)for(f=0;fMath.abs(c[a*s+n])&&(a=r);if(Math.abs(c[a*s+n])=0;n--){for(f=l[n],a=n+1;a=0;s--){for(f=d[s],c=s+1,a=c*l;c=0;)n.data[s]=_.data[s];else n&&this.matmath.transpose(n,_);if(r&&a&i.SVD_V_T)for(s=f*f;--s>=0;)r.data[s]=y.data[s];else r&&this.matmath.transpose(r,y)}else{if(n&&a&i.SVD_U_T)for(s=f*f;--s>=0;)n.data[s]=y.data[s];else n&&this.matmath.transpose(n,y);if(r&&a&i.SVD_V_T)for(s=d*d;--s>=0;)r.data[s]=_.data[s];else r&&this.matmath.transpose(r,_)}this.cache.put_buffer(m),this.cache.put_buffer(h),this.cache.put_buffer(g)}svd_solve(e,t,n){let r=0,a=0,o=0,s=0,l=0,u=e.rows,d=e.cols,f=0,p=0,m=0,h=e.type|i.C1_t,g=this.cache.get_buffer(u*u<<3),_=this.cache.get_buffer(d<<3),v=this.cache.get_buffer(d*d<<3),y=new c(u,u,h,g.data),b=new c(1,d,h,_.data),x=new c(d,d,h,v.data),S=n.data,C=y.data,w=b.data,T=x.data;for(this.svd_decompose(e,b,y,x,0),m=i.EPSILON*w[0]*d;rm){for(o=0,f=0,s=0;op&&(f+=w[s+a]*S[o]/C[a]);x[l]=f}this.cache.put_buffer(h),this.cache.put_buffer(g),this.cache.put_buffer(_)}eigenVV(e,t,n){let r=e.cols,a=r*r,o=e.type|i.C1_t,s=this.cache.get_buffer(r*r<<3),l=this.cache.get_buffer(r<<3),u=new c(r,r,o,s.data),d=new c(1,r,o,l.data);for(;--a>=0;)u.data[a]=e.data[a];if(this.JacobiImpl(u.data,r,d.data,t?t.data:null,r,r),n)for(;--r>=0;)n.data[r]=d.data[r];this.cache.put_buffer(s),this.cache.put_buffer(l)}};function y(e,t,n,r,i){let a=0,o=e[t],s=i,c=0,l=0,u=0;for(;a<25;++a)r[a]=o-e[t+n[a]];for(a=0;a<16;a+=2)c=Math.min(r[a+1],r[a+2]),c=Math.min(c,r[a+3]),!(c<=s)&&(c=Math.min(c,r[a+4]),c=Math.min(c,r[a+5]),c=Math.min(c,r[a+6]),c=Math.min(c,r[a+7]),c=Math.min(c,r[a+8]),s=Math.max(s,Math.min(c,r[a])),s=Math.max(s,Math.min(c,r[a+9])));for(l=-s,a=0;a<16;a+=2)u=Math.max(r[a+1],r[a+2]),u=Math.max(u,r[a+3]),u=Math.max(u,r[a+4]),u=Math.max(u,r[a+5]),!(u>=l)&&(u=Math.max(u,r[a+6]),u=Math.max(u,r[a+7]),u=Math.max(u,r[a+8]),l=Math.min(l,Math.max(u,r[a])),l=Math.min(l,Math.max(u,r[a+9])));return-l-1}var b=class extends s{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(e){this._threshold=Math.min(Math.max(e,0),255);for(let e=-255;e<=255;++e)this.threshold_tab[e+255]=e<-this._threshold?1:e>this._threshold?2:0;return this._threshold}detect(e,t,n){n===void 0&&(n=3);let r=e.data,i=e.cols,a=e.rows,o=0,s=0,c=0,l=0,u=0,d=0,f=this.cache.get_buffer(3*i),p=this.cache.get_buffer((i+1)*3<<2),m=f.u8,h=p.i32,g=this.pixel_off,_=this.score_diff,v=Math.max(3,n),b=Math.min(a-2,a-n),x=Math.max(3,n),S=Math.min(i-3,i-n),C=0,w=0,T,E=y,D=this.threshold_tab,O=this._threshold,k=0,A=0,j=0,M=0,N=0,P=0,F=0,I=0,L=0,R=0,z=0,B=0;this._cmp_offsets(g,i,16);let V=g[0],ee=g[1],H=g[2],U=g[3],W=g[4],G=g[5],K=g[6],te=g[7],q=g[8],ne=g[9],J=g[10],Y=g[11],X=g[12],Z=g[13],Q=g[14],re=g[15];for(o=0;o8){++M,h[N+M]=s,m[P+s]=E(r,F,g,_,O);break}}else C=0;if(j&2)for(l=k+O,C=0,c=0;c<25;++c)if(u=r[F+g[c]],u>l){if(++C,C>8){++M,h[N+M]=s,m[P+s]=E(r,F,g,_,O);break}}else C=0}}if(h[N+i]=M,o!=v)for(d=(o-4+3)%3,I=d*i|0,N=d*(i+1)|0,d=(o-5+3)%3,L=d*i|0,M=h[N+i],c=0;cm[I+R]&&B>m[I+z]&&B>m[L+z]&&B>m[L+s]&&B>m[L+R]&&B>m[P+z]&&B>m[P+s]&&B>m[P+R]&&(T=t[w],T.x=s,T.y=o-1,T.score=B,w++)}return this.cache.put_buffer(f),this.cache.put_buffer(p),w}_cmp_offsets(e,t,n){let r=0,i=this.offsets16;for(;r=0;)this.data[r]=new c(e>>r,t>>r,n)}build(e,t){t===void 0&&(t=!0);let n=2,r=e,i=this.data[0];if(!t){let t=e.cols*e.rows;for(;--t>=0;)i.data[t]=e.data[t]}for(i=this.data[1],this.pyrdown(r,i);n=0;i--,r++)a=Math.sqrt(n*n-i*i)+.5|0,t[r]=i+e*a;for(;-i=0;a--,r++)i=-Math.sqrt(n*n-a*a)-.5|0,t[r]=i+e*a;for(;a>i;a--,r++)i=-Math.sqrt(n*n-a*a)-.5|0,t[r]=i+e*a;for(i++;i<=0;i++,r++)a=-Math.sqrt(n*n-i*i)-.5|0,t[r]=i+e*a;for(;i<-a;i++,r++)a=-Math.sqrt(n*n-i*i)-.5|0,t[r]=i+e*a;for(a++;a<0;a++,r++)i=Math.sqrt(n*n-a*a)+.5|0,t[r]=i+e*a;return t[r]=t[0],t[r+1]=t[1],r}function O(e,t,n){let r=0;return e[t+1]!=0&&r++,e[t-1]!=0&&r++,e[t+n]!=0&&r++,e[t+n+1]!=0&&r++,e[t+n-1]!=0&&r++,e[t-n]!=0&&r++,e[t-n+1]!=0&&r++,e[t-n-1]!=0&&r++,r}function k(e,t,n,r,i){let a,o;if(n>0)for(t-=r*i,o=-i;o<=i;++o){for(a=-i;a<=i;++a)if(e[t+a]>n)return!1;t+=r}else for(t-=r*i,o=-i;o<=i;++o){for(a=-i;a<=i;++a)if(e[t+a]=r)if(f=e[t+a[u]],f<=i)if(f>=r){n[t]=0;return}else if(u++,p=e[t+a[u]],p>i)if(u++,m=e[t+a[u]],m>i)h=3;else if(mi)h=7;else if(mi)if(u++,m=e[t+a[u]],m>i)h=3;else if(mi)h=7;else if(mi){n[t]=0;return}if(u++,p=e[t+a[u]],p>i){n[t]=0;return}if(u++,m=e[t+a[u]],m>i){n[t]=0;return}h=1}else{if(f=e[t+a[u]],fi){if(p=m,u++,m=e[t+a[u]],mi){n[t]=0;return}if(m>i){n[t]=0;return}if(p=m,u++,m=e[t+a[u]],m>i){n[t]=0;return}c-=d+p,h=8;break}if(p<=i){n[t]=0;return}if(m<=i){n[t]=0;return}if(p=m,u++,m=e[t+a[u]],m>i){c-=d+p,h=3;break}if(mi){n[t]=0;return}c-=d+p,h=1;break}if(d>i){if(p=r){n[t]=0;return}if(m>=r){n[t]=0;return}if(p=m,u++,m=e[t+a[u]],mi){c-=d+p,h=7;break}n[t]=0;return;case 2:if(d>i){n[t]=0;return}if(p=m,u++,m=e[t+a[u]],di){n[t]=0;return}c-=d+p,h=4;break}if(m>i){c-=d+p,h=7;break}if(mi){if(mi){c-=d+p,h=3;break}if(mi){n[t]=0;return}if(di){n[t]=0;return}c-=d+p,h=1;break}if(m>=r){n[t]=0;return}if(p=m,u++,m=e[t+a[u]],mi){c-=d+p,h=7;break}n[t]=0;return;case 5:if(di){if(p=m,u++,m=e[t+a[u]],mi){c-=d+p,h=3;break}if(mi){n[t]=0;return}if(di){c-=d+p,h=3;break}if(mi){n[t]=0;return}if(di){c-=d+p,h=7;break}n[t]=0;return;case 8:if(d>i){if(mi){n[t]=0;return}c-=d+p,h=1;break}n[t]=0;return;case 9:if(di){n[t]=0;return}if(p=m,u++,m=e[t+a[u]],m>i){n[t]=0;return}c-=d+p,h=8;break}if(d>i){if(p=m,u++,m=e[t+a[u]],m>i,t>>i,n)}detect(e,t,n=4){let r=this.level_tables[0],i=r.radius|0,a=i-1|0,o=r.dirs,s=r.dirs_count|0,c=s>>1,l=e.data,u=e.cols|0,d=e.rows|0,f=u>>1,p=r.scores,m=0,h=0,g=0,_=0,v=0,y=0,b=0,x=0,S=this.tau|0,C=0,w,T=Math.max(i+1,n)|0,E=Math.max(i+1,n)|0,D=Math.min(u-i-2,u-n)|0,j=Math.min(d-i-2,d-n)|0;for(g=E*u+T|0,h=E;h=3&&k(p,_,x,f,i)&&(w=t[C],w.x=m,w.y=h,w.score=b,++C,m+=a,_+=a);return C}};function N(e,t,n,r,i,a,o,s,c){let l=0,u=0,d=o*n+a|0,f=d;for(l=o;l=0&&f+i=0?t[f]=-4*e[f]+e[f+r]+e[f-r]+e[f+i]+e[f-i]:t[f]=0}function P(e,t,n,r,i,a,o){let s=-2*e[t]+e[t+r]+e[t-r],c=-2*e[t]+e[t+i]+e[t-i],l=e[t+a]+e[t-a]-e[t+o]-e[t-o],u=Math.sqrt((s-c)*(s-c)+4*l*l)|0;return Math.min(Math.abs(n-u),Math.abs(-(n+u)))}var F=class extends s{constructor(){super(),this.laplacian_threshold=30,this.min_eigen_value_threshold=25}detect(e,t,n){n===void 0&&(n=5);let r=0,i=0,a=e.cols,o=e.rows,s=e.data,c=5*a|0,l=3+3*a|0,u=3-3*a|0,d=this.cache.get_buffer(a*o<<2),f=d.i32,p=0,m=0,h=0,g=0,_,v=0,y=this.laplacian_threshold,b=this.min_eigen_value_threshold,x=Math.max(5,n)|0,S=Math.max(3,n)|0,C=Math.min(a-5,a-n)|0,w=Math.min(o-3,o-n)|0;for(r=a*o;--r>=0;)f[r]=0;for(N(s,f,a,5,c,x,S,C,w),m=S*a+x|0,i=S;iy&&p>f[h-1]&&p>f[h+1]&&p>f[h-a]&&p>f[h+a]&&p>f[h-a-1]&&p>f[h+a-1]&&p>f[h-a+1]&&p>f[h+a+1])&&(g=P(s,h,p,5,c,l,u),g>b&&(_=t[v],_.x=r,_.y=i,_.score=g,++v,++r,++h));return this.cache.put_buffer(d),v}},I=class{constructor(e=0,t=.5,n=.5,r=.99){this.size=e,this.thresh=t,this.eps=n,this.prob=r}update_iters(e,t){let n=Math.log(1-this.prob),r=Math.log(1-Math.pow(1-e,this.size));return(r>=0||-n>=t*-r?t:Math.round(n/r))|0}},L=class extends s{constructor(){super()}get_subset(e,t,n,r,i,a,o){let s=1e3,c=[],l=0,u=0,d=0,f=0,p=!1;for(;d=0;)s.data[a]=1;return this.cache.put_buffer(b),this.cache.put_buffer(x),this.cache.put_buffer(S),!0}for(;fMath.max(T,u-1)&&(C.copy_to(o),T=E,s&&w.copy_to(s),d=e.update_iters((a-E)/a,d),p=!0))}return this.cache.put_buffer(b),this.cache.put_buffer(x),this.cache.put_buffer(S),p}lmeds(e,t,n,r,a,o,s,l){if(l===void 0&&(l=1e3),a=0;)s.data[a]=1;return this.cache.put_buffer(S),this.cache.put_buffer(C),this.cache.put_buffer(w),!0}for(;f=u),this.cache.put_buffer(S),this.cache.put_buffer(C),this.cache.put_buffer(w),m}},R=class extends s{constructor(){super(),this.T0=new c(3,3,i.F32_t|i.C1_t),this.T1=new c(3,3,i.F32_t|i.C1_t),this.AtA=new c(6,6,i.F32_t|i.C1_t),this.AtB=new c(6,1,i.F32_t|i.C1_t)}sqr(e){return e*e}iso_normalize_points(e,t,n,r,i){let a=0,o=0,s=0,c=0,l=0,u=0,d=0,f=0,p=0,m=0,h=0;for(;a=0;)u[a]=0;for(a=0;a=0;--X)for(I=1/(1<>X,S=b>>X,A=x<<1,_=h[X].data,v=g[X].data,re=x-o|0,ie=S-o|0,this.scharr_deriv(h[X],E),Z=0;Z=re||G<=0||G>=ie,J!=0){X==0&&(l[Z]=0);continue}for(ae=L-W,oe=R-G,fe=(1-ae)*(1-oe)*le+.5|0,pe=ae*(1-oe)*le+.5|0,me=(1-ae)*oe*le+.5|0,he=le-fe-pe-me,ve=0,ye=0,be=0,Y=0;Y>9,_e=k[M]*fe+k[M+2]*pe+k[M+A]*me+k[M+A+2]*he,_e=_e+ue>>14,$=k[M+1]*fe+k[M+3]*pe+k[M+A+1]*me+k[M+A+3]*he,$=$+ue>>14,D[N]=ge,O[P++]=_e,O[P++]=$,ve+=_e*_e,ye+=_e*$,be+=$*$;if(ve*=de,ye*=de,be*=de,xe=ve*be-ye*ye,Se=(be+ve-Math.sqrt((ve-be)*(ve-be)+4*ye*ye))/m,Se=re||te<=0||te>=ie,J!=0){X==0&&(l[Z]=0);break}for(ae=z-K,oe=B-te,fe=(1-ae)*(1-oe)*le+.5|0,pe=ae*(1-oe)*le+.5|0,me=(1-ae)*oe*le+.5|0,he=le-fe-pe-me,se=0,ce=0,Y=0;Y>9,ge-=D[N],se+=ge*O[P++],ce+=ge*O[P++];if(se*=de,ce*=de,H=(ye*ce-be*se)*xe,U=(ye*se-ve*ce)*xe,z+=H,B+=U,r[q]=z+f,r[ne]=B+f,H*H+U*U<=u)break;if(Q>0&&Math.abs(H+V)<.01&&Math.abs(U+ee)<.01){r[q]-=H*.5,r[ne]-=U*.5;break}V=H,ee=U}}this.cache.put_buffer(C),this.cache.put_buffer(w),this.cache.put_buffer(T)}},ee=s;return s.cache=r,s.pyramid_t=x,s.transform=S,s.matrix_t=c,s.keypoint_t=C,s.fast_corners=b,s.imgproc=m,s.math=p,s.matmath=_,s.linalg=v,s.orb=E,s.yape=M,s.yape06=F,s.motion_estimator=L,s.ransac_params_t=I,s.affine2d=z,s.homography2d=B,s.optical_flow_lk=V,{jsfeatNext:ee}}); \ No newline at end of file +(function(e,t){typeof exports==`object`&&typeof module<`u`?module.exports=t():typeof define==`function`&&define.amd?define([],t):(e=typeof globalThis<`u`?globalThis:e||self,e.jsfeatNext=t())})(this,function(){var e=class{constructor(){this._data_type_size=new Int32Array([-1,1,4,-1,4,-1,-1,-1,8,-1,-1,-1,-1,-1,-1,-1,8])}_get_data_type(e){return e&65280}_get_channel(e){return e&255}_get_data_type_size(e){return this._data_type_size[(e&65280)>>8]}},t=class{constructor(e,t){this.size=(e+7|0)&-8,t===void 0?this.buffer=new ArrayBuffer(this.size):(this.buffer=t,this.size=t.length),this.u8=new Uint8Array(this.buffer),this.i32=new Int32Array(this.buffer),this.f32=new Float32Array(this.buffer),this.f64=new Float64Array(this.buffer)}},n=class{constructor(e){this.next=null,this.data=new t(e),this.size=this.data.size,this.buffer=this.data.buffer,this.u8=this.data.u8,this.i32=this.data.i32,this.f32=this.data.f32,this.f64=this.data.f64}resize(e){delete this.data,this.data=new t(e),this.size=this.data.size,this.buffer=this.data.buffer,this.u8=this.data.u8,this.i32=this.data.i32,this.f32=this.data.f32,this.f64=this.data.f64}},r=class{constructor(){this._pool_head,this._pool_tail,this._pool_size=0}allocate(e,t){this._pool_head=this._pool_tail=new n(t);for(let r=0;rt.size&&t.resize(e),t}put_buffer(e){this._pool_tail=this._pool_tail.next=e,this._pool_size++}},i={EPSILON:1.192092896e-7,FLT_MIN:1e-37,U8_t:256,S32_t:512,F32_t:1024,S64_t:2048,F64_t:4096,C1_t:1,C2_t:2,C3_t:3,C4_t:4,COLOR_RGBA2GRAY:0,COLOR_RGB2GRAY:1,COLOR_BGRA2GRAY:2,COLOR_BGR2GRAY:3,BOX_BLUR_NOSCALE:1,SVD_U_T:1,SVD_V_T:2,U8C1_t:257,U8C3_t:259,U8C4_t:260,F32C1_t:1025,F32C2_t:1026,S32C1_t:513,S32C2_t:514},a={name:`@webarkit/jsfeat-next`,version:`0.8.0`,description:`Typescript version of jsfeat for WebARKit`,main:`dist/jsfeatNext.js`,module:`dist/jsfeatNext.mjs`,types:`types/src/index.d.ts`,unpkg:`dist/jsfeatNext.js`,jsdelivr:`dist/jsfeatNext.js`,exports:{".":{types:`./types/src/index.d.ts`,import:`./dist/jsfeatNext.mjs`,require:`./dist/jsfeatNext.js`},"./package.json":`./package.json`},files:[`dist`,`types`],scripts:{"build-ts":`vite build`,"dev-ts":`vite build --watch`,"format-check":`prettier --check .`,format:`prettier --write .`,test:`vitest run`,"test:watch":`vitest`,docs:`typedoc`},repository:{type:`git`,url:`git+https://github.com/webarkit/jsfeatNext.git`},keywords:[`jsfeat`,`jsfeatNext`,`WebAR`,`WebARKit`,`AugmentedReality`,`computer`,`vision`],author:`Walter Perdan @kalwalt`,license:`LGPL-3.0-or-later`,bugs:{url:`https://github.com/webarkit/jsfeatNext/issues`},homepage:`https://github.com/webarkit/jsfeatNext#readme`,devDependencies:{prettier:`~3.9.4`,typedoc:`^0.28.20`,typescript:`^6.0.3`,vite:`^8.1.4`,"vite-plugin-dts":`^5.0.3`,vitest:`^4.1.10`}},o,s=new r;s.allocate(30,640*4);var c=class{constructor(){this.dt=new e,this.cache=s}get_data_type(e){return this.dt._get_data_type(e)}get_channel(e){return this.dt._get_channel(e)}get_data_type_size(e){return this.dt._get_data_type_size(e)}};o=c,o.VERSION=a.version,o.EPSILON=i.EPSILON,o.FLT_MIN=i.FLT_MIN,o.U8_t=i.U8_t,o.S32_t=i.S32_t,o.F32_t=i.F32_t,o.S64_t=i.S64_t,o.F64_t=i.F64_t,o.C1_t=i.C1_t,o.C2_t=i.C2_t,o.C3_t=i.C3_t,o.C4_t=i.C4_t,o.COLOR_RGBA2GRAY=i.COLOR_RGBA2GRAY,o.COLOR_RGB2GRAY=i.COLOR_RGB2GRAY,o.COLOR_BGRA2GRAY=i.COLOR_BGRA2GRAY,o.COLOR_BGR2GRAY=i.COLOR_BGR2GRAY,o.BOX_BLUR_NOSCALE=i.BOX_BLUR_NOSCALE,o.SVD_U_T=i.SVD_U_T,o.SVD_V_T=i.SVD_V_T,o.U8C1_t=o.U8_t|o.C1_t,o.U8C3_t=o.U8_t|o.C3_t,o.U8C4_t=o.U8_t|o.C4_t,o.F32C1_t=o.F32_t|o.C1_t,o.F32C2_t=o.F32_t|o.C2_t,o.S32C1_t=o.S32_t|o.C1_t,o.S32C2_t=o.S32_t|o.C2_t;var l=class{constructor(t,n,r,a){this.dt=new e,this.type=this.dt._get_data_type(r)|0,this.channel=this.dt._get_channel(r)|0,this.cols=t|0,this.rows=n|0,a===void 0?this.allocate():(this.buffer=a,this.data=this.type&i.U8_t?this.buffer.u8:this.type&i.S32_t?this.buffer.i32:this.type&i.F32_t?this.buffer.f32:this.buffer.f64)}allocate(){delete this.data,delete this.buffer,this.buffer=new t(this.cols*this.dt._get_data_type_size(this.type)*this.channel*this.rows),this.data=this.type&i.U8_t?this.buffer.u8:this.type&i.S32_t?this.buffer.i32:this.type&i.F32_t?this.buffer.f32:this.buffer.f64}copy_to(e){let t=e.data,n=this.data,r=0,i=this.cols*this.rows*this.channel|0;for(;rthis.buffer.size?(this.cols=e,this.rows=t,this.channel=n,this.allocate()):(this.cols=e,this.rows=t,this.channel=n)}};function u(e,t,n,r,i){let a=0,o=e.channel,s=e.cols,c=e.rows,l=e.data,u=t.data,d=s/r,f=c/i,p=d*f*65536|0,m=0,h=0,g=0,_=0,v=0,y=0,b=0,x=0,S=0,C=0,w=0,T=0,E=0,D=0,O=0,k=0,A=n.get_buffer(r*o<<2),j=n.get_buffer(r*o<<2),M=n.get_buffer(s*2*3<<2),N=A.i32,P=j.i32,F=M.i32;for(;mS&&(F[x++]=m*o|0,F[x++]=(v-1)*o|0,F[x++]=(v-S)*256|0,a++),g=v;g.001&&(a++,F[x++]=m*o|0,F[x++]=y*o|0,F[x++]=(C-y)*256|0)}for(m=0;mS&&(a++,F[x++]=(v-1)*o|0,F[x++]=m*o|0,F[x++]=(v-S)*p),g=v;g.001&&(a++,F[x++]=y*o|0,F[x++]=m*o|0,F[x++]=(C-y)*p)}for(m=0;m>8,255),n[f+l+1]=Math.min(m>>8,255),n[f+l+2]=Math.min(h>>8,255),n[f+l+3]=Math.min(g>>8,255)}for(;l>8,255)}d+=r,f+=r}for(c=0;c>8,255),n[f+r]=Math.min(m>>8,255),n[f+y]=Math.min(h>>8,255),n[f+b]=Math.min(g>>8,255)}for(;l>8,255)}}}function p(e,t,n,r,i,a,o,s){let c=0,l=0,u=0,d=0,f=0,p=0,m=0,h=0,g=0,_=a[0],v=0,y=r<<1,b=r*3,x=r<<2;for(;c>1){case 0:f[0]=1,u=1;break;case 1:f[0]=.25,f[1]=.5,f[2]=.25,u=1;break;case 2:f[0]=.0625,f[1]=.25,f[2]=.375,f[3]=.25,f[4]=.0625,u=1;break;case 3:f[0]=.03125,f[1]=.109375,f[2]=.21875,f[3]=.28125,f[4]=.21875,f[5]=.109375,f[6]=.03125,u=1;break}else for(c=t>0?t:((e-1)*.5-1)*.3+.8,l=-.5/(c*c);a=0;)for(l=E[c<<1],u=E[(c<<1)+1],c--;;)if(f=u-l+1,f<=7){for(m=l+1;m<=u;m++)for(h=m;h>l&&r(e[h],e[h-1]);h--)i=e[h],e[h]=e[h-1],e[h-1]=i;break}else{for(T=0,_=l,y=u,x=l+(f>>1),f>40&&(g=f>>3,S=l,C=l+g,w=l+(g<<1),a=e[S],o=e[C],s=e[w],l=r(a,o)?r(o,s)?C:r(a,s)?w:S:r(s,o)?C:r(a,s)?S:w,S=x-g,C=x,w=x+g,a=e[S],o=e[C],s=e[w],x=r(a,o)?r(o,s)?C:r(a,s)?w:S:r(s,o)?C:r(a,s)?S:w,S=u-(g<<1),C=u-g,w=u,a=e[S],o=e[C],s=e[w],u=r(a,o)?r(o,s)?C:r(a,s)?w:S:r(s,o)?C:r(a,s)?S:w),S=l,C=x,w=u,a=e[S],o=e[C],s=e[w],x=r(a,o)?r(o,s)?C:r(a,s)?w:S:r(s,o)?C:r(a,s)?S:w,x!=_&&(i=e[x],e[x]=e[_],e[_]=i,x=_),l=v=_+1,u=b=y,a=e[x];;){for(;l<=u&&!r(a,e[l]);)r(e[l],a)||(l>v&&(i=e[v],e[v]=e[l],e[l]=i),T=1,v++),l++;for(;l<=u&&!r(e[u],a);)r(a,e[u])||(uu)break;i=e[l],e[l]=e[u],e[u]=i,T=1,l++,u--}if(T==0){for(l=_,u=y,m=l+1;m<=u;m++)for(h=m;h>l&&r(e[h],e[h-1]);h--)i=e[h],e[h]=e[h-1],e[h-1]=i;break}for(f=Math.min(v-_,l-v),p=l-f|0,d=0;d1)p>1?f>p?(++c,E[c<<1]=_,E[(c<<1)+1]=_+f-1,l=y-p+1,u=y):(++c,E[c<<1]=y-p+1,E[(c<<1)+1]=y,l=_,u=_+f-1):(l=_,u=_+f-1);else if(p>1)l=y-p+1,u=y;else break}}median(e,t,n){let r,i=0,a=0,o=0,s=t+n>>1;for(;;){if(n<=t)return e[s];if(n==t+1)return e[t]>e[n]&&(r=e[t],e[t]=e[n],e[n]=r),e[s];for(i=t+n>>1,e[i]>e[n]&&(r=e[i],e[i]=e[n],e[n]=r),e[t]>e[n]&&(r=e[t],e[t]=e[n],e[n]=r),e[i]>e[t]&&(r=e[i],e[i]=e[t],e[t]=r),a=t+1,r=e[i],e[i]=e[a],e[a]=r,o=n;;){do++a;while(e[t]>e[a]);do--o;while(e[o]>e[t]);if(o=s&&(n=o-1)}return 0}},h=class extends c{constructor(){super()}grayscale(e,t,n,r,a){a===void 0&&(a=i.COLOR_RGBA2GRAY);let o=0,s=0,c=0,l=0,u=0,d=0,f=4899,p=9617,m=1868,h=4;(a==i.COLOR_BGRA2GRAY||a==i.COLOR_BGR2GRAY)&&(f=1868,m=4899),(a==i.COLOR_RGB2GRAY||a==i.COLOR_BGR2GRAY)&&(h=3);let g=h<<1,_=h*3|0;r.resize(t,n,1);let v=r.data;for(s=0;s>14,v[d+1]=e[u+h]*f+e[u+h+1]*p+e[u+h+2]*m+8192>>14,v[d+2]=e[u+g]*f+e[u+g+1]*p+e[u+g+2]*m+8192>>14,v[d+3]=e[u+_]*f+e[u+_+1]*p+e[u+_+2]*m+8192>>14;for(;o>14}}resample(e,t,n,r){let a=e.rows,o=e.cols;a>r&&o>n&&(t.resize(n,r,e.channel),e.type&i.U8_t&&t.type&i.U8_t&&a*o/(r*n)<256?u(e,t,this.cache,n,r):d(e,t,this.cache,n,r))}box_blur_gray(e,t,n,r){r===void 0&&(r=0);let a=e.cols,o=e.rows,s=o<<1,c=a<<1,l=0,u=0,d=0,f=0,p=(n<<1)+1|0,m=n+1|0,h=m+1|0,g=r&i.BOX_BLUR_NOSCALE?1:1/(p*p),_=this.cache.get_buffer(a*o<<2),v=0,y=0,b=0,x=0,S=0,C=_.i32,w=e.data,T=0;for(t.resize(a,o,e.channel),d=0;d>1,s=e.cols,c=e.rows,l=e.type,u=l&i.U8_t;t.resize(s,c,e.channel);let d=e.data,h=t.data,g,_,v=n+Math.max(c,s)|0,y=this.cache.get_buffer(v<<2),b=this.cache.get_buffer(n<<2);u?(g=y.i32,_=b.i32):l&i.S32_t?(g=y.i32,_=b.f32):(g=y.f32,_=b.f32),a.get_gaussian_kernel(n,r,_,l),u?f(g,d,h,s,c,_,n,o):p(g,d,h,s,c,_,n,o),this.cache.put_buffer(y),this.cache.put_buffer(b)}hough_transform(e,t,n,r){let i,a,o=e.data,s=e.cols,c=e.rows,l=s,u=Math.round((Math.PI-0)/n),d=Math.round(((s+c)*2+1)/t),f=1/t,p=new Int32Array((u+2)*(d+2)),m=new Float32Array(u),h=new Float32Array(u),g=0,_=0;for(;gr&&p[e]>p[e-1]&&p[e]>=p[e+1]&&p[e]>p[e-d-2]&&p[e]>=p[e+d+2]&&v.push(e)}v.sort(function(e,t){return p[e]>p[t]||p[e]==p[t]&&e>1,s=a>>1,c=o-(n<<1),l=s-(r<<1),u=0,d=0,f=n+r*i,p=0,m=0,h=0;t.resize(o,s,e.channel);let g=e.data,_=t.data;for(d=0;d>2,_[h+1]=g[p+2]+g[p+3]+g[p+i+2]+g[p+i+3]+2>>2;for(;u>2;f+=i<<1,m+=o}}scharr_derivatives(e,t){let n=e.cols,r=e.rows,a=n<<1,o=0,s=0,c=0,l,u,d,f,p,m,h=0,g=0,_=0,v=0,y,b;t.resize(n,r,2);let x=e.data,S=t.data,C=this.cache.get_buffer(n+2<<2),w=this.cache.get_buffer(n+2<<2);for(e.type&i.U8_t||e.type&i.S32_t?(y=C.i32,b=w.i32):(y=C.f32,b=w.f32);s0?s-1:1)*n|0,_=(s0?s-1:1)*n|0,_=(s0;--p)for(u=p+a*s,d=u-s,f=a;f>0;--f,u-=s,d-=s)r[u]+=r[d]+r[d+1]}}equalize_histogram(e,t){let n=e.cols,r=e.rows,i=e.data;t.resize(n,r,e.channel);let a=t.data,o=n*r,s=0,c=0,l,u,d=this.cache.get_buffer(1024);for(l=d.i32;s<256;++s)l[s]=0;for(s=0;sr&&(c=n,n=r,r=c),c=3*(a+2)|0;--c>=0;)T[c]=0;for(c=(o+2)*(a+2)|0;--c>=0;)E[c]=0;for(;u>31)-(g>>31)+((_^_>>31)-(_>>31));for(c=1;c<=o;++c,d+=f){if(c==o)for(u=M+a;--u>=M;)T[u]=0;else for(u=0;u>31)-(g>>31)+((_^_>>31)-(_>>31));for(p=d-f|0,E[P-1]=0,m=0,u=0;un){if(g=O[p],_=O[p+1],v=g^_,g=(g^g>>31)-(g>>31)|0,_=(_^_>>31)-(_>>31)|0,y=g*13573,b=y+(g+g<<15),_<<=15,_T[j+u-1]&&h>=T[j+u+1]){h>r&&!m&&E[P+u-N]!=2?(E[P+u]=2,m=1,D[F++]=P+u):E[P+u]=1;continue}}else if(_>b){if(h>T[A+u]&&h>=T[M+u]){h>r&&!m&&E[P+u-N]!=2?(E[P+u]=2,m=1,D[F++]=P+u):E[P+u]=1;continue}}else if(v=v<0?-1:1,h>T[A+u-v]&&h>T[M+u+v]){h>r&&!m&&E[P+u-N]!=2?(E[P+u]=2,m=1,D[F++]=P+u):E[P+u]=1;continue}}E[P+u]=0,m=0}E[P+a]=0,P+=N,u=A,A=j,j=M,M=u}for(u=P-N-1,c=0;c0;)P=D[--F],P-=N+1,E[P]==1&&(E[P]=2,D[F++]=P),P+=1,E[P]==1&&(E[P]=2,D[F++]=P),P+=1,E[P]==1&&(E[P]=2,D[F++]=P),P+=N,E[P]==1&&(E[P]=2,D[F++]=P),P-=2,E[P]==1&&(E[P]=2,D[F++]=P),P+=N,E[P]==1&&(E[P]=2,D[F++]=P),P+=1,E[P]==1&&(E[P]=2,D[F++]=P),P+=1,E[P]==1&&(E[P]=2,D[F++]=P);for(P=N+1,A=0,c=0;c0&&g>0&&p=0&&m>=0&&p95&&r>40&&i>20&&n>r&&n>i&&n-Math.min(r,i)>15&&Math.abs(n-r)>15?t[o]=255:t[o]=0}};function g(e,t,n,r){r=e[t],e[t]=e[n],e[n]=r}function _(e,t){return e=Math.abs(e),t=Math.abs(t),e>t?(t/=e,e*Math.sqrt(1+t*t)):t>0?(e/=t,t*Math.sqrt(1+e*e)):0}var v=class{constructor(){}identity(e,t){t===void 0&&(t=1);let n=e.data,r=e.rows,i=e.cols,a=i+1|0,o=r*i,s=o;for(;--o>=0;)n[o]=0;for(o=s,s=0;s0){for(d=0,b=Math.abs(e[u]),c=1;c1)for(;v0){for(d=0,b=Math.abs(e[p]),c=1;c>16&256?L:-L,e[d*t+p]=I;for(m=0;m<2;m++)for(f=0;fMath.abs(c[a*s+n])&&(a=r);if(Math.abs(c[a*s+n])=0;n--){for(f=l[n],a=n+1;a=0;s--){for(f=d[s],c=s+1,a=c*l;c=0;)n.data[s]=_.data[s];else n&&this.matmath.transpose(n,_);if(r&&a&i.SVD_V_T)for(s=f*f;--s>=0;)r.data[s]=y.data[s];else r&&this.matmath.transpose(r,y)}else{if(n&&a&i.SVD_U_T)for(s=f*f;--s>=0;)n.data[s]=y.data[s];else n&&this.matmath.transpose(n,y);if(r&&a&i.SVD_V_T)for(s=d*d;--s>=0;)r.data[s]=_.data[s];else r&&this.matmath.transpose(r,_)}this.cache.put_buffer(m),this.cache.put_buffer(h),this.cache.put_buffer(g)}svd_solve(e,t,n){let r=0,a=0,o=0,s=0,c=0,u=e.rows,d=e.cols,f=0,p=0,m=0,h=e.type|i.C1_t,g=this.cache.get_buffer(u*u<<3),_=this.cache.get_buffer(d<<3),v=this.cache.get_buffer(d*d<<3),y=new l(u,u,h,g.data),b=new l(1,d,h,_.data),x=new l(d,d,h,v.data),S=n.data,C=y.data,w=b.data,T=x.data;for(this.svd_decompose(e,b,y,x,0),m=i.EPSILON*w[0]*d;rm){for(o=0,f=0,s=0;op&&(f+=w[s+a]*S[o]/C[a]);x[c]=f}this.cache.put_buffer(h),this.cache.put_buffer(g),this.cache.put_buffer(_)}eigenVV(e,t,n){let r=e.cols,a=r*r,o=e.type|i.C1_t,s=this.cache.get_buffer(r*r<<3),c=this.cache.get_buffer(r<<3),u=new l(r,r,o,s.data),d=new l(1,r,o,c.data);for(;--a>=0;)u.data[a]=e.data[a];if(this.JacobiImpl(u.data,r,d.data,t?t.data:null,r,r),n)for(;--r>=0;)n.data[r]=d.data[r];this.cache.put_buffer(s),this.cache.put_buffer(c)}};function b(e,t,n,r,i){let a=0,o=e[t],s=i,c=0,l=0,u=0;for(;a<25;++a)r[a]=o-e[t+n[a]];for(a=0;a<16;a+=2)c=Math.min(r[a+1],r[a+2]),c=Math.min(c,r[a+3]),!(c<=s)&&(c=Math.min(c,r[a+4]),c=Math.min(c,r[a+5]),c=Math.min(c,r[a+6]),c=Math.min(c,r[a+7]),c=Math.min(c,r[a+8]),s=Math.max(s,Math.min(c,r[a])),s=Math.max(s,Math.min(c,r[a+9])));for(l=-s,a=0;a<16;a+=2)u=Math.max(r[a+1],r[a+2]),u=Math.max(u,r[a+3]),u=Math.max(u,r[a+4]),u=Math.max(u,r[a+5]),!(u>=l)&&(u=Math.max(u,r[a+6]),u=Math.max(u,r[a+7]),u=Math.max(u,r[a+8]),l=Math.min(l,Math.max(u,r[a])),l=Math.min(l,Math.max(u,r[a+9])));return-l-1}var x=class extends c{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(e){this._threshold=Math.min(Math.max(e,0),255);for(let e=-255;e<=255;++e)this.threshold_tab[e+255]=e<-this._threshold?1:e>this._threshold?2:0;return this._threshold}detect(e,t,n){n===void 0&&(n=3);let r=e.data,i=e.cols,a=e.rows,o=0,s=0,c=0,l=0,u=0,d=0,f=this.cache.get_buffer(3*i),p=this.cache.get_buffer((i+1)*3<<2),m=f.u8,h=p.i32,g=this.pixel_off,_=this.score_diff,v=Math.max(3,n),y=Math.min(a-2,a-n),x=Math.max(3,n),S=Math.min(i-3,i-n),C=0,w=0,T,E=b,D=this.threshold_tab,O=this._threshold,k=0,A=0,j=0,M=0,N=0,P=0,F=0,I=0,L=0,R=0,z=0,B=0;this._cmp_offsets(g,i,16);let V=g[0],ee=g[1],H=g[2],U=g[3],W=g[4],G=g[5],K=g[6],te=g[7],q=g[8],ne=g[9],J=g[10],Y=g[11],X=g[12],Z=g[13],Q=g[14],re=g[15];for(o=0;o8){++M,h[N+M]=s,m[P+s]=E(r,F,g,_,O);break}}else C=0;if(j&2)for(l=k+O,C=0,c=0;c<25;++c)if(u=r[F+g[c]],u>l){if(++C,C>8){++M,h[N+M]=s,m[P+s]=E(r,F,g,_,O);break}}else C=0}}if(h[N+i]=M,o!=v)for(d=(o-4+3)%3,I=d*i|0,N=d*(i+1)|0,d=(o-5+3)%3,L=d*i|0,M=h[N+i],c=0;cm[I+R]&&B>m[I+z]&&B>m[L+z]&&B>m[L+s]&&B>m[L+R]&&B>m[P+z]&&B>m[P+s]&&B>m[P+R]&&(T=t[w],T.x=s,T.y=o-1,T.score=B,w++)}return this.cache.put_buffer(f),this.cache.put_buffer(p),w}_cmp_offsets(e,t,n){let r=0,i=this.offsets16;for(;r=0;)this.data[r]=new l(e>>r,t>>r,n)}build(e,t){t===void 0&&(t=!0);let n=2,r=e,i=this.data[0];if(!t){let t=e.cols*e.rows;for(;--t>=0;)i.data[t]=e.data[t]}for(i=this.data[1],this.pyrdown(r,i);n=0;i--,r++)a=Math.sqrt(n*n-i*i)+.5|0,t[r]=i+e*a;for(;-i=0;a--,r++)i=-Math.sqrt(n*n-a*a)-.5|0,t[r]=i+e*a;for(;a>i;a--,r++)i=-Math.sqrt(n*n-a*a)-.5|0,t[r]=i+e*a;for(i++;i<=0;i++,r++)a=-Math.sqrt(n*n-i*i)-.5|0,t[r]=i+e*a;for(;i<-a;i++,r++)a=-Math.sqrt(n*n-i*i)-.5|0,t[r]=i+e*a;for(a++;a<0;a++,r++)i=Math.sqrt(n*n-a*a)+.5|0,t[r]=i+e*a;return t[r]=t[0],t[r+1]=t[1],r}function k(e,t,n){let r=0;return e[t+1]!=0&&r++,e[t-1]!=0&&r++,e[t+n]!=0&&r++,e[t+n+1]!=0&&r++,e[t+n-1]!=0&&r++,e[t-n]!=0&&r++,e[t-n+1]!=0&&r++,e[t-n-1]!=0&&r++,r}function A(e,t,n,r,i){let a,o;if(n>0)for(t-=r*i,o=-i;o<=i;++o){for(a=-i;a<=i;++a)if(e[t+a]>n)return!1;t+=r}else for(t-=r*i,o=-i;o<=i;++o){for(a=-i;a<=i;++a)if(e[t+a]=r)if(f=e[t+a[u]],f<=i)if(f>=r){n[t]=0;return}else if(u++,p=e[t+a[u]],p>i)if(u++,m=e[t+a[u]],m>i)h=3;else if(mi)h=7;else if(mi)if(u++,m=e[t+a[u]],m>i)h=3;else if(mi)h=7;else if(mi){n[t]=0;return}if(u++,p=e[t+a[u]],p>i){n[t]=0;return}if(u++,m=e[t+a[u]],m>i){n[t]=0;return}h=1}else{if(f=e[t+a[u]],fi){if(p=m,u++,m=e[t+a[u]],mi){n[t]=0;return}if(m>i){n[t]=0;return}if(p=m,u++,m=e[t+a[u]],m>i){n[t]=0;return}c-=d+p,h=8;break}if(p<=i){n[t]=0;return}if(m<=i){n[t]=0;return}if(p=m,u++,m=e[t+a[u]],m>i){c-=d+p,h=3;break}if(mi){n[t]=0;return}c-=d+p,h=1;break}if(d>i){if(p=r){n[t]=0;return}if(m>=r){n[t]=0;return}if(p=m,u++,m=e[t+a[u]],mi){c-=d+p,h=7;break}n[t]=0;return;case 2:if(d>i){n[t]=0;return}if(p=m,u++,m=e[t+a[u]],di){n[t]=0;return}c-=d+p,h=4;break}if(m>i){c-=d+p,h=7;break}if(mi){if(mi){c-=d+p,h=3;break}if(mi){n[t]=0;return}if(di){n[t]=0;return}c-=d+p,h=1;break}if(m>=r){n[t]=0;return}if(p=m,u++,m=e[t+a[u]],mi){c-=d+p,h=7;break}n[t]=0;return;case 5:if(di){if(p=m,u++,m=e[t+a[u]],mi){c-=d+p,h=3;break}if(mi){n[t]=0;return}if(di){c-=d+p,h=3;break}if(mi){n[t]=0;return}if(di){c-=d+p,h=7;break}n[t]=0;return;case 8:if(d>i){if(mi){n[t]=0;return}c-=d+p,h=1;break}n[t]=0;return;case 9:if(di){n[t]=0;return}if(p=m,u++,m=e[t+a[u]],m>i){n[t]=0;return}c-=d+p,h=8;break}if(d>i){if(p=m,u++,m=e[t+a[u]],m>i,t>>i,n)}detect(e,t,n=4){let r=this.level_tables[0],i=r.radius|0,a=i-1|0,o=r.dirs,s=r.dirs_count|0,c=s>>1,l=e.data,u=e.cols|0,d=e.rows|0,f=u>>1,p=r.scores,m=0,h=0,g=0,_=0,v=0,y=0,b=0,x=0,S=this.tau|0,C=0,w,T=Math.max(i+1,n)|0,E=Math.max(i+1,n)|0,D=Math.min(u-i-2,u-n)|0,O=Math.min(d-i-2,d-n)|0;for(g=E*u+T|0,h=E;h=3&&A(p,_,x,f,i)&&(w=t[C],w.x=m,w.y=h,w.score=b,++C,m+=a,_+=a);return C}};function P(e,t,n,r,i,a,o,s,c){let l=0,u=0,d=o*n+a|0,f=d;for(l=o;l=0&&f+i=0?t[f]=-4*e[f]+e[f+r]+e[f-r]+e[f+i]+e[f-i]:t[f]=0}function F(e,t,n,r,i,a,o){let s=-2*e[t]+e[t+r]+e[t-r],c=-2*e[t]+e[t+i]+e[t-i],l=e[t+a]+e[t-a]-e[t+o]-e[t-o],u=Math.sqrt((s-c)*(s-c)+4*l*l)|0;return Math.min(Math.abs(n-u),Math.abs(-(n+u)))}var I=class extends c{constructor(){super(),this.laplacian_threshold=30,this.min_eigen_value_threshold=25}detect(e,t,n){n===void 0&&(n=5);let r=0,i=0,a=e.cols,o=e.rows,s=e.data,c=5*a|0,l=3+3*a|0,u=3-3*a|0,d=this.cache.get_buffer(a*o<<2),f=d.i32,p=0,m=0,h=0,g=0,_,v=0,y=this.laplacian_threshold,b=this.min_eigen_value_threshold,x=Math.max(5,n)|0,S=Math.max(3,n)|0,C=Math.min(a-5,a-n)|0,w=Math.min(o-3,o-n)|0;for(r=a*o;--r>=0;)f[r]=0;for(P(s,f,a,5,c,x,S,C,w),m=S*a+x|0,i=S;iy&&p>f[h-1]&&p>f[h+1]&&p>f[h-a]&&p>f[h+a]&&p>f[h-a-1]&&p>f[h+a-1]&&p>f[h-a+1]&&p>f[h+a+1])&&(g=F(s,h,p,5,c,l,u),g>b&&(_=t[v],_.x=r,_.y=i,_.score=g,++v,++r,++h));return this.cache.put_buffer(d),v}},L=class{constructor(e=0,t=.5,n=.5,r=.99){this.size=e,this.thresh=t,this.eps=n,this.prob=r}update_iters(e,t){let n=Math.log(1-this.prob),r=Math.log(1-Math.pow(1-e,this.size));return(r>=0||-n>=t*-r?t:Math.round(n/r))|0}},R=class extends c{constructor(){super()}get_subset(e,t,n,r,i,a,o){let s=1e3,c=[],l=0,u=0,d=0,f=0,p=!1;for(;d=0;)s.data[a]=1;return this.cache.put_buffer(b),this.cache.put_buffer(x),this.cache.put_buffer(S),!0}for(;fMath.max(T,u-1)&&(C.copy_to(o),T=E,s&&w.copy_to(s),d=e.update_iters((a-E)/a,d),p=!0))}return this.cache.put_buffer(b),this.cache.put_buffer(x),this.cache.put_buffer(S),p}lmeds(e,t,n,r,a,o,s,c){if(c===void 0&&(c=1e3),a=0;)s.data[a]=1;return this.cache.put_buffer(S),this.cache.put_buffer(C),this.cache.put_buffer(w),!0}for(;f=u),this.cache.put_buffer(S),this.cache.put_buffer(C),this.cache.put_buffer(w),p}},z=class extends c{constructor(){super(),this.T0=new l(3,3,i.F32_t|i.C1_t),this.T1=new l(3,3,i.F32_t|i.C1_t),this.AtA=new l(6,6,i.F32_t|i.C1_t),this.AtB=new l(6,1,i.F32_t|i.C1_t)}sqr(e){return e*e}iso_normalize_points(e,t,n,r,i){let a=0,o=0,s=0,c=0,l=0,u=0,d=0,f=0,p=0,m=0,h=0;for(;a=0;)u[a]=0;for(a=0;a=0;--X)for(I=1/(1<>X,S=b>>X,A=x<<1,_=h[X].data,v=g[X].data,re=x-o|0,ie=S-o|0,this.scharr_deriv(h[X],E),Z=0;Z=re||G<=0||G>=ie,J!=0){X==0&&(c[Z]=0);continue}for(ae=L-W,oe=R-G,fe=(1-ae)*(1-oe)*le+.5|0,pe=ae*(1-oe)*le+.5|0,me=(1-ae)*oe*le+.5|0,he=le-fe-pe-me,ve=0,ye=0,be=0,Y=0;Y>9,_e=k[M]*fe+k[M+2]*pe+k[M+A]*me+k[M+A+2]*he,_e=_e+ue>>14,$=k[M+1]*fe+k[M+3]*pe+k[M+A+1]*me+k[M+A+3]*he,$=$+ue>>14,D[N]=ge,O[P++]=_e,O[P++]=$,ve+=_e*_e,ye+=_e*$,be+=$*$;if(ve*=de,ye*=de,be*=de,xe=ve*be-ye*ye,Se=(be+ve-Math.sqrt((ve-be)*(ve-be)+4*ye*ye))/m,Se=re||te<=0||te>=ie,J!=0){X==0&&(c[Z]=0);break}for(ae=z-K,oe=B-te,fe=(1-ae)*(1-oe)*le+.5|0,pe=ae*(1-oe)*le+.5|0,me=(1-ae)*oe*le+.5|0,he=le-fe-pe-me,se=0,ce=0,Y=0;Y>9,ge-=D[N],se+=ge*O[P++],ce+=ge*O[P++];if(se*=de,ce*=de,H=(ye*ce-be*se)*xe,U=(ye*se-ve*ce)*xe,z+=H,B+=U,r[q]=z+f,r[ne]=B+f,H*H+U*U<=u)break;if(Q>0&&Math.abs(H+V)<.01&&Math.abs(U+ee)<.01){r[q]-=H*.5,r[ne]-=U*.5;break}V=H,ee=U}}this.cache.put_buffer(C),this.cache.put_buffer(w),this.cache.put_buffer(T)}},H=c;return c.cache=s,c.pyramid_t=S,c.matrix_t=l,c.keypoint_t=w,c.ransac_params_t=L,c.transform=new C,c.fast_corners=new x,c.imgproc=new h,c.math=new m,c.matmath=new v,c.linalg=new y,c.orb=new D,c.yape=new N,c.yape06=new I,c.motion_estimator=new R,c.affine2d=new B,c.homography2d=new V,c.optical_flow_lk=new ee,H}); \ No newline at end of file diff --git a/dist/jsfeatNext.mjs b/dist/jsfeatNext.mjs index eb973c0..74859ad 100644 --- a/dist/jsfeatNext.mjs +++ b/dist/jsfeatNext.mjs @@ -137,9 +137,11 @@ var e = class { "vite-plugin-dts": "^5.0.3", vitest: "^4.1.10" } -}, o, s = class { +}, o, s = new r(); +s.allocate(30, 640 * 4); +var c = class { constructor() { - this.dt = new e(), this.cache = new r(), this.cache.allocate(30, 640 * 4); + this.dt = new e(), this.cache = s; } get_data_type(e) { return this.dt._get_data_type(e); @@ -151,10 +153,10 @@ var e = class { return this.dt._get_data_type_size(e); } }; -o = s, o.VERSION = a.version, o.EPSILON = i.EPSILON, o.FLT_MIN = i.FLT_MIN, o.U8_t = i.U8_t, o.S32_t = i.S32_t, o.F32_t = i.F32_t, o.S64_t = i.S64_t, o.F64_t = i.F64_t, o.C1_t = i.C1_t, o.C2_t = i.C2_t, o.C3_t = i.C3_t, o.C4_t = i.C4_t, o.COLOR_RGBA2GRAY = i.COLOR_RGBA2GRAY, o.COLOR_RGB2GRAY = i.COLOR_RGB2GRAY, o.COLOR_BGRA2GRAY = i.COLOR_BGRA2GRAY, o.COLOR_BGR2GRAY = i.COLOR_BGR2GRAY, o.BOX_BLUR_NOSCALE = i.BOX_BLUR_NOSCALE, o.SVD_U_T = i.SVD_U_T, o.SVD_V_T = i.SVD_V_T, o.U8C1_t = o.U8_t | o.C1_t, o.U8C3_t = o.U8_t | o.C3_t, o.U8C4_t = o.U8_t | o.C4_t, o.F32C1_t = o.F32_t | o.C1_t, o.F32C2_t = o.F32_t | o.C2_t, o.S32C1_t = o.S32_t | o.C1_t, o.S32C2_t = o.S32_t | o.C2_t; +o = c, o.VERSION = a.version, o.EPSILON = i.EPSILON, o.FLT_MIN = i.FLT_MIN, o.U8_t = i.U8_t, o.S32_t = i.S32_t, o.F32_t = i.F32_t, o.S64_t = i.S64_t, o.F64_t = i.F64_t, o.C1_t = i.C1_t, o.C2_t = i.C2_t, o.C3_t = i.C3_t, o.C4_t = i.C4_t, o.COLOR_RGBA2GRAY = i.COLOR_RGBA2GRAY, o.COLOR_RGB2GRAY = i.COLOR_RGB2GRAY, o.COLOR_BGRA2GRAY = i.COLOR_BGRA2GRAY, o.COLOR_BGR2GRAY = i.COLOR_BGR2GRAY, o.BOX_BLUR_NOSCALE = i.BOX_BLUR_NOSCALE, o.SVD_U_T = i.SVD_U_T, o.SVD_V_T = i.SVD_V_T, o.U8C1_t = o.U8_t | o.C1_t, o.U8C3_t = o.U8_t | o.C3_t, o.U8C4_t = o.U8_t | o.C4_t, o.F32C1_t = o.F32_t | o.C1_t, o.F32C2_t = o.F32_t | o.C2_t, o.S32C1_t = o.S32_t | o.C1_t, o.S32C2_t = o.S32_t | o.C2_t; //#endregion //#region src/matrix_t/matrix_t.ts -var c = class { +var l = class { constructor(t, n, r, a) { this.dt = new e(), this.type = this.dt._get_data_type(r) | 0, this.channel = this.dt._get_channel(r) | 0, this.cols = t | 0, this.rows = n | 0, a === void 0 ? this.allocate() : (this.buffer = a, this.data = this.type & i.U8_t ? this.buffer.u8 : this.type & i.S32_t ? this.buffer.i32 : this.type & i.F32_t ? this.buffer.f32 : this.buffer.f64); } @@ -172,7 +174,7 @@ var c = class { }; //#endregion //#region src/imgproc/resample.ts -function l(e, t, n, r, i) { +function u(e, t, n, r, i) { let a = 0, o = e.channel, s = e.cols, c = e.rows, l = e.data, u = t.data, d = s / r, f = c / i, p = d * f * 65536 | 0, m = 0, h = 0, g = 0, _ = 0, v = 0, y = 0, b = 0, x = 0, S = 0, C = 0, w = 0, T = 0, E = 0, D = 0, O = 0, k = 0, A = n.get_buffer(r * o << 2), j = n.get_buffer(r * o << 2), M = n.get_buffer(s * 2 * 3 << 2), N = A.i32, P = j.i32, F = M.i32; for (; m < r; m++) { for (S = m * d, C = S + d, v = S + 1 - 1e-6 | 0, y = C | 0, v = Math.min(v, s - 1), y = Math.min(y, s - 1), v > S && (F[x++] = m * o | 0, F[x++] = (v - 1) * o | 0, F[x++] = (v - S) * 256 | 0, a++), g = v; g < y; g++) a++, F[x++] = m * o | 0, F[x++] = g * o | 0, F[x++] = 256; @@ -189,7 +191,7 @@ function l(e, t, n, r, i) { } n.put_buffer(j), n.put_buffer(A), n.put_buffer(M); } -function u(e, t, n, r, i) { +function d(e, t, n, r, i) { let a = 0, o = e.channel, s = e.cols, c = e.rows, l = e.data, u = t.data, d = s / r, f = c / i, p = 1 / (d * f), m = 0, h = 0, g = 0, _ = 0, v = 0, y = 0, b = 0, x = 0, S = 0, C = 0, w = 0, T = 0, E = 0, D = 0, O = 0, k = 0, A = n.get_buffer(r * o << 2), j = n.get_buffer(r * o << 2), M = n.get_buffer(s * 2 * 3 << 2), N = A.f32, P = j.f32, F = M.f32; for (; m < r; m++) { for (S = m * d, C = S + d, v = S + 1 - 1e-6 | 0, y = C | 0, v = Math.min(v, s - 1), y = Math.min(y, s - 1), v > S && (a++, F[x++] = (v - 1) * o | 0, F[x++] = m * o | 0, F[x++] = (v - S) * p), g = v; g < y; g++) a++, F[x++] = g * o | 0, F[x++] = m * o | 0, F[x++] = p; @@ -208,7 +210,7 @@ function u(e, t, n, r, i) { } //#endregion //#region src/imgproc/convol.ts -function d(e, t, n, r, i, a, o, s) { +function f(e, t, n, r, i, a, o, s) { let c = 0, l = 0, u = 0, d = 0, f = 0, p = 0, m = 0, h = 0, g = 0, _ = a[0], v = 0, y = r << 1, b = r * 3, x = r << 2; for (; c < i; ++c) { for (p = t[d], l = 0; l < s; ++l) e[l] = p; @@ -240,7 +242,7 @@ function d(e, t, n, r, i, a, o, s) { } } } -function f(e, t, n, r, i, a, o, s) { +function p(e, t, n, r, i, a, o, s) { let c = 0, l = 0, u = 0, d = 0, f = 0, p = 0, m = 0, h = 0, g = 0, _ = a[0], v = 0, y = r << 1, b = r * 3, x = r << 2; for (; c < i; ++c) { for (p = t[d], l = 0; l < s; ++l) e[l] = p; @@ -274,7 +276,7 @@ function f(e, t, n, r, i, a, o, s) { } //#endregion //#region src/math/math.ts -var p = class extends s { +var m = class extends c { constructor() { super(), this.qsort_stack = /* @__PURE__ */ new Int32Array(96); } @@ -358,7 +360,7 @@ var p = class extends s { } return 0; } -}, m = class extends s { +}, h = class extends c { constructor() { super(); } @@ -376,7 +378,7 @@ var p = class extends s { } resample(e, t, n, r) { let a = e.rows, o = e.cols; - a > r && o > n && (t.resize(n, r, e.channel), e.type & i.U8_t && t.type & i.U8_t && a * o / (r * n) < 256 ? l(e, t, this.cache, n, r) : u(e, t, this.cache, n, r)); + a > r && o > n && (t.resize(n, r, e.channel), e.type & i.U8_t && t.type & i.U8_t && a * o / (r * n) < 256 ? u(e, t, this.cache, n, r) : d(e, t, this.cache, n, r)); } box_blur_gray(e, t, n, r) { r === void 0 && (r = 0); @@ -408,12 +410,12 @@ var p = class extends s { this.cache.put_buffer(_); } gaussian_blur(e, t, n, r) { - let a = new p(); + let a = new m(); r === void 0 && (r = 0), n === void 0 && (n = 0), n = n == 0 ? Math.max(1, 4 * r + 1 - 1e-8) * 2 + 1 | 0 : n; let o = n >> 1, s = e.cols, c = e.rows, l = e.type, u = l & i.U8_t; t.resize(s, c, e.channel); - let m = e.data, h = t.data, g, _, v = n + Math.max(c, s) | 0, y = this.cache.get_buffer(v << 2), b = this.cache.get_buffer(n << 2); - u ? (g = y.i32, _ = b.i32) : l & i.S32_t ? (g = y.i32, _ = b.f32) : (g = y.f32, _ = b.f32), a.get_gaussian_kernel(n, r, _, l), u ? d(g, m, h, s, c, _, n, o) : f(g, m, h, s, c, _, n, o), this.cache.put_buffer(y), this.cache.put_buffer(b); + let d = e.data, h = t.data, g, _, v = n + Math.max(c, s) | 0, y = this.cache.get_buffer(v << 2), b = this.cache.get_buffer(n << 2); + u ? (g = y.i32, _ = b.i32) : l & i.S32_t ? (g = y.i32, _ = b.f32) : (g = y.f32, _ = b.f32), a.get_gaussian_kernel(n, r, _, l), u ? f(g, d, h, s, c, _, n, o) : p(g, d, h, s, c, _, n, o), this.cache.put_buffer(y), this.cache.put_buffer(b); } hough_transform(e, t, n, r) { let i, a, o = e.data, s = e.cols, c = e.rows, l = s, u = Math.round((Math.PI - 0) / n), d = Math.round(((s + c) * 2 + 1) / t), f = 1 / t, p = new Int32Array((u + 2) * (d + 2)), m = new Float32Array(u), h = new Float32Array(u), g = 0, _ = 0; @@ -515,12 +517,12 @@ var p = class extends s { canny(e, t, n, r) { let a = e.cols, o = e.rows; e.data, t.resize(a, o, e.channel); - let s = t.data, l = 0, u = 0, d = 0, f = a << 1, p = 0, m = 0, h = 0, g = 0, _ = 0, v = 0, y = 0, b = 0, x = this.cache.get_buffer(o * f << 2), S = this.cache.get_buffer(3 * (a + 2) << 2), C = this.cache.get_buffer((o + 2) * (a + 2) << 2), w = this.cache.get_buffer(o * a << 2), T = S.i32, E = C.i32, D = w.i32, O = x.i32, k = new c(a, o, i.S32C2_t, x.data), A = 1, j = a + 2 + 1 | 0, M = 2 * (a + 2) + 1 | 0, N = a + 2 | 0, P = N + 1 | 0, F = 0; - for (this.sobel_derivatives(e, k), n > r && (l = n, n = r, r = l), l = 3 * (a + 2) | 0; --l >= 0;) T[l] = 0; - for (l = (o + 2) * (a + 2) | 0; --l >= 0;) E[l] = 0; + let s = t.data, c = 0, u = 0, d = 0, f = a << 1, p = 0, m = 0, h = 0, g = 0, _ = 0, v = 0, y = 0, b = 0, x = this.cache.get_buffer(o * f << 2), S = this.cache.get_buffer(3 * (a + 2) << 2), C = this.cache.get_buffer((o + 2) * (a + 2) << 2), w = this.cache.get_buffer(o * a << 2), T = S.i32, E = C.i32, D = w.i32, O = x.i32, k = new l(a, o, i.S32C2_t, x.data), A = 1, j = a + 2 + 1 | 0, M = 2 * (a + 2) + 1 | 0, N = a + 2 | 0, P = N + 1 | 0, F = 0; + for (this.sobel_derivatives(e, k), n > r && (c = n, n = r, r = c), c = 3 * (a + 2) | 0; --c >= 0;) T[c] = 0; + for (c = (o + 2) * (a + 2) | 0; --c >= 0;) E[c] = 0; for (; u < a; ++u, d += 2) g = O[d], _ = O[d + 1], T[j + u] = (g ^ g >> 31) - (g >> 31) + ((_ ^ _ >> 31) - (_ >> 31)); - for (l = 1; l <= o; ++l, d += f) { - if (l == o) for (u = M + a; --u >= M;) T[u] = 0; + for (c = 1; c <= o; ++c, d += f) { + if (c == o) for (u = M + a; --u >= M;) T[u] = 0; else for (u = 0; u < a; u++) g = O[d + (u << 1)], _ = O[d + (u << 1) + 1], T[M + u] = (g ^ g >> 31) - (g >> 31) + ((_ ^ _ >> 31) - (_ >> 31)); for (p = d - f | 0, E[P - 1] = 0, m = 0, u = 0; u < a; ++u, p += 2) { if (h = T[j + u], h > n) { @@ -543,9 +545,9 @@ var p = class extends s { } E[P + a] = 0, P += N, u = A, A = j, j = M, M = u; } - for (u = P - N - 1, l = 0; l < N; ++l, ++u) E[u] = 0; + for (u = P - N - 1, c = 0; c < N; ++c, ++u) E[u] = 0; for (; F > 0;) P = D[--F], P -= N + 1, E[P] == 1 && (E[P] = 2, D[F++] = P), P += 1, E[P] == 1 && (E[P] = 2, D[F++] = P), P += 1, E[P] == 1 && (E[P] = 2, D[F++] = P), P += N, E[P] == 1 && (E[P] = 2, D[F++] = P), P -= 2, E[P] == 1 && (E[P] = 2, D[F++] = P), P += N, E[P] == 1 && (E[P] = 2, D[F++] = P), P += 1, E[P] == 1 && (E[P] = 2, D[F++] = P), P += 1, E[P] == 1 && (E[P] = 2, D[F++] = P); - for (P = N + 1, A = 0, l = 0; l < o; ++l, P += N) for (u = 0; u < a; ++u) s[A++] = Number(E[P + u] == 2) * 255; + for (P = N + 1, A = 0, c = 0; c < o; ++c, P += N) for (u = 0; u < a; ++u) s[A++] = Number(E[P + u] == 2) * 255; this.cache.put_buffer(x), this.cache.put_buffer(S), this.cache.put_buffer(C), this.cache.put_buffer(w); } warp_perspective(e, t, n, r) { @@ -565,15 +567,15 @@ var p = class extends s { }; //#endregion //#region src/linalg/linalg_base.ts -function h(e, t, n, r) { +function g(e, t, n, r) { r = e[t], e[t] = e[n], e[n] = r; } -function g(e, t) { +function _(e, t) { return e = Math.abs(e), t = Math.abs(t), e > t ? (t /= e, e * Math.sqrt(1 + t * t)) : t > 0 ? (e /= t, t * Math.sqrt(1 + e * e)) : 0; } //#endregion //#region src/matmath/matmath.ts -var _ = class { +var v = class { constructor() {} identity(e, t) { t === void 0 && (t = 1); @@ -640,12 +642,12 @@ var _ = class { determinant_3x3(e, t, n, r, i, a, o, s, c) { return e * i * c - e * a * s - r * t * c + r * n * s + o * t * a - o * n * i; } -}, v = class extends s { +}, y = class extends c { constructor() { - super(), this.matmath = new _(); + super(), this.matmath = new v(); } JacobiImpl(e, t, n, r, a, o) { - let s = i.EPSILON, c = 0, l = 0, u = 0, d = 0, f = 0, p = 0, m = 0, _ = 0, v = 0, y = o * o * 30, b = 0, x = 0, S = 0, C = 0, w = 0, T = 0, E = 0, D = 0, O = 0, k = this.cache.get_buffer(o << 2), A = this.cache.get_buffer(o << 2), j = k.i32, M = A.i32; + let s = i.EPSILON, c = 0, l = 0, u = 0, d = 0, f = 0, p = 0, m = 0, h = 0, v = 0, y = o * o * 30, b = 0, x = 0, S = 0, C = 0, w = 0, T = 0, E = 0, D = 0, O = 0, k = this.cache.get_buffer(o << 2), A = this.cache.get_buffer(o << 2), j = k.i32, M = A.i32; if (r) for (; c < o; c++) { for (u = c * a, l = 0; l < o; l++) r[u + l] = 0; r[u + c] = 1; @@ -664,10 +666,10 @@ var _ = class { for (u = 0, b = Math.abs(e[j[0]]), c = 1; c < o - 1; c++) x = Math.abs(e[t * c + j[c]]), b < x && (b = x, u = c); for (f = j[u], c = 1; c < o; c++) x = Math.abs(e[t * M[c] + c]), b < x && (b = x, u = M[c], f = c); if (S = e[t * u + f], Math.abs(S) <= s) break; - for (C = (n[f] - n[u]) * .5, w = Math.abs(C) + g(S, C), T = g(S, w), E = w / T, T = S / T, w = S / w * S, C < 0 && (T = -T, w = -w), e[t * u + f] = 0, n[u] -= w, n[f] += w, c = 0; c < u; c++) m = t * c + u, _ = t * c + f, D = e[m], O = e[_], e[m] = D * E - O * T, e[_] = D * T + O * E; - for (c = u + 1; c < f; c++) m = t * u + c, _ = t * c + f, D = e[m], O = e[_], e[m] = D * E - O * T, e[_] = D * T + O * E; - for (c = f + 1, m = t * u + c, _ = t * f + c; c < o; c++, m++, _++) D = e[m], O = e[_], e[m] = D * E - O * T, e[_] = D * T + O * E; - if (r) for (m = a * u, _ = a * f, c = 0; c < o; c++, m++, _++) D = r[m], O = r[_], r[m] = D * E - O * T, r[_] = D * T + O * E; + for (C = (n[f] - n[u]) * .5, w = Math.abs(C) + _(S, C), T = _(S, w), E = w / T, T = S / T, w = S / w * S, C < 0 && (T = -T, w = -w), e[t * u + f] = 0, n[u] -= w, n[f] += w, c = 0; c < u; c++) m = t * c + u, h = t * c + f, D = e[m], O = e[h], e[m] = D * E - O * T, e[h] = D * T + O * E; + for (c = u + 1; c < f; c++) m = t * u + c, h = t * c + f, D = e[m], O = e[h], e[m] = D * E - O * T, e[h] = D * T + O * E; + for (c = f + 1, m = t * u + c, h = t * f + c; c < o; c++, m++, h++) D = e[m], O = e[h], e[m] = D * E - O * T, e[h] = D * T + O * E; + if (r) for (m = a * u, h = a * f, c = 0; c < o; c++, m++, h++) D = r[m], O = r[h], r[m] = D * E - O * T, r[h] = D * T + O * E; for (l = 0; l < 2; l++) { if (p = l == 0 ? u : f, p < o - 1) { for (d = p + 1, b = Math.abs(e[t * p + d]), c = p + 2; c < o; c++) x = Math.abs(e[t * p + c]), b < x && (b = x, d = c); @@ -681,12 +683,12 @@ var _ = class { } for (u = 0; u < o - 1; u++) { for (d = u, c = u + 1; c < o; c++) n[d] < n[c] && (d = c); - if (u != d && (h(n, d, u, b), r)) for (c = 0; c < o; c++) h(r, a * d + c, a * u + c, b); + if (u != d && (g(n, d, u, b), r)) for (c = 0; c < o; c++) g(r, a * d + c, a * u + c, b); } this.cache.put_buffer(k), this.cache.put_buffer(A); } JacobiSVDImpl(e, t, n, r, a, o, s, c) { - let l = i.EPSILON * 2, u = i.FLT_MIN, d = 0, f = 0, p = 0, m = 0, _ = Math.max(o, 30), v = 0, y = 0, b = 0, x = 0, S = 0, C = 0, w = 0, T = 0, E = 0, D = 0, O = 0, k = 0, A = 0, j = 0, M = 0, N = 0, P = 0, F = 4660, I = 0, L = 0, R = 0, z = this.cache.get_buffer(s << 3), B = z.f64; + let l = i.EPSILON * 2, u = i.FLT_MIN, d = 0, f = 0, p = 0, m = 0, h = Math.max(o, 30), v = 0, y = 0, b = 0, x = 0, S = 0, C = 0, w = 0, T = 0, E = 0, D = 0, O = 0, k = 0, A = 0, j = 0, M = 0, N = 0, P = 0, F = 4660, I = 0, L = 0, R = 0, z = this.cache.get_buffer(s << 3), B = z.f64; for (; d < s; d++) { for (p = 0, O = 0; p < o; p++) T = e[d * t + p], O += T * T; if (B[d] = O, r) { @@ -694,11 +696,11 @@ var _ = class { r[d * a + d] = 1; } } - for (; m < _; m++) { + for (; m < h; m++) { for (S = 0, d = 0; d < s - 1; d++) for (f = d + 1; f < s; f++) { for (v = d * t | 0, y = f * t | 0, M = B[d], N = 0, P = B[f], p = 2, N += e[v] * e[y], N += e[v + 1] * e[y + 1]; p < o; p++) N += e[v + p] * e[y + p]; if (!(Math.abs(N) <= l * Math.sqrt(M * P))) { - for (N *= 2, k = M - P, A = g(N, k), k < 0 ? (j = (A - k) * .5, w = Math.sqrt(j / A), C = N / (A * w * 2)) : (C = Math.sqrt((A + k) / (A * 2)), w = N / (A * C * 2)), M = 0, P = 0, p = 2, E = C * e[v] + w * e[y], D = -w * e[v] + C * e[y], e[v] = E, e[y] = D, M += E * E, P += D * D, E = C * e[v + 1] + w * e[y + 1], D = -w * e[v + 1] + C * e[y + 1], e[v + 1] = E, e[y + 1] = D, M += E * E, P += D * D; p < o; p++) E = C * e[v + p] + w * e[y + p], D = -w * e[v + p] + C * e[y + p], e[v + p] = E, e[y + p] = D, M += E * E, P += D * D; + for (N *= 2, k = M - P, A = _(N, k), k < 0 ? (j = (A - k) * .5, w = Math.sqrt(j / A), C = N / (A * w * 2)) : (C = Math.sqrt((A + k) / (A * 2)), w = N / (A * C * 2)), M = 0, P = 0, p = 2, E = C * e[v] + w * e[y], D = -w * e[v] + C * e[y], e[v] = E, e[y] = D, M += E * E, P += D * D, E = C * e[v + 1] + w * e[y + 1], D = -w * e[v + 1] + C * e[y + 1], e[v + 1] = E, e[y + 1] = D, M += E * E, P += D * D; p < o; p++) E = C * e[v + p] + w * e[y + p], D = -w * e[v + p] + C * e[y + p], e[v + p] = E, e[y + p] = D, M += E * E, P += D * D; if (B[d] = M, B[f] = P, S = 1, r) for (b = d * a | 0, x = f * a | 0, p = 2, E = C * r[b] + w * r[x], D = -w * r[b] + C * r[x], r[b] = E, r[x] = D, E = C * r[b + 1] + w * r[x + 1], D = -w * r[b + 1] + C * r[x + 1], r[b + 1] = E, r[x + 1] = D; p < s; p++) E = C * r[b + p] + w * r[x + p], D = -w * r[b + p] + C * r[x + p], r[b + p] = E, r[x + p] = D; } } @@ -710,9 +712,9 @@ var _ = class { } for (d = 0; d < s - 1; d++) { for (f = d, p = d + 1; p < s; p++) B[f] < B[p] && (f = p); - if (d != f && (h(B, d, f, O), r)) { - for (p = 0; p < o; p++) h(e, d * t + p, f * t + p, T); - for (p = 0; p < s; p++) h(r, d * a + p, f * a + p, T); + if (d != f && (g(B, d, f, O), r)) { + for (p = 0; p < o; p++) g(e, d * t + p, f * t + p, T); + for (p = 0; p < s; p++) g(r, d * a + p, f * a + p, T); } } for (d = 0; d < s; d++) n[d] = B[d]; @@ -741,8 +743,8 @@ var _ = class { for (a = n, r = n + 1; r < s; r++) Math.abs(c[r * s + n]) > Math.abs(c[a * s + n]) && (a = r); if (Math.abs(c[a * s + n]) < i.EPSILON) return 0; if (a != n) { - for (r = n; r < s; r++) h(c, n * s + r, a * s + r, void 0); - h(l, n, a, void 0), o = -o; + for (r = n; r < s; r++) g(c, n * s + r, a * s + r, void 0); + g(l, n, a, void 0), o = -o; } for (d = -1 / c[n * s + n], r = n + 1; r < s; r++) { for (u = c[r * s + n] * d, a = n + 1; a < s; a++) c[r * s + a] += u * c[n * s + a]; @@ -779,12 +781,12 @@ var _ = class { } svd_decompose(e, t, n, r, a) { a === void 0 && (a = 0); - let o = 0, s = 0, l = e.rows, u = e.cols, d = l, f = u, p = e.type | i.C1_t; + let o = 0, s = 0, c = e.rows, u = e.cols, d = c, f = u, p = e.type | i.C1_t; d < f && (o = 1, s = d, d = f, f = s); - let m = this.cache.get_buffer(d * d << 3), h = this.cache.get_buffer(f << 3), g = this.cache.get_buffer(f * f << 3), _ = new c(d, d, p, m.data), v = new c(1, f, p, h.data), y = new c(f, f, p, g.data); + let m = this.cache.get_buffer(d * d << 3), h = this.cache.get_buffer(f << 3), g = this.cache.get_buffer(f * f << 3), _ = new l(d, d, p, m.data), v = new l(1, f, p, h.data), y = new l(f, f, p, g.data); if (o == 0) this.matmath.transpose(_, e); else { - for (s = 0; s < u * l; s++) _.data[s] = e.data[s]; + for (s = 0; s < u * c; s++) _.data[s] = e.data[s]; for (; s < f * d; s++) _.data[s] = 0; } if (this.JacobiSVDImpl(_.data, d, v.data, y.data, f, d, f, d), t) { @@ -805,34 +807,34 @@ var _ = class { this.cache.put_buffer(m), this.cache.put_buffer(h), this.cache.put_buffer(g); } svd_solve(e, t, n) { - let r = 0, a = 0, o = 0, s = 0, l = 0, u = e.rows, d = e.cols, f = 0, p = 0, m = 0, h = e.type | i.C1_t, g = this.cache.get_buffer(u * u << 3), _ = this.cache.get_buffer(d << 3), v = this.cache.get_buffer(d * d << 3), y = new c(u, u, h, g.data), b = new c(1, d, h, _.data), x = new c(d, d, h, v.data), S = n.data, C = y.data, w = b.data, T = x.data; - for (this.svd_decompose(e, b, y, x, 0), m = i.EPSILON * w[0] * d; r < d; r++, l += d) { + let r = 0, a = 0, o = 0, s = 0, c = 0, u = e.rows, d = e.cols, f = 0, p = 0, m = 0, h = e.type | i.C1_t, g = this.cache.get_buffer(u * u << 3), _ = this.cache.get_buffer(d << 3), v = this.cache.get_buffer(d * d << 3), y = new l(u, u, h, g.data), b = new l(1, d, h, _.data), x = new l(d, d, h, v.data), S = n.data, C = y.data, w = b.data, T = x.data; + for (this.svd_decompose(e, b, y, x, 0), m = i.EPSILON * w[0] * d; r < d; r++, c += d) { for (p = 0, a = 0; a < d; a++) if (w[a] > m) { for (o = 0, f = 0, s = 0; o < u; o++, s += d) f += C[s + a] * S[o]; - p += f * T[l + a] / w[a]; + p += f * T[c + a] / w[a]; } t.data[r] = p; } this.cache.put_buffer(g), this.cache.put_buffer(_), this.cache.put_buffer(v); } svd_invert(e, t) { - let n = 0, r = 0, a = 0, o = 0, s = 0, l = 0, u = t.rows, d = t.cols, f = 0, p = 0, m = t.type | i.C1_t, h = this.cache.get_buffer(u * u << 3), g = this.cache.get_buffer(d << 3), _ = this.cache.get_buffer(d * d << 3), v = new c(u, u, m, h.data), y = new c(1, d, m, g.data), b = new c(d, d, m, _.data), x = e.data, S = v.data, C = y.data, w = b.data; - for (this.svd_decompose(t, y, v, b, 0), p = i.EPSILON * C[0] * d; n < d; n++, s += d) for (r = 0, o = 0; r < u; r++, l++) { + let n = 0, r = 0, a = 0, o = 0, s = 0, c = 0, u = t.rows, d = t.cols, f = 0, p = 0, m = t.type | i.C1_t, h = this.cache.get_buffer(u * u << 3), g = this.cache.get_buffer(d << 3), _ = this.cache.get_buffer(d * d << 3), v = new l(u, u, m, h.data), y = new l(1, d, m, g.data), b = new l(d, d, m, _.data), x = e.data, S = v.data, C = y.data, w = b.data; + for (this.svd_decompose(t, y, v, b, 0), p = i.EPSILON * C[0] * d; n < d; n++, s += d) for (r = 0, o = 0; r < u; r++, c++) { for (a = 0, f = 0; a < d; a++, o++) C[a] > p && (f += w[s + a] * S[o] / C[a]); - x[l] = f; + x[c] = f; } this.cache.put_buffer(h), this.cache.put_buffer(g), this.cache.put_buffer(_); } eigenVV(e, t, n) { - let r = e.cols, a = r * r, o = e.type | i.C1_t, s = this.cache.get_buffer(r * r << 3), l = this.cache.get_buffer(r << 3), u = new c(r, r, o, s.data), d = new c(1, r, o, l.data); + let r = e.cols, a = r * r, o = e.type | i.C1_t, s = this.cache.get_buffer(r * r << 3), c = this.cache.get_buffer(r << 3), u = new l(r, r, o, s.data), d = new l(1, r, o, c.data); for (; --a >= 0;) u.data[a] = e.data[a]; if (this.JacobiImpl(u.data, r, d.data, t ? t.data : null, r, r), n) for (; --r >= 0;) n.data[r] = d.data[r]; - this.cache.put_buffer(s), this.cache.put_buffer(l); + this.cache.put_buffer(s), this.cache.put_buffer(c); } }; //#endregion //#region src/fast_corners/fast_private.ts -function y(e, t, n, r, i) { +function b(e, t, n, r, i) { let a = 0, o = e[t], s = i, c = 0, l = 0, u = 0; for (; a < 25; ++a) r[a] = o - e[t + n[a]]; for (a = 0; a < 16; a += 2) c = Math.min(r[a + 1], r[a + 2]), c = Math.min(c, r[a + 3]), !(c <= s) && (c = Math.min(c, r[a + 4]), c = Math.min(c, r[a + 5]), c = Math.min(c, r[a + 6]), c = Math.min(c, r[a + 7]), c = Math.min(c, r[a + 8]), s = Math.max(s, Math.min(c, r[a])), s = Math.max(s, Math.min(c, r[a + 9]))); @@ -841,7 +843,7 @@ function y(e, t, n, r, i) { } //#endregion //#region src/fast_corners/fast_corners.ts -var b = class extends s { +var x = class extends c { constructor() { super(), this.offsets16 = new Int32Array([ 0, @@ -885,13 +887,13 @@ var b = class extends s { } detect(e, t, n) { n === void 0 && (n = 3); - let r = e.data, i = e.cols, a = e.rows, o = 0, s = 0, c = 0, l = 0, u = 0, d = 0, f = this.cache.get_buffer(3 * i), p = this.cache.get_buffer((i + 1) * 3 << 2), m = f.u8, h = p.i32, g = this.pixel_off, _ = this.score_diff, v = Math.max(3, n), b = Math.min(a - 2, a - n), x = Math.max(3, n), S = Math.min(i - 3, i - n), C = 0, w = 0, T, E = y, D = this.threshold_tab, O = this._threshold, k = 0, A = 0, j = 0, M = 0, N = 0, P = 0, F = 0, I = 0, L = 0, R = 0, z = 0, B = 0; + let r = e.data, i = e.cols, a = e.rows, o = 0, s = 0, c = 0, l = 0, u = 0, d = 0, f = this.cache.get_buffer(3 * i), p = this.cache.get_buffer((i + 1) * 3 << 2), m = f.u8, h = p.i32, g = this.pixel_off, _ = this.score_diff, v = Math.max(3, n), y = Math.min(a - 2, a - n), x = Math.max(3, n), S = Math.min(i - 3, i - n), C = 0, w = 0, T, E = b, D = this.threshold_tab, O = this._threshold, k = 0, A = 0, j = 0, M = 0, N = 0, P = 0, F = 0, I = 0, L = 0, R = 0, z = 0, B = 0; this._cmp_offsets(g, i, 16); let V = g[0], ee = g[1], H = g[2], U = g[3], W = g[4], G = g[5], K = g[6], te = g[7], q = g[8], ne = g[9], J = g[10], Y = g[11], X = g[12], Z = g[13], Q = g[14], re = g[15]; for (o = 0; o < i * 3; ++o) m[o] = 0; - for (o = v; o < b; ++o) { + for (o = v; o < y; ++o) { for (F = o * i + x | 0, d = (o - 3) % 3, P = d * i | 0, N = d * (i + 1) | 0, s = 0; s < i; ++s) m[P + s] = 0; - if (M = 0, o < b - 1) { + if (M = 0, o < y - 1) { for (s = x; s < S; ++s, ++F) if (k = r[F], A = -k + 255, j = D[A + r[F + V]] | D[A + r[F + q]], j != 0 && (j &= D[A + r[F + H]] | D[A + r[F + J]], j &= D[A + r[F + W]] | D[A + r[F + X]], j &= D[A + r[F + K]] | D[A + r[F + Q]], j != 0)) { if (j &= D[A + r[F + ee]] | D[A + r[F + ne]], j &= D[A + r[F + U]] | D[A + r[F + Y]], j &= D[A + r[F + G]] | D[A + r[F + Z]], j &= D[A + r[F + te]] | D[A + r[F + re]], j & 1) for (l = k - O, C = 0, c = 0; c < 25; ++c) if (u = r[F + g[c]], u < l) { if (++C, C > 8) { @@ -916,15 +918,15 @@ var b = class extends s { for (; r < n; ++r) e[r] = i[r << 1] + i[(r << 1) + 1] * t; for (; r < 25; ++r) e[r] = e[r - n]; } -}, x = class extends s { +}, S = class extends c { constructor(e) { super(), this.levels = e | 0, this.data = Array(e); - let t = new m(); + let t = new h(); this.pyrdown = t.pyrdown; } allocate(e, t, n) { let r = this.levels; - for (; --r >= 0;) this.data[r] = new c(e >> r, t >> r, n); + for (; --r >= 0;) this.data[r] = new l(e >> r, t >> r, n); } build(e, t) { t === void 0 && (t = !0); @@ -935,7 +937,7 @@ var b = class extends s { } for (i = this.data[1], this.pyrdown(r, i); n < this.levels; ++n) r = i, i = this.data[n], this.pyrdown(r, i); } -}, S = class { +}, C = class { constructor() {} perspective_4point_transform(e, t, n, r, i, a, o, s, c, l, u, d, f, p, m, h, g) { let _ = t, v = l, y = o, b = _ * v * y, x = m, S = _ * x, C = v * S, w = u, T = _ * w, E = a, D = n, O = p, k = D * O, A = k * E, j = O * E * w, M = O * y, N = O * w, P = v * y, F = x * v, I = x * E, L = w * E, R = 1 / (M - N - P + F - I + L), z = _ * O, B = D * E, V = y * _, ee = x * V, H = D * v, U = k * w, W = D * w * E, G = y * x * v, K = x * D, te = -(C - b + T * E - E * S - k * v + A - j + M * v) * R, q = (b - C - z * y + z * w + A - v * B + I * v - j) * R, ne = _, J = (-w * S + ee + H * y - k * y + U - W + I * w - G) * R, Y = (-ee + V * w - K * v + U - W + K * E + G - M * w) * R, X = D, Z = (-T + V + H - B + N - M - F + I) * R, Q = (-S + T + k - H + I - L - M + P) * R; @@ -961,11 +963,11 @@ var b = class extends s { let n = e.data, r = t.data, i = n[0], a = n[1], o = n[2], s = n[3], c = n[4], l = n[5], u = n[6], d = n[7], f = n[8], p = 1 / (i * (c * f - l * d) - a * (s * f - l * u) + o * (s * d - c * u)); r[0] = p * (c * f - l * d), r[1] = p * (o * d - a * f), r[2] = p * (a * l - o * c), r[3] = p * (l * u - s * f), r[4] = p * (i * f - o * u), r[5] = p * (o * s - i * l), r[6] = p * (s * d - c * u), r[7] = p * (a * u - i * d), r[8] = p * (i * c - a * s); } -}, C = class { +}, w = class { constructor(e = 0, t = 0, n = 0, r = 0, i = -1) { this.x = e, this.y = t, this.score = n, this.level = r, this.angle = i; } -}, w = [ +}, T = [ 8, -3, 9, @@ -1993,29 +1995,29 @@ var b = class extends s { ]; //#endregion //#region src/orb/rectify_patch.ts -function T(e, t, n, r, i, a, o, s) { +function E(e, t, n, r, i, a, o, s) { let c = Math.cos(n), l = Math.sin(n); o.data[0] = c, o.data[1] = -l, o.data[2] = (-c + l) * a * .5 + r, o.data[3] = l, o.data[4] = c, o.data[5] = (-l - c) * a * .5 + i, s.warp_affine(e, t, o, 128); } //#endregion //#region src/orb/orb.ts -var E = class extends s { +var D = class extends c { constructor() { - super(), this.bit_pattern_31_ = new Int32Array(w), this.H = new c(3, 3, i.F32_t | i.C1_t), this.patch_img = new c(32, 32, i.U8_t | i.C1_t), this.imgproc = new m(); + super(), this.bit_pattern_31_ = new Int32Array(T), this.H = new l(3, 3, i.F32_t | i.C1_t), this.patch_img = new l(32, 32, i.U8_t | i.C1_t), this.imgproc = new h(); } describe(e, t, n, r) { let a = 0, o = 0, s = 0, c = 0, l = 0, u = 0, d = 0, f = 0, p = this.patch_img.data, m = 0; r.type & i.U8_t ? r.resize(32, n, 1) : (r.type = i.U8_t, r.cols = 32, r.rows = n, r.channel = 1, r.allocate()); let h = r.data, g = 0; for (a = 0; a < n; ++a) { - for (s = t[a].x, c = t[a].y, l = t[a].angle, T(e, this.patch_img, l, s, c, 32, this.H, this.imgproc), m = 0, o = 0; o < 32; ++o) u = p[528 + this.bit_pattern_31_[m + 1] * 32 + this.bit_pattern_31_[m]], m += 2, d = p[528 + this.bit_pattern_31_[m + 1] * 32 + this.bit_pattern_31_[m]], m += 2, f = u < d | 0, u = p[528 + this.bit_pattern_31_[m + 1] * 32 + this.bit_pattern_31_[m]], m += 2, d = p[528 + this.bit_pattern_31_[m + 1] * 32 + this.bit_pattern_31_[m]], m += 2, f |= (u < d) << 1, u = p[528 + this.bit_pattern_31_[m + 1] * 32 + this.bit_pattern_31_[m]], m += 2, d = p[528 + this.bit_pattern_31_[m + 1] * 32 + this.bit_pattern_31_[m]], m += 2, f |= (u < d) << 2, u = p[528 + this.bit_pattern_31_[m + 1] * 32 + this.bit_pattern_31_[m]], m += 2, d = p[528 + this.bit_pattern_31_[m + 1] * 32 + this.bit_pattern_31_[m]], m += 2, f |= (u < d) << 3, u = p[528 + this.bit_pattern_31_[m + 1] * 32 + this.bit_pattern_31_[m]], m += 2, d = p[528 + this.bit_pattern_31_[m + 1] * 32 + this.bit_pattern_31_[m]], m += 2, f |= (u < d) << 4, u = p[528 + this.bit_pattern_31_[m + 1] * 32 + this.bit_pattern_31_[m]], m += 2, d = p[528 + this.bit_pattern_31_[m + 1] * 32 + this.bit_pattern_31_[m]], m += 2, f |= (u < d) << 5, u = p[528 + this.bit_pattern_31_[m + 1] * 32 + this.bit_pattern_31_[m]], m += 2, d = p[528 + this.bit_pattern_31_[m + 1] * 32 + this.bit_pattern_31_[m]], m += 2, f |= (u < d) << 6, u = p[528 + this.bit_pattern_31_[m + 1] * 32 + this.bit_pattern_31_[m]], m += 2, d = p[528 + this.bit_pattern_31_[m + 1] * 32 + this.bit_pattern_31_[m]], m += 2, f |= (u < d) << 7, h[g + o] = f; + for (s = t[a].x, c = t[a].y, l = t[a].angle, E(e, this.patch_img, l, s, c, 32, this.H, this.imgproc), m = 0, o = 0; o < 32; ++o) u = p[528 + this.bit_pattern_31_[m + 1] * 32 + this.bit_pattern_31_[m]], m += 2, d = p[528 + this.bit_pattern_31_[m + 1] * 32 + this.bit_pattern_31_[m]], m += 2, f = u < d | 0, u = p[528 + this.bit_pattern_31_[m + 1] * 32 + this.bit_pattern_31_[m]], m += 2, d = p[528 + this.bit_pattern_31_[m + 1] * 32 + this.bit_pattern_31_[m]], m += 2, f |= (u < d) << 1, u = p[528 + this.bit_pattern_31_[m + 1] * 32 + this.bit_pattern_31_[m]], m += 2, d = p[528 + this.bit_pattern_31_[m + 1] * 32 + this.bit_pattern_31_[m]], m += 2, f |= (u < d) << 2, u = p[528 + this.bit_pattern_31_[m + 1] * 32 + this.bit_pattern_31_[m]], m += 2, d = p[528 + this.bit_pattern_31_[m + 1] * 32 + this.bit_pattern_31_[m]], m += 2, f |= (u < d) << 3, u = p[528 + this.bit_pattern_31_[m + 1] * 32 + this.bit_pattern_31_[m]], m += 2, d = p[528 + this.bit_pattern_31_[m + 1] * 32 + this.bit_pattern_31_[m]], m += 2, f |= (u < d) << 4, u = p[528 + this.bit_pattern_31_[m + 1] * 32 + this.bit_pattern_31_[m]], m += 2, d = p[528 + this.bit_pattern_31_[m + 1] * 32 + this.bit_pattern_31_[m]], m += 2, f |= (u < d) << 5, u = p[528 + this.bit_pattern_31_[m + 1] * 32 + this.bit_pattern_31_[m]], m += 2, d = p[528 + this.bit_pattern_31_[m + 1] * 32 + this.bit_pattern_31_[m]], m += 2, f |= (u < d) << 6, u = p[528 + this.bit_pattern_31_[m + 1] * 32 + this.bit_pattern_31_[m]], m += 2, d = p[528 + this.bit_pattern_31_[m + 1] * 32 + this.bit_pattern_31_[m]], m += 2, f |= (u < d) << 7, h[g + o] = f; g += 32; } } }; //#endregion //#region src/yape/yape_utils.ts -function D(e, t, n) { +function O(e, t, n) { let r = 0, i, a; for (i = n, a = 0; a < i; a++, r++) i = Math.sqrt(n * n - a * a) + .5 | 0, t[r] = i + e * a; for (i--; i < a && i >= 0; i--, r++) a = Math.sqrt(n * n - i * i) + .5 | 0, t[r] = i + e * a; @@ -2027,11 +2029,11 @@ function D(e, t, n) { for (a++; a < 0; a++, r++) i = Math.sqrt(n * n - a * a) + .5 | 0, t[r] = i + e * a; return t[r] = t[0], t[r + 1] = t[1], r; } -function O(e, t, n) { +function k(e, t, n) { let r = 0; return e[t + 1] != 0 && r++, e[t - 1] != 0 && r++, e[t + n] != 0 && r++, e[t + n + 1] != 0 && r++, e[t + n - 1] != 0 && r++, e[t - n] != 0 && r++, e[t - n + 1] != 0 && r++, e[t - n - 1] != 0 && r++, r; } -function k(e, t, n, r, i) { +function A(e, t, n, r, i) { let a, o; if (n > 0) for (t -= r * i, o = -i; o <= i; ++o) { for (a = -i; a <= i; ++a) if (e[t + a] > n) return !1; @@ -2043,7 +2045,7 @@ function k(e, t, n, r, i) { } return !0; } -function A(e, t, n, r, i, a, o, s) { +function j(e, t, n, r, i, a, o, s) { let c = 0, l = 0, u = o - 1 | 0, d = 0, f = 0, p = 0, m = 0, h = 0; if (d = e[t + a[l]], d <= i) if (d >= r) if (f = e[t + a[u]], f <= i) if (f >= r) { n[t] = 0; @@ -2381,38 +2383,38 @@ function A(e, t, n, r, i, a, o, s) { } n[t] = c + s * e[t]; } -var j = class { +var M = class { constructor(e, t, n) { - this.dirs = /* @__PURE__ */ new Int32Array(1024), this.dirs_count = D(e, this.dirs, n) | 0, this.scores = new Int32Array(e * t), this.radius = n | 0; + this.dirs = /* @__PURE__ */ new Int32Array(1024), this.dirs_count = O(e, this.dirs, n) | 0, this.scores = new Int32Array(e * t), this.radius = n | 0; } -}, M = class { +}, N = class { constructor() { this.level_tables = [], this.tau = 7; } init(e, t, n, r = 1) { n = Math.min(n, 7), n = Math.max(n, 3); - for (let i = 0; i < r; ++i) this.level_tables[i] = new j(e >> i, t >> i, n); + for (let i = 0; i < r; ++i) this.level_tables[i] = new M(e >> i, t >> i, n); } detect(e, t, n = 4) { - let r = this.level_tables[0], i = r.radius | 0, a = i - 1 | 0, o = r.dirs, s = r.dirs_count | 0, c = s >> 1, l = e.data, u = e.cols | 0, d = e.rows | 0, f = u >> 1, p = r.scores, m = 0, h = 0, g = 0, _ = 0, v = 0, y = 0, b = 0, x = 0, S = this.tau | 0, C = 0, w, T = Math.max(i + 1, n) | 0, E = Math.max(i + 1, n) | 0, D = Math.min(u - i - 2, u - n) | 0, j = Math.min(d - i - 2, d - n) | 0; - for (g = E * u + T | 0, h = E; h < j; ++h, g += u) for (m = T, _ = g; m < D; ++m, ++_) v = l[_] + S, y = l[_] - S, y < l[_ + i] && l[_ + i] < v && y < l[_ - i] && l[_ - i] < v ? p[_] = 0 : A(l, _, p, y, v, o, c, s); - for (g = E * u + T | 0, h = E; h < j; ++h, g += u) for (m = T, _ = g; m < D; ++m, ++_) x = p[_], b = Math.abs(x), b < 5 ? (++m, ++_) : O(p, _, u) >= 3 && k(p, _, x, f, i) && (w = t[C], w.x = m, w.y = h, w.score = b, ++C, m += a, _ += a); + let r = this.level_tables[0], i = r.radius | 0, a = i - 1 | 0, o = r.dirs, s = r.dirs_count | 0, c = s >> 1, l = e.data, u = e.cols | 0, d = e.rows | 0, f = u >> 1, p = r.scores, m = 0, h = 0, g = 0, _ = 0, v = 0, y = 0, b = 0, x = 0, S = this.tau | 0, C = 0, w, T = Math.max(i + 1, n) | 0, E = Math.max(i + 1, n) | 0, D = Math.min(u - i - 2, u - n) | 0, O = Math.min(d - i - 2, d - n) | 0; + for (g = E * u + T | 0, h = E; h < O; ++h, g += u) for (m = T, _ = g; m < D; ++m, ++_) v = l[_] + S, y = l[_] - S, y < l[_ + i] && l[_ + i] < v && y < l[_ - i] && l[_ - i] < v ? p[_] = 0 : j(l, _, p, y, v, o, c, s); + for (g = E * u + T | 0, h = E; h < O; ++h, g += u) for (m = T, _ = g; m < D; ++m, ++_) x = p[_], b = Math.abs(x), b < 5 ? (++m, ++_) : k(p, _, u) >= 3 && A(p, _, x, f, i) && (w = t[C], w.x = m, w.y = h, w.score = b, ++C, m += a, _ += a); return C; } }; //#endregion //#region src/yape06/yape06_utils.ts -function N(e, t, n, r, i, a, o, s, c) { +function P(e, t, n, r, i, a, o, s, c) { let l = 0, u = 0, d = o * n + a | 0, f = d; for (l = o; l < c; ++l, d += n, f = d) for (u = a; u < s; ++u, ++f) f + r < e.length && f - r >= 0 && f + i < e.length && f - i >= 0 ? t[f] = -4 * e[f] + e[f + r] + e[f - r] + e[f + i] + e[f - i] : t[f] = 0; } -function P(e, t, n, r, i, a, o) { +function F(e, t, n, r, i, a, o) { let s = -2 * e[t] + e[t + r] + e[t - r], c = -2 * e[t] + e[t + i] + e[t - i], l = e[t + a] + e[t - a] - e[t + o] - e[t - o], u = Math.sqrt((s - c) * (s - c) + 4 * l * l) | 0; return Math.min(Math.abs(n - u), Math.abs(-(n + u))); } //#endregion //#region src/yape06/yape06.ts -var F = class extends s { +var I = class extends c { constructor() { super(), this.laplacian_threshold = 30, this.min_eigen_value_threshold = 25; } @@ -2420,10 +2422,10 @@ var F = class extends s { n === void 0 && (n = 5); let r = 0, i = 0, a = e.cols, o = e.rows, s = e.data, c = 5 * a | 0, l = 3 + 3 * a | 0, u = 3 - 3 * a | 0, d = this.cache.get_buffer(a * o << 2), f = d.i32, p = 0, m = 0, h = 0, g = 0, _, v = 0, y = this.laplacian_threshold, b = this.min_eigen_value_threshold, x = Math.max(5, n) | 0, S = Math.max(3, n) | 0, C = Math.min(a - 5, a - n) | 0, w = Math.min(o - 3, o - n) | 0; for (r = a * o; --r >= 0;) f[r] = 0; - for (N(s, f, a, 5, c, x, S, C, w), m = S * a + x | 0, i = S; i < w; ++i, m += a) for (r = x, h = m; r < C; ++r, ++h) p = f[h], (p < -y && p < f[h - 1] && p < f[h + 1] && p < f[h - a] && p < f[h + a] && p < f[h - a - 1] && p < f[h + a - 1] && p < f[h - a + 1] && p < f[h + a + 1] || p > y && p > f[h - 1] && p > f[h + 1] && p > f[h - a] && p > f[h + a] && p > f[h - a - 1] && p > f[h + a - 1] && p > f[h - a + 1] && p > f[h + a + 1]) && (g = P(s, h, p, 5, c, l, u), g > b && (_ = t[v], _.x = r, _.y = i, _.score = g, ++v, ++r, ++h)); + for (P(s, f, a, 5, c, x, S, C, w), m = S * a + x | 0, i = S; i < w; ++i, m += a) for (r = x, h = m; r < C; ++r, ++h) p = f[h], (p < -y && p < f[h - 1] && p < f[h + 1] && p < f[h - a] && p < f[h + a] && p < f[h - a - 1] && p < f[h + a - 1] && p < f[h - a + 1] && p < f[h + a + 1] || p > y && p > f[h - 1] && p > f[h + 1] && p > f[h - a] && p > f[h + a] && p > f[h - a - 1] && p > f[h + a - 1] && p > f[h - a + 1] && p > f[h + a + 1]) && (g = F(s, h, p, 5, c, l, u), g > b && (_ = t[v], _.x = r, _.y = i, _.score = g, ++v, ++r, ++h)); return this.cache.put_buffer(d), v; } -}, I = class { +}, L = class { constructor(e = 0, t = .5, n = .5, r = .99) { this.size = e, this.thresh = t, this.eps = n, this.prob = r; } @@ -2431,7 +2433,7 @@ var F = class extends s { let n = Math.log(1 - this.prob), r = Math.log(1 - Math.pow(1 - e, this.size)); return (r >= 0 || -n >= t * -r ? t : Math.round(n / r)) | 0; } -}, L = class extends s { +}, R = class extends c { constructor() { super(); } @@ -2458,9 +2460,9 @@ var F = class extends s { for (e.error(n, r, t, o, i); l < i; ++l) u = o[l] <= d, s[l] = u, c += u; return c; } - ransac(e, t, n, r, a, o, s, l) { - if (l === void 0 && (l = 1e3), a < e.size) return !1; - let u = e.size, d = l, f = 0, p = !1, m = [], h = [], g = !1, _ = o.cols, v = o.rows, y = o.type | i.C1_t, b = this.cache.get_buffer(_ * v << 3), x = this.cache.get_buffer(a), S = this.cache.get_buffer(a << 2), C = new c(_, v, y, b.data), w = new c(a, 1, i.U8C1_t, x.data), T = -1, E = 0, D = 0, O = S.f32; + ransac(e, t, n, r, a, o, s, c) { + if (c === void 0 && (c = 1e3), a < e.size) return !1; + let u = e.size, d = c, f = 0, p = !1, m = [], h = [], g = !1, _ = o.cols, v = o.rows, y = o.type | i.C1_t, b = this.cache.get_buffer(_ * v << 3), x = this.cache.get_buffer(a), S = this.cache.get_buffer(a << 2), C = new l(_, v, y, b.data), w = new l(a, 1, i.U8C1_t, x.data), T = -1, E = 0, D = 0, O = S.f32; if (a == u) { if (t.run(n, r, C, a) <= 0) return this.cache.put_buffer(b), this.cache.put_buffer(x), this.cache.put_buffer(S), !1; if (C.copy_to(o), s) for (; --a >= 0;) s.data[a] = 1; @@ -2475,9 +2477,9 @@ var F = class extends s { } return this.cache.put_buffer(b), this.cache.put_buffer(x), this.cache.put_buffer(S), p; } - lmeds(e, t, n, r, a, o, s, l) { - if (l === void 0 && (l = 1e3), a < e.size) return !1; - let u = e.size, d = l, f = 0, m = !1, h = new p(), g = [], _ = [], v = !1, y = o.cols, b = o.rows, x = o.type | i.C1_t, S = this.cache.get_buffer(y * b << 3), C = this.cache.get_buffer(a), w = this.cache.get_buffer(a << 2), T = new c(y, b, x, S.data), E = new c(a, 1, i.U8_t | i.C1_t, C.data), D = 0, O = 0, k = w.f32, A = 1e9, j = 0, M = 0; + lmeds(e, t, n, r, a, o, s, c) { + if (c === void 0 && (c = 1e3), a < e.size) return !1; + let u = e.size, d = c, f = 0, p = !1, h = new m(), g = [], _ = [], v = !1, y = o.cols, b = o.rows, x = o.type | i.C1_t, S = this.cache.get_buffer(y * b << 3), C = this.cache.get_buffer(a), w = this.cache.get_buffer(a << 2), T = new l(y, b, x, S.data), E = new l(a, 1, i.U8_t | i.C1_t, C.data), D = 0, O = 0, k = w.f32, A = 1e9, j = 0, M = 0; if (e.eps = .45, d = e.update_iters(e.eps, d), a == u) { if (t.run(n, r, T, a) <= 0) return this.cache.put_buffer(S), this.cache.put_buffer(C), this.cache.put_buffer(w), !1; if (T.copy_to(o), s) for (; --a >= 0;) s.data[a] = 1; @@ -2488,13 +2490,13 @@ var F = class extends s { if (f == 0) return this.cache.put_buffer(S), this.cache.put_buffer(C), this.cache.put_buffer(w), !1; break; } - O = t.run(g, _, T, u), !(O <= 0) && (t.error(n, r, T, k, a), M = h.median(k, 0, a - 1), M < A && (A = M, T.copy_to(o), m = !0)); + O = t.run(g, _, T, u), !(O <= 0) && (t.error(n, r, T, k, a), M = h.median(k, 0, a - 1), M < A && (A = M, T.copy_to(o), p = !0)); } - return m && (j = 2.5 * 1.4826 * (1 + 5 / (a - u)) * Math.sqrt(A), j = Math.max(j, .001), D = this.find_inliers(t, o, n, r, a, j, k, E.data), s && E.copy_to(s), m = D >= u), this.cache.put_buffer(S), this.cache.put_buffer(C), this.cache.put_buffer(w), m; + return p && (j = 2.5 * 1.4826 * (1 + 5 / (a - u)) * Math.sqrt(A), j = Math.max(j, .001), D = this.find_inliers(t, o, n, r, a, j, k, E.data), s && E.copy_to(s), p = D >= u), this.cache.put_buffer(S), this.cache.put_buffer(C), this.cache.put_buffer(w), p; } -}, R = class extends s { +}, z = class extends c { constructor() { - super(), this.T0 = new c(3, 3, i.F32_t | i.C1_t), this.T1 = new c(3, 3, i.F32_t | i.C1_t), this.AtA = new c(6, 6, i.F32_t | i.C1_t), this.AtB = new c(6, 1, i.F32_t | i.C1_t); + super(), this.T0 = new l(3, 3, i.F32_t | i.C1_t), this.T1 = new l(3, 3, i.F32_t | i.C1_t), this.AtA = new l(6, 6, i.F32_t | i.C1_t), this.AtB = new l(6, 1, i.F32_t | i.C1_t); } sqr(e) { return e * e; @@ -2510,16 +2512,16 @@ var F = class extends s { for (; n < a; ++n) for (o = e[n].x - e[a].x, s = e[n].y - e[a].y, r = 0; r < n; ++r) if (c = e[r].x - e[a].x, l = e[r].y - e[a].y, Math.abs(c * s - l * o) <= i.EPSILON * (Math.abs(o) + Math.abs(s) + Math.abs(c) + Math.abs(l))) return !0; return !1; } -}, z = class extends R { +}, B = class extends z { constructor() { super(); } run(e, t, n, r) { - let a = 0, o = 0, s = n.type | i.C1_t, l = n.data, u = this.T0.data, d = this.T1.data, f, p, m = 0, h = 0, g = new _(), y = new v(); + let a = 0, o = 0, s = n.type | i.C1_t, c = n.data, u = this.T0.data, d = this.T1.data, f, p, m = 0, h = 0, g = new v(), _ = new y(); this.iso_normalize_points(e, t, u, d, r); - let b = this.cache.get_buffer(2 * r * 6 << 3), x = this.cache.get_buffer(2 * r << 3), S = new c(6, 2 * r, s, b.data), C = new c(1, 2 * r, s, x.data), w = S.data, T = C.data; + let b = this.cache.get_buffer(2 * r * 6 << 3), x = this.cache.get_buffer(2 * r << 3), S = new l(6, 2 * r, s, b.data), C = new l(1, 2 * r, s, x.data), w = S.data, T = C.data; for (; a < r; ++a) f = e[a], p = t[a], m = u[0] * f.x + u[1] * f.y + u[2], h = u[3] * f.x + u[4] * f.y + u[5], o = a * 2 * 6, w[o] = m, w[o + 1] = h, w[o + 2] = 1, w[o + 3] = 0, w[o + 4] = 0, w[o + 5] = 0, o += 6, w[o] = 0, w[o + 1] = 0, w[o + 2] = 0, w[o + 3] = m, w[o + 4] = h, w[o + 5] = 1, T[a << 1] = d[0] * p.x + d[1] * p.y + d[2], T[(a << 1) + 1] = d[3] * p.x + d[4] * p.y + d[5]; - return g.multiply_AtA(this.AtA, S), g.multiply_AtB(this.AtB, S, C), y.lu_solve(this.AtA, this.AtB), l[0] = this.AtB.data[0], l[1] = this.AtB.data[1], l[2] = this.AtB.data[2], l[3] = this.AtB.data[3], l[4] = this.AtB.data[4], l[5] = this.AtB.data[5], l[6] = 0, l[7] = 0, l[8] = 1, g.invert_3x3(this.T1, this.T1), g.multiply_3x3(n, this.T1, n), g.multiply_3x3(n, n, this.T0), this.cache.put_buffer(b), this.cache.put_buffer(x), 1; + return g.multiply_AtA(this.AtA, S), g.multiply_AtB(this.AtB, S, C), _.lu_solve(this.AtA, this.AtB), c[0] = this.AtB.data[0], c[1] = this.AtB.data[1], c[2] = this.AtB.data[2], c[3] = this.AtB.data[3], c[4] = this.AtB.data[4], c[5] = this.AtB.data[5], c[6] = 0, c[7] = 0, c[8] = 1, g.invert_3x3(this.T1, this.T1), g.multiply_3x3(n, this.T1, n), g.multiply_3x3(n, n, this.T0), this.cache.put_buffer(b), this.cache.put_buffer(x), 1; } error(e, t, n, r, i) { let a = 0, o, s, c = n.data; @@ -2528,56 +2530,56 @@ var F = class extends s { check_subset(e, t, n) { return !0; } -}, B = class extends R { +}, V = class extends z { constructor() { - super(), this.mLtL = new c(9, 9, i.F32_t | i.C1_t), this.Evec = new c(9, 9, i.F32_t | i.C1_t); + super(), this.mLtL = new l(9, 9, i.F32_t | i.C1_t), this.Evec = new l(9, 9, i.F32_t | i.C1_t); } run(e, t, n, r) { - let a = 0, o = 0, s = n.data, c = this.T0.data, l = this.T1.data, u = this.mLtL.data, d = this.Evec.data, f = 0, p = 0, m = 0, h = 0, g = new v(), y = new _(), b = 0, x = 0, S = 0, C = 0, w = 0, T = 0, E = 0, D = 0; + let a = 0, o = 0, s = n.data, c = this.T0.data, l = this.T1.data, u = this.mLtL.data, d = this.Evec.data, f = 0, p = 0, m = 0, h = 0, g = new y(), _ = new v(), b = 0, x = 0, S = 0, C = 0, w = 0, T = 0, E = 0, D = 0; for (; a < r; ++a) S += t[a].x, C += t[a].y, E += e[a].x, D += e[a].y; for (S /= r, C /= r, E /= r, D /= r, a = 0; a < r; ++a) b += Math.abs(t[a].x - S), x += Math.abs(t[a].y - C), w += Math.abs(e[a].x - E), T += Math.abs(e[a].y - D); if (Math.abs(b) < i.EPSILON || Math.abs(x) < i.EPSILON || Math.abs(w) < i.EPSILON || Math.abs(T) < i.EPSILON) return 0; for (b = r / b, x = r / x, w = r / w, T = r / T, c[0] = w, c[1] = 0, c[2] = -E * w, c[3] = 0, c[4] = T, c[5] = -D * T, c[6] = 0, c[7] = 0, c[8] = 1, l[0] = 1 / b, l[1] = 0, l[2] = S, l[3] = 0, l[4] = 1 / x, l[5] = C, l[6] = 0, l[7] = 0, l[8] = 1, a = 81; --a >= 0;) u[a] = 0; for (a = 0; a < r; ++a) f = (t[a].x - S) * b, p = (t[a].y - C) * x, m = (e[a].x - E) * w, h = (e[a].y - D) * T, u[0] += m * m, u[1] += m * h, u[2] += m, u[6] += m * -f * m, u[7] += m * -f * h, u[8] += m * -f, u[10] += h * h, u[11] += h, u[15] += h * -f * m, u[16] += h * -f * h, u[17] += h * -f, u[20] += 1, u[24] += -f * m, u[25] += -f * h, u[26] += -f, u[30] += m * m, u[31] += m * h, u[32] += m, u[33] += m * -p * m, u[34] += m * -p * h, u[35] += m * -p, u[40] += h * h, u[41] += h, u[42] += h * -p * m, u[43] += h * -p * h, u[44] += h * -p, u[50] += 1, u[51] += -p * m, u[52] += -p * h, u[53] += -p, u[60] += -f * m * -f * m + -p * m * -p * m, u[61] += -f * m * -f * h + -p * m * -p * h, u[62] += -f * m * -f + -p * m * -p, u[70] += -f * h * -f * h + -p * h * -p * h, u[71] += -f * h * -f + -p * h * -p, u[80] += -f * -f + -p * -p; for (a = 0; a < 9; ++a) for (o = 0; o < a; ++o) u[a * 9 + o] = u[o * 9 + a]; - return g.eigenVV(this.mLtL, this.Evec), s[0] = d[72], s[1] = d[73], s[2] = d[74], s[3] = d[75], s[4] = d[76], s[5] = d[77], s[6] = d[78], s[7] = d[79], s[8] = d[80], y.multiply_3x3(n, this.T1, n), y.multiply_3x3(n, n, this.T0), f = 1 / s[8], s[0] *= f, s[1] *= f, s[2] *= f, s[3] *= f, s[4] *= f, s[5] *= f, s[6] *= f, s[7] *= f, s[8] = 1, 1; + return g.eigenVV(this.mLtL, this.Evec), s[0] = d[72], s[1] = d[73], s[2] = d[74], s[3] = d[75], s[4] = d[76], s[5] = d[77], s[6] = d[78], s[7] = d[79], s[8] = d[80], _.multiply_3x3(n, this.T1, n), _.multiply_3x3(n, n, this.T0), f = 1 / s[8], s[0] *= f, s[1] *= f, s[2] *= f, s[3] *= f, s[4] *= f, s[5] *= f, s[6] *= f, s[7] *= f, s[8] = 1, 1; } error(e, t, n, r, i) { let a = 0, o, s, c = 0, l = 0, u = 0, d = n.data; for (; a < i; ++a) o = e[a], s = t[a], c = 1 / (d[6] * o.x + d[7] * o.y + 1), l = (d[0] * o.x + d[1] * o.y + d[2]) * c - s.x, u = (d[3] * o.x + d[4] * o.y + d[5]) * c - s.y, r[a] = l * l + u * u; } check_subset(e, t, n) { - let r = new _(); + let r = new v(); if (n == 4) { let n = 0, i = e[0], a = e[1], o = e[2], s = e[3], c = t[0], l = t[1], u = t[2], d = t[3], f = i.x, p = i.y, m = a.x, h = a.y, g = o.x, _ = o.y, v = c.x, y = c.y, b = l.x, x = l.y, S = u.x, C = u.y, w = r.determinant_3x3(f, p, 1, m, h, 1, g, _, 1), T = r.determinant_3x3(v, y, 1, b, x, 1, S, C, 1); if (w * T < 0 && n++, f = a.x, p = a.y, m = o.x, h = o.y, g = s.x, _ = s.y, v = l.x, y = l.y, b = u.x, x = u.y, S = d.x, C = d.y, w = r.determinant_3x3(f, p, 1, m, h, 1, g, _, 1), T = r.determinant_3x3(v, y, 1, b, x, 1, S, C, 1), w * T < 0 && n++, f = i.x, p = i.y, m = o.x, h = o.y, g = s.x, _ = s.y, v = c.x, y = c.y, b = u.x, x = u.y, S = d.x, C = d.y, w = r.determinant_3x3(f, p, 1, m, h, 1, g, _, 1), T = r.determinant_3x3(v, y, 1, b, x, 1, S, C, 1), w * T < 0 && n++, f = i.x, p = i.y, m = a.x, h = a.y, g = s.x, _ = s.y, v = c.x, y = c.y, b = l.x, x = l.y, S = d.x, C = d.y, w = r.determinant_3x3(f, p, 1, m, h, 1, g, _, 1), T = r.determinant_3x3(v, y, 1, b, x, 1, S, C, 1), w * T < 0 && n++, n != 0 && n != 4) return !1; } return !0; } -}, V = class extends s { +}, ee = class extends c { constructor() { super(); - let e = new m(); + let e = new h(); this.scharr_deriv = e.scharr_derivatives; } - track(e, t, n, r, a, o, s, l, u, d) { - s === void 0 && (s = 30), l === void 0 && (l = new Uint8Array(a)), u === void 0 && (u = .01), d === void 0 && (d = 1e-4); - let f = (o - 1) * .5, p = o * o | 0, m = p << 1, h = e.data, g = t.data, _ = h[0].data, v = g[0].data, y = h[0].cols, b = h[0].rows, x = 0, S = 0, C = this.cache.get_buffer(p << 2), w = this.cache.get_buffer(m << 2), T = this.cache.get_buffer(b * (y << 1) << 2), E = new c(y, b, i.S32C2_t, T.data), D = C.i32, O = w.i32, k = T.i32, A = 0, j = 0, M = 0, N = 0, P = 0, F = 0, I = 0, L = 0, R = 0, z = 0, B = 0, V = 0, ee = 0, H = 0, U = 0, W = 0, G = 0, K = 0, te = 0, q = 0, ne = 0, J = 0, Y = 0, X = 0, Z = 0, Q = 0, re = 0, ie = 0, ae = 0, oe = 0, se = 0, ce = 0, le = 16384, ue = 8192, de = 1 / (1 << 20), fe = 0, pe = 0, me = 0, he = 0, ge = 0, _e = 0, $ = 0, ve = 0, ye = 0, be = 0, xe = 0, Se = 0; - for (u *= u; q < a; ++q) l[q] = 1; + track(e, t, n, r, a, o, s, c, u, d) { + s === void 0 && (s = 30), c === void 0 && (c = new Uint8Array(a)), u === void 0 && (u = .01), d === void 0 && (d = 1e-4); + let f = (o - 1) * .5, p = o * o | 0, m = p << 1, h = e.data, g = t.data, _ = h[0].data, v = g[0].data, y = h[0].cols, b = h[0].rows, x = 0, S = 0, C = this.cache.get_buffer(p << 2), w = this.cache.get_buffer(m << 2), T = this.cache.get_buffer(b * (y << 1) << 2), E = new l(y, b, i.S32C2_t, T.data), D = C.i32, O = w.i32, k = T.i32, A = 0, j = 0, M = 0, N = 0, P = 0, F = 0, I = 0, L = 0, R = 0, z = 0, B = 0, V = 0, ee = 0, H = 0, U = 0, W = 0, G = 0, K = 0, te = 0, q = 0, ne = 0, J = 0, Y = 0, X = 0, Z = 0, Q = 0, re = 0, ie = 0, ae = 0, oe = 0, se = 0, ce = 0, le = 16384, ue = 8192, de = 1 / (1 << 20), fe = 0, pe = 0, me = 0, he = 0, ge = 0, _e = 0, $ = 0, ve = 0, ye = 0, be = 0, xe = 0, Se = 0; + for (u *= u; q < a; ++q) c[q] = 1; let Ce = e.levels - 1 | 0; for (X = Ce; X >= 0; --X) for (I = 1 / (1 << X), x = y >> X, S = b >> X, A = x << 1, _ = h[X].data, v = g[X].data, re = x - o | 0, ie = S - o | 0, this.scharr_deriv(h[X], E), Z = 0; Z < a; ++Z) { if (q = Z << 1, ne = q + 1, L = n[q] * I, R = n[ne] * I, X == Ce ? (z = L, B = R) : (z = r[q] * 2, B = r[ne] * 2), r[q] = z, r[ne] = B, L -= f, R -= f, W = L | 0, G = R | 0, J = W <= 0 || W >= re || G <= 0 || G >= ie, J != 0) { - X == 0 && (l[Z] = 0); + X == 0 && (c[Z] = 0); continue; } for (ae = L - W, oe = R - G, fe = (1 - ae) * (1 - oe) * le + .5 | 0, pe = ae * (1 - oe) * le + .5 | 0, me = (1 - ae) * oe * le + .5 | 0, he = le - fe - pe - me, ve = 0, ye = 0, be = 0, Y = 0; Y < o; ++Y) for (j = (Y + G) * x + W | 0, M = j << 1, N = Y * o | 0, P = N << 1, J = 0; J < o; ++J, ++j, ++N, M += 2) ge = _[j] * fe + _[j + 1] * pe + _[j + x] * me + _[j + x + 1] * he, ge = ge + 256 >> 9, _e = k[M] * fe + k[M + 2] * pe + k[M + A] * me + k[M + A + 2] * he, _e = _e + ue >> 14, $ = k[M + 1] * fe + k[M + 3] * pe + k[M + A + 1] * me + k[M + A + 3] * he, $ = $ + ue >> 14, D[N] = ge, O[P++] = _e, O[P++] = $, ve += _e * _e, ye += _e * $, be += $ * $; if (ve *= de, ye *= de, be *= de, xe = ve * be - ye * ye, Se = (be + ve - Math.sqrt((ve - be) * (ve - be) + 4 * ye * ye)) / m, Se < d || xe < 1.1920929e-7) { - X == 0 && (l[Z] = 0); + X == 0 && (c[Z] = 0); continue; } for (xe = 1 / xe, z -= f, B -= f, V = 0, ee = 0, Q = 0; Q < s; ++Q) { if (K = z | 0, te = B | 0, J = K <= 0 || K >= re || te <= 0 || te >= ie, J != 0) { - X == 0 && (l[Z] = 0); + X == 0 && (c[Z] = 0); break; } for (ae = z - K, oe = B - te, fe = (1 - ae) * (1 - oe) * le + .5 | 0, pe = ae * (1 - oe) * le + .5 | 0, me = (1 - ae) * oe * le + .5 | 0, he = le - fe - pe - me, se = 0, ce = 0, Y = 0; Y < o; ++Y) for (F = (Y + te) * x + K | 0, N = Y * o | 0, P = N << 1, J = 0; J < o; ++J, ++F, ++N) ge = v[F] * fe + v[F + 1] * pe + v[F + x] * me + v[F + x + 1] * he, ge = ge + 256 >> 9, ge -= D[N], se += ge * O[P++], ce += ge * O[P++]; @@ -2591,10 +2593,10 @@ var F = class extends s { } this.cache.put_buffer(C), this.cache.put_buffer(w), this.cache.put_buffer(T); } -}, ee = s; -s.cache = r, s.pyramid_t = x, s.transform = S, s.matrix_t = c, s.keypoint_t = C, s.fast_corners = b, s.imgproc = m, s.math = p, s.matmath = _, s.linalg = v, s.orb = E, s.yape = M, s.yape06 = F, s.motion_estimator = L, s.ransac_params_t = I, s.affine2d = z, s.homography2d = B, s.optical_flow_lk = V; +}, H = c; +c.cache = s, c.pyramid_t = S, c.matrix_t = l, c.keypoint_t = w, c.ransac_params_t = L, c.transform = new C(), c.fast_corners = new x(), c.imgproc = new h(), c.math = new m(), c.matmath = new v(), c.linalg = new y(), c.orb = new D(), c.yape = new N(), c.yape06 = new I(), c.motion_estimator = new R(), c.affine2d = new B(), c.homography2d = new V(), c.optical_flow_lk = new ee(); //#endregion //#region src/index.ts -var H = { jsfeatNext: ee }; +var U = H; //#endregion -export { H as default }; +export { U as default }; diff --git a/docs/jsfeat-parity-and-refactor-audit.md b/docs/jsfeat-parity-and-refactor-audit.md index 64b90e8..4052e24 100644 --- a/docs/jsfeat-parity-and-refactor-audit.md +++ b/docs/jsfeat-parity-and-refactor-audit.md @@ -13,16 +13,16 @@ jsfeatNext is a TypeScript port of jsfeat for WebARKit. The core algorithms are ### Severity-ranked issues (to be confirmed/expanded during execution) -| # | Severity | Issue | Axis | -|---|----------|-------|------| -| 1 | 🔴 High | **Not drop-in compatible** with jsfeat: algorithm modules require `new` (instance methods) vs jsfeat's static namespace functions | 2 | -| 2 | 🔴 High | **Type-only stub files** (`src/**/.ts` throw `"Method not implemented"`); real logic is duplicated inline in `src/jsfeatNext.ts` | 3, 4 | -| 3 | 🟠 Med-High | **Missing modules**: `haar` (Haar cascade) and `bbf` (BBF) object/face detection | 1 | -| 4 | 🟠 Med | **Per-instance cache allocation** — each `new jsfeatNext.imgproc()` builds its own 30-buffer pool; jsfeat shares one global cache | 2, 4 | -| 5 | 🟠 Med | **Double namespace** `jsfeatNext.jsfeatNext` forced on every consumer | 2, 5 | -| 6 | 🟡 Low-Med | **Dead / trap code**: `src/orb/rectify_patch.ts` imports the *stub* `imgproc` (throws or is unused) | 3 | -| 7 | 🟡 Low | **Build/deps**: double transpile (ts-loader→babel), `prettier`/`ts-loader` mis-listed as runtime deps, `rimraf ./build` clears the wrong dir, UMD-only output | 5 | -| 8 | 🟡 Low | **No automated tests** — only manual HTML examples; blocks safe refactoring | 4, App. B | +| # | Severity | Issue | Axis | Status | +|---|----------|-------|------|--------| +| 1 | 🔴 High | **Not drop-in compatible** with jsfeat: algorithm modules require `new` (instance methods) vs jsfeat's static namespace functions | 2 | ✅ **resolved in 0.9.0** — singletons, no `new` (#41, `docs/migration-0.9.md`) | +| 2 | 🔴 High | **Type-only stub files** (`src/**/.ts` throw `"Method not implemented"`); real logic is duplicated inline in `src/jsfeatNext.ts` | 3, 4 | ✅ resolved in 0.8.0 (#47) | +| 3 | 🟠 Med-High | **Missing modules**: `haar` (Haar cascade) and `bbf` (BBF) object/face detection | 1 | ⬜ open (#43/#44) | +| 4 | 🟠 Med | **Per-instance cache allocation** — each `new jsfeatNext.imgproc()` builds its own 30-buffer pool; jsfeat shares one global cache | 2, 4 | ✅ **resolved in 0.9.0** — one `shared_cache` (#41) | +| 5 | 🟠 Med | **Double namespace** `jsfeatNext.jsfeatNext` forced on every consumer | 2, 5 | ✅ **resolved in 0.9.0** — default export is the namespace (#41) | +| 6 | 🟡 Low-Med | **Dead / trap code**: `src/orb/rectify_patch.ts` imports the *stub* `imgproc` (throws or is unused) | 3 | ✅ resolved in 0.8.0 (#47) | +| 7 | 🟡 Low | **Build/deps**: double transpile (ts-loader→babel), `prettier`/`ts-loader` mis-listed as runtime deps, `rimraf ./build` clears the wrong dir, UMD-only output | 5 | ✅ resolved in 0.8.0 (#42/#53/#60) | +| 8 | 🟡 Low | **No automated tests** — only manual HTML examples; blocks safe refactoring | 4, App. B | ✅ resolved in 0.8.0 (#39/#49) | --- @@ -70,6 +70,14 @@ For **each** module, produce a table: `jsfeat symbol → jsfeatNext symbol → s ## 2. Axis 2 — API / behavioral parity +> **✅ Status update (0.9.0, issue #41):** rows 1–4 below are RESOLVED — algorithm +> modules are now singletons (`jsfeatNext.imgproc.grayscale(...)`, no `new`), +> the double namespace is gone (the default export is the namespace itself), +> all modules share ONE buffer pool (`jsfeatNext.cache`), and stateful modules +> behave like jsfeat's (`jsfeatNext.yape06.laplacian_threshold = 30`). +> Full mapping + motivation: [`docs/migration-0.9.md`](migration-0.9.md). +> The table below is kept as the historical record of the pre-0.9.0 state. + Even where algorithms match, **how you call them differs** — so jsfeat code is *not* copy-paste compatible, contradicting the README claim ("if it works on jsfeat it works on jsfeatNext"). | Aspect | jsfeat | jsfeatNext | Impact | diff --git a/docs/migration-0.9.md b/docs/migration-0.9.md new file mode 100644 index 0000000..f2cdcf2 --- /dev/null +++ b/docs/migration-0.9.md @@ -0,0 +1,134 @@ +# Migrating to jsfeatNext 0.9.0 — API-parity release (issue #41) + +jsfeatNext **0.9.0** is a deliberate breaking release that restores original +[jsfeat](https://github.com/inspirit/jsfeat)'s calling convention. If you are +coming from 0.8.x or earlier, two things change; if you are coming from +original jsfeat, your code now largely works as-is. + +## TL;DR + +```js +// ── BEFORE (0.8.x) ──────────────────────────────────────────── +import pkg from "@webarkit/jsfeat-next"; +const jsfeatNext = pkg.jsfeatNext; // double-namespace unwrap +const imgproc = new jsfeatNext.imgproc(); // instantiate every module +imgproc.grayscale(rgba, w, h, gray); + +// ── AFTER (0.9.0) ───────────────────────────────────────────── +import jsfeatNext from "@webarkit/jsfeat-next"; // default export IS the namespace +jsfeatNext.imgproc.grayscale(rgba, w, h, gray); // singleton — no `new` +``` + +Browser / UMD: + +```html + + +``` + +## What changed + +### 1. The `jsfeatNext.jsfeatNext` double namespace is gone + +The package entry (`src/index.ts`) used to export `{ jsfeatNext }` — a wrapper +object — so every consumer (npm and UMD alike) had to unwrap +`jsfeatNext.jsfeatNext` before doing anything. The default export is now the +namespace itself. + +### 2. Algorithm modules are singletons — `new` is gone + +These **14 modules** are now pre-constructed singleton objects on the +namespace. Calling `new jsfeatNext.imgproc()` no longer makes sense (the slot +holds an instance, not a class): + +| Module | Before (0.8.x) | After (0.9.0) | +|---|---|---| +| `imgproc` | `const ip = new jsfeatNext.imgproc(); ip.grayscale(...)` | `jsfeatNext.imgproc.grayscale(...)` | +| `math` | `new jsfeatNext.math().qsort(...)` | `jsfeatNext.math.qsort(...)` | +| `matmath` | `new jsfeatNext.matmath().multiply(...)` | `jsfeatNext.matmath.multiply(...)` | +| `linalg` | `new jsfeatNext.linalg().lu_solve(...)` | `jsfeatNext.linalg.lu_solve(...)` | +| `transform` | `new jsfeatNext.transform().invert_affine_transform(...)` | `jsfeatNext.transform.invert_affine_transform(...)` | +| `fast_corners` | `const fc = new jsfeatNext.fast_corners(); fc.detect(...)` | `jsfeatNext.fast_corners.detect(...)` | +| `yape` | `const y = new jsfeatNext.yape(); y.init(...); y.detect(...)` | `jsfeatNext.yape.init(...); jsfeatNext.yape.detect(...)` | +| `yape06` | `const y = new jsfeatNext.yape06(); y.laplacian_threshold = 30` | `jsfeatNext.yape06.laplacian_threshold = 30` | +| `orb` | `new jsfeatNext.orb().describe(...)` | `jsfeatNext.orb.describe(...)` | +| `optical_flow_lk` | `new jsfeatNext.optical_flow_lk().track(...)` | `jsfeatNext.optical_flow_lk.track(...)` | +| `motion_estimator` | `new jsfeatNext.motion_estimator().ransac(...)` | `jsfeatNext.motion_estimator.ransac(...)` | +| `affine2d` | `const k = new jsfeatNext.affine2d()` (as RANSAC kernel) | `jsfeatNext.affine2d` (pass the singleton) | +| `homography2d` | `const k = new jsfeatNext.homography2d()` | `jsfeatNext.homography2d` | +| `cache` | `jsfeatNext.cache` was the **class**: `const c = new jsfeatNext.cache(); c.allocate(30, 2560); c.get_buffer(n)` | `jsfeatNext.cache` is the shared **pool instance**, pre-allocated: `jsfeatNext.cache.get_buffer(n)` / `put_buffer(node)` — no `new`, no `allocate()` (matches original jsfeat, where `jsfeat.cache` was never a constructor either) | + +### 3. NOT changed: the data-structure constructors + +These are genuine per-object constructors in original jsfeat too, and remain +classes you instantiate exactly as before: + +```js +new jsfeatNext.matrix_t(w, h, jsfeatNext.U8_t | jsfeatNext.C1_t) +new jsfeatNext.keypoint_t(x, y, score, level, angle) +new jsfeatNext.pyramid_t(levels) +new jsfeatNext.ransac_params_t(size, thresh, eps, prob) +``` + +Constants are also unchanged: `jsfeatNext.U8_t`, `jsfeatNext.F32C1_t`, +`jsfeatNext.COLOR_RGBA2GRAY`, … all still live on the namespace. + +### 4. Behavior change: ONE shared buffer pool + +Every algorithm module now borrows scratch buffers from a single library-wide +pool (`jsfeatNext.cache`), exactly like original jsfeat's global +`jsfeat.cache`. Previously **each `new`ed module instance allocated its own +30-buffer / 76.8 KB pool** — a realistic per-frame pipeline +(grayscale → blur → detect → describe) touching 4+ modules carried several +hundred KB of duplicate pools, and re-instantiating modules inside a frame +loop silently reallocated pools every frame. + +This is a behavior change only in memory profile, not in results: all 57 +numeric-parity tests against original jsfeat pass identically before and +after. + +## Why this change is the right call (the ground truth) + +This is not just "matching jsfeat for its own sake" — the original design is +verifiably intentional and better for this library's use case: + +1. **Original jsfeat modules are singletons by design, even stateful ones.** + From jsfeat's own `jsfeat_fast_corners.js`: + ```js + var fast_corners = (function() { + var _threshold = 20; // module-level state + return { set_threshold: ..., detect: ... }; + })(); + global.fast_corners = fast_corners; + fast_corners.set_threshold(20); // configured ONCE, at load time + ``` + There was never a concept of multiple independently-configured detector + instances. +2. **The cache's whole purpose requires sharing.** jsfeat's `imgproc` calls + `jsfeat.cache.get_buffer(...)` — the one global pool — so buffers freed by + one stage are immediately reusable by the next. Per-instance pools defeat + the optimization the cache exists to provide. +3. **It removes a footgun.** With classes, a consumer following jsfeat-style + examples could `new` a module inside a per-frame loop and silently + reallocate 76.8 KB pools every frame. With singletons that mistake is + impossible by construction. +4. **Known limitation, inherited not introduced:** two concurrently, + differently-configured `fast_corners` are not supported — same as original + jsfeat. Power users can still `import { fast_corners } from + "@webarkit/jsfeat-next/..."`-style deep imports of the classes (they still + exist, unchanged) and construct isolated instances; those instances now + bind to the shared pool. + +## Versioning notes + +- Ships as **0.9.0** (pre-1.0 semver allows breaking minors). A `1.0.0-alpha` + was considered and rejected because the release pipeline doesn't support + prerelease tags yet (tracked in + [#81](https://github.com/webarkit/jsfeatNext/issues/81)). +- This is a **hard cut**: 0.8.x's `new jsfeatNext.imgproc()` convention is not + shimmed. Pin `@webarkit/jsfeat-next@0.8.0` if you need the old API while + migrating. diff --git a/examples/browser.html b/examples/browser.html index 0df1350..52c5c88 100644 --- a/examples/browser.html +++ b/examples/browser.html @@ -11,7 +11,7 @@ diff --git a/examples/grayscale.html b/examples/grayscale.html index 3da9c7c..f79fa1f 100644 --- a/examples/grayscale.html +++ b/examples/grayscale.html @@ -14,8 +14,6 @@ diff --git a/examples/mat_math_example.html b/examples/mat_math_example.html index 8112626..d942646 100644 --- a/examples/mat_math_example.html +++ b/examples/mat_math_example.html @@ -11,14 +11,12 @@ diff --git a/examples/matrix_t_example.html b/examples/matrix_t_example.html index a679760..3ef4bba 100644 --- a/examples/matrix_t_example.html +++ b/examples/matrix_t_example.html @@ -11,11 +11,10 @@ diff --git a/examples/orb_test.html b/examples/orb_test.html index 8568411..8e64706 100644 --- a/examples/orb_test.html +++ b/examples/orb_test.html @@ -11,18 +11,15 @@ diff --git a/examples/sample_boxblur.html b/examples/sample_boxblur.html index 2456d03..6a3e8cc 100644 --- a/examples/sample_boxblur.html +++ b/examples/sample_boxblur.html @@ -34,8 +34,6 @@ // lets do some fun var video = document.getElementById('webcam'); var canvas = document.getElementById('canvas'); - var jsfeat = jsfeatNext.jsfeatNext; - var imgproc = new jsfeat.imgproc(); try { var attempts = 0; var readyListener = function(event) { @@ -104,7 +102,7 @@ ctx.fillStyle = "rgb(0,255,0)"; ctx.strokeStyle = "rgb(0,255,0)"; - img_u8 = new jsfeat.matrix_t(640, 480, jsfeat.U8_t | jsfeat.C1_t); + img_u8 = new jsfeatNext.matrix_t(640, 480, jsfeatNext.U8_t | jsfeatNext.C1_t); options = new demo_opt(); gui = new dat.GUI(); @@ -123,13 +121,13 @@ var imageData = ctx.getImageData(0, 0, 640, 480); stat.start("grayscale"); - imgproc.grayscale(imageData.data, 640, 480, img_u8); + jsfeatNext.imgproc.grayscale(imageData.data, 640, 480, img_u8); stat.stop("grayscale"); var r = options.radius|0; stat.start("box blur"); - imgproc.box_blur_gray(img_u8, img_u8, r, 0); + jsfeatNext.imgproc.box_blur_gray(img_u8, img_u8, r, 0); stat.stop("box blur"); // render result back to canvas diff --git a/examples/sample_canny_edge.html b/examples/sample_canny_edge.html index dc15e41..f4f374c 100644 --- a/examples/sample_canny_edge.html +++ b/examples/sample_canny_edge.html @@ -33,8 +33,6 @@ // lets do some fun var video = document.getElementById('webcam'); var canvas = document.getElementById('canvas'); - var jsfeat = jsfeatNext.jsfeatNext; - var imgproc = new jsfeat.imgproc(); try { var attempts = 0; var readyListener = function(event) { @@ -105,7 +103,7 @@ ctx.fillStyle = "rgb(0,255,0)"; ctx.strokeStyle = "rgb(0,255,0)"; - img_u8 = new jsfeat.matrix_t(640, 480, jsfeat.U8C1_t); + img_u8 = new jsfeatNext.matrix_t(640, 480, jsfeatNext.U8C1_t); options = new demo_opt(); gui = new dat.GUI(); @@ -127,18 +125,18 @@ var imageData = ctx.getImageData(0, 0, 640, 480); stat.start("grayscale"); - imgproc.grayscale(imageData.data, 640, 480, img_u8); + jsfeatNext.imgproc.grayscale(imageData.data, 640, 480, img_u8); stat.stop("grayscale"); var r = options.blur_radius|0; var kernel_size = (r+1) << 1; stat.start("gauss blur"); - imgproc.gaussian_blur(img_u8, img_u8, kernel_size, 0); + jsfeatNext.imgproc.gaussian_blur(img_u8, img_u8, kernel_size, 0); stat.stop("gauss blur"); stat.start("canny edge"); - imgproc.canny(img_u8, img_u8, options.low_threshold|0, options.high_threshold|0); + jsfeatNext.imgproc.canny(img_u8, img_u8, options.low_threshold|0, options.high_threshold|0); stat.stop("canny edge"); // render result back to canvas diff --git a/examples/sample_equalize_hist.html b/examples/sample_equalize_hist.html index 629cf0d..b011aa3 100644 --- a/examples/sample_equalize_hist.html +++ b/examples/sample_equalize_hist.html @@ -32,8 +32,6 @@ // lets do some fun var video = document.getElementById('webcam'); var canvas = document.getElementById('canvas'); - var jsfeat = jsfeatNext.jsfeatNext; - var imgproc = new jsfeat.imgproc(); try { var attempts = 0; var readyListener = function(event) { @@ -98,7 +96,7 @@ ctx.fillStyle = "rgb(0,255,0)"; ctx.strokeStyle = "rgb(0,255,0)"; - img_u8 = new jsfeat.matrix_t(640, 480, jsfeat.U8_t | jsfeat.C1_t); + img_u8 = new jsfeatNext.matrix_t(640, 480, jsfeatNext.U8_t | jsfeatNext.C1_t); stat.add("grayscale"); stat.add("equalize histogram"); @@ -112,11 +110,11 @@ var imageData = ctx.getImageData(0, 0, 640, 480); stat.start("grayscale"); - imgproc.grayscale(imageData.data, 640, 480, img_u8); + jsfeatNext.imgproc.grayscale(imageData.data, 640, 480, img_u8); stat.stop("grayscale"); stat.start("equalize histogram"); - imgproc.equalize_histogram(img_u8, img_u8); + jsfeatNext.imgproc.equalize_histogram(img_u8, img_u8); stat.stop("equalize histogram"); // render result back to canvas diff --git a/examples/sample_fast_corners.html b/examples/sample_fast_corners.html index 88328a5..2777c1d 100644 --- a/examples/sample_fast_corners.html +++ b/examples/sample_fast_corners.html @@ -35,9 +35,6 @@ // lets do some fun var video = document.getElementById('webcam'); var canvas = document.getElementById('canvas'); - var jsfeat = jsfeatNext.jsfeatNext; - var imgproc = new jsfeat.imgproc(); - var fast_corners = new jsfeat.fast_corners(); try { var attempts = 0; var readyListener = function (event) { @@ -108,17 +105,17 @@ ctx.fillStyle = "rgb(0,255,0)"; ctx.strokeStyle = "rgb(0,255,0)"; - img_u8 = new jsfeat.matrix_t(640, 480, jsfeat.U8_t | jsfeat.C1_t); + img_u8 = new jsfeatNext.matrix_t(640, 480, jsfeatNext.U8_t | jsfeatNext.C1_t); corners = []; var i = 640 * 480; while (--i >= 0) { - corners[i] = new jsfeat.keypoint_t(0, 0, 0, 0); + corners[i] = new jsfeatNext.keypoint_t(0, 0, 0, 0); } threshold = 20; - fast_corners.set_threshold(threshold); + jsfeatNext.fast_corners.set_threshold(threshold); options = new demo_opt(); gui = new dat.GUI(); @@ -137,16 +134,16 @@ var imageData = ctx.getImageData(0, 0, 640, 480); stat.start("grayscale"); - imgproc.grayscale(imageData.data, 640, 480, img_u8); + jsfeatNext.imgproc.grayscale(imageData.data, 640, 480, img_u8); stat.stop("grayscale"); if (threshold != options.threshold) { threshold = options.threshold | 0; - fast_corners.set_threshold(threshold); + jsfeatNext.fast_corners.set_threshold(threshold); } stat.start("fast corners"); - var count = fast_corners.detect(img_u8, corners, 5); + var count = jsfeatNext.fast_corners.detect(img_u8, corners, 5); stat.stop("fast corners"); // render result back to canvas diff --git a/examples/sample_gaussblur.html b/examples/sample_gaussblur.html index ad57c48..2808a54 100644 --- a/examples/sample_gaussblur.html +++ b/examples/sample_gaussblur.html @@ -33,8 +33,6 @@ // lets do some fun var video = document.getElementById('webcam'); var canvas = document.getElementById('canvas'); - var jsfeat = jsfeatNext.jsfeatNext; - var imgproc = new jsfeat.imgproc(); try { var attempts = 0; var readyListener = function(event) { @@ -104,7 +102,7 @@ ctx.fillStyle = "rgb(0,255,0)"; ctx.strokeStyle = "rgb(0,255,0)"; - img_u8 = new jsfeat.matrix_t(640, 480, jsfeat.U8_t | jsfeat.C1_t); + img_u8 = new jsfeatNext.matrix_t(640, 480, jsfeatNext.U8_t | jsfeatNext.C1_t); options = new demo_opt(); gui = new dat.GUI(); @@ -124,14 +122,14 @@ var imageData = ctx.getImageData(0, 0, 640, 480); stat.start("grayscale"); - imgproc.grayscale(imageData.data, 640, 480, img_u8); + jsfeatNext.imgproc.grayscale(imageData.data, 640, 480, img_u8); stat.stop("grayscale"); var r = options.radius|0; var kernel_size = (r+1) << 1; stat.start("gauss blur"); - imgproc.gaussian_blur(img_u8, img_u8, kernel_size, options.sigma); + jsfeatNext.imgproc.gaussian_blur(img_u8, img_u8, kernel_size, options.sigma); stat.stop("gauss blur"); // render result back to canvas diff --git a/examples/sample_oflow_lk.html b/examples/sample_oflow_lk.html index b0e572d..b6ea224 100644 --- a/examples/sample_oflow_lk.html +++ b/examples/sample_oflow_lk.html @@ -36,9 +36,6 @@ // lets do some fun var video = document.getElementById('webcam'); var canvas = document.getElementById('canvas'); - var jsfeat = jsfeatNext.jsfeatNext; - var imgproc = new jsfeat.imgproc(); - var optical_flow_lk = new jsfeat.optical_flow_lk(); try { var attempts = 0; var readyListener = function (event) { @@ -110,10 +107,10 @@ ctx.fillStyle = "rgb(0,255,0)"; ctx.strokeStyle = "rgb(0,255,0)"; - curr_img_pyr = new jsfeat.pyramid_t(3); - prev_img_pyr = new jsfeat.pyramid_t(3); - curr_img_pyr.allocate(640, 480, jsfeat.U8_t | jsfeat.C1_t); - prev_img_pyr.allocate(640, 480, jsfeat.U8_t | jsfeat.C1_t); + curr_img_pyr = new jsfeatNext.pyramid_t(3); + prev_img_pyr = new jsfeatNext.pyramid_t(3); + curr_img_pyr.allocate(640, 480, jsfeatNext.U8_t | jsfeatNext.C1_t); + prev_img_pyr.allocate(640, 480, jsfeatNext.U8_t | jsfeatNext.C1_t); point_count = 0; point_status = new Uint8Array(100); @@ -149,7 +146,7 @@ curr_img_pyr = _pyr; stat.start("grayscale"); - imgproc.grayscale(imageData.data, 640, 480, curr_img_pyr.data[0]); + jsfeatNext.imgproc.grayscale(imageData.data, 640, 480, curr_img_pyr.data[0]); stat.stop("grayscale"); stat.start("build image pyramid"); @@ -157,7 +154,7 @@ stat.stop("build image pyramid"); stat.start("optical flow lk"); - optical_flow_lk.track(prev_img_pyr, curr_img_pyr, prev_xy, curr_xy, point_count, options.win_size | 0, options.max_iterations | 0, point_status, options.epsilon, options.min_eigen); + jsfeatNext.optical_flow_lk.track(prev_img_pyr, curr_img_pyr, prev_xy, curr_xy, point_count, options.win_size | 0, options.max_iterations | 0, point_status, options.epsilon, options.min_eigen); stat.stop("optical flow lk"); prune_oflow_points(ctx); diff --git a/examples/sample_orb.html b/examples/sample_orb.html index 6b2b8fc..f69ce66 100644 --- a/examples/sample_orb.html +++ b/examples/sample_orb.html @@ -35,14 +35,14 @@ // lets do some fun var video = document.getElementById('webcam'); var canvas = document.getElementById('canvas'); - var jsfeat = jsfeatNext.jsfeatNext; - var imgproc = new jsfeat.imgproc(); - var orb = new jsfeat.orb(); - var math = new jsfeat.math(); - var yape06 = new jsfeat.yape06(); - var motion_estimator = new jsfeat.motion_estimator(); - var homography2d = new jsfeat.homography2d(); - var matmath = new jsfeat.matmath(); + var jsfeat = jsfeatNext; + var imgproc = jsfeat.imgproc; + var orb = jsfeat.orb; + var math = jsfeat.math; + var yape06 = jsfeat.yape06; + var motion_estimator = jsfeat.motion_estimator; + var homography2d = jsfeat.homography2d; + var matmath = jsfeat.matmath; try { var attempts = 0; var readyListener = function (event) { diff --git a/examples/sample_orb_pinball.html b/examples/sample_orb_pinball.html index 5c6e160..2eafd98 100644 --- a/examples/sample_orb_pinball.html +++ b/examples/sample_orb_pinball.html @@ -52,14 +52,14 @@ // lets do some fun var video = document.getElementById('webcam'); var canvas = document.getElementById('canvas'); - var jsfeat = jsfeatNext.jsfeatNext; - var imgproc = new jsfeat.imgproc(); - var orb = new jsfeat.orb(); - var math = new jsfeat.math(); - var yape06 = new jsfeat.yape06(); - var motion_estimator = new jsfeat.motion_estimator(); - var homography2d = new jsfeat.homography2d(); - var matmath = new jsfeat.matmath(); + var jsfeat = jsfeatNext; + var imgproc = jsfeat.imgproc; + var orb = jsfeat.orb; + var math = jsfeat.math; + var yape06 = jsfeat.yape06; + var motion_estimator = jsfeat.motion_estimator; + var homography2d = jsfeat.homography2d; + var matmath = jsfeat.matmath; try { var attempts = 0; var readyListener = function (event) { diff --git a/examples/sample_pyrdown.html b/examples/sample_pyrdown.html index db3029a..029db74 100644 --- a/examples/sample_pyrdown.html +++ b/examples/sample_pyrdown.html @@ -32,8 +32,6 @@ // lets do some fun var video = document.getElementById('webcam'); var canvas = document.getElementById('canvas'); - var jsfeat = jsfeatNext.jsfeatNext; - var imgproc = new jsfeat.imgproc(); try { var attempts = 0; var readyListener = function(event) { @@ -98,8 +96,8 @@ ctx.fillStyle = "rgb(0,255,0)"; ctx.strokeStyle = "rgb(0,255,0)"; - img_pyr = new jsfeat.pyramid_t(4); - img_pyr.allocate(640, 480, jsfeat.U8_t | jsfeat.C1_t); + img_pyr = new jsfeatNext.pyramid_t(4); + img_pyr.allocate(640, 480, jsfeatNext.U8_t | jsfeatNext.C1_t); stat.add("grayscale"); stat.add("pyrdown"); @@ -113,17 +111,17 @@ var imageData = ctx.getImageData(0, 0, 640, 480); stat.start("grayscale"); - imgproc.grayscale(imageData.data, 640, 480, img_pyr.data[0]); + jsfeatNext.imgproc.grayscale(imageData.data, 640, 480, img_pyr.data[0]); stat.stop("grayscale"); stat.start("pyrdown"); // you do the same by executing img_pyr.build(img_pyr.data[0], true); var i = 2, a = img_pyr.data[0], b = img_pyr.data[1]; - imgproc.pyrdown(a, b); + jsfeatNext.imgproc.pyrdown(a, b); for(; i < img_pyr.levels; ++i) { a = b; b = img_pyr.data[i]; - imgproc.pyrdown(a, b); + jsfeatNext.imgproc.pyrdown(a, b); } stat.stop("pyrdown"); diff --git a/examples/sample_scharr.html b/examples/sample_scharr.html index 8759472..c06ecc9 100644 --- a/examples/sample_scharr.html +++ b/examples/sample_scharr.html @@ -32,8 +32,6 @@ // lets do some fun var video = document.getElementById('webcam'); var canvas = document.getElementById('canvas'); - var jsfeat = jsfeatNext.jsfeatNext; - var imgproc = new jsfeat.imgproc(); try { var attempts = 0; var readyListener = function(event) { @@ -98,8 +96,8 @@ ctx.fillStyle = "rgb(0,255,0)"; ctx.strokeStyle = "rgb(0,255,0)"; - img_u8 = new jsfeat.matrix_t(640, 480, jsfeat.U8C1_t); - img_gxgy = new jsfeat.matrix_t(640, 480, jsfeat.S32C2_t); + img_u8 = new jsfeatNext.matrix_t(640, 480, jsfeatNext.U8C1_t); + img_gxgy = new jsfeatNext.matrix_t(640, 480, jsfeatNext.S32C2_t); stat.add("grayscale"); stat.add("scharr"); @@ -113,11 +111,11 @@ var imageData = ctx.getImageData(0, 0, 640, 480); stat.start("grayscale"); - imgproc.grayscale(imageData.data, 640, 480, img_u8); + jsfeatNext.imgproc.grayscale(imageData.data, 640, 480, img_u8); stat.stop("grayscale"); stat.start("scharr"); - imgproc.scharr_derivatives(img_u8, img_gxgy); + jsfeatNext.imgproc.scharr_derivatives(img_u8, img_gxgy); stat.stop("scharr"); // render result back to canvas diff --git a/examples/sample_sobel.html b/examples/sample_sobel.html index 02973d7..276f0a5 100644 --- a/examples/sample_sobel.html +++ b/examples/sample_sobel.html @@ -33,8 +33,6 @@ // lets do some fun var video = document.getElementById('webcam'); var canvas = document.getElementById('canvas'); - var jsfeat = jsfeatNext.jsfeatNext; - var imgproc = new jsfeat.imgproc(); try { var attempts = 0; var readyListener = function(event) { @@ -99,8 +97,8 @@ ctx.fillStyle = "rgb(0,255,0)"; ctx.strokeStyle = "rgb(0,255,0)"; - img_u8 = new jsfeat.matrix_t(640, 480, jsfeat.U8C1_t); - img_gxgy = new jsfeat.matrix_t(640, 480, jsfeat.S32C2_t); + img_u8 = new jsfeatNext.matrix_t(640, 480, jsfeatNext.U8C1_t); + img_gxgy = new jsfeatNext.matrix_t(640, 480, jsfeatNext.S32C2_t); stat.add("grayscale"); stat.add("sobel"); @@ -114,11 +112,11 @@ var imageData = ctx.getImageData(0, 0, 640, 480); stat.start("grayscale"); - imgproc.grayscale(imageData.data, 640, 480, img_u8); + jsfeatNext.imgproc.grayscale(imageData.data, 640, 480, img_u8); stat.stop("grayscale"); stat.start("sobel"); - imgproc.sobel_derivatives(img_u8, img_gxgy); + jsfeatNext.imgproc.sobel_derivatives(img_u8, img_gxgy); stat.stop("sobel"); // render result back to canvas diff --git a/examples/sample_sobel_edge.html b/examples/sample_sobel_edge.html index 87403ae..7620567 100644 --- a/examples/sample_sobel_edge.html +++ b/examples/sample_sobel_edge.html @@ -32,8 +32,8 @@ // lets do some fun var video = document.getElementById('webcam'); var canvas = document.getElementById('canvas'); - var jsfeat = jsfeatNext.jsfeatNext; - var imgproc = new jsfeat.imgproc(); + var jsfeat = jsfeatNext; + var imgproc = jsfeat.imgproc; try { var attempts = 0; var readyListener = function(event) { @@ -98,9 +98,9 @@ ctx.fillStyle = "rgb(0,255,0)"; ctx.strokeStyle = "rgb(0,255,0)"; - img_u8 = new jsfeat.matrix_t(640, 480, jsfeat.U8C1_t); - img_gxgy = new jsfeat.matrix_t(640, 480, jsfeat.S32C2_t); - img_mag = new jsfeat.matrix_t(640, 480, jsfeat.S32C1_t); + img_u8 = new jsfeatNext.matrix_t(640, 480, jsfeatNext.U8C1_t); + img_gxgy = new jsfeatNext.matrix_t(640, 480, jsfeatNext.S32C2_t); + img_mag = new jsfeatNext.matrix_t(640, 480, jsfeatNext.S32C1_t); stat.add("grayscale"); stat.add("blur"); @@ -116,15 +116,15 @@ var imageData = ctx.getImageData(0, 0, 640, 480); stat.start("grayscale"); - imgproc.grayscale(imageData.data, 640, 480, img_u8); + jsfeatNext.imgproc.grayscale(imageData.data, 640, 480, img_u8); stat.stop("grayscale"); stat.start("blur"); - imgproc.gaussian_blur(img_u8, img_u8, 3); + jsfeatNext.imgproc.gaussian_blur(img_u8, img_u8, 3); stat.stop("blur"); stat.start("sobel"); - imgproc.sobel_derivatives(img_u8, img_gxgy); + jsfeatNext.imgproc.sobel_derivatives(img_u8, img_gxgy); stat.stop("sobel"); stat.start("edges"); diff --git a/examples/sample_warp_affine.html b/examples/sample_warp_affine.html index 32b778a..4ef41d0 100644 --- a/examples/sample_warp_affine.html +++ b/examples/sample_warp_affine.html @@ -31,8 +31,6 @@ // lets do some fun var video = document.getElementById('webcam'); var canvas = document.getElementById('canvas'); - var jsfeat = jsfeatNext.jsfeatNext; - var imgproc = new jsfeat.imgproc(); try { var attempts = 0; var readyListener = function(event) { @@ -97,9 +95,9 @@ ctx.fillStyle = "rgb(0,255,0)"; ctx.strokeStyle = "rgb(0,255,0)"; - img_u8 = new jsfeat.matrix_t(640, 480, jsfeat.U8_t | jsfeat.C1_t); - img_u8_warp = new jsfeat.matrix_t(640, 480, jsfeat.U8_t | jsfeat.C1_t); - mat_affine = new jsfeat.matrix_t(3, 2, jsfeat.F32_t | jsfeat.C1_t); + img_u8 = new jsfeatNext.matrix_t(640, 480, jsfeatNext.U8_t | jsfeatNext.C1_t); + img_u8_warp = new jsfeatNext.matrix_t(640, 480, jsfeatNext.U8_t | jsfeatNext.C1_t); + mat_affine = new jsfeatNext.matrix_t(3, 2, jsfeatNext.F32_t | jsfeatNext.C1_t); mat_affine.data[0] = 1.1548494156391083; mat_affine.data[1] = 0.4783542904563622; @@ -120,11 +118,11 @@ var imageData = ctx.getImageData(0, 0, 640, 480); stat.start("grayscale"); - imgproc.grayscale(imageData.data, 640, 480, img_u8); + jsfeatNext.imgproc.grayscale(imageData.data, 640, 480, img_u8); stat.stop("grayscale"); stat.start("warp affine"); - imgproc.warp_affine(img_u8, img_u8_warp, mat_affine, 0); + jsfeatNext.imgproc.warp_affine(img_u8, img_u8_warp, mat_affine, 0); stat.stop("warp affine"); // render result back to canvas diff --git a/examples/sample_warp_perspective.html b/examples/sample_warp_perspective.html index 629eb31..9c7b466 100644 --- a/examples/sample_warp_perspective.html +++ b/examples/sample_warp_perspective.html @@ -32,10 +32,6 @@ // lets do some fun var video = document.getElementById('webcam'); var canvas = document.getElementById('canvas'); - var jsfeat = jsfeatNext.jsfeatNext; - var imgproc = new jsfeat.imgproc(); - var transform_c = new jsfeat.transform(); - var matmath = new jsfeat.matmath(); try { var attempts = 0; var readyListener = function(event) { @@ -100,15 +96,15 @@ ctx.fillStyle = "rgb(0,255,0)"; ctx.strokeStyle = "rgb(0,255,0)"; - img_u8 = new jsfeat.matrix_t(640, 480, jsfeat.U8_t | jsfeat.C1_t); - img_u8_warp = new jsfeat.matrix_t(640, 480, jsfeat.U8_t | jsfeat.C1_t); - transform = new jsfeat.matrix_t(3, 3, jsfeat.F32_t | jsfeat.C1_t); + img_u8 = new jsfeatNext.matrix_t(640, 480, jsfeatNext.U8_t | jsfeatNext.C1_t); + img_u8_warp = new jsfeatNext.matrix_t(640, 480, jsfeatNext.U8_t | jsfeatNext.C1_t); + transform = new jsfeatNext.matrix_t(3, 3, jsfeatNext.F32_t | jsfeatNext.C1_t); - transform_c.perspective_4point_transform(transform, 0, 0, 50, 50, + jsfeatNext.transform.perspective_4point_transform(transform, 0, 0, 50, 50, 640, 0, 550, 100, 640, 480, 300, 400, 0, 480, 100, 400); - matmath.invert_3x3(transform, transform); + jsfeatNext.matmath.invert_3x3(transform, transform); stat.add("grayscale"); stat.add("warp perspective"); @@ -122,11 +118,11 @@ var imageData = ctx.getImageData(0, 0, 640, 480); stat.start("grayscale"); - imgproc.grayscale(imageData.data, 640, 480, img_u8); + jsfeatNext.imgproc.grayscale(imageData.data, 640, 480, img_u8); stat.stop("grayscale"); stat.start("warp perspective"); - imgproc.warp_perspective(img_u8, img_u8_warp, transform, 0); + jsfeatNext.imgproc.warp_perspective(img_u8, img_u8_warp, transform, 0); stat.stop("warp perspective"); // render result back to canvas diff --git a/examples/sample_yape.html b/examples/sample_yape.html index b46fbdc..edcbfe0 100644 --- a/examples/sample_yape.html +++ b/examples/sample_yape.html @@ -32,9 +32,6 @@ // lets do some fun var video = document.getElementById('webcam'); var canvas = document.getElementById('canvas'); - var jsfeat = jsfeatNext.jsfeatNext; - var imgproc = new jsfeat.imgproc(); - var yape = new jsfeat.yape(); try { var attempts = 0; var readyListener = function(event) { @@ -99,16 +96,16 @@ ctx.fillStyle = "rgb(0,255,0)"; ctx.strokeStyle = "rgb(0,255,0)"; - img_u8 = new jsfeat.matrix_t(640, 480, jsfeat.U8_t | jsfeat.C1_t); + img_u8 = new jsfeatNext.matrix_t(640, 480, jsfeatNext.U8_t | jsfeatNext.C1_t); corners = []; var i = 640*480; while(--i >= 0) { - corners[i] = new jsfeat.keypoint_t(0,0,0,0); + corners[i] = new jsfeatNext.keypoint_t(0,0,0,0); } // YAPE detector needs init first - yape.init(canvasWidth, canvasHeight, 5, 1); + jsfeatNext.yape.init(canvasWidth, canvasHeight, 5, 1); stat.add("grayscale"); stat.add("box blur"); @@ -123,15 +120,15 @@ var imageData = ctx.getImageData(0, 0, 640, 480); stat.start("grayscale"); - imgproc.grayscale(imageData.data, 640, 480, img_u8); + jsfeatNext.imgproc.grayscale(imageData.data, 640, 480, img_u8); stat.stop("grayscale"); stat.start("box blur"); - imgproc.box_blur_gray(img_u8, img_u8, 2, 0); + jsfeatNext.imgproc.box_blur_gray(img_u8, img_u8, 2, 0); stat.stop("box blur"); stat.start("yape"); - var count = yape.detect(img_u8, corners, 5); + var count = jsfeatNext.yape.detect(img_u8, corners, 5); stat.stop("yape"); // render result back to canvas diff --git a/examples/sample_yape06.html b/examples/sample_yape06.html index ec10d29..2c84cd5 100644 --- a/examples/sample_yape06.html +++ b/examples/sample_yape06.html @@ -33,9 +33,6 @@ // lets do some fun var video = document.getElementById('webcam'); var canvas = document.getElementById('canvas'); - var jsfeat = jsfeatNext.jsfeatNext; - var imgproc = new jsfeat.imgproc(); - var yape06 = new jsfeat.yape06(); try { var attempts = 0; var readyListener = function(event) { @@ -105,12 +102,12 @@ ctx.fillStyle = "rgb(0,255,0)"; ctx.strokeStyle = "rgb(0,255,0)"; - img_u8 = new jsfeat.matrix_t(640, 480, jsfeat.U8_t | jsfeat.C1_t); + img_u8 = new jsfeatNext.matrix_t(640, 480, jsfeatNext.U8_t | jsfeatNext.C1_t); corners = []; var i = 640*480; while(--i >= 0) { - corners[i] = new jsfeat.keypoint_t(0,0,0,0); + corners[i] = new jsfeatNext.keypoint_t(0,0,0,0); } options = new demo_opt(); @@ -132,18 +129,18 @@ var imageData = ctx.getImageData(0, 0, 640, 480); stat.start("grayscale"); - imgproc.grayscale(imageData.data, 640, 480, img_u8); + jsfeatNext.imgproc.grayscale(imageData.data, 640, 480, img_u8); stat.stop("grayscale"); stat.start("box blur"); - imgproc.box_blur_gray(img_u8, img_u8, 2, 0); + jsfeatNext.imgproc.box_blur_gray(img_u8, img_u8, 2, 0); stat.stop("box blur"); - yape06.laplacian_threshold = options.lap_thres|0; - yape06.min_eigen_value_threshold = options.eigen_thres|0; + jsfeatNext.yape06.laplacian_threshold = options.lap_thres|0; + jsfeatNext.yape06.min_eigen_value_threshold = options.eigen_thres|0; stat.start("yape06"); - var count = yape06.detect(img_u8, corners); + var count = jsfeatNext.yape06.detect(img_u8, corners); stat.stop("yape06"); // render result back to canvas diff --git a/src/cache/cache.ts b/src/cache/cache.ts index d114f46..f07e707 100644 --- a/src/cache/cache.ts +++ b/src/cache/cache.ts @@ -19,10 +19,9 @@ export interface ICache { * views (`u8`/`i32`/`f32`/`f64`), and must hand it back with * {@link put_buffer} when done. * - * Mirrors `jsfeat.cache` from the original library, with one difference: - * the original keeps a single global pool, while jsfeatNext currently - * allocates one pool per module instance (see the base-class constructor - * in `src/core/core.ts`). + * Mirrors `jsfeat.cache` from the original library: since 0.9.0 (issue #41) + * jsfeatNext keeps a single library-wide pool too — the `shared_cache` + * exported by `src/core/core.ts` and exposed publicly as `jsfeatNext.cache`. */ export class cache implements ICache { /** First free node in the pool (borrow end of the list). */ diff --git a/src/core/core.ts b/src/core/core.ts index bf57c50..de68f99 100644 --- a/src/core/core.ts +++ b/src/core/core.ts @@ -20,46 +20,56 @@ import type { orb } from "../orb/orb"; import type { affine2d, homography2d } from "../motion_model/motion_model"; /** - * Base class of the library: holds the shared constants, the per-instance - * cache/data-type helpers, and the static slots the algorithm modules are - * attached to (in src/jsfeatNext.ts, the aggregator). + * The ONE shared scratch-buffer pool of the library (30 buffers of 2560 + * bytes, growable), matching original jsfeat's design where every module + * borrows from the single global `jsfeat.cache`. Exposed publicly as + * `jsfeatNext.cache`. Until 0.9.0 each module instance allocated its own + * pool — a diagnosed memory/GC cost (see docs/migration-0.9.md, issue #41). + */ +export const shared_cache = new cache(); +shared_cache.allocate(30, 640 * 4); + +/** + * Base class of the library: holds the shared constants, the cache/data-type + * helpers, and the static slots the algorithm singletons are attached to + * (in src/jsfeatNext.ts, the aggregator). * * Extracted from the src/jsfeatNext.ts monolith (issue #47) so that module * files can `extend` it without creating a circular import with the * aggregator. + * + * Since 0.9.0 (issue #41) the algorithm slots hold SINGLETON INSTANCES — + * consumers call `jsfeatNext.imgproc.grayscale(...)` with no `new`, exactly + * like original jsfeat's static namespaces. The data-structure slots + * (matrix_t, keypoint_t, pyramid_t, ransac_params_t) remain constructors. */ export default class jsfeatNext { /** Decoder for packed matrix type signatures. */ private dt: IData_Type; - /** - * Per-instance scratch-buffer pool (30 buffers of 2560 bytes, growable). - * NOTE: original jsfeat shares ONE global cache; jsfeatNext currently - * allocates a pool per module instance (see the parity audit, Axis 2). - */ + /** The library-wide shared buffer pool (same object as {@link shared_cache}). */ 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 cache: cache; + static fast_corners: fast_corners; + static imgproc: imgproc; + static linalg: linalg; + static math: math; + static matmath: matmath; static matrix_t: typeof matrix_t; static pyramid_t: typeof pyramid_t; - static transform: typeof transform; + static transform: transform; static keypoint_t: typeof keypoint_t; - static yape: typeof yape; - static yape06: typeof yape06; + static yape: yape; + static yape06: 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; + static affine2d: affine2d; + static homography2d: homography2d; + static motion_estimator: motion_estimator; + static optical_flow_lk: optical_flow_lk; + static orb: orb; constructor() { this.dt = new data_type(); - this.cache = new cache(); - this.cache.allocate(30, 640 * 4); + this.cache = shared_cache; } /** Library version, read from package.json at build time. */ diff --git a/src/index.ts b/src/index.ts index b29c052..f81a8dc 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,19 +1,15 @@ import jsfeatNext from "./jsfeatNext"; /** - * Package entry point. The default export wraps the {@link jsfeatNext} class - * in an object, which is why consumers of the UMD bundle (global - * `jsfeatNext`) and of the npm package access the library as - * `jsfeatNext.jsfeatNext` — a known quirk scheduled to be addressed in the - * API-parity work (issue #41). + * Package entry point. Since 0.9.0 the default export IS the `jsfeatNext` + * namespace directly — the old `pkg.jsfeatNext` double-namespace unwrap is + * gone (issue #41; see docs/migration-0.9.md). * * @example * ```ts - * import pkg from "@webarkit/jsfeat-next"; - * const jsfeat = pkg.jsfeatNext; - * const ip = new jsfeat.imgproc(); + * import jsfeatNext from "@webarkit/jsfeat-next"; + * const gray = new jsfeatNext.matrix_t(w, h, jsfeatNext.U8_t | jsfeatNext.C1_t); + * jsfeatNext.imgproc.grayscale(rgba, w, h, gray); // singleton — no `new` * ``` */ -export default { - jsfeatNext, -}; +export default jsfeatNext; diff --git a/src/jsfeatNext.ts b/src/jsfeatNext.ts index e721bd0..1b3e48b 100644 --- a/src/jsfeatNext.ts +++ b/src/jsfeatNext.ts @@ -1,5 +1,4 @@ -import jsfeatNext from "./core/core"; -import { cache } from "./cache/cache"; +import jsfeatNext, { shared_cache } from "./core/core"; import { imgproc } from "./imgproc/imgproc"; import { linalg } from "./linalg/linalg"; import { fast_corners } from "./fast_corners/fast_corners"; @@ -18,42 +17,55 @@ import { affine2d, homography2d } from "./motion_model/motion_model"; import { optical_flow_lk } from "./optical_flow_lk/optical_flow_lk"; // Thin aggregator (issue #47): every algorithm lives in its own module under -// src//, extending the base class from src/core/core.ts. This file -// only attaches the modules to the public jsfeatNext namespace. +// src//, extending the base class from src/core/core.ts. +// +// Since 0.9.0 (issue #41) the algorithm modules are attached as SINGLETON +// INSTANCES, restoring original jsfeat's calling convention: +// +// jsfeatNext.imgproc.grayscale(...) // no `new` +// jsfeatNext.fast_corners.set_threshold(20) // state lives on the singleton +// +// All singletons share the ONE library-wide buffer pool (`shared_cache`), +// exactly like jsfeat's global `jsfeat.cache` — see docs/migration-0.9.md. +// The data-structure classes (matrix_t, keypoint_t, pyramid_t, +// ransac_params_t) remain constructors: `new jsfeatNext.matrix_t(...)`. export default jsfeatNext; -jsfeatNext.cache = cache; +// the shared buffer pool itself, like original jsfeat's `jsfeat.cache` +jsfeatNext.cache = shared_cache; +// data-structure constructors (unchanged: consumers `new` these) jsfeatNext.pyramid_t = pyramid_t; -jsfeatNext.transform = transform; - jsfeatNext.matrix_t = matrix_t; jsfeatNext.keypoint_t = keypoint_t; -jsfeatNext.fast_corners = fast_corners; +jsfeatNext.ransac_params_t = ransac_params_t; + +// algorithm singletons (no `new` — call methods directly) +jsfeatNext.transform = new transform(); -jsfeatNext.imgproc = imgproc; +jsfeatNext.fast_corners = new fast_corners(); -jsfeatNext.math = math; +jsfeatNext.imgproc = new imgproc(); -jsfeatNext.matmath = matmath; +jsfeatNext.math = new math(); -jsfeatNext.linalg = linalg; +jsfeatNext.matmath = new matmath(); -jsfeatNext.orb = orb; +jsfeatNext.linalg = new linalg(); -jsfeatNext.yape = yape; +jsfeatNext.orb = new orb(); -jsfeatNext.yape06 = yape06; +jsfeatNext.yape = new yape(); -jsfeatNext.motion_estimator = motion_estimator; +jsfeatNext.yape06 = new yape06(); -jsfeatNext.ransac_params_t = ransac_params_t; +jsfeatNext.motion_estimator = new motion_estimator(); -jsfeatNext.affine2d = affine2d; +jsfeatNext.affine2d = new affine2d(); -jsfeatNext.homography2d = homography2d; +jsfeatNext.homography2d = new homography2d(); -jsfeatNext.optical_flow_lk = optical_flow_lk; +jsfeatNext.optical_flow_lk = new optical_flow_lk(); diff --git a/tests/api-shape.test.ts b/tests/api-shape.test.ts new file mode 100644 index 0000000..bc4f18b --- /dev/null +++ b/tests/api-shape.test.ts @@ -0,0 +1,76 @@ +import { describe, it, expect } from "vitest"; +import jsfeatNext from "../src/index"; +import { shared_cache } from "../src/core/core"; +import { imgproc } from "../src/imgproc/imgproc"; + +/** + * API-shape tests for the 0.9.0 breaking change (issue #41): + * - the package default export IS the namespace (no jsfeatNext.jsfeatNext) + * - algorithm modules are singletons (no `new`), like original jsfeat + * - every module shares the ONE library-wide cache pool + * - data-structure classes remain constructors + */ +describe("0.9.0 API shape (issue #41)", () => { + it("default export is the namespace directly — the double unwrap is gone", () => { + // old shape was { jsfeatNext: }; the namespace itself has VERSION + expect((jsfeatNext as any).jsfeatNext).toBeUndefined(); + expect(typeof jsfeatNext.VERSION).toBe("string"); + }); + + it("algorithm modules are callable singletons, no `new` required", () => { + expect(typeof jsfeatNext.imgproc.grayscale).toBe("function"); + expect(typeof jsfeatNext.math.qsort).toBe("function"); + expect(typeof jsfeatNext.matmath.multiply).toBe("function"); + expect(typeof jsfeatNext.linalg.lu_solve).toBe("function"); + expect(typeof jsfeatNext.transform.perspective_4point_transform).toBe("function"); + expect(typeof jsfeatNext.fast_corners.detect).toBe("function"); + expect(typeof jsfeatNext.yape.detect).toBe("function"); + expect(typeof jsfeatNext.yape06.detect).toBe("function"); + expect(typeof jsfeatNext.orb.describe).toBe("function"); + expect(typeof jsfeatNext.optical_flow_lk.track).toBe("function"); + expect(typeof jsfeatNext.motion_estimator.ransac).toBe("function"); + expect(typeof jsfeatNext.affine2d.run).toBe("function"); + expect(typeof jsfeatNext.homography2d.run).toBe("function"); + expect(typeof jsfeatNext.cache.get_buffer).toBe("function"); + }); + + it("singletons are stable identities (state persists across accesses)", () => { + expect(jsfeatNext.imgproc).toBe(jsfeatNext.imgproc); + const t = jsfeatNext.fast_corners.set_threshold(42); + expect(t).toBe(42); + expect(jsfeatNext.fast_corners._threshold).toBe(42); + jsfeatNext.fast_corners.set_threshold(20); // restore the jsfeat default + }); + + it("every module shares the ONE library-wide cache pool", () => { + // jsfeatNext.cache IS the shared pool object + expect(jsfeatNext.cache).toBe(shared_cache); + // a buffer borrowed from the public pool is visible to module internals: + // borrow every free node via the public handle, then verify a fresh + // module instance sees the same (now-recycled) pool rather than its own. + const a = shared_cache.get_buffer(16); + shared_cache.put_buffer(a); + const b = jsfeatNext.cache.get_buffer(16); + jsfeatNext.cache.put_buffer(b); + // a fresh instance created from the class still binds to the shared pool + const fresh = new imgproc(); + expect((fresh as any).cache).toBe(shared_cache); + }); + + it("data-structure classes remain constructors", () => { + const m = new jsfeatNext.matrix_t(4, 4, jsfeatNext.U8_t | jsfeatNext.C1_t); + expect(m.cols).toBe(4); + const k = new jsfeatNext.keypoint_t(1, 2, 3, 0, -1); + expect(k.x).toBe(1); + const p = new jsfeatNext.pyramid_t(3); + expect(p.levels).toBe(3); + const r = new jsfeatNext.ransac_params_t(4, 3.0, 0.5, 0.99); + expect(r.size).toBe(4); + }); + + it("constants remain on the namespace", () => { + expect(jsfeatNext.U8_t).toBe(0x0100); + expect(jsfeatNext.F32C1_t).toBe(0x0400 | 0x01); + expect(jsfeatNext.COLOR_RGBA2GRAY).toBe(0); + }); +}); diff --git a/tests/parity/detectors.test.ts b/tests/parity/detectors.test.ts index 565f1ef..f874824 100644 --- a/tests/parity/detectors.test.ts +++ b/tests/parity/detectors.test.ts @@ -84,7 +84,7 @@ describe("parity: fast_corners vs original jsfeat.fast_corners", () => { const { next, orig } = grayPair(); const { nextC, origC } = makeCorners(W * H); - const fc = new jsfeatNext.fast_corners(); + const fc = jsfeatNext.fast_corners; fc.set_threshold(20); jsfeat.fast_corners.set_threshold(20); @@ -102,7 +102,7 @@ describe("parity: yape06 vs original jsfeat.yape06", () => { const { next, orig } = grayPair(); const { nextC, origC } = makeCorners(W * H); - const y06 = new jsfeatNext.yape06(); + const y06 = jsfeatNext.yape06; // original is a static namespace with mutable thresholds; align them jsfeat.yape06.laplacian_threshold = y06.laplacian_threshold; jsfeat.yape06.min_eigen_value_threshold = y06.min_eigen_value_threshold; @@ -142,7 +142,7 @@ describe("parity: orb.describe vs original jsfeat.orb", () => { // corners from the (already parity-verified) FAST detector const { nextC, origC } = makeCorners(W * H); - const fc = new jsfeatNext.fast_corners(); + const fc = jsfeatNext.fast_corners; fc.set_threshold(20); jsfeat.fast_corners.set_threshold(20); const count = fc.detect(next, nextC, 20); // generous border for 32px patches @@ -158,7 +158,7 @@ describe("parity: orb.describe vs original jsfeat.orb", () => { const descN = new jsfeatNext.matrix_t(32, count, U8C1); const descO = new jsfeat.matrix_t(32, count, OU8C1); - const orb = new jsfeatNext.orb(); + const orb = jsfeatNext.orb; orb.describe(next, nextC, count, descN); jsfeat.orb.describe(orig, origC, count, descO); @@ -194,7 +194,7 @@ describe("parity: optical_flow_lk vs original jsfeat.optical_flow_lk", () => { // background fail the min-eigenvalue check on both sides — aperture // problem — so corners are the meaningful trackable set) const { nextC } = makeCorners(W * H); - const fc = new jsfeatNext.fast_corners(); + const fc = jsfeatNext.fast_corners; fc.set_threshold(20); const nCorners = fc.detect(prevN, nextC, 16); const pts: number[] = []; @@ -210,7 +210,7 @@ describe("parity: optical_flow_lk vs original jsfeat.optical_flow_lk", () => { const currXYO = new Float32Array(count * 2); const statusO = new Uint8Array(count); - const lk = new jsfeatNext.optical_flow_lk(); + const lk = jsfeatNext.optical_flow_lk; lk.track(prevPyrN, currPyrN, prevXYN, currXYN, count, 9, 30, statusN, 0.01, 0.0001); jsfeat.optical_flow_lk.track(prevPyrO, currPyrO, prevXYO, currXYO, count, 9, 30, statusO, 0.01, 0.0001); diff --git a/tests/parity/imgproc.test.ts b/tests/parity/imgproc.test.ts index 430ca54..8ce3886 100644 --- a/tests/parity/imgproc.test.ts +++ b/tests/parity/imgproc.test.ts @@ -13,7 +13,7 @@ const H = 48; const U8C1 = jsfeatNext.U8_t | jsfeatNext.C1_t; const OU8C1 = jsfeat.U8_t | jsfeat.C1_t; -const ip = new jsfeatNext.imgproc(); +const ip = jsfeatNext.imgproc; /** deterministic grayscale test pattern with edges, gradients and texture */ function grayValue(x: number, y: number): number { @@ -178,7 +178,7 @@ describe("parity: imgproc vs original jsfeat.imgproc", () => { const F32C1 = jsfeatNext.F32_t | jsfeatNext.C1_t; const tN = new jsfeatNext.matrix_t(3, 3, F32C1); const tO = new jsfeat.matrix_t(3, 3, jsfeat.F32_t | jsfeat.C1_t); - const tr = new jsfeatNext.transform(); + const tr = jsfeatNext.transform; tr.perspective_4point_transform(tN, 0, 0, 4, 2, 63, 0, 60, 1, 63, 47, 62, 46, 0, 47, 2, 44); for (let i = 0; i < 9; i++) tO.data[i] = tN.data[i]; const dstN = new jsfeatNext.matrix_t(W, H, U8C1); diff --git a/tests/parity/linalg.test.ts b/tests/parity/linalg.test.ts index 4f94aec..1cb2163 100644 --- a/tests/parity/linalg.test.ts +++ b/tests/parity/linalg.test.ts @@ -21,7 +21,7 @@ function rng(seed: number): () => number { const F32C1 = jsfeatNext.F32_t | jsfeatNext.C1_t; const OF32C1 = jsfeat.F32_t | jsfeat.C1_t; -const la = new jsfeatNext.linalg(); +const la = jsfeatNext.linalg; function fillPair( next: { data: Float32Array }, diff --git a/tests/parity/math.get_gaussian_kernel.test.ts b/tests/parity/math.get_gaussian_kernel.test.ts index edbad46..772a8ca 100644 --- a/tests/parity/math.get_gaussian_kernel.test.ts +++ b/tests/parity/math.get_gaussian_kernel.test.ts @@ -24,7 +24,7 @@ describe("parity: math.get_gaussian_kernel vs original jsfeat", () => { jsfeat.math.get_gaussian_kernel(size, sigma, expected, jsfeat.F32_t | jsfeat.C1_t); const actual = new Float32Array(size); - const m = new jsfeatNext.math(); + const m = jsfeatNext.math; m.get_gaussian_kernel(size, sigma, actual, jsfeatNext.F32_t | jsfeatNext.C1_t); for (let i = 0; i < size; i++) { diff --git a/tests/parity/math.test.ts b/tests/parity/math.test.ts index 179fdda..ff475f1 100644 --- a/tests/parity/math.test.ts +++ b/tests/parity/math.test.ts @@ -20,7 +20,7 @@ function rng(seed: number): () => number { }; } -const m = new jsfeatNext.math(); +const m = jsfeatNext.math; // identical "less than" comparator for both implementations const lt = (a: number, b: number) => (a < b ? 1 : 0); diff --git a/tests/parity/motion_estimator.test.ts b/tests/parity/motion_estimator.test.ts index 1e4bd41..9bf0468 100644 --- a/tests/parity/motion_estimator.test.ts +++ b/tests/parity/motion_estimator.test.ts @@ -68,8 +68,8 @@ describe("parity: motion_estimator vs original jsfeat.motion_estimator", () => { const { from, to } = makeCorrespondences(N, OUT); const params = new jsfeatNext.ransac_params_t(4, 3.0, 0.5, 0.99); - const me = new jsfeatNext.motion_estimator(); - const kernel = new jsfeatNext.homography2d(); + const me = jsfeatNext.motion_estimator; + const kernel = jsfeatNext.homography2d; const model = new jsfeatNext.matrix_t(3, 3, F32C1); const mask = new jsfeatNext.matrix_t(N, 1, U8C1); @@ -104,8 +104,8 @@ describe("parity: motion_estimator vs original jsfeat.motion_estimator", () => { const { from, to } = makeCorrespondences(N, OUT); const params = new jsfeatNext.ransac_params_t(4, 0, 0.45, 0.99); - const me = new jsfeatNext.motion_estimator(); - const kernel = new jsfeatNext.homography2d(); + const me = jsfeatNext.motion_estimator; + const kernel = jsfeatNext.homography2d; const model = new jsfeatNext.matrix_t(3, 3, F32C1); const mask = new jsfeatNext.matrix_t(N, 1, U8C1); @@ -154,8 +154,8 @@ describe("parity: motion_estimator vs original jsfeat.motion_estimator", () => { // there; only homography2d overrides it), which made RANSAC with an // affine2d kernel throw TypeError. Now fixed — full parity check. const params = new jsfeatNext.ransac_params_t(3, 3.0, 0.5, 0.99); - const me = new jsfeatNext.motion_estimator(); - const kernel = new jsfeatNext.affine2d(); + const me = jsfeatNext.motion_estimator; + const kernel = jsfeatNext.affine2d; const model = new jsfeatNext.matrix_t(3, 3, F32C1); const mask = new jsfeatNext.matrix_t(N, 1, U8C1); diff --git a/tests/parity/structs.test.ts b/tests/parity/structs.test.ts index 22533f8..08a65c8 100644 --- a/tests/parity/structs.test.ts +++ b/tests/parity/structs.test.ts @@ -83,7 +83,7 @@ describe("parity: imgproc.resample f32 path", () => { it("resamples float matrices identically", () => { const W = 40, H = 30; - const ip = new jsfeatNext.imgproc(); + const ip = jsfeatNext.imgproc; const nx = new jsfeatNext.matrix_t(W, H, F32C1); const ox = new jsfeat.matrix_t(W, H, jsfeat.F32_t | jsfeat.C1_t); for (let y = 0; y < H; y++) { diff --git a/types/src/cache/cache.d.ts b/types/src/cache/cache.d.ts index 5848ec6..8d31d7b 100644 --- a/types/src/cache/cache.d.ts +++ b/types/src/cache/cache.d.ts @@ -17,10 +17,9 @@ export interface ICache { * views (`u8`/`i32`/`f32`/`f64`), and must hand it back with * {@link put_buffer} when done. * - * Mirrors `jsfeat.cache` from the original library, with one difference: - * the original keeps a single global pool, while jsfeatNext currently - * allocates one pool per module instance (see the base-class constructor - * in `src/core/core.ts`). + * Mirrors `jsfeat.cache` from the original library: since 0.9.0 (issue #41) + * jsfeatNext keeps a single library-wide pool too — the `shared_cache` + * exported by `src/core/core.ts` and exposed publicly as `jsfeatNext.cache`. */ export declare class cache implements ICache { /** First free node in the pool (borrow end of the list). */ diff --git a/types/src/core/core.d.ts b/types/src/core/core.d.ts index 0908ae6..23a254c 100644 --- a/types/src/core/core.d.ts +++ b/types/src/core/core.d.ts @@ -16,41 +16,50 @@ import { optical_flow_lk } from '../optical_flow_lk/optical_flow_lk'; import { orb } from '../orb/orb'; import { affine2d, homography2d } from '../motion_model/motion_model'; /** - * Base class of the library: holds the shared constants, the per-instance - * cache/data-type helpers, and the static slots the algorithm modules are - * attached to (in src/jsfeatNext.ts, the aggregator). + * The ONE shared scratch-buffer pool of the library (30 buffers of 2560 + * bytes, growable), matching original jsfeat's design where every module + * borrows from the single global `jsfeat.cache`. Exposed publicly as + * `jsfeatNext.cache`. Until 0.9.0 each module instance allocated its own + * pool — a diagnosed memory/GC cost (see docs/migration-0.9.md, issue #41). + */ +export declare const shared_cache: cache; +/** + * Base class of the library: holds the shared constants, the cache/data-type + * helpers, and the static slots the algorithm singletons are attached to + * (in src/jsfeatNext.ts, the aggregator). * * Extracted from the src/jsfeatNext.ts monolith (issue #47) so that module * files can `extend` it without creating a circular import with the * aggregator. + * + * Since 0.9.0 (issue #41) the algorithm slots hold SINGLETON INSTANCES — + * consumers call `jsfeatNext.imgproc.grayscale(...)` with no `new`, exactly + * like original jsfeat's static namespaces. The data-structure slots + * (matrix_t, keypoint_t, pyramid_t, ransac_params_t) remain constructors. */ export default class jsfeatNext { /** Decoder for packed matrix type signatures. */ private dt; - /** - * Per-instance scratch-buffer pool (30 buffers of 2560 bytes, growable). - * NOTE: original jsfeat shares ONE global cache; jsfeatNext currently - * allocates a pool per module instance (see the parity audit, Axis 2). - */ + /** The library-wide shared buffer pool (same object as {@link shared_cache}). */ 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 cache: cache; + static fast_corners: fast_corners; + static imgproc: imgproc; + static linalg: linalg; + static math: math; + static matmath: matmath; static matrix_t: typeof matrix_t; static pyramid_t: typeof pyramid_t; - static transform: typeof transform; + static transform: transform; static keypoint_t: typeof keypoint_t; - static yape: typeof yape; - static yape06: typeof yape06; + static yape: yape; + static yape06: 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; + static affine2d: affine2d; + static homography2d: homography2d; + static motion_estimator: motion_estimator; + static optical_flow_lk: optical_flow_lk; + static orb: orb; constructor(); /** Library version, read from package.json at build time. */ static VERSION: string; diff --git a/types/src/index.d.ts b/types/src/index.d.ts index 8108520..c2eb071 100644 --- a/types/src/index.d.ts +++ b/types/src/index.d.ts @@ -1,19 +1,14 @@ import { default as jsfeatNext } from './jsfeatNext'; /** - * Package entry point. The default export wraps the {@link jsfeatNext} class - * in an object, which is why consumers of the UMD bundle (global - * `jsfeatNext`) and of the npm package access the library as - * `jsfeatNext.jsfeatNext` — a known quirk scheduled to be addressed in the - * API-parity work (issue #41). + * Package entry point. Since 0.9.0 the default export IS the `jsfeatNext` + * namespace directly — the old `pkg.jsfeatNext` double-namespace unwrap is + * gone (issue #41; see docs/migration-0.9.md). * * @example * ```ts - * import pkg from "@webarkit/jsfeat-next"; - * const jsfeat = pkg.jsfeatNext; - * const ip = new jsfeat.imgproc(); + * import jsfeatNext from "@webarkit/jsfeat-next"; + * const gray = new jsfeatNext.matrix_t(w, h, jsfeatNext.U8_t | jsfeatNext.C1_t); + * jsfeatNext.imgproc.grayscale(rgba, w, h, gray); // singleton — no `new` * ``` */ -declare const _default: { - jsfeatNext: typeof jsfeatNext; -}; -export default _default; +export default jsfeatNext;