Every time I needed a coordinates of box, polygon and line for tasks like YOLO RegionCounter, YOLOE visual prompt and SAM box coordinate, I had to do this:
- Open CVAT/Roboflow
- Upload an image
- Draw a region
- Copy the coordinates
- Go back to VS Code
- Paste them in
OR
Write complex opencv code.
All that just to get coordinates.
And then the camera angle shifts slightly, or I want to test a different region, and I repeat the whole thing again.
After doing this enough times I stopped accepting it as normal and started looking for a better way. There wasn't one. So I built it.
CVAT and Label Studio are great — just not for this
Let me be clear: CVAT, Label Studio, Roboflow — these are excellent tools. If you're annotating a dataset with thousands of images, use them.
But when you're iterating on a CV pipeline and you just need the coordinates of one region on one frame, spinning up a full annotation platform is massive overkill. You don't need a project, a task, an annotation job, an export step. You need coordinates and you need them in the next ten seconds.
That's the gap I was trying to fill.
What I built
pip install pixpick
import pixpick
region = pixpick.box("frame.jpg") # for video ("video.mp4", frame=5)
print(region.xyxy) # [120, 80, 640, 480]
A window opens on your image. You drag a box. The window closes. You have coordinates.
No accounts, no uploads, no annotation projects. Just Python.
YOLO RegionCounter — the use case that started this
This is the workflow that pushed me to build pixpick.
YOLO's RegionCounter needs a polygon defining the counting zone. Without pixpick:
# where did these coordinates come from? you had to get them somewhere
regioncounter = RegionCounter(region=[120, 80, 640, 480])
With pixpick:
import pixpick
zone = pixpick.polygon("video.mp4", frame=5)
regioncounter = RegionCounter(
region=zone.yolo_region(), # pass region points
model="yolo26n.pt",
)
Click the zone vertices directly on the frame. No mental arithmetic, no pixel hunting.
Real-world use cases
Ultralytics YOLO RegionCounter
import pixpick
region = pixpick.box("video.mp4", frame=10) # drag a box on a specific video frame
zone = pixpick.polygon("image.jpg") # click polygon vertices
# coordinates are ready — unpack directly into any framework
# YOLO:
regioncounter = RegionCounter(
region=zone.yolo_region(), # pass region points
model="yolo26n.pt",
)
SAM / SAM2 box prompt
import pixpick
region = pixpick.box("image.jpg")
predictor.predict(box=region.sam())
YOLOE visual prompt
region = pixpick.box("frame.jpg")
model.predict("frame.jpg", visual_prompt=region.yolo_prompt())
Supervision PolygonZone
import supervision as sv
import pixpick
zone = pixpick.polygon("frame.jpg")
sv_zone = sv.PolygonZone(polygon=zone.as_numpy)
What formats does it give you?
Every selection object carries all the coordinate formats you'd need — no manual conversion:
region = pixpick.box("frame.jpg")
region.xyxy # [x1, y1, x2, y2] ← absolute pixels
region.xywh # [x, y, w, h] ← absolute pixels
region.norm_xywh # [x, y, w, h] ← normalised 0–1 (YOLO label format)
region.center # (cx, cy)
region.area # pixels²
region.raw() # all formats at once as a dict
zone = pixpick.polygon("frame.jpg")
zone.points # [(x0,y0), (x1,y1), ...]
zone.as_numpy # np.array shape (N, 2)
zone.norm # normalised points
zone.bbox # tight Box around the polygon
zone.npoints # vertex count
Why not just use OpenCV's selectROI()?
Good question. cv2.selectROI() does let you draw a box interactively. But it has real limitations:
cv2.selectROI() |
pixpick | |
|---|---|---|
| Box selection | ✅ | ✅ |
| Polygon selection | ❌ | ✅ |
| Line selection | ❌ | ✅ |
| Normalised coordinates | ❌ | ✅ |
| YOLO format | ❌ | ✅ |
| SAM2 format | ❌ | ✅ |
| Save and reload | ❌ | ✅ |
| Works on arrays | limited | ✅ |
selectROI gives you (x, y, w, h). That's it. You then have to convert to xyxy, normalise, reformat for SAM, reformat for Supervision — all manually, every time.
Saving selections and reloading them
This is the feature I didn't know I needed until I had it.
Production pipelines often run the same camera feed indefinitely. You don't want to pick the counting zone every time the script restarts. With pixpick:
from pathlib import Path
import pixpick
ZONE_FILE = "config/zone.json"
if Path(ZONE_FILE).exists():
zone = pixpick.load(ZONE_FILE)
else:
zone = pixpick.polygon("video.mp4", frame=5)
zone.save(ZONE_FILE)
# rest of the pipeline uses zone normally
Pick once. Reload forever. If the camera shifts, delete the JSON and pick again.
Being transparent about why I built this
I didn't build pixpick because I had a grand plan. I built it because I was in the middle of setting up a YOLO RegionCounter and other framewokrs, I got annoyed enough to stop and fix the underlying problem instead of just powering through it.
The library is not trying to replace annotation tools. It's trying to remove one specific friction point that slows down CV engineers during development and iteration.
Current selectors
| Selector | How | Returns |
|---|---|---|
pixpick.box() |
Left-click and drag | Box |
pixpick.polygon() |
Click vertices → Enter to confirm | Polygon |
pixpick.line() |
Click two endpoints | Line |
Controls
Box: drag to draw · Z to reset · Esc to cancel
Polygon: LMB add point · RMB undo last · Z clear · Enter confirm
Line: click start · click end · Z to reset · Esc to cancel
About the 30K downloads
pixpick was released a few weeks ago and has already crossed 30,000 downloads.
I'll be honest — that number surprised me. I built this for myself. But apparently the CVAT round-trip was frustrating enough people that they immediately installed a library the moment it existed.
The download count includes CI bots and mirrors, so the real number is lower. But the GitHub issues, feature requests, and questions that came in after release are real. People are using it for use cases I hadn't even considered when I built it.
Links
- GitHub: github.com/K-saif/pixpick
- PyPI: pypi.org/project/pixpick
- Docs: github.com/K-saif/pixpick/tree/main/docs
pip install pixpick
Give a star If PixPick saves you time, a GitHub star ⭐ would mean a lot.
What's the most repetitive part of your computer vision workflow that you've just accepted as normal? I'd like to know — might be the next thing worth fixing.

Top comments (0)