xiaomi-lens-mnn
Run Xiaomi-Lens-0 (4.6 B vision-language grounding + segmentation model) on MNN with one command. No torch, no transformers, no 8.6 GB bf16 checkpoint — the CLI downloads three MNN graphs plus the embedding table and runs the vision encoder, the Qwen3.5 hybrid decoder and the pixel-mask decoder entirely in MNN (CPU).
uvx xiaomi-lens-mnn --image cow.jpg \
--prompt 'a large brown cow standing in the grass'
Output:
[xiaomi-lens-mnn] fp16 result
prompt tokens : 255 (216 image, grid 24x36)
completion : 32 tokens
bboxes : [[462, 302, 854, 754]]
masks : 1
timings : vision 1.80s, preprocess 0.04s, prefill 70.11s, decode 6.74s, mask 0.85s, total 79.54s
overlay : /path/lens-out/overlay.png
<bbox>[462, 302, 854, 754]</bbox><mask><|mask|></mask>
Files written to lens-out/: overlay.png (coloured masks on the input image),
mask_0.png, masks.npy (float32 logits on the 384×576 model canvas, K×H×W)
and result.json. result.json also records bboxes mapped back to the
original image (0–1000), the raw bboxes_canvas, content_box and
original_size.
Install
uvx runs it without installing anything globally:
# first run downloads ~10.5 GB (fp16) into ~/.cache/xiaomi-lens-mnn/fp16
uvx xiaomi-lens-mnn --image photo.jpg --prompt "the red car on the left"
# smaller / faster download (6.2 GB), slight mask degradation
uvx xiaomi-lens-mnn --image photo.jpg --precision int8
# ModelScope mirror (recommended from mainland China)
uvx xiaomi-lens-mnn --image photo.jpg --source ms
# fully offline after downloading the repo
uvx xiaomi-lens-mnn --image photo.jpg --model-dir ./Xiaomi-Lens-0-MNN-fp16
Classic install: pip install xiaomi-lens-mnn then xiaomi-lens-mnn --help.
Requires Python 3.10–3.14 and a platform with an MNN wheel (macOS arm64/x86_64,
Linux aarch64/x86_64, Windows x86_64).
If the package is not on your PyPI index yet, run the wheel attached to the model repo:
uvx --from https://huggingface.co/yunfengwang/Xiaomi-Lens-0-MNN-fp16/resolve/main/dist/xiaomi_lens_mnn-0.1.0-py3-none-any.whl \
xiaomi-lens-mnn --image photo.jpg
# mainland-China mirror
uvx --from https://modelscope.cn/models/yunfeng/Xiaomi-Lens-0-MNN-int8/resolve/master/dist/xiaomi_lens_mnn-0.1.0-py3-none-any.whl \
xiaomi-lens-mnn --image photo.jpg --precision int8 --source ms
CLI
xiaomi-lens-mnn --image IMAGE [--prompt PROMPT] [--precision fp16|int8]
[--source auto|hf|ms] [--model-dir DIR] [--cache-dir DIR]
[--threads N] [--max-new N] [--no-think] [--out-dir DIR] [--json]
| flag | default | meaning |
|---|---|---|
--image |
required | input image, any format Pillow reads |
--prompt |
cow demo prompt | referring expression / question |
--precision |
fp16 |
fp16 or int8 MNN weights |
--source |
auto |
hf, ms, or auto (try HF, fall back to ModelScope) |
--model-dir |
— | already downloaded repo directory; skips the network |
--cache-dir |
~/.cache/xiaomi-lens-mnn |
download cache |
--threads |
15 |
MNN CPU threads |
--max-new |
128 |
maximum generated tokens |
--no-think |
off | disable the <think> block (shorter, faster, less reasoning) |
--out-dir |
lens-out |
output directory |
--json |
off | print the full result JSON |
Python API
from PIL import Image
from xiaomi_lens_mnn import LensMNN
from xiaomi_lens_mnn.download import ensure_model
model_dir = ensure_model("fp16", "auto") # cached after the first call
runner = LensMNN(model_dir, threads=15)
result = runner.run(Image.open("cow.jpg"), "the large brown cow", out_dir="out")
print(result["bboxes"], result["masks"], result["timings"])
Speed
Measured on an Apple M5 Pro (48 GB), CPU backend, 15 threads, same 586×394 image,
255-token prompt (216 image tokens), 32 generated tokens, one mask. Lower is
better. Full data: benchmarks.json in the weight repos.
| stage | torch fp32 (HF) | ONNX Runtime | MNN fp16 | MNN int8 |
|---|---|---|---|---|
| vision (24×36 grid) | 0.74 s | 1.41 s | 1.80 s | 1.84 s |
| LLM prefill (255 tok) | 24.78 s | 84.46 s | 70.11 s | 64.46 s |
| LLM decode (32 tok) | 600.07 s | 10.36 s | 6.74 s | 6.10 s |
| pixel mask (1 mask) | 0.35 s | 0.15 s | 0.85 s | 0.53 s |
| end to end | 625.9 s | 96.4 s | 79.5 s | 73.0 s |
| speed-up vs torch | 1× | 6.5× | 7.9× | 8.6× |
| weights on disk | 18.5 GB (fp32) | 17.6 GB | 10.5 GB | 6.2 GB |
| decode per token | 18.75 s | 0.324 s | 0.211 s | 0.191 s |
The MNN decoder graph is incremental (one token per session run), so its
prefill is a 255-step loop and is slower than torch's parallel prefill. Vision
and mask are also a little slower in MNN than in torch/ONNX. The win is decode:
MNN is 89× faster per token than the official HF CPU path, because torch's
GatedDeltaNet conv1d falls back to the slow im2col kernel there. End to end,
MNN fp16 is 7.9× and int8 8.6× faster than torch while using 44 % / 67 % less
disk. int8 is ~8 % faster than fp16, with a small mask drift (see below).
ONNX Runtime sits between them: its fp32 decoder is numerically exact but pays a large MatMul cost on CPU; its fp16 ViT/mask graphs are fast (mask 0.15 s).
Precision / verification
| check | fp16 | int8 |
|---|---|---|
| generated text / bbox vs HF | identical | identical |
| pixel-mask IoU vs HF | 1.0000 | 0.9970 |
| pixel agreement | 100 % | 99.970 % |
| decoder logits max / mean abs error (8 steps) | 1.8e-3 / 3.6e-5 | 0.4268 / 0.3519 |
| ViT merged max / mean abs error | 7.2e-3 / 3.6e-5 | 1.095 / 2.8e-3 |
| mask logits max / mean abs error | 6.0e-3 / 2.2e-3 | 3.143 / 0.2852 |
int8 is weight-only 8-bit block quantization (--weightQuantBits 8 --weightQuantAsymmetric --weightQuantBlock 64). The ViT and mask decoder show
visible numerical drift but the final binary mask is still within IoU 0.997.
Use fp16 when mask boundaries matter.
How it works
Three MNN graphs:
lens_vit.mnn— Qwen3.5 ViT (24 blocks, 1024 hidden, patch 16, merge 2). Position-embedding interpolation and 2-D rotary cos/sin are computed in numpy outside the graph, so arbitrary patch grids work. Outputs pre-merge features (for the mask decoder) and merged image features (for the LLM).lens_llm_decode.mnn— one-token Qwen3.5-4B decoder step with explicit KV / conv / recurrent states. 24 GatedDeltaNet layers + 8 full-attention layers, hidden 2560, vocab 248,078 (tied lm_head split into two 124,039-wide MatMuls). M-RoPE positions are computed in numpy with interleaved[11, 11, 10]sections. The<|mask|>hidden state is captured while decoding.lens_mask.mnn— pixel-mask decoder over the 24×36 patch canvas: mask embedding, patch self-attention, channel reduce, two transposed convolutions + 4× bilinear upsampling, output 32 masks at 384×576.
The package itself has no torch/transformers dependency. It ports the Qwen2-VL
smart-resize + patchify (BICUBIC, factor 32, min 65,536 / max 8,388,608 pixels),
the official chat template, the tokenizer (tokenizers), M-RoPE and the vision
position/rotary tables to numpy/Pillow. Parity with the official processor is
byte-exact for token ids and < 1e-2 max for pixel values (fp16 rounding).
Weights
| repo | precision | files |
|---|---|---|
| Xiaomi-Lens-0-MNN-fp16 | fp16 | ViT 633 MB, mask 159 MB, decoder 7.8 GB, embeddings 1.2 GB |
| Xiaomi-Lens-0-MNN-int8 | int8 | ViT 337 MB, mask 85 MB, decoder 4.2 GB, embeddings 1.2 GB |
| Xiaomi-Lens-0-onnx | fp32 / fp16 | ONNX graphs for re-conversion |
ModelScope mirror: yunfeng/Xiaomi-Lens-0-MNN-fp16 / ...-int8.
The first run downloads manifest.json, the three graphs and the fp16 embedding
table into ~/.cache/xiaomi-lens-mnn/<precision>/. Downloads resume after
interruption. No token is required; public repos.
Limitations
- Fixed 576×384 input canvas (24×36 patches). Every image is fitted onto
that canvas: near-3:2 images are resized directly, others are centre
letterboxed with neutral grey and boxes/masks are mapped back to the original
image. High-resolution detail is therefore reduced to ~0.22 MP; crop first if
you need small-object accuracy. Re-export
lens_maskfor other canvas sizes. - The decoder graph is incremental; prefill is sequential. See the speed table.
- CPU only (MNN Metal is not enabled; the release was verified on CPU).
- An image + one prompt per process. The states are reset for each
run(). int8mask boundaries drift slightly (IoU 0.997 vs fp16); prefer fp16 for precise segmentation.- The mask decoder is trained for the model's
<|mask|>token; the CLI overlays only answer masks by default (thinking masks are counted but not drawn).
License
Apache-2.0, following the Xiaomi-Lens-0 release. The MNN graphs are derivative weights of SeerRay-Lab/Xiaomi-Lens-0.
Metadata
Release files for xiaomi-lens-mnn 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| xiaomi_lens_mnn-0.1.0.tar.gz | 25.0 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| xiaomi_lens_mnn-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 48.2 kB
Release files / xiaomi_lens_mnn-0.1.0.tar.gz
| Download URL | xiaomi_lens_mnn-0.1.0.tar.gz |
|---|---|
| Size | 25.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
76e7acc3cda2c87301f368aef45aff08871e3294204461d7cad01a8c70c14c1b
|
|
BLAKE2b-256 checksum How to use checksums |
fd6ef5e0e754ccc7bd2f7d270519053f6014019b00eb1b75b22fc4dc85d60898
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / xiaomi_lens_mnn-0.1.0-py3-none-any.whl
| Download URL | xiaomi_lens_mnn-0.1.0-py3-none-any.whl |
|---|---|
| Size | 23.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
32274bac4a91793be1801f96436078b7f2b6d68c9bbfdbc700048a2348814ba9
|
|
BLAKE2b-256 checksum How to use checksums |
14aaea2100d51200fe4c4d878dd395e698fb324da5fe868bfe91379a3918c26b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Security for xiaomi-lens-mnn 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.