This toolkit accompanies the work presented in the article TargetFinder: Detecting Widgets from Pixels on Desktop Interfaces.
It provides a real-time detection system using the YOLO26 model to predict the bounding boxes of GUI widgets from desktop screenshots — without requiring access to application internals or accessibility APIs.
The system is lightweight and easy to integrate, enabling the implementation of advanced interaction techniques.
As proof of concept, we include two interaction techniques built on top of TargetFinder:
Compatibility Note
TargetFinder uses themsslibrary for fast screen capture, and the detection engine is theoretically cross-platform. However, the system has been validated on Windows 10/11 and Linux (Ubuntu X11), and macOS (tested on Apple Silicon in beta). Other Linux setups (e.g., distributions other than Ubuntu, or Wayland instead of X11) may also require adjustments.
pip install target-finder-toolkitOr after cloning the repository:
pip install .Linux prerequisites (click to expand)
During installation on Linux, you may need to install some system packages to avoid common errors:
-
evdev build tools
If installation fails due to missingevdevheaders:sudo apt install build-essential python3-dev
-
X11 vs Wayland screen capture
mssrelies on X11 (does not work with Wayland sinceXGetImageis not available).
If you see the following error:mss.exception.ScreenShotError: XGetImage() failedSwitch to an X11 session at login.
-
Qt X11 plugin (
xcb)
If you encounter errors like:qt.qpa.plugin: Could not load the Qt platform plugin "xcb" ...Install the required libraries:
sudo apt install libxcb-cursor0 libxkbcommon-x11-0 libxcb-xinerama0
-
tk (MouseInfo) support
pyautoguiorpynputmay fail iftkis missing:sudo apt install python3-tk python3-dev
Minimal examples:
import time
from target_finder_toolkit.targetfinder import TargetFinder
from target_finder_toolkit import postprocess as pp
def on_change(detections, added, removed, frame):
"""
Called every time the detector refreshes.
- detections: [{id, x, y, width, height, score, class_id, class_name} ...]
- added: new detections since last callback
- removed: detections that disappeared since last callback
- frame: RGB numpy array (only if with_frame=True), else None
"""
pp.pretty_print_change(detections, added, removed)
if __name__ == "__main__":
det = TargetFinder()
det.set_callback(on_change, with_frame=False, diff_iou=0.5)
det.start()
print("TargetFinder started — press Ctrl+C to stop.")
try:
while True:
time.sleep(0.2)
except KeyboardInterrupt:
det.stop()
print("Stopped.")import time
from pathlib import Path
from target_finder_toolkit.targetfinder import TargetFinder
from target_finder_toolkit import postprocess as pp
# global flag to ensure we only save once
_saved_once = False
def on_change(detections, added, removed, frame):
"""
Save a single annotated frame and crops (first time we get detections+frame).
"""
global _saved_once
if _saved_once:
return
if frame is None or not detections:
return
out_dir = Path("out")
out_dir.mkdir(parents=True, exist_ok=True)
# Save annotated frame
annot_path = out_dir / "annotated.png"
pp.save_annotated(annot_path, frame, detections)
# Extract and save crops
crops = pp.extract_crops(frame, detections)
crop_paths = pp.save_crops(out_dir / "crops", crops, prefix="crop", ext=".png")
print(f"Saved annotated frame: {annot_path}")
print(f"Saved {len(crop_paths)} crops under: {out_dir/'crops'}")
_saved_once = True
if __name__ == "__main__":
det = TargetFinder()
# with_frame=True is required to receive the RGB frame in the callback
det.set_callback(on_change, with_frame=True, diff_iou=0.5)
det.start()
print("TargetFinder started — will save once when detections are available. Ctrl+C to stop.")
try:
while True:
time.sleep(0.2)
except KeyboardInterrupt:
det.stop()
print("Stopped.")from target_finder_toolkit.targetfinder import TargetFinder
if __name__ == "__main__":
image_path = "screenshot.png" # change this to your image
det = TargetFinder()
detections = det.detect_image(
image_path,
save_annotated=True, # creates e.g. screenshot_annotated.png
save_json=True # creates e.g. screenshot_detections.json
)For the full API reference and detailed explanations of all parameters,
visit the documentation site:
👉 Documentation (API & Developer Guide)
After installation, targetfinder-gui: launches the main overlay GUI.
| Windows | Linux |
|---|---|
![]() |
![]() |
| Full video: (see in /demo/Videos/) | Full video: (see in /demo/Videos/) |
After installation, bubblecursor runs the Bubble Cursor interaction technique.
| Windows | Linux |
|---|---|
![]() |
![]() |
| Full video: (see in /demo/Videos/) | Full video: (see in /demo/Videos/) |
After installation, semanticpointing runs the Semantic Pointing interaction technique.
| Windows | Linux |
|---|---|
![]() |
![]() |
| Full video: (see in /demo/Videos/) | Full video: (see in /demo/Videos/) |
Note: The models are automatically loaded by the toolkit. They are also available on Hugging Face: ab-dev26/targetfinder.
| Option | Description |
|---|---|
--model |
Select one of our trained YOLO26 models. A higher input resolution or a larger model size provides better performance, especially for small widgets, but increases inference time. Available models: yolo26[n|s|m]-[640|1280|1920] (default = yolo26n-640). You can also supply your own model by passing the path to your .pt weights file. |
--change-thresh |
Screen change detection threshold. A higher value makes detection less sensitive to small variations. (default = 100). |
--capture-interval |
Time between screen captures in seconds. Lower values = higher frequency but more CPU/GPU usage. (default = 1/30 ≈ 0.033s). |
--confidence |
Minimum YOLO confidence required to keep a detection. ([0.0–1.0], default = 0.4). |
--iou |
IoU threshold for non-max suppression (controls overlap merging). ([0.0–1.0], default = 0.3). |
--display (semanticpointing only) |
Show visual feedback (motor vs visual space). |
--disable-accel (semanticpointing only) |
Disable system mouse acceleration. |
Example:
bubblecursor \
--change-thresh 100 \
--capture-interval 0.033 \
--confidence 0.4 \
--iou 0.3




