Skip to main content

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.

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

Preset — ImageCompareOptions.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
65
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