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 ร 4bytesmaskโ grayscale mask,width ร heightbytes.255= fill this pixel,0= keep originaloptions.engineโauto(default),patchMatch, orneuraloptions.qualityโfast,balanced(default), orquality
Returns SeikaInpaintResult with:
imageโ outputUint8ListRGBAengineUsedโ which engine randurationโ wall-clock timeanalysisโ 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 masktextureVarianceโ background texture complexityedgeCoherenceโ regularity of edges near the maskrecommendedโSeikaEngine.patchMatchorSeikaEngine.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,sha256sizeBytes/sizeMbtypeโSeikaModelType.lamaorSeikaModelType.migantagsโ 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