pixelflow 0.1.2__tar.gz → 0.1.3__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.
Files changed (61) hide show
  1. {pixelflow-0.1.2 → pixelflow-0.1.3}/PKG-INFO +22 -17
  2. {pixelflow-0.1.2 → pixelflow-0.1.3}/README.md +20 -16
  3. {pixelflow-0.1.2 → pixelflow-0.1.3}/pixelflow/__init__.py +23 -6
  4. {pixelflow-0.1.2 → pixelflow-0.1.3}/pixelflow/annotators/__init__.py +5 -2
  5. {pixelflow-0.1.2 → pixelflow-0.1.3}/pixelflow/annotators/anchors.py +2 -2
  6. {pixelflow-0.1.2 → pixelflow-0.1.3}/pixelflow/annotators/box.py +3 -3
  7. {pixelflow-0.1.2 → pixelflow-0.1.3}/pixelflow/annotators/crossing.py +6 -6
  8. {pixelflow-0.1.2 → pixelflow-0.1.3}/pixelflow/annotators/filled_box.py +4 -4
  9. {pixelflow-0.1.2 → pixelflow-0.1.3}/pixelflow/annotators/fps_counter.py +9 -6
  10. {pixelflow-0.1.2 → pixelflow-0.1.3}/pixelflow/annotators/grid_overlay.py +4 -4
  11. {pixelflow-0.1.2 → pixelflow-0.1.3}/pixelflow/annotators/keypoint.py +3 -3
  12. {pixelflow-0.1.2 → pixelflow-0.1.3}/pixelflow/annotators/keypoint_skeleton.py +2 -2
  13. {pixelflow-0.1.2 → pixelflow-0.1.3}/pixelflow/annotators/label.py +4 -4
  14. {pixelflow-0.1.2 → pixelflow-0.1.3}/pixelflow/annotators/mask.py +3 -3
  15. {pixelflow-0.1.2 → pixelflow-0.1.3}/pixelflow/annotators/oval.py +3 -3
  16. {pixelflow-0.1.2 → pixelflow-0.1.3}/pixelflow/annotators/polygon.py +4 -4
  17. {pixelflow-0.1.2 → pixelflow-0.1.3}/pixelflow/annotators/utils.py +9 -9
  18. {pixelflow-0.1.2 → pixelflow-0.1.3}/pixelflow/annotators/zones.py +1 -1
  19. pixelflow-0.1.3/pixelflow/assets.py +364 -0
  20. {pixelflow-0.1.2 → pixelflow-0.1.3}/pixelflow/buffer.py +24 -25
  21. pixelflow-0.1.3/pixelflow/classes.py +86 -0
  22. pixelflow-0.1.3/pixelflow/colors.py +100 -0
  23. {pixelflow-0.1.2 → pixelflow-0.1.3}/pixelflow/detections/__init__.py +14 -29
  24. pixelflow-0.1.3/pixelflow/detections/converters.py +1072 -0
  25. {pixelflow-0.1.2 → pixelflow-0.1.3}/pixelflow/detections/detections.py +7 -209
  26. {pixelflow-0.1.2 → pixelflow-0.1.3}/pixelflow/detections/filters.py +31 -345
  27. pixelflow-0.1.3/pixelflow/media.py +458 -0
  28. {pixelflow-0.1.2 → pixelflow-0.1.3}/pixelflow/smoother.py +11 -6
  29. {pixelflow-0.1.2 → pixelflow-0.1.3}/pixelflow/transforms/detections.py +6 -6
  30. {pixelflow-0.1.2 → pixelflow-0.1.3}/pixelflow/transforms/image.py +14 -14
  31. {pixelflow-0.1.2 → pixelflow-0.1.3}/pixelflow.egg-info/PKG-INFO +22 -17
  32. {pixelflow-0.1.2 → pixelflow-0.1.3}/pixelflow.egg-info/SOURCES.txt +2 -0
  33. {pixelflow-0.1.2 → pixelflow-0.1.3}/pixelflow.egg-info/requires.txt +1 -0
  34. {pixelflow-0.1.2 → pixelflow-0.1.3}/pyproject.toml +3 -2
  35. pixelflow-0.1.2/pixelflow/colors.py +0 -100
  36. pixelflow-0.1.2/pixelflow/detections/converters.py +0 -2214
  37. pixelflow-0.1.2/pixelflow/media.py +0 -427
  38. {pixelflow-0.1.2 → pixelflow-0.1.3}/LICENSE +0 -0
  39. {pixelflow-0.1.2 → pixelflow-0.1.3}/pixelflow/annotators/blur.py +0 -0
  40. {pixelflow-0.1.2 → pixelflow-0.1.3}/pixelflow/annotators/dot.py +0 -0
  41. {pixelflow-0.1.2 → pixelflow-0.1.3}/pixelflow/annotators/heatmap.py +0 -0
  42. {pixelflow-0.1.2 → pixelflow-0.1.3}/pixelflow/annotators/motion_dots.py +0 -0
  43. {pixelflow-0.1.2 → pixelflow-0.1.3}/pixelflow/annotators/motion_trails.py +0 -0
  44. {pixelflow-0.1.2 → pixelflow-0.1.3}/pixelflow/annotators/pixelate.py +0 -0
  45. {pixelflow-0.1.2 → pixelflow-0.1.3}/pixelflow/annotators/scale_bar.py +0 -0
  46. {pixelflow-0.1.2 → pixelflow-0.1.3}/pixelflow/crossings.py +0 -0
  47. {pixelflow-0.1.2 → pixelflow-0.1.3}/pixelflow/slicer.py +0 -0
  48. {pixelflow-0.1.2 → pixelflow-0.1.3}/pixelflow/strategies.py +0 -0
  49. {pixelflow-0.1.2 → pixelflow-0.1.3}/pixelflow/timer.py +0 -0
  50. {pixelflow-0.1.2 → pixelflow-0.1.3}/pixelflow/tracker/__init__.py +0 -0
  51. {pixelflow-0.1.2 → pixelflow-0.1.3}/pixelflow/tracker/bytetrack.py +0 -0
  52. {pixelflow-0.1.2 → pixelflow-0.1.3}/pixelflow/tracker/kalman_filter.py +0 -0
  53. {pixelflow-0.1.2 → pixelflow-0.1.3}/pixelflow/tracker/matching.py +0 -0
  54. {pixelflow-0.1.2 → pixelflow-0.1.3}/pixelflow/tracker/track.py +0 -0
  55. {pixelflow-0.1.2 → pixelflow-0.1.3}/pixelflow/transforms/__init__.py +0 -0
  56. {pixelflow-0.1.2 → pixelflow-0.1.3}/pixelflow/transforms/inverse.py +0 -0
  57. {pixelflow-0.1.2 → pixelflow-0.1.3}/pixelflow/validators.py +0 -0
  58. {pixelflow-0.1.2 → pixelflow-0.1.3}/pixelflow/zones.py +0 -0
  59. {pixelflow-0.1.2 → pixelflow-0.1.3}/pixelflow.egg-info/dependency_links.txt +0 -0
  60. {pixelflow-0.1.2 → pixelflow-0.1.3}/pixelflow.egg-info/top_level.txt +0 -0
  61. {pixelflow-0.1.2 → pixelflow-0.1.3}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pixelflow
