- Rust 76.7%
- Scheme 17%
- Makefile 4.7%
- Shell 1.1%
- Common Lisp 0.5%
| .builds | ||
| examples | ||
| scripts | ||
| src | ||
| tests | ||
| vision | ||
| .gitignore | ||
| AGENTS.md | ||
| Cargo.lock | ||
| Cargo.toml | ||
| jpkg.sexp | ||
| LICENSE | ||
| Makefile | ||
| README.md | ||
| SECURITY.md | ||
| test-runner.ss | ||
jerboa-vision
jerboa-vision is the Rust detector that replaces the SSD review app's OpenCV
and NumPy dependency path.
It also contains the Jerboa-facing OCR contract in vision/ocr.ss. The OCR
engine is deliberately a backend, not a Scheme reimplementation of Tesseract:
backends return bounded word records in rendered-image coordinates, and Jerboa
owns normalization, nearby-text lookup, and candidate matching. This lets the
same recognizer run on the server, in the browser path, and later inside the APK
using Android ML Kit or another platform text recognizer.
The first implementation is a native CLI proof:
cargo run --release -- detect /path/to/rendered-ssd-page.png > session.json
It emits JSON with:
- image width/height
- detected colored SSD cells
- adjacent-cell groups
The same core algorithm can run from a CLI, plain C ABI, or Android JNI. Android passes rendered bitmap RGBA bytes directly to Rust, so the APK can detect SSD boxes without Python, OpenCV, MuPDF, Poppler, temp image files, or external process forks.
Detector
Current detector:
- Load an RGB image.
- Convert pixels to HSV-like saturation/value.
- Mask colored cells with
s > 12,120 < v < 252. - Run 8-connected-components.
- Keep components with SSD-cell-like size/density.
- Group adjacent cells with the Python review app's adjacency heuristic.
- Emit stable
c0001andg0001ids.
Inputs are treated as untrusted. The CLI opens the file once, checks its compressed size and image header before decoding, accepts only PNG/JPEG, and enforces width, height, pixel, decoded-byte, component, queue, grouping, output, and wall-clock budgets. Cell grouping uses a spatial grid rather than an all-pairs graph.
detect always delegates decoding to a disposable Unix worker with a 384 MiB
process-memory limit (a kernel-accounted supervisor on macOS and an address-space
limit elsewhere), ten CPU seconds, a seven-second parent deadline, and a small
file-descriptor limit. The command fails closed on platforms where those worker
controls are unavailable. Deployments may add stricter container or cgroup
limits, but must not bypass the detect entry point for uploads.
This intentionally starts with the color detector only. Contour/edge fallback can be added after parity scoring shows it is worth the complexity.
Android
Build the Android arm64 shared library:
make android-jni
The staged output is:
build/android-jni/arm64-v8a/libjerboa_vision.so
The shared library exports:
jerboa_vision_detect_file_jsonfor plain C callers with a rendered PNG/JPEG path.jerboa_vision_detect_rgba_jsonfor plain C callers with rendered RGBA bytes.jerboa_vision_last_errorfor ABI error diagnostics.Java_com_sfb_ssdreview_SsdVisionBridge_nativeDetectRgbaJsonfor the Android SSD review APK.
The JNI method returns compact detection JSON. On failure it returns
{"ok":false,"error":"..."} so Kotlin can fall back to its detector without
crashing the app.
The C ABI and JNI entry points decode in the caller's process and enforce the
application-level byte, dimension, work, decode wall-clock, and output budgets,
but not the disposable-worker OS limits used by detect. Sandbox untrusted
uploads at the process level (container/cgroup) when calling these entry points
directly.
OCR Rule
OCR is deliberately not hard-coded to one engine. The required flow is:
rendered SSD bitmap -> backend OCR words -> vision/ocr.ss nearby text helpers
Android currently uses bundled Google ML Kit text recognition as the OCR backend. A desktop/server build can use another backend, but it must return the same bounded word shape.
The backend word shape is:
((text . "PH-1") (conf . 88) (bbox . (x1 y1 x2 y2)))
The coordinate space is always the rendered SSD image in pixels. That keeps the rest of the SSD recognizer independent of whether OCR came from Android ML Kit, a Rust native backend, or a browser-side implementation.
Browser Rule
The browser path should remain:
pdf.js page render -> Canvas ImageData -> jerboa-vision WASM/native bridge -> session JSON
No native PDF renderer is required on Termux for the browser/server path.