Repository navigation
Conversation
Add a `freak` group to gen-opencv-fixtures.py and the freak.json fixture: eight cases (defaults, no orientation, no scale normalization, pattern_scale 8, n_octaves 3 and 8, a 90 degree rotated scene, a flat image), border keypoints placed at the exact patternSizes thresholds, and the rotation pairs with OpenCV's max Hamming distance. Refs #80 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The committed scene gives angle 0 for most keypoints. Add an integer-only procedural texture (rebuildable bit for bit in TypeScript), the cases texture-default, texture-pattern-scale-8 and texture-rot90-default, move the rotation payload to the texture pair, and gate the generator on thetaIdx and negative-angle coverage. Refs #80 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Add the 512 selected-pair table, its (i, j) expansion and the 45 orientation pairs from OpenCV 4.10.0 freak.cpp, plus the BSD-3-Clause licence variant (header snippet, LICENSES file, check script, bundle banner). Refs #80 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Add src/freak/freak.ts: freak.describe ports FREAK_Impl's buildPattern, computeDescriptors, meanIntensity and the generic extractDescriptor, with pattern points computed on demand (no 8.5 MB lookup table). Kept keypoints are compacted in place and their angle is written in radians. Parity tests pin all 11 freak.json cases bit for bit; property tests cover validation, compaction, a large-frame integral and rotation. TEBLID's TSDoc now notes that FREAK produces negative angles (F5). Refs #80 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Keep patternSizes in double: equal to OpenCV's int value wherever its cast is defined, and a huge or infinite pattern now drops the keypoint instead of wrapping into a small or negative border. Cast the orientation sums to float before atan2, as OpenCV does. Pin the validation messages in the property tests, qualify the TEBLID angle note, and clarify the FREAK example's keypoint pool. Refs #80 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Wire the freak singleton into the namespace, the named exports and the
core static typing, with FreakOptions exported as a type. Rewrite the
FREAK table and ring constants so a { imgproc } bundle carries no FREAK
code (no residual argument expressions or member reads after the pure
call is dropped). Add API-shape, named-export, bundle-marker and
bfmatcher round-trip tests.
Refs #80
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Refs #80 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Refs #80 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…sured (#80) Two intra-library groups: the plan's size-31 workload (132 of 300 keypoints kept by FREAK) and a size-7 one where all 300 are kept, for a like-for-like per-keypoint ratio. FREAK gets a fresh keypoint copy per iteration outside the timed region. Not measured yet: the machine stayed over the 20% CPU-load gate; the numbers and the bench/README.md section follow in a later commit. Refs #80 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
PR Summary by QodoAdd OpenCV-compatible FREAK binary descriptors
AI Description
Diagram
High-Level Assessment
Files changed (30)
|
Code Review by Qodo
1. Using the image as output corrupts descriptors
|
| } else { | ||
| descriptors.resize(64, kept, 1); | ||
| } |
There was a problem hiding this comment.
1. Using the image as output corrupts descriptors 🐞 Bug ≡ Correctness
freak.describe resizes descriptors before computing the integral image, without rejecting descriptors === src. If a caller passes the source matrix as the output, resizing changes its dimensions or reallocates its pixels before the integral and descriptor loops read them.
Agent Prompt
## Issue description
Using the same matrix for `src` and `descriptors` mutates the image before FREAK reads it.
## Fix Focus Areas
- src/freak/freak.ts[408-450]
- src/freak/freak.ts[475-495]
## Recommended Fix
Reject an aliased source and output before compacting keypoints or resizing the matrix. Document the restriction and test that rejection leaves the source unchanged.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
| count = MAX_KEYPOINTS; | ||
| } | ||
| for (let i = 0; i < count; i++) { | ||
| corners[i].size = 31; // required by freak.describe; one level, so pixels of this image |
There was a problem hiding this comment.
2. Scaled-view features use the wrong size 🐞 Bug ≡ Correctness
detect assigns size = 31 to keypoints in both example views, although the right-hand image is drawn at 0.8 times the source scale. Because FREAK selects its sampling pattern from each keypoint's size, corresponding right-hand features are sampled at the unscaled size and can fail the example's ratio test.
Agent Prompt
## Issue description
The scaled view assigns its keypoints the same feature size as the original view.
## Fix Focus Areas
- examples/sample_freak.html[102-122]
- examples/sample_freak.html[171-186]
## Recommended Fix
Pass each view's feature size into `detect`, using 31 for the original and `31 * SCALE` for the scaled view.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
| let count = jsfeatNext.fast_corners.detect(smooth, corners, BORDER); | ||
| if (count > MAX_KEYPOINTS) { | ||
| jsfeatNext.math.qsort(corners, 0, count - 1, (a, b) => b.score < a.score); | ||
| count = MAX_KEYPOINTS; | ||
| } | ||
| for (let i = 0; i < count; i++) { | ||
| corners[i].size = 31; // required by freak.describe; one level, so pixels of this image | ||
| } | ||
| return { smooth, corners, count, detected: count }; |
There was a problem hiding this comment.
3. Example's 'detected' count shows the 500 cap, not what fast found 🐞 Bug ◔ Observability
In detect(), count is capped at MAX_KEYPOINTS before it is returned as detected, so the stat does not retain FAST's actual result. When FAST finds more than 500 corners, the page displays detected: 500, obscuring how many corners were found versus how many were removed by the cap or dropped by freak.describe.
Agent Prompt
## Issue description
The sample's `detected` stat reports the capped selection count rather than the number of corners FAST found.
## Fix Focus Areas
- examples/sample_freak.html[113-122]
## Recommended Fix
Save the result of `jsfeatNext.fast_corners.detect(...)` as `const detected` before limiting the selected keypoints. Set `let count = Math.min(detected, MAX_KEYPOINTS)`, sorting first when the result exceeds the cap, and return `{ smooth, corners, count, detected }`. Optionally add a separate stats line showing the capped count.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
Closes #80
Adds
jsfeatNext.freak, the FREAK binary descriptor (Alahi, Ortiz and Vandergheynst, CVPR 2012). It is ported from opencv_contrib 4.10.0freak.cpp(FREAK_Impl). It is bit-exact withcv2.xfeatures2d.FREAK_create(...).computeon three outputs: the descriptor bytes, the kept keypoints and their order, and the angles. The comparison has no tolerance and drops no input. Bit-exactness is what lets the PureCV backend (webarkit/purecv#103) produce identical results.Spec and plan:
docs/superpowers/specs/2026-10-30-freak-design.mdanddocs/superpowers/plans/2026-10-30-freak.md(merged in #257).API
Decisions (F1–F6, approved)
buildPatternstores 64 scales × 256 orientations × 43 points, about 8.5 MB. Here a point is recomputed frombuildPattern's double expressions when it is needed. Resident memory:Int16, plus twoUint8index arrays), the 45 orientation pairs, and the ring radii and sigmas.Float64buffer of(cols+1)·(rows+1)entries borrowed from the shared pool and returned in afinally.cornersis compacted in place: the order is stable, the objects are moved rather than copied, andcorners[count..]is untouched.describereturns the kept count, and rowibelongs tocorners[i].opencv-contrib-python==4.10.0.84with IPP off, generated on this Windows x86-64 machine;meta.platformandmeta.compilerare recorded.float32(deg) · 0.017453292519943295. It lies in (−180°, 180°], negative values included, and is never wrapped into [0, 360). FREAK followed by TEBLID diverges from OpenCV for negative angles. Both TSDocs say so, andteblid.ts's sentence claiming that OpenCV angles are never negative has been corrected.freak.cppis BSD-3-Clause (© 2011-2012 EPFL), not Apache-2.0. Its notice ships verbatim asLICENSES/BSD-3-Clause-FREAK.txt. A newopencv-freakheader variant applies tosrc/freak/. The bundle banner names the file and the holder. AGENTS.md, CLAUDE.md, the licence-header skill and the agent-instruction mirrors are updated.Fixtures and parity
tests/fixtures/opencv/freak.jsonhas 11 cases, all bit-exact:orientation_normalized=false,scale_normalized=false,pattern_scale=8, andn_octaves= 3 and 8. A 90°-rotated copy of the scene, and a flat image (which pinsatan2(0, 0)), are added.patternSizes[s]− 0.5, exactly at it, and + 0.5, for scales 0 and 40. The generator replicates the drop logic and exits non-zero if it disagrees with OpenCV.thetaIdxvalues and 147 negative angles at the defaults, and its rotated copy provides the rotation property test, whose Hamming bound comes from OpenCV.atan2here is the float overload (atan2f); the double overload fails 9 of 11 cases. No maths-library ulp mismatch was found, so nothing was escalated.The bilinear branch.
meanIntensitytakes it for radius < 0.5, whichpattern_scale = 8reaches. It is ported with OpenCV's bug as is: it adds2·1024²and divides by4·1024², so it returns about a quarter of the intensity. Thepattern_scale=8case matches only because of that, and the TSDoc says so.Beyond OpenCV: if
patternSizeswould overflowint(UB in OpenCV, for an extremen_octaveswith a hugesize), the keypoint is dropped instead of being described from out-of-buffer reads. The value is identical to OpenCV's wherever OpenCV is defined.Size and tree-shaking
dist/jsfeatNext.jsraw / gzipdist/jsfeatNext.mjsraw / gzipA Vite bundle of
{ imgproc }is unchanged (23262 / 8198) and contains no FREAK code;tests/build/bundle.test.tschecks the404,431,818,511,181marker. A bundle of{ freak, … }is 30087 / 11476.Bench: committed, not yet measured
bench/freak.bench.tsis committed but not measured yet. The machine stayed above the 20% CPU-load gate: 59 gated attempts were discarded, with in-run peaks of 72–79% from other load. The gate stays strict, and the numbers and thebench/README.mdsection follow in a later commit.The bench has two groups:
Example
examples/sample_freak.htmlshows two views ofpinball.jpg(rotated 30°, scaled 0.8), FAST corners withsize = 31, and noic_angle: FREAK writes the angle, and the page draws it as a tick. It matches withknnMatchand the 0.75 ratio test.Checked in a browser: 302 and 480 keypoints kept, 140 matches of which 133 agree with the known transform, and no console errors.
Checks
npm test(776 passed, 1 skipped),npm run typecheck,prettier --check .,check-license-headers.mjsandnpm run build-tsare all green, and every commit is GPG-signed.Note for the merge: commit
fix(freak): drop keypoints whose pattern size overflowsfixes code that was never released. Squash-merging, or rewording it, keeps a spurious "Bug Fixes" line out of the git-cliff release notes.For webarkit #232 (adapter), recorded here, not done here
sizeand coordinates in level-0 pixels.describedrops keypoints, so use the returned count and the compacted array, never the input length.corners[i].angleis radians, and can be negative (F5).Follow-ups
bench/README.mdsection.🤖 Generated with Claude Code