3
- Version: 0.1.2
3
+ Version: 0.1.3
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
@@ -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,12 +51,12 @@ import pixelflow as pf
50
51
  from ultralytics import YOLO
51
52
 
52
53
  model = YOLO("yolo11n.pt")
53
- media = pf.Media("traffic.mp4")
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 media.frames:
59
+ for frame in video:
59
60
  detections = pf.detections.from_ultralytics(model.predict(frame))
60
61
  detections = tracker.update(detections)
61
62
  zones.update(detections)
@@ -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
@@ -139,29 +141,32 @@ english_text = detections.filter_by_text_language("en")
139
141
 
140
142
  ### Media Handling
141
143
 
142
- Unified interface for videos, images, webcams, and streams:
144
+ Purpose-built classes for videos, cameras, and images:
143
145
 
144
146
  ```python
145
147
  import pixelflow as pf
146
148
 
147
- # Video file with automatic resizing
148
- media = pf.Media("video.mp4", width=640)
149
- print(media.info) # MediaInfo(resolution=1920x1080, fps=30.00, frames=900, duration=30.00s)
149
+ # Read a video file
150
+ video = pf.VideoReader("video.mp4", width=640)
151
+ print(f"{video.width}x{video.height}, {video.fps}fps, {len(video)} frames")
150
152
 
151
- for frame in media.frames: # Lazy loading, memory efficient
152
- # Process frame
153
- pf.show_frame("Preview", frame)
153
+ for frame in video: # Replayable — resets on each iteration
154
+ if pf.show_frame("Preview", frame) == ord('q'):
155
+ break
154
156
 
155
- # Webcam
156
- webcam = pf.Media(0)
157
+ # Load a single image
158
+ image = pf.read_image("photo.jpg", width=640)
157
159
 
158
- # Network stream
159
- stream = pf.Media("rtsp://camera.local/stream")
160
+ # Webcam / network stream
161
+ cam = pf.CameraStream(0, width=640)
162
+ cam = pf.CameraStream("rtsp://camera.local/stream")
160
163
 
161
164
  # Write processed video
162
- for frame in pf.Media("input.mp4").frames:
165
+ writer = pf.VideoWriter("output.mp4", fps=video.fps)
166
+ for frame in video:
163
167
  processed = process(frame)
164
- pf.write_frame("output.mp4", processed, media.info)
168
+ writer.write(processed)
169
+ writer.close()
165
170
  ```
