seika

Smart on-device image inpainting for Flutter.

Seika combines a C/FFI PatchMatch engine (zero extra MB, works offline) with downloadable neural models โ€” LaMa and MI-GAN โ€” running via ONNX Runtime. Pick the engine manually or let seika route automatically based on mask complexity.


Features

  • ๐Ÿ–ผ Three engines โ€” PatchMatch (0 MB), LaMa (209 MB), MI-GAN (80 MB)
  • ๐Ÿ“ฆ On-demand downloads โ€” models are downloaded to device storage, not bundled in the APK
  • ๐Ÿ”„ Auto routing โ€” analyzes texture variance and edge coherence to pick the best engine
  • ๐ŸŽญ Mask dilation โ€” automatically expands the mask before neural inference to reduce edge artifacts
  • ๐Ÿชก Soft blending โ€” BFS boundary blending for seamless transitions between filled and original regions
  • ๐Ÿ”’ SHA-256 verification โ€” model integrity checked after download
  • ๐Ÿงต Isolate-safe โ€” all heavy work runs off the UI thread

Installation

dependencies:
  seika: ^0.1.0

Android permissions

Add to android/app/src/main/AndroidManifest.xml:

<uses-permission android:name="android.permission.INTERNET" />

Quick start

import 'package:seika/seika.dart';

final seika = Seika();

// 1. Download a model (one-time, ~209 MB)
if (!await seika.modelManager.isAvailable()) {
  await for (final p in seika.modelManager.download()) {
    print('${(p.fraction * 100).toStringAsFixed(0)}%');
  }
}

// 2. Inpaint โ€” auto-selects the best engine
final result = await seika.inpaint(
  rgbaBytes,  // Uint8List โ€” RGBA, width ร— height ร— 4 bytes
  maskBytes,  // Uint8List โ€” grayscale, width ร— height bytes (255 = fill, 0 = keep)
  width,
  height,
);

print(result.engineUsed);   // SeikaEngine.neural
print(result.duration);     // Duration(milliseconds: 1200)
// result.image โ†’ Uint8List RGBA ready for display

Engines

Engine Size Quality Offline
SeikaEngine.patchMatch 0 MB โ˜…โ˜…โ˜…โ˜† โœ…
SeikaEngine.neural + LaMa 209 MB โ˜…โ˜…โ˜…โ˜… After download
SeikaEngine.neural + MI-GAN 80 MB โ˜…โ˜…โ˜…โ˜† After download

PatchMatch

C implementation using Fast Fourier-based patch search with:

  • Multi-scale coarse-to-fine pyramid
  • Bidirectional NNF (forward + backward) with EM voting
  • Seam blending at boundaries

Best for: simple backgrounds, textures, sky, grass, walls.

LaMa

Large Mask inpainting via Fast Fourier Convolutions (WACV 2022). ONNX model from Carve/LaMa-ONNX.

Best for: object removal, watermarks, text, complex scenes.

MI-GAN

Mobile Inpainting GAN (ICCV 2023) by Picsart AI Research. Pipeline ONNX with built-in pre/post-processing. 7ร— smaller than LaMa.

Best for: mobile deployments, fast inference, small objects.


API reference

Seika

The main entry point.

final seika = Seika();
// or specify the starting model:
final seika = Seika(model: SeikaModelInfo.migan);

inpaint

Future<SeikaInpaintResult> inpaint(
  Uint8List rgba,
  Uint8List mask,
  int width,
  int height, {
  SeikaInpaintOptions options = const SeikaInpaintOptions(),
})
  • rgba โ€” RGBA pixel data, width ร— height ร— 4 bytes
  • mask โ€” grayscale mask, width ร— height bytes. 255 = fill this pixel, 0 = keep original
  • options.engine โ€” auto (default), patchMatch, or neural
  • options.quality โ€” fast, balanced (default), or quality

Returns SeikaInpaintResult with:

  • image โ€” output Uint8List RGBA
  • engineUsed โ€” which engine ran
  • duration โ€” wall-clock time
  • analysis โ€” mask complexity metrics

analyze

Future<SeikaAnalysis> analyze(
  Uint8List rgba, Uint8List mask, int width, int height,
)

Runs the complexity analyzer without inpainting. Returns:

  • maskRatio โ€” fraction of image covered by mask
  • textureVariance โ€” background texture complexity
  • edgeCoherence โ€” regularity of edges near the mask
  • recommended โ€” SeikaEngine.patchMatch or SeikaEngine.neural

switchModel

Future<void> switchModel(SeikaModelInfo info)

Unloads the current model session and switches to a different model.

unloadModel

Future<void> unloadModel()

Releases the ONNX session from memory. The model file on disk is not deleted.


SeikaModelManager

Manages the lifecycle of a downloadable ONNX model.

final manager = SeikaModelManager(info: SeikaModelInfo.lama);

// Check availability
final ready = await manager.isAvailable();

// Download with progress
await for (final p in manager.download()) {
  print('${(p.fraction * 100).toInt()}% โ€” ${p.bytesReceived} bytes');
}

// Get path to load manually
final path = await manager.localPath();

// Delete from disk
await manager.delete();

SeikaModelInfo

Static model descriptors.

SeikaModelInfo.lama    // LaMa FP32, 209 MB
SeikaModelInfo.migan   // MI-GAN pipeline v2, 80 MB
SeikaModelInfo.all     // List<SeikaModelInfo> โ€” all registered models

Each entry has:

  • id, name, description, url, sha256
  • sizeBytes / sizeMb
  • type โ€” SeikaModelType.lama or SeikaModelType.migan
  • tags โ€” descriptive labels for UI display

SeikaInpaintOptions

const SeikaInpaintOptions({
  SeikaEngine engine  = SeikaEngine.auto,
  SeikaQuality quality = SeikaQuality.balanced,
})
Quality PatchMatch config Neural effect
fast patch=5, iter=2, single-scale same model, faster mask
balanced patch=7, iter=5, 3-level pyramid same model
quality patch=9, iter=8, 4-level pyramid same model

Quality only affects PatchMatch. Both LaMa and MI-GAN run the same ONNX model regardless of quality setting.


Model catalog โ€” adding a new model

Extend SeikaModelInfo in your own code or submit a PR:

static const myModel = SeikaModelInfo(
  id:          'my-model-v1',
  name:        'My Model',
  description: 'Custom inpainting model.',
  url:         'https://example.com/model.onnx',
  sha256:      'abc123...',
  sizeBytes:   50_000_000,
  version:     'my-model-v1',
  type:        SeikaModelType.lama,  // LaMa-compatible I/O
  tags:        ['custom'],
);

Models with type: SeikaModelType.lama must accept:

  • Input image: Float32 [1, 3, 512, 512] normalized to [0, 1]
  • Input mask: Float32 [1, 1, 512, 512] binary (1.0 = fill)
  • Output: Float32 [1, 3, 512, 512] in [0, 255] range

Models with type: SeikaModelType.migan must accept:

  • Input image: Uint8 [1, 3, H, W] RGB
  • Input mask: Uint8 [1, 1, H, W] (0 = fill, 255 = keep)
  • Output: Uint8 [1, 3, H, W] RGB

Platform support

Platform PatchMatch LaMa MI-GAN
Android โœ… โœ… โœ…
iOS ๐Ÿ”œ ๐Ÿ”œ ๐Ÿ”œ

License

MIT โ€” see LICENSE.

Libraries

seika
Smart inpainting for Flutter.
seika_bindings_generated