Skip to content

fix(scanner): bring the detector back in line with Android, on matching OpenCV - #630

Merged
bmc08gt merged 2 commits into
mainfrom
fix/scanner-parity-with-android
Aug 22, 2026
Merged

fix(scanner): bring the detector back in line with Android, on matching OpenCV#630
bmc08gt merged 2 commits into
mainfrom
fix/scanner-parity-with-android

Conversation

@bmc08gt

@bmc08gt bmc08gt commented Aug 22, 2026

Copy link
Copy Markdown
Collaborator

Brings the iOS copy of the C++ detector back in line with Android, and lines up the OpenCV version underneath it.

The detector bug

The candidate prune dropped an ellipse whenever a smaller one sat within 50px of it:

if (dist < 50 && 2 * potential_ellipses[i].size.area() > potential_ellipses[j].size.area()) {

The centre badge is a disc with the logo glyph knocked out by the even-odd rule, so the glyph's round dot is itself a clean ellipse sitting ~0.08·D from the badge centre. Past a certain on-screen size both land in the same 50px bucket, and this forward-only comparison always discards the larger one — the badge, the only candidate that can produce finder points. The search then runs from the dot and finds 1 finder point instead of 9.

The rule exists to collapse one physical circle fitted twice, from the inner and outer edge of its stroke. Those two fits are comparable in area, so requiring the areas to be within 2× of each other keeps the intended behaviour and stops it eating nested features.

This is a hard failure band, not a slowdown: hold the phone too close and the code will not decode until you back off.

Also

  • The contour prune indexed the ellipse mask with contour points without bounds-checking them. Android bounds-checks the same lookup.
  • build_opencv.sh could not run on a current Xcode at all: OpenCV still defaults IPHONEOS_DEPLOYMENT_TARGET to 9.0 (Xcode 16+ rejects it outright) and still passes -fembed-bitcode / -bitcode_verify, dead since Xcode 14. The generated Info.plist also hardcoded MinimumOSVersion 12.0.
  • OpenCV 4.10.0 → 4.12.0, matching Android. 49MB, device slice at minos 15.0, well under CodeScanner's own iOS 18 Package.swift floor.
  • Deletes opencv2.xcframework.backup.20251202_202647 — 427 files, 100MB of a framework nothing references, committed by an earlier run of the same script.

Result

FlipcashTests/CodeScanSweepTests on iPhone 17 Pro, OpenCV 4.12.0, identical in both runs except the one prune expression:

sweep padded/packed parity
without the prune fix fail fail
with it pass pass

Full suite green: 4/4.

Same fix as code-payments/code-android-app#1306.

Three divergences between the two hand-maintained copies of the C++ detector,
all of them iOS being behind:

- The candidate prune dropped an ellipse whenever a *smaller* one sat within
  50px of it. The centre badge is a disc with the logo glyph knocked out, and
  the glyph's dot is itself a clean ellipse near the badge centre, so past a
  certain on-screen size the badge and the dot land in the same bucket and the
  forward-only comparison always discards the badge -- the only candidate that
  can yield finder points. Require the two areas to be within 2x of each other,
  so the rule prunes what it was written for (one physical circle fitted twice,
  from the inner and outer edge of its stroke) and nothing else. Same fix as
  code-android-app#1306.

- The contour prune indexed the ellipse mask with contour points without
  checking them against the mask bounds. Android bounds-checks the same lookup.

- build_opencv.sh could not run on a current Xcode: OpenCV still defaults
  IPHONEOS_DEPLOYMENT_TARGET to 9.0, which Xcode 16+ rejects, and still passes
  -fembed-bitcode / -bitcode_verify, which have been dead since Xcode 14. Pass
  --iphoneos_deployment_target and --disable-bitcode.
…mework

Android has been on OpenCV 4.12.0 for a while; iOS was still on 4.10.0. Same
detector source, two different OpenCV versions, is a bad place to debug a
parity bug from -- when Android's 4.12 reproduced iOS's 4.10 failures on the
same frames it was clear the version was not the variable, but that is only
knowable after the fact. Line them up.

Rebuilt with the (now working) build_opencv.sh: 49MB, arm64 device plus
arm64/x86_64 simulator, same 13 excluded modules, device slice at minos 15.0.
The generated Info.plist hardcoded MinimumOSVersion 12.0, which no longer
matched the binary -- it now follows the deployment target.

Also deletes opencv2.xcframework.backup.20251202_202647, 427 files and 100MB of
a framework nothing references, committed by an earlier run of the same script.
The script backs up before installing, so this is recoverable by rebuilding.
@bmc08gt
bmc08gt merged commit ee252a4 into main Aug 22, 2026
bmc08gt added a commit that referenced this pull request Aug 23, 2026
The 4.12.0 rebuild in #630 was made with the only Xcode on the build
machine, a 27.0 beta, so both framework slices carry LC_BUILD_VERSION
sdk 27.0. The App Store rejects any binary stamped above the newest
released SDK (ITMS-90512), which took out 2026.8.3 build 500 — and
would take out every release until fixed, since this sits on main.

Restamp both slices to 26.1, the sdk the shipping 2026.8.2 framework
carried, with vtool. Nothing else in the binary changes and the
frameworks are unsigned, so there is nothing to re-sign.

Also refuse to build the framework at all under a too-new SDK. This
script's output is committed and shipped as-is, so a beta Xcode here
surfaces as a failed submission weeks later, long after whoever ran it
has moved on.
@bmc08gt
bmc08gt deleted the fix/scanner-parity-with-android branch August 27, 2026 15:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant