whitecodel_image_compare 1.1.0 copy "whitecodel_image_compare: ^1.1.0" to clipboard
whitecodel_image_compare: ^1.1.0 copied to clipboard

Flutter image similarity — combines OpenCV pixel matching, perceptual hashes, and optional OCR into one tuned pipeline with production-ready performance.

whitecodel_image_compare #

Flutter image similarity — OpenCV + perceptual hashes + optional OCR via ImageCompareOptions.
Tested on Flutter 3.44.6 (FVM). Verified: Android phones-only APK ~77–81 MB; iOS device Release ~74 MB (arm64).

Install #

dependencies:
  whitecodel_image_compare: ^1.1.0

Then:

flutter pub get
# iOS
cd ios && pod install && cd ..

Why this package? #

Single options can miss a true match or falsely accept a bad crop. Enabling multiple hash flags and combining them (package Visual weights) recovers / rejects correctly on cases where one algorithm alone is wrong.


Usage #

import 'package:whitecodel_image_compare/whitecodel_image_compare.dart';

final score = await compareQueriesToReference(
  refBytes,
  captures,
  maxImageDimension: 1024,
  openCvBidirectional: false,
  openCvReuseInstance: true,
  ocr: true, // Latin text only
);

// Or use the production preset (1024, forward+reuse, partial OCR, offload):
final scores = await compareQueriesWithOptions(
  refBytes,
  captures,
  ImageCompareOptions.production,
);

final passed = score >= 50; // choose your own threshold

openCv / ocr need a real Android/iOS device (or a compatible emulator). Desktop VM unit tests mark them N/A — use the example Compare Lab on device for those flags.


Packages used #

Capability Option Source
OpenCV pixel matching openCv Vendored FFI (Android/iOS)
ML Kit OCR ocr Vendored plugin (GoogleMLKit 3.2.0, Latin only)
Perceptual hashes perceptualHash / differenceHash / averageHash image_hash
Image decode / resize maxImageDimension image

Native OpenCV + OCR are vendored into this plugin (no path/git deps). Pinned ML Kit avoids iOS CocoaPods conflicts with mobile_scanner 2.x.


Android setup (required) #

Align your app with these (same as example/android/):

Setting Recommended Where
minSdk 24+ android/app/build.gradle
compileSdk / targetSdk 35 android/app/build.gradle
Android Gradle Plugin (AGP) 8.11.1+ android/settings.gradle
Kotlin 2.2.20+ android/settings.gradle
Gradle 8.13+ (example: 8.14.3) gradle-wrapper.properties
NDK 28.2.13676358 (or highest reported by plugins) android/app/build.gradle

android/settings.gradle:

plugins {
  id "com.android.application" version "8.11.1" apply false
  id "org.jetbrains.kotlin.android" version "2.2.20" apply false
}

android/app/build.gradle (recommended baseline):

android {
  compileSdk = 35
  ndkVersion = "28.2.13676358"

  defaultConfig {
    minSdk = 24
    targetSdk = 35
  }
}

If Flutter reports mismatched NDK versions across plugins, set ndkVersion to the highest one (backward compatible).

Production size — AAB vs APK vs iOS #

Release type Fat multi-ABI size issue? What to do
Android App Bundle No — Play delivers one ABI per device Prefer flutter build appbundle --release — no abiFilters required
iOS App Store / IPA No No Android ABI filters; device Release is arm64
Android fat APK Yes — can include x86_64 OpenCV (~50 MB) Filters + packaging excludes + --target-platform (below)

Verified (example app, Flutter 3.44.6):

Build Result
Phones-only APK (--target-platform android-arm,android-arm64 + filters + excludes) ~77–81 MB; armeabi-v7a + arm64-v8a only
Fat APK without stripping ~164 MB; still had x86_64 OpenCV
iOS flutter build ios --release --no-codesign ✓ ~74 MB Runner.app; arm64 only; no Android .so files

Shrinking an Android APK (not needed for Play AAB) #

Plugin can ship OpenCV for:

