doc_scan_lite 0.0.2 copy "doc_scan_lite: ^0.0.2" to clipboard
doc_scan_lite: ^0.0.2 copied to clipboard

Lightweight document scanner plugin: live edge detection and perspective crop via classical CV over FFI. No ML models, no Play Services, no OpenCV.

doc_scan_lite #

A lightweight Flutter FFI plugin for live document edge detection and perspective-crop. Classical computer vision only — no ML models, no Google Play Services / ML Kit dependency — targeting a native binary in the hundreds-of-KB range per ABI.

Usage #

The API is layered — pick the level of control you need.

1. Prebuilt UI (drop-in) #

DocScannerView owns the camera, its lifecycle, the live overlay, the capture button, an optional manual corner-adjust step, and a result preview. One callback and you have a working scanner:

DocScannerView(
  enableManualAdjust: true, // optional drag-to-fix-corners step before crop
  onCaptured: (CapturedDocument doc) {
    // doc.bytes = grayscale pixels, doc.width x doc.height (deskewed crop)
  },
)

2. Prebuilt, customized #

Every visual piece is overridable while keeping the built-in camera/detection plumbing:

DocScannerView(
  onCaptured: saveDoc,
  lockedColor: Colors.tealAccent,      // overlay colors + stroke
  searchingColor: Colors.white54,
  showResultPreview: false,            // skip the built-in result screen
  controlsBuilder: (context, c) =>     // your own capture bar
      MyShutterBar(locked: c.isLocked, busy: c.isCapturing, onTap: c.capture),
  resultBuilder: (context, doc, dismiss) => MyReviewPage(doc, onDone: dismiss),
  loadingBuilder: (context) => const MySplash(),
  errorBuilder: (context, err) => MyCameraError(err),
  onError: () => toast('No document found'),   // detection/warp failed
  onCancelled: () => toast('Cancelled'),       // user backed out of adjust
)

CapturedDocument.bytes is raw single-channel grayscale (one byte per pixel, width * height long), not PNG/JPEG — the pipeline only ever reads the camera's luma plane. Use decodeCapturedDocument(doc) to get a displayable ui.Image (dispose it when done), or expand to RGB and encode with a package like image.

3. Full DIY (bring your own camera + UI) #

Drive detection yourself with the low-level pieces — DocScanController + DocScannerPreview + QuadCornerEditor — when you already manage the camera or need a bespoke flow. See example/ history or the source docs; the controller exposes processFrame, currentQuad, isLocked, captureAndCropLatest, latestFrame, warpFrame, pause / resume, and onDetectionError.

For a manual corner-correction flow, snapshot controller.latestFrame, show it under a QuadCornerEditor, then finalize with controller.warpFrame(frame, adjustedQuad) — pass the same frame you showed the user, since the live stream will have moved on by the time they finish dragging.

How it works #

src/ implements the detection pipeline from scratch in C:

  1. YUV420 Y-plane extraction (grayscale, no RGB conversion)
  2. 5x5 Gaussian blur
  3. Canny edge detection (Sobel + non-max suppression + hysteresis)
  4. Morphological dilate
  5. Contour tracing (Moore-neighbor border following)
  6. Douglas-Peucker polygon simplification
  7. Quad filtering (4 points, convex, area > 20% of frame)
  8. Corner ordering (TL/TR/BR/BL)
  9. Homography solve + perspective warp

Exposed via two C functions (src/doc_scan_lite.h):

bool detect_quad(const uint8_t* frame, int width, int height, Quad* out);
bool warp_perspective(const uint8_t* frame, int width, int height, Quad quad,
                       uint8_t* out_buffer, int out_width, int out_height);

lib/ wraps these with an isolate-based Dart API so detection never blocks the UI thread:

  • DocScanController — owns a persistent background isolate, throttles incoming camera frames to a target detection fps independent of the camera's preview rate, smooths corners with a moving average, and exposes currentQuad / isLocked.
  • DocScannerView — batteries-included widget that owns the camera and the full scan flow (see Usage above).
  • DocScannerPreview — camera preview widget with a live quad overlay.
  • QuadCornerEditor — manual drag-adjustment widget for correcting a bad auto-detection, pure Dart/Flutter with no native cost.
  • captureAndCrop() re-runs detection on a full-resolution frame and returns the warped/cropped image as a CapturedDocument.

Testing #

  • test/run_tests.sh builds and runs the C pipeline unit tests standalone (no Flutter/FFI toolchain involved) — corner ordering, Douglas-Peucker, homography, and a full synthetic detect+warp smoke test.
  • example/ demonstrates live camera detection end-to-end. Camera streams on emulators are unreliable for this — test on a real device.

Regenerating FFI bindings #

dart run ffigen --config ffigen.yaml

If ffigen's bundled libclang can't find stdbool.h on Linux, point CPATH at your system clang's resource include dir first:

CPATH="$(clang -print-resource-dir)/include" dart run ffigen --config ffigen.yaml

Non-goals #

No OCR, no multi-page PDF assembly, no ML models or trained weights, no Google Play Services / ML Kit dependency anywhere in the tree.

3
likes
160
points
186
downloads

Documentation

API reference

Publisher

verified publisherchinmaysinghmodak.com

Weekly Downloads

Lightweight document scanner plugin: live edge detection and perspective crop via classical CV over FFI. No ML models, no Play Services, no OpenCV.

Repository (GitHub)
View/report issues

Topics

#document-scanner #computer-vision #image-processing #camera #ffi

License

MIT (license)

Dependencies

camera, ffi, flutter, plugin_platform_interface

More

Packages that depend on doc_scan_lite

Packages that implement doc_scan_lite