WeVisOne-VL
WeVisOne-VL is a vision-language model from WeChat Vision at Tencent, fine-tuned from Qwen3-VL to answer perception requests as tagged text, with a small pixel decoder that turns its boxes into masks and alpha mattes. LibreYOLO loads it through LibreVLM and serves five tasks from one checkpoint: detect, point, segment, matte and ocr.
- Tasks
- detection, point detection, instance segmentation, background removal, ocr
- Sizes
- 2b, 4b at 1440 px
- Install
pip install "libreyolo[vlm]"- Support tier
- Sibling tier, since v1.7.0. A separate product surface with its own factory and contract.
- Licenses
- Code MIT and Apache-2.0, weights Apache-2.0. Commercial use
Install
WeVisOne-VL needs the vlm extra, which pulls in transformers. The
backbone loads through the native Qwen3-VL classes in transformers, so no
code from the model repository runs.
pip install "libreyolo[vlm]"Predict
LibreWeVisOneVL is a Python class, not a .pt checkpoint: the LibreYOLO()
factory and the libreyolo CLI do not resolve it. Load it through
LibreVLM("wevisone-vl"), which constructs that class, or import
LibreWeVisOneVL directly. The first call downloads the weights from
Tencent's own Hugging Face repositories, pinned to one commit per size, into
weights/LibreWeVisOneVL2b/ (or 4b) under the current working directory.
The download includes mask_decoder.pt, the pixel decoder that the segment
and matte tasks run, and the pin keeps it on the same snapshot as the
backbone it was trained against.
device="auto", the default, picks CUDA, then Apple MPS, then CPU. On CUDA
the backbone loads in bf16, or fp16 on a GPU without bf16 support; on MPS and
CPU it loads in fp32. The pixel decoder always runs in fp32.
from libreyolo import LibreVLM, SAMPLE_IMAGE # The vocabulary is sticky: it holds for every later predict() until# set again. detect asks for the whole list in one generation.model = LibreVLM("wevisone-vl", names=["person", "railing"])result = model.predict(SAMPLE_IMAGE) for cls, xyxy in zip(result.boxes.cls, result.boxes.xyxy): print(result.names[int(cls)], xyxy.tolist())from libreyolo import LibreVLM, SAMPLE_IMAGE # referring=True sends one generation per phrase. A phrase the model# finds nothing for returns no box.model = LibreVLM( "wevisone-vl", names=["the person wearing white shoes", "a red car"], referring=True,)result = model.predict(SAMPLE_IMAGE) for cls, xyxy in zip(result.boxes.cls, result.boxes.xyxy): print(result.names[int(cls)], xyxy.tolist())from libreyolo import LibreVLM, SAMPLE_IMAGE # point sends one generation per vocabulary entry.model = LibreVLM("wevisone-vl", names=["person"], task="point")result = model.predict(SAMPLE_IMAGE) print(len(result.points)) # how many were foundprint(result.points.xy) # (N, 2) in original-image pixelsfrom libreyolo import LibreVLM, SAMPLE_IMAGE # segment and matte read each vocabulary entry as a phrase, so they# raise without one.model = LibreVLM("wevisone-vl", names=["person"], task="segment")result = model.predict(SAMPLE_IMAGE)print(result.boxes.xyxy)print(result.masks.data.shape) # (N, H, W) binary masks # set_task() switches tasks on the loaded weights, without reloading.model.set_task("matte")result = model.predict(SAMPLE_IMAGE)print(result.matte.array.shape) # (H, W) float32 alpha in [0, 1]result.save("people.png") # transparent PNG cutoutfrom libreyolo import LibreVLM, SAMPLE_IMAGE # ocr ignores the vocabulary. SAMPLE_IMAGE keeps this runnable; point# it at an image with text of your own.model = LibreVLM("wevisone-vl", task="ocr")result = model.predict(SAMPLE_IMAGE) for text, quad in zip(result.ocr.texts, result.ocr.data): print(repr(text), quad.tolist())from libreyolo import LibreWeVisOneVL, SAMPLE_IMAGE model = LibreWeVisOneVL(size="2b") # chat() returns the answer verbatim, tags included: boxes and points# are bins on a 0-1000 grid over the image.text = model.chat(SAMPLE_IMAGE, "point to: person")print(text)The five tasks share one loaded checkpoint, and set_task() switches between
them without reloading. Each task fills a different Results payload and
sends a different number of generations per image:
| Task | Generations per image | Fills |
|---|---|---|
detect | One for the whole vocabulary; with referring=True, one per phrase | result.boxes |
point | One per vocabulary entry | result.points |
segment | One per phrase, then one more backbone pass per phrase for the pixel decoder | result.boxes and binary result.masks |
matte | As segment | result.matte, the pixelwise maximum of every instance matte |
ocr | One, with the vocabulary ignored | result.ocr |
names= at construction calls set_classes() for you. Left unset, the
vocabulary is the COCO-80 names, so point sends 80 generations per image.
segment, matte and referring=True detection treat each entry as a
phrase, such as "the dog on the left", and raise ValueError at predict
time when no vocabulary was set. With referring=True each phrase gets the
referring instruction, which the model was tuned to answer with None when
nothing matches; a None answer returns no box for that phrase.
segment thresholds each instance matte at mask_threshold (default 0.5) to
get its binary mask. decoder_batch_size (default 1) sets how many instances
the pixel decoder refines per call. It bounds memory and does not change the
masks: refinement runs on a 1024x1024 canvas, at about 2 GB of fp32
activations per instance by the library's own figure. matte writes a
transparent PNG through result.save(), and result.cutout() returns the
same RGBA array in memory. For ocr, the released instruction asks for every
word in box format, and each box comes back as an axis-aligned quad with its
transcript, in reading order.
Confidence is a constant 1.0 on every box, point and OCR region, because
WeVisOne-VL emits no score: conf= only drops rows below that constant, it
does not rank them. On detect and segment, iou drops a later box of the
same class once it overlaps a kept one past the threshold. chat() returns
the raw answer with its coordinate tags, which is the route for the upstream
abilities LibreYOLO does not wrap as tasks, such as visual-prompt detection
and OCR grounding. See prediction for sources, streaming and
result handling.
Variants
Two sizes, fine-tuned from Qwen3-VL-2B-Instruct and Qwen3-VL-4B-Instruct.
LibreVLM("wevisone-vl") loads 2B, as do the aliases wevisone, wevisonevl
and wevisone-vl-2b; LibreVLM("wevisone-vl-4b") loads 4B, and
LibreWeVisOneVL(size="4b") is the class form. Both declare a nominal 1440 px
input, but the Qwen processor resizes each image within a pixel budget set by
min_pixels and max_pixels (16x32x32 to 2025x32x32 pixels by default), so
that figure is not a fixed operating resolution. Lowering max_pixels shrinks
the image the backbone sees, trading accuracy for speed.
Tencent reports per-task results for both sizes in the upstream README, and LibreYOLO has not reproduced them. LibreYOLO's recorded check ran the 2B checkpoint through all five tasks on CPU in fp32, and the ported pixel decoder matches the upstream module's logits to within 1e-4. The 4B decoder weights load against the 4B configuration, but the 4B backbone has not been run end to end, and no CUDA run is recorded for either size.
LibreYOLO exposes this family for prediction only. train() raises
NotImplementedError because the upstream release ships no training recipe
for the backbone or the pixel decoder; Qwen3-VL is the VLM that trains, see
VLM fine-tuning. val() raises because a
constant confidence would make COCO mAP misleading, and export() raises for
every format.
Licensing
Check the license on the Hugging Face repository of the specific weights you download. Every checkpoint in the LibreYOLO org carries one, and they are not always the same across a family. That repository is the authoritative source; the summary below describes what applied when this page was last verified.
This is a description of the licenses involved, not legal advice. If the answer matters commercially, read the licenses yourself and take your own counsel.
- Original work
- WeVisOne-VL, Tencent (WeChat Vision)
- Upstream license
- Apache-2.0
- Upstream source
- github.com/Tencent/WeVisOne-VL
- LibreYOLO code
- MIT
- Weights
- Apache-2.0, distributed by their authors. LibreYOLO does not host or mirror them.
- Interpretation
- Apache-2.0 is a permissive license, so the WeVisOne-VL code and weights can be used in commercial and closed-source products. Redistributing them means keeping the license text and notices, and the license grants a patent license. Tencent declares Apache-2.0 on both Hugging Face repositories LibreYOLO downloads, tencent/WeVisOne-VL-2B and tencent/WeVisOne-VL-4B, in the license tag and LICENSE file. Each LICENSE prefixes the Apache-2.0 text with a Tencent preamble that adds no restrictions, and names the model it builds upon, Qwen3-VL-2B-Instruct or Qwen3-VL-4B-Instruct (Apache-2.0, Copyright 2024 Alibaba Cloud). LibreYOLO's wrapper is MIT; the pixel decoder, feature bridge, prompts and response parser it ports from the Tencent/WeVisOne-VL repository keep Apache-2.0, with their notices in libreyolo/models/wevisone/NOTICE. The Qwen3-VL backbone runs from transformers, so no Qwen code is copied. The weights are not redistributed: they download from Tencent at pinned revisions, 798ffc7c87ad6104410812b589e240ba32d92315 for 2B and 4a7bed92750ac9d8b8a124cede14759036932dc2 for 4B.
Citation
@article{liang2026wevisone,
title={WeVisOne-VL: Unified Visual Perception from Objects to Pixels},
author={Liang, Tianming and Pan, Junyi and Li, Yuxi and Fu, Canmiao and Huang, Zhipeng and Sun, Chong and Li, Chen and Lyu, Jing},
journal={arXiv preprint arXiv:2610.31329},
year={2026}
}Copied from the authors' citation block at github.com/Tencent/WeVisOne-VL#-citation.