Fraser/cursors
Cursors A dataset of labelled OS cursor images for compositing onto cursor-free screenshots as synthetic training data for GUI grounding models. 366 rows across 7 OS/theme sources, covering 31 cursor types. Intended use Given a screenshot with a cursor composited into it, a model is asked: "What cursor type is shown in the red box?" The cursor_type labels are chosen to be visually distinguishable — if two themes render a cursor name differently (e.g. one theme… See the full description on the dataset page: https://huggingface.co/datasets/Fraser/cursors.
Cursors
A dataset of labelled OS cursor images for compositing onto cursor-free screenshots as synthetic training data for GUI grounding models.
366 rows across 7 OS/theme sources, covering 31 cursor types.
Intended use
Given a screenshot with a cursor composited into it, a model is asked: "What cursor type is shown in the red box?"
The cursor_type labels are chosen to be visually distinguishable — if two themes render a cursor name differently (e.g. one theme draws move as a four-way arrow and another as a closed fist), the label reflects what the cursor actually looks like, not what the X11 name says.
Cursor type taxonomy
Pointers
Drag & drop
Resize
Status
Other
Schema
Sources
Usage
from datasets import load_dataset
ds = load_dataset("Fraser/cursors", split="train")
# Get all arrow cursors
arrows = ds.filter(lambda r: r["cursor_type"] == "arrow")
# Get a specific cursor image
row = ds[0]
cursor_image = row["frames"][0] # PIL Image
hotspot = (row["hotspot_x"], row["hotspot_y"])Compositing a cursor onto a screenshot
Place a cursor so its hotspot (the click-point) lands at a target pixel position. The cursor is scaled to a realistic size and alpha-composited without artifacts.
from datasets import load_dataset
from PIL import Image
def composite_cursor(screenshot, cursor_frame, hotspot_x, hotspot_y,
target_x, target_y, cursor_size=32):
"""Place a cursor on a screenshot with the hotspot at (target_x, target_y)."""
canvas = screenshot.convert("RGBA")
cw, ch = cursor_frame.size
scale = cursor_size / max(cw, ch)
new_w, new_h = max(1, round(cw * scale)), max(1, round(ch * scale))
cursor_scaled = cursor_frame.convert("RGBA").resize((new_w, new_h), Image.LANCZOS)
# Offset so the hotspot lands on the target position
paste_x = target_x - round(hotspot_x * new_w)
paste_y = target_y - round(hotspot_y * new_h)
# Crop if the cursor extends past the screenshot edges
sw, sh = canvas.size
src_left = max(0, -paste_x)
src_top = max(0, -paste_y)
src_right = min(new_w, sw - paste_x)
src_bottom = min(new_h, sh - paste_y)
if src_left >= src_right or src_top >= src_bottom:
return screenshot
cropped = cursor_scaled.crop((src_left, src_top, src_right, src_bottom))
canvas.alpha_composite(cropped, (paste_x + src_left, paste_y + src_top))
return canvas.convert(screenshot.mode)
ds = load_dataset("Fraser/cursors", split="train")
# Pick an arrow cursor from the Adwaita theme
row = ds.filter(lambda r: r["cursor_type"] == "arrow" and r["os"] == "linux-adwaita")[0]
screenshot = Image.open("my_screenshot.png")
result = composite_cursor(
screenshot,
row["frames"][0],
row["hotspot_x"], row["hotspot_y"],
target_x=500, target_y=300, # where the arrow tip should point
cursor_size=32, # realistic size for a 1080p screenshot
)
result.save("my_screenshot_with_cursor.png")