ABI ~size Use
armeabi-v7a ~10 MB 32-bit phones
arm64-v8a ~17 MB 64-bit phones
x86_64 ~50 MB Emulator

ndk.abiFilters alone is often not enough. In android/app/build.gradle:

android {
  defaultConfig {
    ndk {
      abiFilters 'armeabi-v7a', 'arm64-v8a'
    }
  }
  packaging {
    jniLibs {
      excludes += ['lib/x86/**', 'lib/x86_64/**']
    }
  }
}

Then:

fvm flutter build apk --release --target-platform android-arm,android-arm64
# or split / arm64-only:
fvm flutter build apk --release --split-per-abi
fvm flutter build apk --release --target-platform android-arm64

Play production: use an App Bundle — Play serves the matching ABI automatically:

fvm flutter build appbundle --release

iOS setup (required) #

  • Deployment target 13.0+ recommended (example / plugin use 13.0)
  • CocoaPods must fetch OpenCV 4.3.0 and GoogleMLKit/TextRecognition ~> 3.2.0 (network required on first pod install)
  • OCR is Latin only
  • Device / App Store builds are arm64 — no Android-style abiFilters
  • Set a real version: in your app pubspec.yaml (build name + number) before App Store submit
  • OpenCV FFI is linked via an iOS Classes forwarder (pixelmatching_native.cpp); use a full restart after upgrading the plugin (not hot reload alone)
cd ios && pod install && cd ..
flutter build ios --release

Simulator note (important): Google ML Kit + OpenCV CocoaPods do not ship official arm64 simulator slices (still true as of mid‑2026). That breaks iOS 26+ / iPhone 16–17 Pro style simulators (arm64-only).

Works Does not work
Physical iPhone iPhone 17 Pro simulator (iOS 26+, arm64)
Older x86_64 simulators (e.g. iPhone 15 / iOS 17.5 under Rosetta) Expecting OpenCV/OCR on arm64-only simulators
# Use FVM Flutter (this repo pins 3.44.6 — current stable)
fvm flutter run -d <your-iphone-id>
# OR an older x86_64 simulator from the list, e.g.:
fvm flutter run -d "iPhone 15"

App Store / device Release builds remain arm64 and are fine.


Troubleshooting #

Symptom Fix
AGP version too low / Kotlin too old Bump AGP 8.11.1+, Kotlin 2.2.20+, Gradle 8.13+ (see Android setup)
NDK mismatch across plugins Set ndkVersion to the highest version Flutter prints
APK ~160 MB+ with unused x86_64 Add abiFilters + packaging.jniLibs.excludes + --target-platform — or ship an AAB
OpenCV fails on x86 emulator Include x86_64 (larger APK) or use an arm64 system image / real device
iOS: iPhone 17 Pro / iOS 26 simulator fails (arm64) OpenCV/ML Kit lack arm64 simulator slices — use a physical iPhone or an older x86_64 simulator (e.g. iPhone 15 / iOS 17.5): fvm flutter run -d "iPhone 15"
iOS: undeclared plugin / pod errors cd ios && rm -rf Pods Podfile.lock && pod install
Wrong Flutter version in IDE This repo uses FVM 3.44.6 (.fvmrc). Run via fvm flutter …; VS Code/Cursor should use .fvm/flutter_sdk
SPM warning for this plugin Expected — ML Kit has no official SPM; CocoaPods fallback still works on device
Failed to lookup symbol 'initialize' on iOS Full restart / reinstall after plugin upgrade; ensure pod install picked up ios/Classes/pixelmatching_native.cpp
OCR wrong script / empty text Only Latin is supported; ensure clear Latin text in the image
openCv / ocr N/A in unit tests Expected on desktop — run example app on device

Reference projects: example/android/, example/ios/.


ImageCompareOptions defaults #

Flag Default
openCv true
perceptualHash true
differenceHash true
averageHash true
ocr false
skipOcrIfAverageAbove50 false
ocrMatchMode exact
maxImageDimension null
openCvBidirectional true
openCvReuseInstance false
offloadVisualWork true
maxQueriesPerBatch null
logCompareSteps false