166
171
 
167
172
  ### Zone-Based Analytics
@@ -13,12 +13,12 @@ import pixelflow as pf
13
13
  from ultralytics import YOLO
14
14
 
15
15
  model = YOLO("yolo11n.pt")
16
- media = pf.Media("traffic.mp4")
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 media.frames:
21
+ for frame in video:
22
22
  detections = pf.detections.from_ultralytics(model.predict(frame))
23
23
  detections = tracker.update(detections)
24
24
  zones.update(detections)
@@ -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
@@ -102,29 +103,32 @@ english_text = detections.filter_by_text_language("en")
102
103
 
103
104
  ### Media Handling
104
105
 
105
- Unified interface for videos, images, webcams, and streams:
106
+ Purpose-built classes for videos, cameras, and images:
106
107
 
107
108
  ```python
108
109
  import pixelflow as pf
109
110
 
110
- # Video file with automatic resizing
111
- media = pf.Media("video.mp4", width=640)
112
- print(media.info) # MediaInfo(resolution=1920x1080, fps=30.00, frames=900, duration=30.00s)
111
+ # Read a video file
112
+ video = pf.VideoReader("video.mp4", width=640)
113
+ print(f"{video.width}x{video.height}, {video.fps}fps, {len(video)} frames")
113
114
 
114
- for frame in media.frames: # Lazy loading, memory efficient
115
- # Process frame
116
- pf.show_frame("Preview", frame)
115
+ for frame in video: # Replayable — resets on each iteration
116
+ if pf.show_frame("Preview", frame) == ord('q'):
117
+ break
117
118
 
118
- # Webcam
119
- webcam = pf.Media(0)
119
+ # Load a single image
120
+ image = pf.read_image("photo.jpg", width=640)
120
121
 
121
- # Network stream
122
- stream = pf.Media("rtsp://camera.local/stream")
122
+ # Webcam / network stream
123
+ cam = pf.CameraStream(0, width=640)
124
+ cam = pf.CameraStream("rtsp://camera.local/stream")
123
125
 
124
126
  # Write processed video
125
- for frame in pf.Media("input.mp4").frames:
127
+ writer = pf.VideoWriter("output.mp4", fps=video.fps)
128
+ for frame in video:
126
129
  processed = process(frame)
127
- pf.write_frame("output.mp4", processed, media.info)
130
+ writer.write(processed)
131
+ writer.close()
128
132
  ```
129
133
 
130
134
  ### Zone-Based Analytics
@@ -1,4 +1,4 @@
1
- __version__ = "0.1.2"
1
+ __version__ = "0.1.3"
2
2
  __author__ = "Datamarkin"
3
3
 
4
4
  # Import core modules
@@ -14,9 +14,15 @@ from . import slicer
14
14
  from . import smoother
15
15
  from . import timer
16
16
  from . import tracker
17
+ from . import assets
18
+ from .classes import COCO_CLASSES
17
19
 
18
20
  # Import specific functions for top-level access
19
- from .media import Media, MediaInfo, write_frame, show_frame, close_display
21
+ from .media import (
22
+ DisplayExit, VideoReader, CameraStream, VideoWriter,
23
+ read_image, read_video, display_video, display_image, save_image,
24
+ close_display, to_pil, from_pil,
25
+ )
20
26
  from .zones import Zones
