pixpick 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
pixpick-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Saif Khan
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
pixpick-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,164 @@
1
+ Metadata-Version: 2.4
2
+ Name: pixpick
3
+ Version: 0.1.0
4
+ Summary: Interactive coordinate picker for Computer Vision frameworks
5
+ License: MIT
6
+ Project-URL: Homepage, https://github.com/K-saif/pixpick
7
+ Project-URL: Documentation, https://github.com/K-saif/pixpick/tree/main/docs
8
+ Project-URL: Bug Tracker, https://github.com/K-saif/pixpick/issues
9
+ Keywords: computer-vision,yolo,sam2,supervision,roi,annotation
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Operating System :: OS Independent
13
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
14
+ Classifier: Topic :: Scientific/Engineering :: Image Recognition
15
+ Requires-Python: >=3.8
16
+ Description-Content-Type: text/markdown
17
+ License-File: LICENSE
18
+ Requires-Dist: opencv-python>=4.5
19
+ Requires-Dist: numpy>=1.21
20
+ Dynamic: license-file
21
+
22
+ <div align="center">
23
+
24
+ # pixpick 🎯
25
+
26
+ **Interactive coordinate picker for Computer Vision — no external tools needed.**
27
+
28
+ [![PyPI version](https://badge.fury.io/py/pixpick.svg)](https://badge.fury.io/py/pixpick)
29
+ [![Python 3.8+](https://img.shields.io/badge/python-3.8+-blue.svg)](https://www.python.org/downloads/)
30
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
31
+
32
+ </div>
33
+
34
+ ---
35
+
36
+ ## The problem
37
+
38
+ Every major CV framework needs coordinates before it can run.
39
+
40
+ ```python
41
+ regioncounter = RegionCounter(region=[120, 80, 640, 480]) # YOLO — where does this region come from?
42
+ predictor.predict(box=np.array([120, 80, 640, 480])) # SAM2 — same problem
43
+ ```
44
+
45
+ The standard workflow: open CVAT or Roboflow → grab coordinates → paste them back into code. Every. Single. Time.
46
+
47
+ ## The fix
48
+
49
+ ```python
50
+ import pixpick
51
+
52
+ region = pixpick.box("frame.jpg") # drag a box on the image
53
+ zone = pixpick.polygon("frame.jpg") # click polygon vertices
54
+
55
+ # coordinates are ready — unpack directly into any framework
56
+ # YOLO:
57
+ regioncounter = RegionCounter(
58
+ region=region.yolo_region, # pass region points
59
+ model="yolo26n.pt",
60
+ )
61
+
62
+ # same for YOLOE
63
+ model.predict("frame.jpg", visual_prompt= region.yolo_prompt())
64
+
65
+ # SAM1/SAM2:
66
+ predictor.predict(box=region.sam())
67
+ ```
68
+
69
+ A window opens on your image. You interact. You get framework-ready coordinates back in Python. No round-trips.
70
+
71
+ ---
72
+
73
+ ## Install
74
+
75
+ ```bash
76
+ pip install pixpick
77
+ ```
78
+
79
+ ---
80
+
81
+ ## Selectors
82
+
83
+ | Selector | How to use | Returns |
84
+ |---|---|---|
85
+ | `pixpick.box()` | Left-click + drag | `Box` |
86
+ | `pixpick.polygon()` | Click vertices → `Enter` to confirm | `Polygon` |
87
+
88
+ **Box controls** — `drag` to draw · `R` to reset · `Esc` to cancel
89
+
90
+ **Polygon controls** — `LMB` add point · `RMB` undo · `Z` clear · `Enter` confirm · `Esc` cancel
91
+
92
+ ---
93
+
94
+ ## Output formats
95
+
96
+ Every selection object carries all the formats you'll ever need.
97
+
98
+ ```python
99
+ # ── Box ──────────────────────────────────────────────────────
100
+ region = pixpick.box("frame.jpg")
101
+
102
+ region.xyxy # [x1, y1, x2, y2] absolute pixels
103
+ region.xywh # [x, y, w, h] absolute pixels
104
+ region.norm_xywh # [x, y, w, h] 0.0 – 1.0 ← YOLO label format
105
+ region.center # (cx, cy)
106
+ region.area # pixels²
107
+
108
+
109
+ # ── Polygon ───────────────────────────────────────────────────
110
+ zone = pixpick.polygon("frame.jpg")
111
+
112
+ zone.points # [(x0,y0), (x1,y1), ...] absolute pixels
113
+ zone.as_numpy # np.array shape (N, 2)
114
+ zone.norm # [(x0n,y0n), ...] 0.0 – 1.0
115
+ zone.bbox # → Box tight bbox around the polygon
116
+ zone.npoints # int
117
+ ```
118
+ For more details, see [Selectors](docs/selectors.md).
119
+ ---
120
+
121
+ ## Framework integration
122
+
123
+ | Framework | Selector | Method |
124
+ |---|---|---|
125
+ | Ultralytics YOLOE — visual prompt | `Box` | `region.yolo_prompt()` |
126
+ | Ultralytics YOLO — region | `Box` | `region.yolo_region()` |
127
+ | SAM / SAM2 — box prompt | `Box` | `region.sam()` |
128
+ | Any other format | `Box` / `Polygon` | `region.to_raw()` |
129
+
130
+ ---
131
+
132
+ ## Persistence
133
+
134
+ Pick once, reuse forever.
135
+
136
+ ```python
137
+ region.save("zone.json")
138
+ region = pixpick.load("zone.json") # Box and Polygon both work
139
+ ```
140
+
141
+ Production pattern — pick interactively the first time, load on every subsequent run:
142
+
143
+ ```python
144
+ from pathlib import Path
145
+ import pixpick
146
+
147
+ ZONE = "config/count_zone.json"
148
+
149
+ zone = pixpick.load(ZONE) if Path(ZONE).exists() else pixpick.polygon("frame.jpg")
150
+ zone.save(ZONE)
151
+ ```
152
+
153
+ ---
154
+
155
+ ## Docs
156
+
157
+ | | |
158
+ |---|---|
159
+ | 🚀 [Getting Started](docs/getting-started.md) | Installation, first selection, controls |
160
+ | 🎯 [Selectors](docs/selectors.md) | All properties and methods for Box and Polygon |
161
+ | 🔌 [Framework Integration](docs/frameworks.md) | YOLO, SAM2 and more |
162
+ | 💾 [Persistence](docs/persistence.md) | Save, load, JSON schema |
163
+ | 🏗️ [Architecture](docs/architecture.md) | How it's built and how to extend it |
164
+ | 🗺️ [Roadmap](docs/roadmap.md) | What's coming next |
@@ -0,0 +1,143 @@
1
+ <div align="center">
2
+
3
+ # pixpick 🎯
4
+
5
+ **Interactive coordinate picker for Computer Vision — no external tools needed.**
6
+
7
+ [![PyPI version](https://badge.fury.io/py/pixpick.svg)](https://badge.fury.io/py/pixpick)
8
+ [![Python 3.8+](https://img.shields.io/badge/python-3.8+-blue.svg)](https://www.python.org/downloads/)
9
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
10
+
11
+ </div>
12
+
13
+ ---
14
+
15
+ ## The problem
16
+
17
+ Every major CV framework needs coordinates before it can run.
18
+
19
+ ```python
20
+ regioncounter = RegionCounter(region=[120, 80, 640, 480]) # YOLO — where does this region come from?
21
+ predictor.predict(box=np.array([120, 80, 640, 480])) # SAM2 — same problem
22
+ ```
23
+
24
+ The standard workflow: open CVAT or Roboflow → grab coordinates → paste them back into code. Every. Single. Time.
25
+
26
+ ## The fix
27
+
28
+ ```python
29
+ import pixpick
30
+
31
+ region = pixpick.box("frame.jpg") # drag a box on the image
32
+ zone = pixpick.polygon("frame.jpg") # click polygon vertices
33
+
34
+ # coordinates are ready — unpack directly into any framework
35
+ # YOLO:
36
+ regioncounter = RegionCounter(
37
+ region=region.yolo_region, # pass region points
38
+ model="yolo26n.pt",
39
+ )
40
+
41
+ # same for YOLOE
42
+ model.predict("frame.jpg", visual_prompt= region.yolo_prompt())
43
+
44
+ # SAM1/SAM2:
45
+ predictor.predict(box=region.sam())
46
+ ```
47
+
48
+ A window opens on your image. You interact. You get framework-ready coordinates back in Python. No round-trips.
49
+
50
+ ---
51
+
52
+ ## Install
53
+
54
+ ```bash
55
+ pip install pixpick
56
+ ```
57
+
58
+ ---
59
+
60
+ ## Selectors
61
+
62
+ | Selector | How to use | Returns |
63
+ |---|---|---|
64
+ | `pixpick.box()` | Left-click + drag | `Box` |
65
+ | `pixpick.polygon()` | Click vertices → `Enter` to confirm | `Polygon` |
66
+
67
+ **Box controls** — `drag` to draw · `R` to reset · `Esc` to cancel
68
+
69
+ **Polygon controls** — `LMB` add point · `RMB` undo · `Z` clear · `Enter` confirm · `Esc` cancel
70
+
71
+ ---
72
+
73
+ ## Output formats
74
+
75
+ Every selection object carries all the formats you'll ever need.
76
+
77
+ ```python
78
+ # ── Box ──────────────────────────────────────────────────────
79
+ region = pixpick.box("frame.jpg")
80
+
81
+ region.xyxy # [x1, y1, x2, y2] absolute pixels
82
+ region.xywh # [x, y, w, h] absolute pixels
83
+ region.norm_xywh # [x, y, w, h] 0.0 – 1.0 ← YOLO label format
84
+ region.center # (cx, cy)
85
+ region.area # pixels²
86
+
87
+
88
+ # ── Polygon ───────────────────────────────────────────────────
89
+ zone = pixpick.polygon("frame.jpg")
90
+
91
+ zone.points # [(x0,y0), (x1,y1), ...] absolute pixels
92
+ zone.as_numpy # np.array shape (N, 2)
93
+ zone.norm # [(x0n,y0n), ...] 0.0 – 1.0
94
+ zone.bbox # → Box tight bbox around the polygon
95
+ zone.npoints # int
96
+ ```
97
+ For more details, see [Selectors](docs/selectors.md).
98
+ ---
99
+
100
+ ## Framework integration
101
+
102
+ | Framework | Selector | Method |
103
+ |---|---|---|
104
+ | Ultralytics YOLOE — visual prompt | `Box` | `region.yolo_prompt()` |
105
+ | Ultralytics YOLO — region | `Box` | `region.yolo_region()` |
106
+ | SAM / SAM2 — box prompt | `Box` | `region.sam()` |
107
+ | Any other format | `Box` / `Polygon` | `region.to_raw()` |
108
+
109
+ ---
110
+
111
+ ## Persistence
112
+
113
+ Pick once, reuse forever.
114
+
115
+ ```python
116
+ region.save("zone.json")
117
+ region = pixpick.load("zone.json") # Box and Polygon both work
118
+ ```
119
+
120
+ Production pattern — pick interactively the first time, load on every subsequent run:
121
+
122
+ ```python
123
+ from pathlib import Path
124
+ import pixpick
125
+
126
+ ZONE = "config/count_zone.json"
127
+
128
+ zone = pixpick.load(ZONE) if Path(ZONE).exists() else pixpick.polygon("frame.jpg")
129
+ zone.save(ZONE)
130
+ ```
131
+
132
+ ---
133
+
134
+ ## Docs
135
+
136
+ | | |
137
+ |---|---|
138
+ | 🚀 [Getting Started](docs/getting-started.md) | Installation, first selection, controls |
139
+ | 🎯 [Selectors](docs/selectors.md) | All properties and methods for Box and Polygon |
140
+ | 🔌 [Framework Integration](docs/frameworks.md) | YOLO, SAM2 and more |
141
+ | 💾 [Persistence](docs/persistence.md) | Save, load, JSON schema |
142
+ | 🏗️ [Architecture](docs/architecture.md) | How it's built and how to extend it |
143
+ | 🗺️ [Roadmap](docs/roadmap.md) | What's coming next |
@@ -0,0 +1,106 @@
1
+ """
2
+ pixpick
3
+ -------
4
+ Interactive coordinate picker for Computer Vision frameworks.
5
+
6
+ Quick start
7
+ -----------
8
+ import pixpick
9
+
10
+ # Box
11
+ region = pixpick.box("frame.jpg")
12
+ model.predict("frame.jpg", **region.yolo_region())
13
+ print(region.xyxy) # [x1, y1, x2, y2]
14
+ print(region.norm) # [0.12, 0.08, 0.64, 0.48]
15
+
16
+ # Polygon
17
+ zone = pixpick.polygon("frame.jpg")
18
+ sv.PolygonZone(**zone.to_supervision())
19
+ print(zone.points) # [(x0,y0), (x1,y1), ...]
20
+
21
+ # Save and reload either type
22
+ region.save("zone.json")
23
+ region = pixpick.load("zone.json")
24
+ """
25
+
26
+ from pixpick.selectors.box import BoxSelector
27
+ from pixpick.selectors.polygon import PolygonSelector, SelectionCancelled
28
+ from pixpick.core.selection import Box, Polygon
29
+ from pixpick.utils import ImageSource
30
+
31
+
32
+ def box(source: ImageSource, title: str = "pixpick") -> Box:
33
+ """
34
+ Open an interactive window on `source`, drag a rectangle, return a Box.
35
+
36
+ Parameters
37
+ ----------
38
+ source : str | Path | np.ndarray
39
+ Image file path or BGR numpy array.
40
+ title : str
41
+ Window title shown to the user.
42
+
43
+ Returns
44
+ -------
45
+ Box
46
+
47
+ Raises
48
+ ------
49
+ SelectionCancelled
50
+ If the user pressed Esc.
51
+ """
52
+ return BoxSelector().select(source, title=title)
53
+
54
+
55
+ def polygon(source: ImageSource, title: str = "pixpick") -> Polygon:
56
+ """
57
+ Open an interactive window on `source`, click vertices, return a Polygon.
58
+
59
+ Controls: LMB=add point RMB=undo last Enter=confirm Z=clear Esc=cancel
60
+
61
+ Parameters
62
+ ----------
63
+ source : str | Path | np.ndarray
64
+ Image file path or BGR numpy array.
65
+ title : str
66
+ Window title shown to the user.
67
+
68
+ Returns
69
+ -------
70
+ Polygon
71
+
72
+ Raises
73
+ ------
74
+ SelectionCancelled
75
+ If the user pressed Esc.
76
+ """
77
+ return PolygonSelector().select(source, title=title)
78
+
79
+
80
+ def load(path: str) -> Box | Polygon:
81
+ """
82
+ Load a previously saved selection from a JSON file.
83
+ Dispatches to Box.load or Polygon.load based on the 'type' field.
84
+ """
85
+ import json
86
+ from pathlib import Path
87
+ data = json.loads(Path(path).read_text())
88
+ sel_type = data.get("type")
89
+ if sel_type == "box":
90
+ return Box.load(path)
91
+ elif sel_type == "polygon":
92
+ return Polygon.load(path)
93
+ else:
94
+ raise ValueError(f"Unknown selection type in JSON: '{sel_type}'")
95
+
96
+
97
+ __all__ = [
98
+ "box",
99
+ "polygon",
100
+ "load",
101
+ "Box",
102
+ "Polygon",
103
+ "BoxSelector",
104
+ "PolygonSelector",
105
+ "SelectionCancelled",
106
+ ]
@@ -0,0 +1,46 @@
1
+ from abc import ABC, abstractmethod
2
+ import numpy as np
3
+
4
+
5
+ class BaseBackend(ABC):
6
+ """
7
+ A backend owns the UI layer — it opens a window (or widget),
8
+ captures user interactions, and returns raw pixel coordinates.
9
+
10
+ It knows nothing about Selection objects or adapters.
11
+ The selector calls the backend and wraps the raw result in the
12
+ appropriate Selection type.
13
+
14
+ Adding a new environment (Jupyter, Gradio, …) means adding a new
15
+ backend — zero changes to selectors or adapters.
16
+ """
17
+
18
+ @abstractmethod
19
+ def select_box(
20
+ self,
21
+ image: np.ndarray,
22
+ title: str = "pixpick",
23
+ ) -> tuple[int, int, int, int] | None:
24
+ """
25
+ Let the user drag a rectangle on the image.
26
+
27
+ Returns
28
+ -------
29
+ (x1, y1, x2, y2) in absolute pixels, or None if cancelled.
30
+ """
31
+ ...
32
+
33
+ @abstractmethod
34
+ def select_polygon(
35
+ self,
36
+ image: np.ndarray,
37
+ title: str = "pixpick",
38
+ ) -> list[tuple[int, int]] | None:
39
+ """
40
+ Let the user click polygon vertices on the image.
41
+
42
+ Returns
43
+ -------
44
+ List of (x, y) tuples (≥ 3 points), or None if cancelled.
45
+ """
46
+ ...