Performance (1.1.0+) — by default, decode / resize / OpenCV / hashes run on a worker isolate so the UI thread can keep painting. ML Kit OCR always runs on the caller isolate. Use offloadVisualWork: false only for lab comparisons against legacy blocking behavior.

BatchcompareQueriesWithOptions runs all queries in one visual isolate trip when possible. Set maxQueriesPerBatch (1–10) to split very large lists into chunks.

PresetImageCompareOptions.production = maxImageDimension: 1024, forward OpenCV + reuse, ocr: true, ocrMatchMode: partial, offloadVisualWork: true, maxQueriesPerBatch: 5.

Hash combine (Visual) when several hash flags are on:

Visual = (pHash×0.30 + dHash×0.20 + aHash×0.15) / sum(enabled weights)

(+ OpenCV 35% when openCv: true on device)

When ocr: true and tokens overlap: Combined ≈ Visual×0.60 + OCR×0.40 (+ token boost). Same rules in exported mergeCompareWithOcr for custom pipelines.


Benchmark — real cases where alone ≠ combine #

Example match threshold used below: 50 (score >= 50 → match).
Scores from:

fvm flutter test test/readme_benchmark_test.dart

Scenarios #

# Image Expected @50
Ref
A Easy same 1 (match)
B Hard same (heavy crop) 1 (match)
C Bad framing (corner only) 0 (reject)

Scores by ImageCompareOptions (real) #

Options Easy same Hard same Bad framing
perceptualHash: true only 62.5 59.4 50.0
differenceHash: true only 64.1 54.7 46.9
averageHash: true only 68.8 43.8 40.6
Combine pHash+dHash+aHash 64.4 54.3 46.9
// Alone
ImageCompareOptions(
  openCv: false,
  perceptualHash: true,
  differenceHash: false,
  averageHash: false,
  maxImageDimension: 1024,
)

// Combine hashes
ImageCompareOptions(
  openCv: false,
  perceptualHash: true,
  differenceHash: true,
  averageHash: true,
  ocr: false,
  maxImageDimension: 1024,
  openCvBidirectional: false,
  openCvReuseInstance: true,
)

Result @50 — where combine helps #

Options Easy same (want 1) Hard same (want 1) Bad framing (want 0)
perceptualHash only Correct 1 Correct 1 Wrong 1 (false accept)
differenceHash only Correct 1 Correct 1 Correct 0
averageHash only Correct 1 Wrong 0 (miss) Correct 0
Combine p+d+a Correct 1 Correct 1 Correct 0
  1. Hard same: averageHash alone = 43.8 → Wrong. Combine = 54.3 → Correct.
  2. Bad framing: perceptualHash alone = 50.0 → Wrong. Combine = 46.9 → Correct.
xychart-beta
    title "Hard same @50 — aHash alone fails, combine passes"
    x-axis ["pHash", "dHash", "aHash", "Combine"]
    y-axis "match" 0 --> 1
    bar [1, 1, 0, 1]
xychart-beta
    title "Bad framing @50 — pHash alone false accept, combine rejects"
    x-axis ["pHash", "dHash", "aHash", "Combine"]
    y-axis "match" 0 --> 1
    bar [1, 0, 0, 0]

Dev (FVM) #

fvm install 3.44.6 && fvm use 3.44.6
fvm flutter pub get && fvm flutter test
cd example && fvm flutter run

Image sources #

Adapted from Wikimedia Commons (CC0 / CC BY-SA). See doc/benchmark/source_*.jpg.

License #

MIT — LICENSE

3
likes
140
points
104
downloads

Documentation

API reference

Publisher

verified publisherwhitecodel.com

Weekly Downloads

Flutter image similarity — combines OpenCV pixel matching, perceptual hashes, and optional OCR into one tuned pipeline with production-ready performance.

Repository (GitHub)
View/report issues

Topics

#image #comparison #opencv #ocr #computer-vision

License

MIT (license)

Dependencies

ffi, flutter, image, image_hash

More

Packages that depend on whitecodel_image_compare

Packages that implement whitecodel_image_compare