21
27
  from .crossings import Crossings
22
28
  from .slicer import SlicedInference, auto_slice_size
@@ -38,11 +44,18 @@ __all__ = [
38
44
 
39
45
  # Media handling
40
46
  "media",
41
- "Media",
42
- "MediaInfo",
43
- "write_frame",
44
- "show_frame",
47
+ "DisplayExit",
48
+ "VideoReader",
49
+ "CameraStream",
50
+ "VideoWriter",
51
+ "read_image",
52
+ "read_video",
53
+ "display_video",
54
+ "display_image",
55
+ "save_image",
45
56
  "close_display",
57
+ "to_pil",
58
+ "from_pil",
46
59
 
47
60
  # Spatial analysis
48
61
  "zones",
@@ -51,6 +64,7 @@ __all__ = [
51
64
  "Crossings",
52
65
 
53
66
  # Processing utilities
67
+ "assets",
54
68
  "slicer",
55
69
  "SlicedInference",
56
70
  "auto_slice_size",
@@ -62,6 +76,9 @@ __all__ = [
62
76
  "TimeTracker",
63
77
  "tracker",
64
78
 
79
+ # Constants
80
+ "COCO_CLASSES",
81
+
65
82
  # Metadata
66
83
  "__version__",
67
84
  "__author__"
@@ -23,7 +23,7 @@ from .zones import zones
23
23
  # TODO: Import these once implemented
24
24
  from .filled_box import filled_box
25
25
  # from .dot import dot
26
- # from .fps_counter import fps_counter
26
+ from .fps_counter import fps_counter
27
27
  from .grid_overlay import grid_overlay
28
28
  # from .heatmap import heatmap
29
29
  from .keypoint import keypoint
@@ -56,5 +56,8 @@ __all__ = [
56
56
 
57
57
  # Zone annotators
58
58
  'zones',
59
- 'crossings'
59
+ 'crossings',
60
+
61
+ # Overlay annotators
62
+ 'fps_counter',
60
63
  ]
@@ -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 BGR format.
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 BGR color tuples for custom colors.
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
 
@@ -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 (BGR format).
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 BGR color tuples to override default colors.
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
 
@@ -54,7 +54,7 @@ def box(
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)] # Blue, Green, Red
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
@@ -29,18 +29,18 @@ def crossing(
29
29
  The annotation adapts automatically to image resolution for optimal visibility.
30
30
 
31
31
  Args:
32
- image (np.ndarray): Input BGR image to annotate. Must be a 3-channel NumPy array
32
+ image (np.ndarray): Input RGB image to annotate. Must be a 3-channel NumPy array
33
33
  with shape (height, width, 3).
34
34
  crossing_line: Crossing object from pixelflow.crossings containing line geometry,
35
35
  counts, and color. Must have attributes: start, end, color,
36
36
  in_count, out_count.
37
37
  thickness (Optional[int]): Line thickness in pixels. If None, uses adaptive
38
38
  sizing based on image resolution. Range: [1, ∞].
39
- color (Optional[Tuple[int, int, int]]): Line color as BGR tuple (0-255 each).
39
+ color (Optional[Tuple[int, int, int]]): Line color as RGB tuple (0-255 each).
40
40
  If None, uses crossing_line.color.
41
41
  text_thickness (Optional[int]): Text stroke thickness in pixels. If None,
42
42
  uses adaptive sizing. Range: [1, ∞].
43
- text_color (Optional[Tuple[int, int, int]]): Text color as BGR tuple (0-255 each).
43
+ text_color (Optional[Tuple[int, int, int]]): Text color as RGB tuple (0-255 each).
44
44
  Default is white (255, 255, 255).
45
45
  text_scale (Optional[float]): Text scale factor. If None, uses adaptive sizing.
46
46
  Range: (0, ∞). Default ~0.5.
@@ -146,7 +146,7 @@ def crossing(
146
146
  start_point = tuple(map(int, crossing_line.start))
147
147
  end_point = tuple(map(int, crossing_line.end))
148
148
  cv2.line(image, start_point, end_point, crossing_color, thickness, cv2.LINE_AA)
149
-
149
+
150
150
  # Draw end point markers (scale with image size)
151
151
  marker_size = max(3, int(params['thickness'] * 2.5))
152
152
  cv2.circle(image, start_point, marker_size, text_color, -1, cv2.LINE_AA)
@@ -191,7 +191,7 @@ def crossing(
191
191
  bg_x2 = text_x + text_width + text_padding
192
192
  bg_y2 = text_y + text_padding
193
193
  cv2.rectangle(image, (bg_x1, bg_y1), (bg_x2, bg_y2), crossing_color, -1)
194
-
194
+
195
195
  # Draw text
196
196
  cv2.putText(
197
197
  image, text, (text_x, text_y),
@@ -211,7 +211,7 @@ def crossings(image: np.ndarray, crossings_manager) -> np.ndarray:
211
211
  information using the crossing() function.
212
212
 
213
213
  Args:
214
- image (np.ndarray): Input BGR image to annotate. Must be a 3-channel NumPy array
214
+ image (np.ndarray): Input RGB image to annotate. Must be a 3-channel NumPy array
215
215
  with shape (height, width, 3).
216
216
  crossings_manager: Crossings manager object containing multiple crossing lines.
217
217
  Must have a 'crossings' attribute that is iterable,
@@ -23,7 +23,7 @@ def filled_box(
23
23
  dimensions for optimal visibility and uses alpha blending for professional-quality results.
24
24
 
25
25
  Args:
26
- image (np.ndarray): Input image to draw filled boxes on in BGR color format.
26
+ image (np.ndarray): Input image to draw filled boxes on in RGB color format.
27
27
  Expected shape: (height, width, 3) with uint8 values [0, 255].
28
28
  detections (Detections): Detections object containing bounding boxes.
29
29
  Each detection must have a 'bbox' attribute with (x1, y1, x2, y2)
@@ -33,8 +33,8 @@ def filled_box(
33
33
  Range: [0.0, 1.0] where 0.0 is fully transparent and 1.0 is opaque.
34
34
  Values outside this range are automatically clamped.
35
35
  If None, automatically calculated based on image size (typically 0.3-0.5).
36
- colors (Optional[List[tuple]]): List of BGR color tuples to override default colors.
37
- Each tuple should contain (B, G, R) values in range [0, 255].
36
+ colors (Optional[List[tuple]]): List of RGB color tuples to override default colors.
37
+ Each tuple should contain (R, G, B) values in range [0, 255].
38
38
  Colors are mapped to unique class_ids in order of appearance.
39
39
  If None, uses default ColorManager colors based on class_id.
40
40
 
@@ -66,7 +66,7 @@ def filled_box(
66
66
  >>> annotated = pf.annotate.filled_box(image, detections, opacity=0.25)
67
67
  >>>
68
68
  >>> # Override with custom colors for specific visualization needs
69
- >>> custom_colors = [(255, 0, 0), (0, 255, 0), (0, 0, 255)] # Blue, Green, Red
69
+ >>> custom_colors = [(255, 0, 0), (0, 255, 0), (0, 0, 255)] # Red, Green, Blue
70
70
  >>> annotated = pf.annotate.filled_box(image, detections, opacity=0.4, colors=custom_colors)
71
71
  >>>
72
72
  >>> # High opacity for maximum emphasis in presentations
@@ -32,12 +32,15 @@ def fps_counter(image: np.ndarray, detections: 'Detections', thickness=None):
32
32
  else:
33
33
  font_scale = 1
34
34
 
35
- # Import colors locally to avoid circular dependency
36
- from .. import colors as color_module
37
- colors = color_module.ColorManager()
38
-
39
- # Draw FPS text
35
+ # Compute adaptive position based on image dimensions
36
+ params = _get_adaptive_params(image)
37
+ padding = params['padding']
38
+ text_size = cv2.getTextSize(f"FPS: 000", cv2.FONT_HERSHEY_SIMPLEX, font_scale, thickness)[0]
39
+ position = (padding, padding + text_size[1])
40
+
41
+ # Draw FPS text in green (RGB)
42
+ fps_color = (0, 255, 0)
40
43
  fps_text = f"FPS: {int(fps_counter.fps)}"
41
- cv2.putText(image, fps_text, (10, 30), cv2.FONT_HERSHEY_SIMPLEX, font_scale, colors.ui('fps'), thickness)
44
+ cv2.putText(image, fps_text, position, cv2.FONT_HERSHEY_SIMPLEX, font_scale, fps_color, thickness)
42
45
 
43
46
  return image
@@ -26,7 +26,7 @@ def grid_overlay(
26
26
  Can create filled checkerboard patterns for visual segmentation.
27
27
 
28
28
  Args:
29
- image (np.ndarray): Input image to draw grid overlays on. Must be in BGR format
29
+ image (np.ndarray): Input image to draw grid overlays on. Must be in RGB format
30
30
  with shape (H, W, 3) and dtype uint8.
31
31
  detections (Detections): Detections object containing bounding boxes.
32
32
  Each detection must have a 'bbox' attribute with (x1, y1, x2, y2) coordinates
@@ -38,7 +38,7 @@ def grid_overlay(
38
38
  thickness (Optional[int]): Line thickness for grid lines in pixels.
39
39
  If None, automatically determined based on image size (typically 1-3).
40
40
  Range: Must be >= 1. Values > 10 may cause visual overlap.
41
- colors (Optional[List[tuple]]): List of BGR color tuples to override default colors.
41
+ colors (Optional[List[tuple]]): List of RGB color tuples to override default colors.
42
42
  Each tuple must be 3 integers in range [0, 255].
43
43
  Colors are mapped to unique class_ids in order of appearance.
44
44
  If None, uses default ColorManager colors.
@@ -151,7 +151,7 @@ def grid_overlay(
151
151
  cell_x2 = int(x1 + (col + 1) * cell_width)
152
152
  cell_y2 = int(y1 + (row + 1) * cell_height)
153
153
 
154
- cv2.rectangle(overlay, (cell_x1, cell_y1), (cell_x2, cell_y2),
154
+ cv2.rectangle(overlay, (cell_x1, cell_y1), (cell_x2, cell_y2),
155
155
  color, thickness=cv2.FILLED)
156
156
 
157
157
  # Blend overlay with original image
@@ -161,7 +161,7 @@ def grid_overlay(
161
161
  for i in range(1, cols):
162
162
  x_pos = int(x1 + (width * i / cols))
163
163
  cv2.line(image, (x_pos, y1), (x_pos, y2), color=color, thickness=thickness)
164
-
164
+
165
165
  # Draw horizontal grid lines
166
166
  for i in range(1, rows):
167
167
  y_pos = int(y1 + (height * i / rows))
@@ -26,7 +26,7 @@ def keypoint(
26
26
  point annotations.
27
27
 
28
28
  Args:
29
- image (np.ndarray): Input image to draw keypoints on (BGR format).
29
+ image (np.ndarray): Input image to draw keypoints on (RGB format).
30
30
  Modified in-place with drawn keypoint markers.
31
31
  detections (Detections): Detections object containing keypoints.
32
32
  Each detection may have a 'keypoints' attribute with
@@ -36,7 +36,7 @@ def keypoint(
36
36
  thickness (Optional[int]): Thickness of circle outline in pixels.
37
37
  Use -1 for filled circles (default).
38
38
  If None, defaults to -1 (filled).
39
- colors (Optional[List[tuple]]): List of BGR color tuples for custom colors.
39
+ colors (Optional[List[tuple]]): List of RGB color tuples for custom colors.
40
40
  Colors mapped to unique class_ids in order.
41
41
  If None, uses default ColorManager colors.
42
42
  show_names (bool): If True, draws keypoint names as text labels next to points.
@@ -67,7 +67,7 @@ def keypoint(
67
67
  ... image, detections,
68
68
  ... radius=5,
69
69
  ... thickness=2,
70
- ... colors=[(0, 255, 0)], # Green
70
+ ... colors=[(0, 255, 0)], # Green (RGB)
71
71
  ... show_names=True
72
72
  ... )
73
73
  >>>
@@ -50,7 +50,7 @@ def keypoint_skeleton(
50
50
  body structure, wing structure, or any connected landmark system.
51
51
 
52
52
  Args:
53
- image (np.ndarray): Input image to draw skeleton on (BGR format).
53
+ image (np.ndarray): Input image to draw skeleton on (RGB format).
54
54
  Modified in-place with drawn skeleton lines.
55
55
  detections (Detections): Detections object containing keypoints.
56
56
  Each detection may have a 'keypoints' attribute with
@@ -61,7 +61,7 @@ def keypoint_skeleton(
61
61
  If None, uses COCO human pose skeleton.
62
62
  thickness (Optional[int]): Line thickness for skeleton connections in pixels.
63
63
  If None, automatically determined based on image size.
64
- colors (Optional[List[tuple]]): List of BGR color tuples for custom colors.
64
+ colors (Optional[List[tuple]]): List of RGB color tuples for custom colors.
65
65
  Colors mapped to unique class_ids in order.
66
66
  If None, uses default ColorManager colors.
67
67
 
@@ -29,7 +29,7 @@ def label(
29
29
 
30
30
  Args:
31
31
  image (np.ndarray): Input image to annotate. Must be a valid OpenCV image array
32
- in BGR format with shape (H, W, 3) or (H, W).
32
+ in RGB format with shape (H, W, 3) or (H, W).
33
33
  detections (Detections): PixelFlow detections object containing bounding box
34
34
  coordinates and optional attributes (class_name, confidence,
35
35
  class_id, tracker_id).
@@ -48,10 +48,10 @@ def label(
48
48
  Range: [0, 50]. Default is 6.
49
49
  line_spacing (int): Additional spacing in pixels between lines for multi-line text.
50
50
  Range: [0, 20]. Default is 2.
51
- bg_color (Optional[Union[tuple, str]]): Background rectangle color in BGR format.
51
+ bg_color (Optional[Union[tuple, str]]): Background rectangle color in RGB format.
52
52
  If None, uses automatic color based on class_id.
53
- Can be tuple (B, G, R) or color string.
54
- text_color (tuple): Text color in BGR format. Default is white (255, 255, 255).
53
+ Can be tuple (R, G, B) or color string.
54
+ text_color (tuple): Text color in RGB format. Default is white (255, 255, 255).
55
55
 
56
56
  Returns:
57
57
  np.ndarray: Input image with labels drawn directly on it (in-place modification).
@@ -22,7 +22,7 @@ def mask(
22
22
  lists, with automatic format detection and conversion for seamless integration.
23
23
 
24
24
  Args:
25
- frame (np.ndarray): Input video frame in BGR format (height, width, 3).
25
+ frame (np.ndarray): Input video frame in RGB format (height, width, 3).
26
26
  Modified in-place with overlaid masks.
27
27
  detections (Detections): Detections object containing mask data.
28
28
  Each detection's masks attribute can contain binary arrays
@@ -30,7 +30,7 @@ def mask(
30
30
  opacity (float): Opacity level for blending masks with the frame.
31
31
  Range: [0.0, 1.0]. Default is 0.5 (semi-transparent).
32
32
  Value of 1.0 creates opaque masks, 0.0 makes them invisible.
33
- colors (Optional[List[tuple]]): List of BGR color tuples to override default colors.
33
+ colors (Optional[List[tuple]]): List of RGB color tuples to override default colors.
34
34
  Colors are mapped to unique class_ids in order of appearance.
35
35
  If None, uses default ColorManager colors.
36
36
 
@@ -57,7 +57,7 @@ def mask(
57
57
  >>> annotated = pf.annotators.mask(image, detections)
58
58
  >>>
59
59
  >>> # Use custom colors for specific classes
60
- >>> custom_colors = [(255, 0, 0), (0, 255, 0), (0, 0, 255)] # Blue, Green, Red
60
+ >>> custom_colors = [(255, 0, 0), (0, 255, 0), (0, 0, 255)] # Red, Green, Blue
61
61
  >>> annotated = pf.annotators.mask(image, detections, colors=custom_colors)
62
62
  >>>
63
63
  >>> # Create opaque masks for clear segmentation boundaries
@@ -26,7 +26,7 @@ def oval(
26
26
  effects, and ground-based tracking visualizations.
27
27
 
28
28
  Args:
29
- image (np.ndarray): Input image array with shape (H, W, 3) in BGR color format.
29
+ image (np.ndarray): Input image array with shape (H, W, 3) in RGB color format.
30
30
  detections (Detections): PixelFlow detections object containing bounding boxes.
31
31
  Each detection must have a `.bbox` attribute with
32
32
  (x1, y1, x2, y2) coordinates.
@@ -38,8 +38,8 @@ def oval(
38
38
  end_angle (int): Ending angle of the ellipse arc in degrees.
39
39
  Range: -180 to 360. Default is 235 (bottom-right, creating
40
40
  bottom arc spanning ~280 degrees).
41
- colors (Optional[List[Tuple[int, int, int]]]): List of BGR color tuples
42
- (B, G, R) where each value is
41
+ colors (Optional[List[Tuple[int, int, int]]]): List of RGB color tuples
42
+ (R, G, B) where each value is
43
43
  0-255. Colors are mapped to unique
44
44
  class_ids in order of appearance.
45
45
  If None, uses default ColorManager colors.
@@ -23,15 +23,15 @@ def polygon(
23
23
  color schemes for visual distinction between different object classes.
24
24
 
25
25
  Args:
26
- image (np.ndarray): Input image to draw polygons on. Must be a valid BGR image array.
26
+ image (np.ndarray): Input image to draw polygons on. Must be a valid RGB image array.
27
27
  detections (Detections): Detections object containing segmentation data.
28
28
  Each detection must have a 'segments' attribute with polygon coordinates.
29
29
  thickness (Optional[int]): Line thickness for polygon outlines in pixels.
30
30
  If None, automatically calculated based on image dimensions.
31
31
  Range: [1-50]. Default is adaptive (typically 1-6).
32
- colors (Optional[List[Tuple[int, int, int]]]): List of BGR color tuples to override default colors.
32
+ colors (Optional[List[Tuple[int, int, int]]]): List of RGB color tuples to override default colors.
33
33
  Colors are cycled through unique class_ids in order of appearance.
34
- Each tuple should be (B, G, R) with values [0-255].
34
+ Each tuple should be (R, G, B) with values [0-255].
35
35
  If None, uses default ColorManager colors.
36
36
 
37
37
  Returns:
@@ -57,7 +57,7 @@ def polygon(
57
57
  >>> annotated = pf.annotators.polygon(image, results)
58
58
  >>>
59
59
  >>> # Customize line thickness and colors
60
- >>> custom_colors = [(255, 0, 0), (0, 255, 0), (0, 0, 255)] # Blue, Green, Red
60
+ >>> custom_colors = [(255, 0, 0), (0, 255, 0), (0, 0, 255)] # Red, Green, Blue
61
61
  >>> annotated = pf.annotators.polygon(image, results, thickness=3, colors=custom_colors)
62
62
  >>>
63
63
  >>> # Use adaptive thickness on high-resolution image
@@ -32,16 +32,16 @@ def _get_adaptive_params(image):
32
32
  base_scale = np.sqrt(width * height) / 1000 * ADAPTIVE_SCALE_MULTIPLIER
33
33
 
34
34
  params = {
35
- 'thickness': max(1, int(base_scale * 2)),
36
- 'font_scale': max(0.3, base_scale * 0.5),
37
- 'font_thickness': max(1, int(base_scale * 1.5)),
38
- 'padding': max(2, int(base_scale * 5)),
39
- 'margin': max(1, int(base_scale * 2)),
40
- 'text_offset': max(10, int(base_scale * 20)),
41
- 'blur_kernel': max(3, int(base_scale * 15)) | 1, # ensure odd number
35
+ 'thickness': max(1, int(base_scale * 3)),
36
+ 'font_scale': max(0.3, base_scale * 0.6),
37
+ 'font_thickness': max(1, int(base_scale * 2.5)),
38
+ 'padding': max(2, int(base_scale * 8)),
39
+ 'margin': max(1, int(base_scale * 3)),
40
+ 'text_offset': max(10, int(base_scale * 25)),
41
+ 'blur_kernel': max(3, int(base_scale * 25)) | 1, # ensure odd number
42
42
  'pixel_size': max(2, int(base_scale * 10)),
43
- 'corner_radius': max(0, int(base_scale * 5)),
44
- 'shadow_offset': max(1, int(base_scale * 2)),
43
+ 'corner_radius': max(0, int(base_scale * 8)),
44
+ 'shadow_offset': max(1, int(base_scale * 3)),
45
45
  }
46
46
 
47
47
  _adaptive_cache[shape_key] = params
@@ -28,7 +28,7 @@ def zones(
28
28
  thickness based on image resolution for optimal visibility across different image sizes.
29
29
 
30
30
  Args:
31
- image (np.ndarray): Input image to annotate. Must be 3-channel BGR format.
31
+ image (np.ndarray): Input image to annotate. Must be 3-channel RGB format.
32
32
  zone_manager: ZoneManager instance containing polygon zones with associated metadata.
33
33
  Must have 'zones' attribute containing Zone objects with polygon, color,
34
34
  name, and count attributes.