pixelflow 0.1.2__tar.gz → 0.3.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.
- {pixelflow-0.1.2 → pixelflow-0.3.0}/PKG-INFO +104 -37
- {pixelflow-0.1.2 → pixelflow-0.3.0}/README.md +101 -35
- pixelflow-0.3.0/pixelflow/__init__.py +91 -0
- {pixelflow-0.1.2 → pixelflow-0.3.0}/pixelflow/annotators/__init__.py +7 -2
- {pixelflow-0.1.2 → pixelflow-0.3.0}/pixelflow/annotators/anchors.py +3 -3
- {pixelflow-0.1.2 → pixelflow-0.3.0}/pixelflow/annotators/blur.py +1 -1
- {pixelflow-0.1.2 → pixelflow-0.3.0}/pixelflow/annotators/box.py +4 -4
- pixelflow-0.3.0/pixelflow/annotators/classification.py +122 -0
- {pixelflow-0.1.2 → pixelflow-0.3.0}/pixelflow/annotators/crossing.py +7 -7
- {pixelflow-0.1.2 → pixelflow-0.3.0}/pixelflow/annotators/filled_box.py +5 -5
- {pixelflow-0.1.2 → pixelflow-0.3.0}/pixelflow/annotators/fps_counter.py +9 -6
- {pixelflow-0.1.2 → pixelflow-0.3.0}/pixelflow/annotators/grid_overlay.py +5 -5
- {pixelflow-0.1.2 → pixelflow-0.3.0}/pixelflow/annotators/keypoint.py +17 -13
- {pixelflow-0.1.2 → pixelflow-0.3.0}/pixelflow/annotators/keypoint_skeleton.py +17 -10
- {pixelflow-0.1.2 → pixelflow-0.3.0}/pixelflow/annotators/label.py +18 -15
- {pixelflow-0.1.2 → pixelflow-0.3.0}/pixelflow/annotators/mask.py +4 -4
- {pixelflow-0.1.2 → pixelflow-0.3.0}/pixelflow/annotators/oval.py +4 -4
- {pixelflow-0.1.2 → pixelflow-0.3.0}/pixelflow/annotators/pixelate.py +1 -1
- {pixelflow-0.1.2 → pixelflow-0.3.0}/pixelflow/annotators/polygon.py +10 -6
- pixelflow-0.3.0/pixelflow/annotators/utils.py +71 -0
- {pixelflow-0.1.2 → pixelflow-0.3.0}/pixelflow/annotators/zones.py +1 -1
- pixelflow-0.3.0/pixelflow/arrays.py +20 -0
- pixelflow-0.3.0/pixelflow/assets.py +378 -0
- {pixelflow-0.1.2 → pixelflow-0.3.0}/pixelflow/buffer.py +29 -30
- pixelflow-0.3.0/pixelflow/classifications/__init__.py +16 -0
- pixelflow-0.3.0/pixelflow/classifications/classifications.py +314 -0
- pixelflow-0.3.0/pixelflow/classifications/converters.py +171 -0
- pixelflow-0.3.0/pixelflow/colors.py +108 -0
- {pixelflow-0.1.2 → pixelflow-0.3.0}/pixelflow/crossings.py +4 -4
- pixelflow-0.3.0/pixelflow/detections/__init__.py +45 -0
- pixelflow-0.3.0/pixelflow/detections/converters.py +1398 -0
- {pixelflow-0.1.2 → pixelflow-0.3.0}/pixelflow/detections/detections.py +219 -323
- {pixelflow-0.1.2 → pixelflow-0.3.0}/pixelflow/detections/filters.py +36 -350
- pixelflow-0.3.0/pixelflow/labels.py +73 -0
- pixelflow-0.3.0/pixelflow/media.py +458 -0
- {pixelflow-0.1.2 → pixelflow-0.3.0}/pixelflow/slicer.py +4 -2
- {pixelflow-0.1.2 → pixelflow-0.3.0}/pixelflow/smoother.py +13 -6
- {pixelflow-0.1.2 → pixelflow-0.3.0}/pixelflow/strategies.py +2 -2
- {pixelflow-0.1.2 → pixelflow-0.3.0}/pixelflow/timer.py +5 -5
- {pixelflow-0.1.2 → pixelflow-0.3.0}/pixelflow/tracker/bytetrack.py +7 -7
- {pixelflow-0.1.2 → pixelflow-0.3.0}/pixelflow/transforms/detections.py +38 -95
- {pixelflow-0.1.2 → pixelflow-0.3.0}/pixelflow/transforms/image.py +14 -14
- {pixelflow-0.1.2 → pixelflow-0.3.0}/pixelflow/transforms/inverse.py +42 -127
- pixelflow-0.3.0/pixelflow/validators.py +137 -0
- {pixelflow-0.1.2 → pixelflow-0.3.0}/pixelflow/zones.py +6 -6
- {pixelflow-0.1.2 → pixelflow-0.3.0}/pixelflow.egg-info/PKG-INFO +104 -37
- {pixelflow-0.1.2 → pixelflow-0.3.0}/pixelflow.egg-info/SOURCES.txt +7 -0
- {pixelflow-0.1.2 → pixelflow-0.3.0}/pixelflow.egg-info/requires.txt +1 -0
- {pixelflow-0.1.2 → pixelflow-0.3.0}/pyproject.toml +7 -2
- pixelflow-0.1.2/pixelflow/__init__.py +0 -68
- pixelflow-0.1.2/pixelflow/annotators/utils.py +0 -48
- pixelflow-0.1.2/pixelflow/colors.py +0 -100
- pixelflow-0.1.2/pixelflow/detections/__init__.py +0 -91
- pixelflow-0.1.2/pixelflow/detections/converters.py +0 -2214
- pixelflow-0.1.2/pixelflow/media.py +0 -427
- pixelflow-0.1.2/pixelflow/validators.py +0 -139
- {pixelflow-0.1.2 → pixelflow-0.3.0}/LICENSE +0 -0
- {pixelflow-0.1.2 → pixelflow-0.3.0}/pixelflow/annotators/dot.py +0 -0
- {pixelflow-0.1.2 → pixelflow-0.3.0}/pixelflow/annotators/heatmap.py +0 -0
- {pixelflow-0.1.2 → pixelflow-0.3.0}/pixelflow/annotators/motion_dots.py +0 -0
- {pixelflow-0.1.2 → pixelflow-0.3.0}/pixelflow/annotators/motion_trails.py +0 -0
- {pixelflow-0.1.2 → pixelflow-0.3.0}/pixelflow/annotators/scale_bar.py +0 -0
- {pixelflow-0.1.2 → pixelflow-0.3.0}/pixelflow/tracker/__init__.py +0 -0
- {pixelflow-0.1.2 → pixelflow-0.3.0}/pixelflow/tracker/kalman_filter.py +0 -0
- {pixelflow-0.1.2 → pixelflow-0.3.0}/pixelflow/tracker/matching.py +0 -0
- {pixelflow-0.1.2 → pixelflow-0.3.0}/pixelflow/tracker/track.py +0 -0
- {pixelflow-0.1.2 → pixelflow-0.3.0}/pixelflow/transforms/__init__.py +0 -0
- {pixelflow-0.1.2 → pixelflow-0.3.0}/pixelflow.egg-info/dependency_links.txt +0 -0
- {pixelflow-0.1.2 → pixelflow-0.3.0}/pixelflow.egg-info/top_level.txt +0 -0
- {pixelflow-0.1.2 → pixelflow-0.3.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pixelflow
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Computer vision processing library for visualizing and analyzing detection results from ML models
|
|
5
5
|
Author-email: Emrah Nazif <emrah@datamarkin.com>, Datamarkin <support@datamarkin.com>
|
|
6
6
|
License: MIT
|
|
@@ -8,7 +8,7 @@ Project-URL: Homepage, https://github.com/datamarkin/pixelflow
|
|
|
8
8
|
Project-URL: Repository, https://github.com/datamarkin/pixelflow
|
|
9
9
|
Project-URL: Issues, https://github.com/datamarkin/pixelflow/issues
|
|
10
10
|
Project-URL: Documentation, https://pixelflow.datamarkin.com
|
|
11
|
-
Keywords: computer-vision,visualization,annotation,post-processing,tracking,opencv,yolo,detectron2,ml-results
|
|
11
|
+
Keywords: computer-vision,visualization,annotation,post-processing,tracking,opencv,yolo,detectron2,mayaku,ml-results
|
|
12
12
|
Classifier: Development Status :: 3 - Alpha
|
|
13
13
|
Classifier: Intended Audience :: Developers
|
|
14
14
|
Classifier: Intended Audience :: Science/Research
|
|
@@ -29,6 +29,7 @@ Requires-Dist: shapely>=2.0.0
|
|
|
29
29
|
Requires-Dist: numpy>=1.21.0
|
|
30
30
|
Requires-Dist: opencv-python>=4.8.0
|
|
31
31
|
Requires-Dist: scipy>=1.10.0
|
|
32
|
+
Requires-Dist: Pillow>=9.0.0
|
|
32
33
|
Provides-Extra: dev
|
|
33
34
|
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
34
35
|
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
|
|
@@ -50,13 +51,13 @@ import pixelflow as pf
|
|
|
50
51
|
from ultralytics import YOLO
|
|
51
52
|
|
|
52
53
|
model = YOLO("yolo11n.pt")
|
|
53
|
-
|
|
54
|
+
video = pf.VideoReader("traffic.mp4", width=640)
|
|
54
55
|
tracker = pf.tracker.ByteTracker()
|
|
55
56
|
zones = pf.Zones()
|
|
56
57
|
zones.add_zone([(100, 400), (500, 400), (500, 600), (100, 600)], zone_id="entrance")
|
|
57
58
|
|
|
58
|
-
for frame in
|
|
59
|
-
detections = pf.
|
|
59
|
+
for frame in video:
|
|
60
|
+
detections = pf.from_ultralytics(model.predict(frame))
|
|
60
61
|
detections = tracker.update(detections)
|
|
61
62
|
zones.update(detections)
|
|
62
63
|
|
|
@@ -64,7 +65,8 @@ for frame in media.frames:
|
|
|
64
65
|
frame = pf.annotate.label(frame, detections)
|
|
65
66
|
frame = pf.annotate.zones(frame, zones)
|
|
66
67
|
|
|
67
|
-
pf.show_frame("Live", frame)
|
|
68
|
+
if pf.show_frame("Live", frame) == ord('q'):
|
|
69
|
+
break
|
|
68
70
|
```
|
|
69
71
|
|
|
70
72
|
## Installation
|
|
@@ -75,6 +77,12 @@ pip install pixelflow
|
|
|
75
77
|
|
|
76
78
|
## Core Concepts
|
|
77
79
|
|
|
80
|
+
### Two Result Types
|
|
81
|
+
|
|
82
|
+
Every model output becomes one of two things. **Detections** for anything that localises — detection, segmentation, keypoints, OCR. **Classifications** for anything that only names.
|
|
83
|
+
|
|
84
|
+
They are peers, not variants of each other: three detections are three objects, while three classifications are three competing answers about one image.
|
|
85
|
+
|
|
78
86
|
### Detections - One Format, Every Framework
|
|
79
87
|
|
|
80
88
|
Convert outputs from any ML framework into a unified format:
|
|
@@ -85,28 +93,80 @@ import pixelflow as pf
|
|
|
85
93
|
# Ultralytics YOLO
|
|
86
94
|
from ultralytics import YOLO
|
|
87
95
|
model = YOLO("yolo11n.pt")
|
|
88
|
-
detections = pf.
|
|
96
|
+
detections = pf.from_ultralytics(model.predict(image))
|
|
89
97
|
|
|
90
98
|
# Detectron2
|
|
91
99
|
from detectron2.engine import DefaultPredictor
|
|
92
100
|
predictor = DefaultPredictor(cfg)
|
|
93
|
-
detections = pf.
|
|
101
|
+
detections = pf.from_detectron2(predictor(image), labels=["person", "car"])
|
|
102
|
+
|
|
103
|
+
# Mayaku (returns Instances directly; take labels from the checkpoint)
|
|
104
|
+
from mayaku import from_pretrained
|
|
105
|
+
predictor = from_pretrained("mayaku-n-det")
|
|
106
|
+
detections = pf.from_mayaku(predictor("photo.jpg"), labels=predictor.class_names)
|
|
94
107
|
|
|
95
108
|
# HuggingFace Transformers
|
|
96
109
|
from transformers import pipeline
|
|
97
110
|
detector = pipeline("object-detection")
|
|
98
|
-
detections = pf.
|
|
111
|
+
detections = pf.from_transformers(detector(image))
|
|
99
112
|
|
|
100
|
-
# Florence-2
|
|
101
|
-
detections = pf.
|
|
113
|
+
# Florence-2 - captions and OCR reads land in det.text, <OD> classes in det.class_name
|
|
114
|
+
detections = pf.from_florence2(model_output, task_prompt="<OD>")
|
|
102
115
|
|
|
103
116
|
# SAM (Segment Anything)
|
|
104
|
-
detections = pf.
|
|
117
|
+
detections = pf.from_sam(masks, scores)
|
|
118
|
+
|
|
119
|
+
# RF-DETR
|
|
120
|
+
detections = pf.from_rfdetr(model_output)
|
|
121
|
+
|
|
122
|
+
# Supervision
|
|
123
|
+
detections = pf.from_supervision(sv_detections)
|
|
105
124
|
|
|
106
|
-
#
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
detections = pf.
|
|
125
|
+
# EasyOCR - text lands in det.text, the quad in det.segments
|
|
126
|
+
import easyocr
|
|
127
|
+
reader = easyocr.Reader(['en'])
|
|
128
|
+
detections = pf.from_easyocr(reader.readtext("sign.jpg"))
|
|
129
|
+
|
|
130
|
+
# Plain arrays (numpy or torch) - no framework container to convert from
|
|
131
|
+
detections = pf.from_arrays(boxes, scores, class_ids, labels=model.class_names)
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### Classifications - When the Model Only Names
|
|
135
|
+
|
|
136
|
+
A classifier localises nothing, so its result carries no geometry at all:
|
|
137
|
+
|
|
138
|
+
```python
|
|
139
|
+
import pixelflow as pf
|
|
140
|
+
|
|
141
|
+
# Ultralytics -cls checkpoints
|
|
142
|
+
from ultralytics import YOLO
|
|
143
|
+
model = YOLO("yolo11n-cls.pt")
|
|
144
|
+
result = pf.from_ultralytics_classification(model.predict("dog.jpg"))
|
|
145
|
+
|
|
146
|
+
result.top1.class_name # 'golden retriever'
|
|
147
|
+
result.top_k(5) # -> Classifications, best first
|
|
148
|
+
result.to_json()
|
|
149
|
+
|
|
150
|
+
# Plain scores - CLIP zero-shot, a vendored ResNet, anything with a score vector
|
|
151
|
+
prompts = ["a photo of a cat", "a photo of a dog"]
|
|
152
|
+
result = pf.from_scores(similarities, labels=prompts)
|
|
153
|
+
|
|
154
|
+
# Multi-label: however many labels genuinely match, not a fixed count
|
|
155
|
+
matches = result.filter_by_confidence(0.3)
|
|
156
|
+
|
|
157
|
+
# Draw it
|
|
158
|
+
frame = pf.annotate.classification(frame, result, top_k=3)
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Scores are reported exactly as the model emitted them. PixelFlow does not normalise,
|
|
162
|
+
re-softmax, or assume they sum to 1, so a softmax over a fixed class list and an
|
|
163
|
+
independent per-label similarity both survive intact. A model emitting logits should
|
|
164
|
+
be converted by the caller, who is the only one who knows which convention applies.
|
|
165
|
+
|
|
166
|
+
Per-frame paths can skip building a row per class:
|
|
167
|
+
|
|
168
|
+
```python
|
|
169
|
+
result = pf.from_ultralytics_classification(model.predict(frame), top_k=5)
|
|
110
170
|
```
|
|
111
171
|
|
|
112
172
|
### Powerful Filtering
|
|
@@ -139,29 +199,32 @@ english_text = detections.filter_by_text_language("en")
|
|
|
139
199
|
|
|
140
200
|
### Media Handling
|
|
141
201
|
|
|
142
|
-
|
|
202
|
+
Purpose-built classes for videos, cameras, and images:
|
|
143
203
|
|
|
144
204
|
```python
|
|
145
205
|
import pixelflow as pf
|
|
146
206
|
|
|
147
|
-
#
|
|
148
|
-
|
|
149
|
-
print(
|
|
207
|
+
# Read a video file
|
|
208
|
+
video = pf.VideoReader("video.mp4", width=640)
|
|
209
|
+
print(f"{video.width}x{video.height}, {video.fps}fps, {len(video)} frames")
|
|
150
210
|
|
|
151
|
-
for frame in
|
|
152
|
-
|
|
153
|
-
|
|
211
|
+
for frame in video: # Replayable — resets on each iteration
|
|
212
|
+
if pf.show_frame("Preview", frame) == ord('q'):
|
|
213
|
+
break
|
|
154
214
|
|
|
155
|
-
#
|
|
156
|
-
|
|
215
|
+
# Load a single image
|
|
216
|
+
image = pf.read_image("photo.jpg", width=640)
|
|
157
217
|
|
|
158
|
-
#
|
|
159
|
-
|
|
218
|
+
# Webcam / network stream
|
|
219
|
+
cam = pf.CameraStream(0, width=640)
|
|
220
|
+
cam = pf.CameraStream("rtsp://camera.local/stream")
|
|
160
221
|
|
|
161
222
|
# Write processed video
|
|
162
|
-
|
|
223
|
+
writer = pf.VideoWriter("output.mp4", fps=video.fps)
|
|
224
|
+
for frame in video:
|
|
163
225
|
processed = process(frame)
|
|
164
|
-
|
|
226
|
+
writer.write(processed)
|
|
227
|
+
writer.close()
|
|
165
228
|
```
|
|
166
229
|
|
|
167
230
|
### Zone-Based Analytics
|
|
@@ -217,7 +280,7 @@ import pixelflow as pf
|
|
|
217
280
|
tracker = pf.tracker.ByteTracker()
|
|
218
281
|
|
|
219
282
|
for frame in media.frames:
|
|
220
|
-
detections = pf.
|
|
283
|
+
detections = pf.from_ultralytics(model.predict(frame))
|
|
221
284
|
detections = tracker.update(detections)
|
|
222
285
|
|
|
223
286
|
for det in detections:
|
|
@@ -297,7 +360,7 @@ slicer = pf.SlicedInference(
|
|
|
297
360
|
)
|
|
298
361
|
|
|
299
362
|
def detector(image):
|
|
300
|
-
return pf.
|
|
363
|
+
return pf.from_ultralytics(model.predict(image))
|
|
301
364
|
|
|
302
365
|
# Run on 4K image - automatically slices, detects, and merges
|
|
303
366
|
large_image = cv2.imread("satellite.jpg") # 4000x3000
|
|
@@ -320,7 +383,7 @@ crossings.add_line(
|
|
|
320
383
|
)
|
|
321
384
|
|
|
322
385
|
for frame in media.frames:
|
|
323
|
-
detections = tracker.update(pf.
|
|
386
|
+
detections = tracker.update(pf.from_ultralytics(model.predict(frame)))
|
|
324
387
|
crossings.update(detections)
|
|
325
388
|
|
|
326
389
|
print(f"Crossed: {crossings.get_counts()}")
|
|
@@ -347,15 +410,19 @@ report = detections.to_json_with_metrics()
|
|
|
347
410
|
|
|
348
411
|
| Framework | Converter | Features |
|
|
349
412
|
|-----------|-----------|----------|
|
|
413
|
+
| None (plain numpy/torch arrays) | `from_arrays()` | Boxes, masks, keypoints |
|
|
350
414
|
| Ultralytics (YOLO) | `from_ultralytics()` | Boxes, masks, keypoints |
|
|
351
415
|
| Detectron2 | `from_detectron2()` | Boxes, masks, keypoints |
|
|
416
|
+
| Mayaku | `from_mayaku()` | Boxes, masks, keypoints |
|
|
352
417
|
| HuggingFace Transformers | `from_transformers()` | Boxes, scores |
|
|
353
|
-
| Florence-2 | `from_florence2()` | Boxes,
|
|
418
|
+
| Florence-2 | `from_florence2()` | Boxes, quads, polygons, captions |
|
|
354
419
|
| SAM | `from_sam()` | Masks, scores |
|
|
355
|
-
|
|
|
356
|
-
|
|
|
357
|
-
|
|
|
358
|
-
|
|
|
420
|
+
| EfficientTAM | `from_efficienttam()` | Masks, scores |
|
|
421
|
+
| RF-DETR | `from_rfdetr()` | Boxes, masks |
|
|
422
|
+
| Supervision | `from_supervision()` | Boxes, masks, keypoints |
|
|
423
|
+
| Falcon Perception | `from_falcon_perception()` | Boxes, masks |
|
|
424
|
+
| EasyOCR | `from_easyocr()` | Quads, text, scores |
|
|
425
|
+
| Datamarkin API | `from_datamarkin()` | Boxes, masks, keypoints |
|
|
359
426
|
|
|
360
427
|
## Documentation
|
|
361
428
|
|
|
@@ -13,13 +13,13 @@ import pixelflow as pf
|
|
|
13
13
|
from ultralytics import YOLO
|
|
14
14
|
|
|
15
15
|
model = YOLO("yolo11n.pt")
|
|
16
|
-
|
|
16
|
+
video = pf.VideoReader("traffic.mp4", width=640)
|
|
17
17
|
tracker = pf.tracker.ByteTracker()
|
|
18
18
|
zones = pf.Zones()
|
|
19
19
|
zones.add_zone([(100, 400), (500, 400), (500, 600), (100, 600)], zone_id="entrance")
|
|
20
20
|
|
|
21
|
-
for frame in
|
|
22
|
-
detections = pf.
|
|
21
|
+
for frame in video:
|
|
22
|
+
detections = pf.from_ultralytics(model.predict(frame))
|
|
23
23
|
detections = tracker.update(detections)
|
|
24
24
|
zones.update(detections)
|
|
25
25
|
|
|
@@ -27,7 +27,8 @@ for frame in media.frames:
|
|
|
27
27
|
frame = pf.annotate.label(frame, detections)
|
|
28
28
|
frame = pf.annotate.zones(frame, zones)
|
|
29
29
|
|
|
30
|
-
pf.show_frame("Live", frame)
|
|
30
|
+
if pf.show_frame("Live", frame) == ord('q'):
|
|
31
|
+
break
|
|
31
32
|
```
|
|
32
33
|
|
|
33
34
|
## Installation
|
|
@@ -38,6 +39,12 @@ pip install pixelflow
|
|
|
38
39
|
|
|
39
40
|
## Core Concepts
|
|
40
41
|
|
|
42
|
+
### Two Result Types
|
|
43
|
+
|
|
44
|
+
Every model output becomes one of two things. **Detections** for anything that localises — detection, segmentation, keypoints, OCR. **Classifications** for anything that only names.
|
|
45
|
+
|
|
46
|
+
They are peers, not variants of each other: three detections are three objects, while three classifications are three competing answers about one image.
|
|
47
|
+
|
|
41
48
|
### Detections - One Format, Every Framework
|
|
42
49
|
|
|
43
50
|
Convert outputs from any ML framework into a unified format:
|
|
@@ -48,28 +55,80 @@ import pixelflow as pf
|
|
|
48
55
|
# Ultralytics YOLO
|
|
49
56
|
from ultralytics import YOLO
|
|
50
57
|
model = YOLO("yolo11n.pt")
|
|
51
|
-
detections = pf.
|
|
58
|
+
detections = pf.from_ultralytics(model.predict(image))
|
|
52
59
|
|
|
53
60
|
# Detectron2
|
|
54
61
|
from detectron2.engine import DefaultPredictor
|
|
55
62
|
predictor = DefaultPredictor(cfg)
|
|
56
|
-
detections = pf.
|
|
63
|
+
detections = pf.from_detectron2(predictor(image), labels=["person", "car"])
|
|
64
|
+
|
|
65
|
+
# Mayaku (returns Instances directly; take labels from the checkpoint)
|
|
66
|
+
from mayaku import from_pretrained
|
|
67
|
+
predictor = from_pretrained("mayaku-n-det")
|
|
68
|
+
detections = pf.from_mayaku(predictor("photo.jpg"), labels=predictor.class_names)
|
|
57
69
|
|
|
58
70
|
# HuggingFace Transformers
|
|
59
71
|
from transformers import pipeline
|
|
60
72
|
detector = pipeline("object-detection")
|
|
61
|
-
detections = pf.
|
|
73
|
+
detections = pf.from_transformers(detector(image))
|
|
62
74
|
|
|
63
|
-
# Florence-2
|
|
64
|
-
detections = pf.
|
|
75
|
+
# Florence-2 - captions and OCR reads land in det.text, <OD> classes in det.class_name
|
|
76
|
+
detections = pf.from_florence2(model_output, task_prompt="<OD>")
|
|
65
77
|
|
|
66
78
|
# SAM (Segment Anything)
|
|
67
|
-
detections = pf.
|
|
79
|
+
detections = pf.from_sam(masks, scores)
|
|
80
|
+
|
|
81
|
+
# RF-DETR
|
|
82
|
+
detections = pf.from_rfdetr(model_output)
|
|
83
|
+
|
|
84
|
+
# Supervision
|
|
85
|
+
detections = pf.from_supervision(sv_detections)
|
|
68
86
|
|
|
69
|
-
#
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
detections = pf.
|
|
87
|
+
# EasyOCR - text lands in det.text, the quad in det.segments
|
|
88
|
+
import easyocr
|
|
89
|
+
reader = easyocr.Reader(['en'])
|
|
90
|
+
detections = pf.from_easyocr(reader.readtext("sign.jpg"))
|
|
91
|
+
|
|
92
|
+
# Plain arrays (numpy or torch) - no framework container to convert from
|
|
93
|
+
detections = pf.from_arrays(boxes, scores, class_ids, labels=model.class_names)
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Classifications - When the Model Only Names
|
|
97
|
+
|
|
98
|
+
A classifier localises nothing, so its result carries no geometry at all:
|
|
99
|
+
|
|
100
|
+
```python
|
|
101
|
+
import pixelflow as pf
|
|
102
|
+
|
|
103
|
+
# Ultralytics -cls checkpoints
|
|
104
|
+
from ultralytics import YOLO
|
|
105
|
+
model = YOLO("yolo11n-cls.pt")
|
|
106
|
+
result = pf.from_ultralytics_classification(model.predict("dog.jpg"))
|
|
107
|
+
|
|
108
|
+
result.top1.class_name # 'golden retriever'
|
|
109
|
+
result.top_k(5) # -> Classifications, best first
|
|
110
|
+
result.to_json()
|
|
111
|
+
|
|
112
|
+
# Plain scores - CLIP zero-shot, a vendored ResNet, anything with a score vector
|
|
113
|
+
prompts = ["a photo of a cat", "a photo of a dog"]
|
|
114
|
+
result = pf.from_scores(similarities, labels=prompts)
|
|
115
|
+
|
|
116
|
+
# Multi-label: however many labels genuinely match, not a fixed count
|
|
117
|
+
matches = result.filter_by_confidence(0.3)
|
|
118
|
+
|
|
119
|
+
# Draw it
|
|
120
|
+
frame = pf.annotate.classification(frame, result, top_k=3)
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Scores are reported exactly as the model emitted them. PixelFlow does not normalise,
|
|
124
|
+
re-softmax, or assume they sum to 1, so a softmax over a fixed class list and an
|
|
125
|
+
independent per-label similarity both survive intact. A model emitting logits should
|
|
126
|
+
be converted by the caller, who is the only one who knows which convention applies.
|
|
127
|
+
|
|
128
|
+
Per-frame paths can skip building a row per class:
|
|
129
|
+
|
|
130
|
+
```python
|
|
131
|
+
result = pf.from_ultralytics_classification(model.predict(frame), top_k=5)
|
|
73
132
|
```
|
|
74
133
|
|
|
75
134
|
### Powerful Filtering
|
|
@@ -102,29 +161,32 @@ english_text = detections.filter_by_text_language("en")
|
|
|
102
161
|
|
|
103
162
|
### Media Handling
|
|
104
163
|
|
|
105
|
-
|
|
164
|
+
Purpose-built classes for videos, cameras, and images:
|
|
106
165
|
|
|
107
166
|
```python
|
|
108
167
|
import pixelflow as pf
|
|
109
168
|
|
|
110
|
-
#
|
|
111
|
-
|
|
112
|
-
print(
|
|
169
|
+
# Read a video file
|
|
170
|
+
video = pf.VideoReader("video.mp4", width=640)
|
|
171
|
+
print(f"{video.width}x{video.height}, {video.fps}fps, {len(video)} frames")
|
|
113
172
|
|
|
114
|
-
for frame in
|
|
115
|
-
|
|
116
|
-
|
|
173
|
+
for frame in video: # Replayable — resets on each iteration
|
|
174
|
+
if pf.show_frame("Preview", frame) == ord('q'):
|
|
175
|
+
break
|
|
117
176
|
|
|
118
|
-
#
|
|
119
|
-
|
|
177
|
+
# Load a single image
|
|
178
|
+
image = pf.read_image("photo.jpg", width=640)
|
|
120
179
|
|
|
121
|
-
#
|
|
122
|
-
|
|
180
|
+
# Webcam / network stream
|
|
181
|
+
cam = pf.CameraStream(0, width=640)
|
|
182
|
+
cam = pf.CameraStream("rtsp://camera.local/stream")
|
|
123
183
|
|
|
124
184
|
# Write processed video
|
|
125
|
-
|
|
185
|
+
writer = pf.VideoWriter("output.mp4", fps=video.fps)
|
|
186
|
+
for frame in video:
|
|
126
187
|
processed = process(frame)
|
|
127
|
-
|
|
188
|
+
writer.write(processed)
|
|
189
|
+
writer.close()
|
|
128
190
|
```
|
|
129
191
|
|
|
130
192
|
### Zone-Based Analytics
|
|
@@ -180,7 +242,7 @@ import pixelflow as pf
|
|
|
180
242
|
tracker = pf.tracker.ByteTracker()
|
|
181
243
|
|
|
182
244
|
for frame in media.frames:
|
|
183
|
-
detections = pf.
|
|
245
|
+
detections = pf.from_ultralytics(model.predict(frame))
|
|
184
246
|
detections = tracker.update(detections)
|
|
185
247
|
|
|
186
248
|
for det in detections:
|
|
@@ -260,7 +322,7 @@ slicer = pf.SlicedInference(
|
|
|
260
322
|
)
|
|
261
323
|
|
|
262
324
|
def detector(image):
|
|
263
|
-
return pf.
|
|
325
|
+
return pf.from_ultralytics(model.predict(image))
|
|
264
326
|
|
|
265
327
|
# Run on 4K image - automatically slices, detects, and merges
|
|
266
328
|
large_image = cv2.imread("satellite.jpg") # 4000x3000
|
|
@@ -283,7 +345,7 @@ crossings.add_line(
|
|
|
283
345
|
)
|
|
284
346
|
|
|
285
347
|
for frame in media.frames:
|
|
286
|
-
detections = tracker.update(pf.
|
|
348
|
+
detections = tracker.update(pf.from_ultralytics(model.predict(frame)))
|
|
287
349
|
crossings.update(detections)
|
|
288
350
|
|
|
289
351
|
print(f"Crossed: {crossings.get_counts()}")
|
|
@@ -310,15 +372,19 @@ report = detections.to_json_with_metrics()
|
|
|
310
372
|
|
|
311
373
|
| Framework | Converter | Features |
|
|
312
374
|
|-----------|-----------|----------|
|
|
375
|
+
| None (plain numpy/torch arrays) | `from_arrays()` | Boxes, masks, keypoints |
|
|
313
376
|
| Ultralytics (YOLO) | `from_ultralytics()` | Boxes, masks, keypoints |
|
|
314
377
|
| Detectron2 | `from_detectron2()` | Boxes, masks, keypoints |
|
|
378
|
+
| Mayaku | `from_mayaku()` | Boxes, masks, keypoints |
|
|
315
379
|
| HuggingFace Transformers | `from_transformers()` | Boxes, scores |
|
|
316
|
-
| Florence-2 | `from_florence2()` | Boxes,
|
|
380
|
+
| Florence-2 | `from_florence2()` | Boxes, quads, polygons, captions |
|
|
317
381
|
| SAM | `from_sam()` | Masks, scores |
|
|
318
|
-
|
|
|
319
|
-
|
|
|
320
|
-
|
|
|
321
|
-
|
|
|
382
|
+
| EfficientTAM | `from_efficienttam()` | Masks, scores |
|
|
383
|
+
| RF-DETR | `from_rfdetr()` | Boxes, masks |
|
|
384
|
+
| Supervision | `from_supervision()` | Boxes, masks, keypoints |
|
|
385
|
+
| Falcon Perception | `from_falcon_perception()` | Boxes, masks |
|
|
386
|
+
| EasyOCR | `from_easyocr()` | Quads, text, scores |
|
|
387
|
+
| Datamarkin API | `from_datamarkin()` | Boxes, masks, keypoints |
|
|
322
388
|
|
|
323
389
|
## Documentation
|
|
324
390
|
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
__version__ = "0.3.0"
|
|
2
|
+
__author__ = "Datamarkin"
|
|
3
|
+
|
|
4
|
+
# Import core modules
|
|
5
|
+
from . import media
|
|
6
|
+
from . import annotators as annotate
|
|
7
|
+
from . import transforms as transform
|
|
8
|
+
from . import colors
|
|
9
|
+
from . import slicer
|
|
10
|
+
from . import smoother
|
|
11
|
+
from . import timer
|
|
12
|
+
from . import tracker
|
|
13
|
+
from . import assets
|
|
14
|
+
|
|
15
|
+
# Result types and their converters. Every model output PixelFlow understands becomes
|
|
16
|
+
# one of two things: Detections for anything that localises, Classifications for
|
|
17
|
+
# anything that only names. They are peers, not variants of each other.
|
|
18
|
+
#
|
|
19
|
+
# Converters are flat -- pf.from_ultralytics(...) -- and each subpackage owns its own
|
|
20
|
+
# export list, so a new one is declared once rather than re-typed here. Detection is
|
|
21
|
+
# the unmarked case because almost every model that produces a container localises
|
|
22
|
+
# something; only classification needs the suffix.
|
|
23
|
+
from .detections import *
|
|
24
|
+
from .detections import __all__ as _DETECTION_EXPORTS
|
|
25
|
+
from .classifications import *
|
|
26
|
+
from .classifications import __all__ as _CLASSIFICATION_EXPORTS
|
|
27
|
+
|
|
28
|
+
# Import specific functions for top-level access
|
|
29
|
+
from .media import (
|
|
30
|
+
DisplayExit, VideoReader, CameraStream, VideoWriter,
|
|
31
|
+
read_image, read_video, display_video, display_image, save_image,
|
|
32
|
+
close_display, to_pil, from_pil,
|
|
33
|
+
)
|
|
34
|
+
from .zones import Zones
|
|
35
|
+
from .crossings import Crossings
|
|
36
|
+
from .slicer import SlicedInference, auto_slice_size
|
|
37
|
+
from .buffer import Buffer
|
|
38
|
+
from .smoother import smooth
|
|
39
|
+
from .timer import TimeTracker
|
|
40
|
+
|
|
41
|
+
# Define the public API
|
|
42
|
+
__all__ = [
|
|
43
|
+
# Result types and converters, declared by the subpackages that own them
|
|
44
|
+
*_DETECTION_EXPORTS,
|
|
45
|
+
*_CLASSIFICATION_EXPORTS,
|
|
46
|
+
|
|
47
|
+
# Visual components
|
|
48
|
+
"annotate",
|
|
49
|
+
"colors",
|
|
50
|
+
|
|
51
|
+
# Image transformations
|
|
52
|
+
"transform",
|
|
53
|
+
|
|
54
|
+
# Media handling
|
|
55
|
+
"media",
|
|
56
|
+
"DisplayExit",
|
|
57
|
+
"VideoReader",
|
|
58
|
+
"CameraStream",
|
|
59
|
+
"VideoWriter",
|
|
60
|
+
"read_image",
|
|
61
|
+
"read_video",
|
|
62
|
+
"display_video",
|
|
63
|
+
"display_image",
|
|
64
|
+
"save_image",
|
|
65
|
+
"close_display",
|
|
66
|
+
"to_pil",
|
|
67
|
+
"from_pil",
|
|
68
|
+
|
|
69
|
+
# Spatial analysis
|
|
70
|
+
"zones",
|
|
71
|
+
"Zones",
|
|
72
|
+
"crossings",
|
|
73
|
+
"Crossings",
|
|
74
|
+
|
|
75
|
+
# Processing utilities
|
|
76
|
+
"assets",
|
|
77
|
+
"slicer",
|
|
78
|
+
"SlicedInference",
|
|
79
|
+
"auto_slice_size",
|
|
80
|
+
"Buffer",
|
|
81
|
+
"smooth",
|
|
82
|
+
|
|
83
|
+
# Performance & tracking
|
|
84
|
+
"timer",
|
|
85
|
+
"TimeTracker",
|
|
86
|
+
"tracker",
|
|
87
|
+
|
|
88
|
+
# Metadata
|
|
89
|
+
"__version__",
|
|
90
|
+
"__author__"
|
|
91
|
+
]
|
|
@@ -12,6 +12,7 @@ allowing easy chaining of annotation operations.
|
|
|
12
12
|
from .anchors import anchors
|
|
13
13
|
from .blur import blur
|
|
14
14
|
from .box import box
|
|
15
|
+
from .classification import classification
|
|
15
16
|
from .label import label
|
|
16
17
|
from .crossing import crossings
|
|
17
18
|
from .mask import mask
|
|
@@ -23,7 +24,7 @@ from .zones import zones
|
|
|
23
24
|
# TODO: Import these once implemented
|
|
24
25
|
from .filled_box import filled_box
|
|
25
26
|
# from .dot import dot
|
|
26
|
-
|
|
27
|
+
from .fps_counter import fps_counter
|
|
27
28
|
from .grid_overlay import grid_overlay
|
|
28
29
|
# from .heatmap import heatmap
|
|
29
30
|
from .keypoint import keypoint
|
|
@@ -41,6 +42,7 @@ __all__ = [
|
|
|
41
42
|
# Core annotators
|
|
42
43
|
'anchors',
|
|
43
44
|
'box',
|
|
45
|
+
'classification',
|
|
44
46
|
'label',
|
|
45
47
|
'mask',
|
|
46
48
|
'keypoint',
|
|
@@ -56,5 +58,8 @@ __all__ = [
|
|
|
56
58
|
|
|
57
59
|
# Zone annotators
|
|
58
60
|
'zones',
|
|
59
|
-
'crossings'
|
|
61
|
+
'crossings',
|
|
62
|
+
|
|
63
|
+
# Overlay annotators
|
|
64
|
+
'fps_counter',
|
|
60
65
|
]
|
|
@@ -38,7 +38,7 @@ def anchors(
|
|
|
38
38
|
Supports drawing individual anchor points or multiple points simultaneously.
|
|
39
39
|
|
|
40
40
|
Args:
|
|
41
|
-
image (np.ndarray): Input image to draw anchor points on in
|
|
41
|
+
image (np.ndarray): Input image to draw anchor points on in RGB format.
|
|
42
42
|
Image is modified in-place.
|
|
43
43
|
detections (Detections): Detection results containing bounding boxes.
|
|
44
44
|
Each detection must have a 'bbox' attribute with (x1, y1, x2, y2) coordinates.
|
|
@@ -51,7 +51,7 @@ def anchors(
|
|
|
51
51
|
thickness (Optional[int]): Thickness of circle outline in pixels.
|
|
52
52
|
Use -1 for filled circles.
|
|
53
53
|
If None, defaults to -1 (filled).
|
|
54
|
-
colors (Optional[List[tuple]]): List of
|
|
54
|
+
colors (Optional[List[tuple]]): List of RGB color tuples for custom colors.
|
|
55
55
|
Colors mapped to unique class_ids in order of appearance.
|
|
56
56
|
If None, uses default ColorManager colors.
|
|
57
57
|
|
|
@@ -71,7 +71,7 @@ def anchors(
|
|
|
71
71
|
>>> image = cv2.imread("path/to/image.jpg")
|
|
72
72
|
>>> model = YOLO("yolo11n.pt")
|
|
73
73
|
>>> outputs = model.predict(image)
|
|
74
|
-
>>> detections = pf.
|
|
74
|
+
>>> detections = pf.from_ultralytics(outputs)
|
|
75
75
|
>>>
|
|
76
76
|
>>> # Draw all main anchor points (default)
|
|
77
77
|
>>> annotated = pf.annotators.anchors(image, detections)
|
|
@@ -61,7 +61,7 @@ def blur(
|
|
|
61
61
|
>>> image = cv2.imread("faces_in_crowd.jpg")
|
|
62
62
|
>>> model = YOLO("yolo11n.pt")
|
|
63
63
|
>>> outputs = model.predict(image) # Raw model outputs
|
|
64
|
-
>>> detections = pf.
|
|
64
|
+
>>> detections = pf.from_ultralytics(outputs) # Convert to PixelFlow format
|
|
65
65
|
>>>
|
|
66
66
|
>>> # Apply blur with default adaptive settings
|
|
67
67
|
>>> blurred_image = pf.annotators.blur(image, detections)
|
|
@@ -22,13 +22,13 @@ def box(
|
|
|
22
22
|
Automatically adapts line thickness based on image dimensions for optimal visibility.
|
|
23
23
|
|
|
24
24
|
Args:
|
|
25
|
-
image (np.ndarray): Input image to draw boxes on (
|
|
25
|
+
image (np.ndarray): Input image to draw boxes on (RGB format).
|
|
26
26
|
Modified in-place with drawn bounding boxes.
|
|
27
27
|
detections (Detections): Detections object containing bounding boxes.
|
|
28
28
|
Each detection must have a 'bbox' attribute with (x1, y1, x2, y2) coordinates.
|
|
29
29
|
thickness (Optional[int]): Line thickness for bounding boxes in pixels.
|
|
30
30
|
If None, automatically determined based on image size.
|
|
31
|
-
colors (Optional[List[tuple]]): List of
|
|
31
|
+
colors (Optional[List[tuple]]): List of RGB color tuples to override default colors.
|
|
32
32
|
Colors are mapped to unique class_ids in order of appearance.
|
|
33
33
|
If None, uses default ColorManager colors.
|
|
34
34
|
|
|
@@ -48,13 +48,13 @@ def box(
|
|
|
48
48
|
>>> image = cv2.imread("path/to/image.jpg")
|
|
49
49
|
>>> model = YOLO("yolo11n.pt")
|
|
50
50
|
>>> outputs = model.predict(image) # Raw model outputs
|
|
51
|
-
>>> detections = pf.
|
|
51
|
+
>>> detections = pf.from_ultralytics(outputs) # Convert to PixelFlow format
|
|
52
52
|
>>>
|
|
53
53
|
>>> # Draw boxes with default colors and adaptive thickness
|
|
54
54
|
>>> annotated = pf.annotators.box(image, detections)
|
|
55
55
|
>>>
|
|
56
56
|
>>> # Override with custom colors for specific classes
|
|
57
|
-
>>> custom_colors = [(255, 0, 0), (0, 255, 0), (0, 0, 255)] #
|
|
57
|
+
>>> custom_colors = [(255, 0, 0), (0, 255, 0), (0, 0, 255)] # Red, Green, Blue
|
|
58
58
|
>>> annotated = pf.annotators.box(image, detections, colors=custom_colors)
|
|
59
59
|
>>>
|
|
60
60
|
>>> # Use custom thickness for fine control
|