form-field-v1-state / README.md
hung-k-nguyen's picture
form-field-v1 release
62b1049
|
Raw History Blame Contribute Delete
3.89 kB
---
license: apache-2.0
pipeline_tag: image-classification
library_name: onnxruntime
language: [en]
tags:
- image-classification
- document-ai
- form-field-detection
- onnx
- open-weights
metrics:
- accuracy
---
# form-field-v1-state β€” open-weight
**A 96k-parameter filled/empty classifier for detected form fields.** Given a cropped field, it reads whether
the field is **filled** (filled / checked / signed) or **empty** (empty / unchecked / blank) β€” a single shared,
detector-agnostic model that supplies state for any of the `form-field-v1` detectors. ~0.4 MB, CPU-fast,
Apache-2.0, runs via ONNX Runtime with no custom code.
Part of the `form-field-v1` family β€” pair it with [`-nano`](https://huggingface.co/nutrientdocs/form-field-v1-nano)
(open) or [`-small`](https://huggingface.co/nutrientdocs/form-field-v1-small) /
[`-medium`](https://huggingface.co/nutrientdocs/form-field-v1-medium) (commercial).
- 🎯 **Try it:** [form-field-v1-demo](https://huggingface.co/spaces/nutrientdocs/form-field-v1-demo)
- πŸ† **Leaderboard:** [form-field-v1-leaderboard](https://huggingface.co/spaces/nutrientdocs/form-field-v1-leaderboard)
- πŸ“Š **Benchmark:** [form-field-v1-benchmark](https://huggingface.co/datasets/nutrientdocs/form-field-v1-benchmark)
## What it does
Field detectors locate `Text`, `Choice`, and `Signature` widgets; this model answers the follow-up question
**"is that field filled?"** on the detected crop. It takes a grayscale field crop, letterboxes it to **40Γ—160**
(aspect-preserving β€” form fields are wide, median aspect ~3.3, so a square resize destroys thin printed text),
and outputs a binary **empty (0) / filled (1)** label. State is mapped per type: Text β†’ filled/empty,
Choice β†’ checked/unchecked, Signature β†’ signed/blank.
## Results
Binary field-state accuracy, one shared evaluation.
| | Accuracy |
|---|---|
| **form-field-v1-state** (this model) | **0.981** |
| Training-free ink heuristic | 0.915 |
On a held-out synthetic split it reaches **0.981** vs a training-free ink-presence heuristic's **0.915**. On a
real filled form (the ACME credit-card application, 25 fields) it matches the heuristic at **23/25**.
## What's in this repo
| File | |
|---|---|
| `model.onnx` | ONNX graph, input `1Γ—1Γ—40Γ—160` grayscale, output `logits [n,2]` |
| `state_cnn.pt` | PyTorch state dict (depthwise-separable CNN, 96k params) |
Classes: `0 = empty`, `1 = filled`.
## Usage (ONNX Runtime)
```python
import numpy as np, onnxruntime as ort
from PIL import Image
sess = ort.InferenceSession("model.onnx", providers=["CPUExecutionProvider"])
def letterbox(crop, th=40, tw=160): # aspect-preserving, pad 255 (paper)
g = crop.convert("L"); w, h = g.size
s = min(tw / w, th / h); nw, nh = max(1, round(w * s)), max(1, round(h * s))
out = Image.new("L", (tw, th), 255); out.paste(g.resize((nw, nh), Image.BILINEAR), ((tw - nw) // 2, (th - nh) // 2))
return np.asarray(out, np.uint8)
crop = Image.open("field_crop.png") # a detected field, cropped from the page
x = (((letterbox(crop).astype(np.float32) / 255) - 0.5) / 0.5)[None, None] # normalize, shape [1,1,40,160]
filled = int(sess.run(None, {sess.get_inputs()[0].name: x})[0].argmax(1)[0]) == 1
```
## License & data
Apache-2.0. Trained on synthetic form renders (empty + filled + handwritten field crops, with on-the-fly
augmentation for scan robustness); no PII.
## About the author
<a href="https://nutrient.io/">
<img src="https://avatars2.githubusercontent.com/u/1527679?v=3&s=200" height="80" />
</a>
This project is maintained and funded by [Nutrient](https://nutrient.io/) - The deterministic document infrastructure enterprises run their highest-stakes workflows on: replayable output, clear exceptions, and full audit trails on the messy, regulated documents where AI alone breaks.