# Seg2Click: Learning Pixel-Level Action Masks for GUI Visual Grounding
Paper |
Models |
Data
## News
- **2026-10-20:** The [Seg2Click interactive demo](https://huggingface.co/spaces/jcwang0602/Seg2Click-Demo)
is now live on Hugging Face Spaces. Upload a GUI screenshot, enter an instruction, and inspect the predicted
pixel-level action mask and executable click point.
**Left:** ScreenSpot-Pro accuracy versus number of training screenshots; marker area denotes backbone size. Seg2Click and GUI-Actor share the same ~1M-screenshot grounding data.
**Right:** Qwen3.5-4B predicts the action mask and click for "run the code" (a 55×49 px toolbar icon).
##
Highlights
- **Problem.** Coordinate generation with single-point supervision reduces a spatially extended target to one
position, even though many click locations are valid. Region-based patch scoring relaxes this, but its spatial
decisions remain tied to visual-token footprints that may span the target, neighboring controls and background.
- **Executable-region prediction.** Seg2Click reformulates GUI grounding as predicting *where a click can validly
execute the instruction*. It learns a box-supervised **action mask** and computes the click with a
deterministic rule.
- **Sub-token action fields.** A query-conditioned pixel-shuffle decoder learns distinct scores *within* each
visual-token footprint. This yields masks at the processed-screenshot resolution and makes Seg2Click well suited
to small, densely arranged controls.
- **Simple inference.** One mask-producing forward pass per input. No verifier, zoom-in pass, contour labels or
coordinate-token generation.
- **Strong results.** Seg2Click-9B reaches **92.1 / 94.1 / 55.2** on ScreenSpot / ScreenSpot-v2 / ScreenSpot-Pro.
On matched backbones it outperforms both coordinate generation and patch-based grounding (GUI-Actor).
##
Method
1. **Query-conditioned visual features.** At each visual-token location, the post-merge vision feature
**z** and the final language-model state **v** are fused residually: **f** = **v** + MLP([**v**; φ(**z**)]).
The final hidden state of an appended action placeholder `[ACT]` serves as the action query **q**. It modulates
the fused grid with a learned scale and bias, as in FiLM.
2. **Sub-token mask prediction.** Two 3×3 convolutional blocks and an output projection produce s² logits per
grid cell. Pixel shuffle rearranges them into a dense field L at the processed-image resolution
(s = 32 for Qwen3.5, 28 for Qwen2.5-VL).
3. **Box-supervised learning.** Target boxes are rasterized into binary masks. Training uses
`0.5·BCE + 0.5·Dice`, with LoRA on the backbone and an FP32 mask decoder.
4. **Deterministic click extraction.** Threshold `sigmoid(L)` at 0.5, keep the largest 4-connected component,
and click its centroid.
##
Main Results
Hit@1 (%): a prediction is correct when its single click lies inside the annotated target box.
| Method | Backbone | ScreenSpot | ScreenSpot-v2 | ScreenSpot-Pro |
|---|---|:---:|:---:|:---:|
| UI-TARS-7B | Qwen2-VL-7B | 89.5 | 91.6 | 35.7 |
| Jedi-7B | Qwen2.5-VL-7B | – | 91.7 | 39.5 |
| GUI-Actor-3B | Qwen2.5-VL-3B | – | 91.0 | 42.2 |
| GUI-Actor-3B + Verifier | Qwen2.5-VL-3B | – | 92.4 | 45.9 |
| GUI-Actor-7B | Qwen2.5-VL-7B | – | 92.1 | 44.6 |
| GUI-Actor-7B + Verifier | Qwen2.5-VL-7B | – | 92.5 | 47.7 |
| **Seg2Click-3B** | Qwen2.5-VL-3B | 89.1 | 91.3 | 47.0 |
| **Seg2Click-0.8B** | Qwen3.5-0.8B | 87.4 | 89.9 | 44.3 |
| **Seg2Click-2B** | Qwen3.5-2B | 89.8 | 91.6 | 49.8 |
| **Seg2Click-4B** | Qwen3.5-4B | 91.6 | 93.1 | 50.2 |
| **Seg2Click-9B** | Qwen3.5-9B | **92.1** | **94.1** | **55.2** |
The paper reports full per-domain and per-target-type breakdowns.
##
Installation
```bash
git clone https://github.com/jcwang0602/Seg2Click.git
cd Seg2Click
conda create -n seg2click python=3.10 -y
conda activate seg2click
# 1. PyTorch matching your CUDA driver (tested: torch 2.13 + CUDA 13.2)
pip install torch torchvision --index-url https://download.pytorch.org/whl/
# 2. Seg2Click and its dependencies (transformers 5.16.1, peft 0.20.0, ...)
pip install -e .
# 3. Optional: fast Qwen3.5 linear-attention kernels, and FlashAttention-2
# (required to train Qwen2-VL / Qwen2.5-VL / Qwen3-VL)
pip install -e ".[qwen35-kernels]"
pip install flash-attn --no-build-isolation
```
##
Data Preparation
**Training data.** Seg2Click uses the same processed grounding data as
[GUI-Actor](https://huggingface.co/datasets/cckevinn/GUI-Actor-Data): 1,255,208 records from six sources
(UGround, GUI-Env, GUI-Act, AMEX, AndroidControl, Wave-UI). Download it to `data/GUI-Actor-Data/`:
```text
data/GUI-Actor-Data/
├── uground_bbox.json + Uground/images/
├── guienv_bbox.json + guienvs/images/
├── guiact_bbox.json + GUIAct/web_imgs/
├── amex_bbox.json + screenshots/
├── androidcontrol_bbox.json + AndroidControl/tfrecord/images/
└── wave_ui_bbox.json + Wave-UI/images_fixed/
```
The paths are listed in [`data/data_config.yaml`](data/data_config.yaml). Then build the line-indexed
`.jsonl` / `.jsonl.idx` files once. This is a streaming conversion that never overwrites existing files:
```bash
python scripts/prepare_training_data.py # convert
python scripts/prepare_training_data.py --check-only # verify an existing copy
```
Box masks are rasterized on the fly at the visual-token grid; no mask files are generated.
**Evaluation data.**
```text
data/eval/
├── ScreenSpot/data/*.parquet # huggingface.co/datasets/rootsautomation/ScreenSpot
├── ScreenSpot_v2/data/*.parquet # huggingface.co/datasets/HongxinLi/ScreenSpot_v2
└── ScreenSpot-Pro/ # huggingface.co/datasets/likaixin/ScreenSpot-Pro
├── annotations/*.json
└── images/
```
**Base models.** Put the Hugging Face snapshots under `checkpoints/`, e.g. `checkpoints/Qwen3.5-0.8B`,
`checkpoints/Qwen3.5-9B`, `checkpoints/Qwen2.5-VL-3B-Instruct`, `checkpoints/Qwen3-VL-2B-Instruct` or
`checkpoints/Qwen2-VL-2B-Instruct`. Scripts run offline by default; to use another
location, set `BASE_MODEL=/path/to/model`.
##
Training
All recipes train for one epoch on one node with 8 GPUs (effective batch 32, LoRA r=16, lr 1e-4, cosine decay):
```bash
bash scripts/train/qwen35_seg2click.sh # Seg2Click-0.8B
MODEL_SIZE=2b bash scripts/train/qwen35_seg2click.sh # 2b | 4b | 9b
MODEL_SIZE=3b bash scripts/train/qwen25vl_seg2click.sh # Qwen2.5-VL-3B (or 7b)
bash scripts/train/qwen3vl_seg2click.sh # Qwen3-VL-2B-Instruct
bash scripts/train/qwen2vl_seg2click.sh # Qwen2-VL-2B-Instruct
```
Common overrides: `OUTPUT_DIR`, `BASE_MODEL`, `NUM_GPUS`, `BATCH_SIZE`, `GRAD_ACCUM`, `LR`, `SEED`, and
`RESUME_FROM_CHECKPOINT` (a `checkpoint-*` directory inside `OUTPUT_DIR`). If you change `NUM_GPUS`, adjust
`BATCH_SIZE` / `GRAD_ACCUM` to keep the effective batch at 32. Each script lists its defaults in its header.
##
Evaluation
Each benchmark runs as an independent process. The launcher starts all three in parallel, one GPU each:
```bash
CHECKPOINT=checkpoints/Seg2Click-Qwen3.5-0.8B GPUS=0,1,2 bash scripts/eval/evaluate_seg2click.sh
```
Or evaluate a single benchmark:
```bash
PYTHONPATH=. python eval/evaluate_seg2click.py \
--checkpoint checkpoints/Seg2Click-Qwen3.5-0.8B \
--benchmark screenspot_pro --output_dir results/seg2click-0.8b
```
Results are written to `//metrics.json` (overall and per-category Hit@1) and
`predictions.json`.
### Example usage
```python
from PIL import Image
from seg2click.predictor import Seg2ClickPredictor
predictor = Seg2ClickPredictor.from_pretrained("checkpoints/Seg2Click-Qwen3.5-0.8B") # cuda:0 if available
image = Image.open("screenshot.png")
pred = predictor.predict(image, "close this window")
x, y = pred.point # normalized click on the original screenshot
click_px = (x * image.width, y * image.height)
mask = pred.probability # [H, W] action-mask probabilities (processed resolution)
print(click_px, pred.decode_reason) # e.g. (1864.3, 186.4) component_centroid
```
##
Model Zoo
| Model | Backbone | ScreenSpot | ScreenSpot-v2 | ScreenSpot-Pro |
|---|---|:---:|:---:|:---:|
| [Seg2Click-0.8B](https://huggingface.co/jcwang0602/Seg2Click-Qwen3.5-0.8B) | Qwen3.5-0.8B | 87.4 | 89.9 | 44.3 |
| [Seg2Click-2B](https://huggingface.co/jcwang0602/Seg2Click-Qwen3.5-2B) | Qwen3.5-2B | 89.8 | 91.6 | 49.8 |
| [Seg2Click-3B](https://huggingface.co/jcwang0602/Seg2Click-Qwen2.5-VL-3B) | Qwen2.5-VL-3B | 89.1 | 91.3 | 47.0 |
| [Seg2Click-4B](https://huggingface.co/jcwang0602/Seg2Click-Qwen3.5-4B) | Qwen3.5-4B | 91.6 | 93.1 | 50.2 |
| [Seg2Click-9B](https://huggingface.co/jcwang0602/Seg2Click-Qwen3.5-9B) | Qwen3.5-9B | 92.1 | 94.1 | 55.2 |
All five checkpoints are publicly available on Hugging Face via the links above. Each checkpoint contains
a merged backbone (`model.safetensors`), config, tokenizer and processor, and the FP32 mask decoder
(`phase1_mask_decoder.json` / `.safetensors`). No separate LoRA adapter is needed.
For example, download the 0.8B checkpoint to the path used by the evaluation scripts:
```bash
hf download jcwang0602/Seg2Click-Qwen3.5-0.8B --local-dir checkpoints/Seg2Click-Qwen3.5-0.8B
```
Then use `Seg2ClickPredictor.from_pretrained("checkpoints/Seg2Click-Qwen3.5-0.8B")` as shown above.
Model licenses are documented in each Hugging Face repository; they are separate from the source-code license.
##
Repository Structure
```text
seg2click/
├── predictor.py # checkpoint loading + inference API
├── pixel_shuffle.py # query-conditioned pixel-shuffle mask decoder
├── fusion.py # residual vision / LM feature fusion (Qwen3.5, Qwen3-VL)
├── mask_model.py # Qwen3.5 training wrapper, BCE + Dice loss
├── qwen_vl_model.py, qwen_vl_decoder.py, checkpointing.py # Qwen2-VL / Qwen2.5-VL
├── mask_utils.py # box-mask rasterization and loss helpers
├── dataset.py, runtime.py, constants.py # data pipeline
training/ # one training entry per backbone family
eval/ # ScreenSpot / ScreenSpot-v2 / ScreenSpot-Pro evaluation
scripts/train, scripts/eval
```
##
Acknowledgements
This codebase builds on [GUI-Actor](https://github.com/microsoft/GUI-Actor), whose processed training data and
data pipeline we reuse. We also thank the authors of
[Qwen-VL](https://github.com/QwenLM), [Transformers](https://github.com/huggingface/transformers),
[PEFT](https://github.com/huggingface/peft) and the ScreenSpot, ScreenSpot-v2 and ScreenSpot-Pro benchmarks.
##
Citation
```bibtex
@article{seg2click2026,
title = {Seg2Click: Learning Pixel-Level Action Masks for GUI Visual Grounding},
author = {Wang, Jingchao and Huang, Dingjiang},
journal = {arXiv preprint arXiv:2610.16234},
year = {2026},
doi = {10.48550/arXiv.2610.16234},
url = {https://arxiv.org/abs/2610.16234}
}
```
##
License
MIT. See [LICENSE](LICENSE).