nedo-vision-worker-core 0.3.9__tar.gz → 0.4.1__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.
Potentially problematic release.
This version of nedo-vision-worker-core might be problematic. Click here for more details.
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/PKG-INFO +1 -1
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/__init__.py +2 -2
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/ai/FrameDrawer.py +25 -16
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/ai/VideoDebugger.py +29 -23
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/pipeline/PipelineProcessor.py +37 -45
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/streams/RTMPStreamer.py +64 -18
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/util/DrawingUtils.py +4 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core.egg-info/PKG-INFO +1 -1
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/MANIFEST.in +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/README.md +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/ai/ImageDebugger.py +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/ai/__init__.py +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/callbacks/DetectionCallbackManager.py +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/callbacks/DetectionCallbackTypes.py +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/callbacks/__init__.py +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/cli.py +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/config/ConfigurationManager.py +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/config/__init__.py +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/core_service.py +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/database/DatabaseManager.py +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/database/__init__.py +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/detection/BaseDetector.py +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/detection/RFDETRDetector.py +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/detection/YOLODetector.py +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/detection/__init__.py +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/detection/detection_processing/DetectionProcessor.py +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/detection/detection_processing/HumanDetectionProcessor.py +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/detection/detection_processing/PPEDetectionProcessor.py +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/detection/detection_processing/__init__.py +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/doctor.py +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/drawing_assets/blue/inner_corner.png +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/drawing_assets/blue/inner_frame.png +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/drawing_assets/blue/line.png +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/drawing_assets/blue/top_left.png +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/drawing_assets/blue/top_right.png +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/drawing_assets/red/inner_corner.png +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/drawing_assets/red/inner_frame.png +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/drawing_assets/red/line.png +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/drawing_assets/red/top_left.png +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/drawing_assets/red/top_right.png +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/icons/boots-green.png +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/icons/boots-red.png +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/icons/gloves-green.png +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/icons/gloves-red.png +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/icons/goggles-green.png +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/icons/goggles-red.png +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/icons/helmet-green.png +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/icons/helmet-red.png +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/icons/mask-red.png +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/icons/vest-green.png +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/icons/vest-red.png +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/models/__init__.py +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/models/ai_model.py +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/models/auth.py +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/models/config.py +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/models/dataset_source.py +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/models/logs.py +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/models/ppe_detection.py +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/models/ppe_detection_label.py +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/models/restricted_area_violation.py +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/models/user.py +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/models/worker_source.py +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/models/worker_source_pipeline.py +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/models/worker_source_pipeline_config.py +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/models/worker_source_pipeline_debug.py +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/models/worker_source_pipeline_detection.py +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/pipeline/ModelManager.py +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/pipeline/PipelineConfigManager.py +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/pipeline/PipelineManager.py +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/pipeline/PipelinePrepocessor.py +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/pipeline/PipelineSyncThread.py +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/pipeline/__init__.py +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/preprocessing/ImageResizer.py +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/preprocessing/ImageRoi.py +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/preprocessing/Preprocessor.py +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/preprocessing/__init__.py +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/repositories/AIModelRepository.py +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/repositories/BaseRepository.py +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/repositories/PPEDetectionRepository.py +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/repositories/RestrictedAreaRepository.py +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/repositories/WorkerSourcePipelineDebugRepository.py +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/repositories/WorkerSourcePipelineDetectionRepository.py +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/repositories/WorkerSourcePipelineRepository.py +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/repositories/WorkerSourceRepository.py +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/repositories/__init__.py +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/services/SharedVideoStreamServer.py +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/services/VideoSharingDaemon.py +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/services/VideoSharingDaemonManager.py +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/streams/SharedVideoDeviceManager.py +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/streams/StreamSyncThread.py +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/streams/VideoStream.py +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/streams/VideoStreamManager.py +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/streams/__init__.py +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/tracker/SFSORT.py +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/tracker/TrackerManager.py +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/tracker/__init__.py +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/util/BoundingBoxMetrics.py +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/util/ModelReadinessChecker.py +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/util/PersonAttributeMatcher.py +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/util/PersonRestrictedAreaMatcher.py +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/util/PipelinePreviewChecker.py +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/util/PlatformDetector.py +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/util/TablePrinter.py +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/util/__init__.py +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core.egg-info/SOURCES.txt +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core.egg-info/dependency_links.txt +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core.egg-info/entry_points.txt +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core.egg-info/requires.txt +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core.egg-info/top_level.txt +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/pyproject.toml +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/requirements.txt +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/setup.cfg +0 -0
- {nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/setup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: nedo-vision-worker-core
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.1
|
|
4
4
|
Summary: Nedo Vision Worker Core Library for AI Vision Processing
|
|
5
5
|
Author-email: Willy Achmat Fauzi <willy.achmat@gmail.com>
|
|
6
6
|
Maintainer-email: Willy Achmat Fauzi <willy.achmat@gmail.com>
|
{nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/__init__.py
RENAMED
|
@@ -7,10 +7,10 @@ A library for running AI vision processing and detection in the Nedo Vision plat
|
|
|
7
7
|
from .core_service import CoreService
|
|
8
8
|
from .callbacks import DetectionType, CallbackTrigger, DetectionData, IntervalMetadata
|
|
9
9
|
|
|
10
|
-
__version__ = "0.
|
|
10
|
+
__version__ = "0.4.1"
|
|
11
11
|
__all__ = [
|
|
12
12
|
"CoreService",
|
|
13
|
-
"DetectionType",
|
|
13
|
+
"DetectionType",
|
|
14
14
|
"CallbackTrigger",
|
|
15
15
|
"DetectionData",
|
|
16
16
|
"IntervalMetadata",
|
|
@@ -3,12 +3,16 @@ import cv2
|
|
|
3
3
|
import logging
|
|
4
4
|
import os
|
|
5
5
|
from pathlib import Path
|
|
6
|
+
import threading
|
|
6
7
|
|
|
7
8
|
import numpy as np
|
|
8
9
|
from ..util.DrawingUtils import DrawingUtils
|
|
9
10
|
|
|
10
11
|
class FrameDrawer:
|
|
11
12
|
"""Handles frame processing by drawing objects, annotations, and managing icons."""
|
|
13
|
+
|
|
14
|
+
# Share the same lock as DrawingUtils for consistent locking
|
|
15
|
+
_cv_lock = DrawingUtils._cv_lock
|
|
12
16
|
|
|
13
17
|
def __init__(self):
|
|
14
18
|
self.icons = {}
|
|
@@ -67,7 +71,8 @@ class FrameDrawer:
|
|
|
67
71
|
(int(x * width), int(y * height)) for (x, y) in normalized_points
|
|
68
72
|
]
|
|
69
73
|
if len(points) >= 3:
|
|
70
|
-
|
|
74
|
+
with self._cv_lock:
|
|
75
|
+
cv2.polylines(frame, [np.array(points, np.int32)], isClosed=True, color=color, thickness=2)
|
|
71
76
|
|
|
72
77
|
def draw_frame(self, frame, tracked_objects, with_trails=False, trail_length=10):
|
|
73
78
|
current_ids = set()
|
|
@@ -83,7 +88,9 @@ class FrameDrawer:
|
|
|
83
88
|
attributes = obj.get("attributes", [])
|
|
84
89
|
labels = [attr.get("label") for attr in attributes]
|
|
85
90
|
(color, flag) = self._get_color_from_labels(labels)
|
|
86
|
-
|
|
91
|
+
|
|
92
|
+
with self._cv_lock:
|
|
93
|
+
DrawingUtils.draw_bbox_info(frame, bbox, (color, flag), f"{track_id}", self.location_name, f"{obj.get('confidence', 0):.2f}")
|
|
87
94
|
|
|
88
95
|
# Trailing
|
|
89
96
|
if with_trails:
|
|
@@ -109,26 +116,28 @@ class FrameDrawer:
|
|
|
109
116
|
num_points = len(points)
|
|
110
117
|
|
|
111
118
|
# Draw faded trail
|
|
112
|
-
|
|
113
|
-
|
|
119
|
+
with self._cv_lock:
|
|
120
|
+
for i in range(1, num_points):
|
|
121
|
+
cv2.line(frame, points[i-1], points[i], color, 1)
|
|
114
122
|
|
|
115
123
|
current_ids.add(track_id)
|
|
116
124
|
|
|
117
|
-
|
|
125
|
+
with self._cv_lock:
|
|
126
|
+
DrawingUtils.draw_main_bbox(frame, bbox, (color, flag))
|
|
118
127
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
128
|
+
for attr in attributes:
|
|
129
|
+
attr_bbox = attr.get("bbox", [])
|
|
130
|
+
if len(attr_bbox) != 4:
|
|
131
|
+
continue
|
|
123
132
|
|
|
124
|
-
|
|
125
|
-
|
|
133
|
+
attr_label = attr.get("label", "")
|
|
134
|
+
DrawingUtils.draw_inner_box(frame, attr_bbox, self._get_color(attr_label))
|
|
126
135
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
136
|
+
icon_x = x1
|
|
137
|
+
for (label, icon) in self.icons.items():
|
|
138
|
+
if label in labels:
|
|
139
|
+
DrawingUtils.draw_alpha_overlay(frame, icon, icon_x, y1 - 25)
|
|
140
|
+
icon_x += 25
|
|
132
141
|
|
|
133
142
|
# Cleanup trails for objects that disappeared
|
|
134
143
|
if with_trails and hasattr(self, "trails"):
|
|
@@ -22,43 +22,49 @@ class VideoDebugger:
|
|
|
22
22
|
window_name = f"Pipeline {pipeline_id} - {worker_source_id}"
|
|
23
23
|
|
|
24
24
|
try:
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
elapsed_time = time.time() - self.fps_tracker[window_name]["start_time"]
|
|
31
|
-
fps = self.fps_tracker[window_name]["frame_count"] / max(elapsed_time, 1e-5)
|
|
32
|
-
|
|
33
|
-
cv2.putText(frame, f"FPS: {fps:.2f}", (10, 20), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 255), 1)
|
|
25
|
+
# Serialize ALL OpenCV operations to prevent segfaults
|
|
26
|
+
with self._cv_lock:
|
|
27
|
+
with self.lock:
|
|
28
|
+
if window_name not in self.fps_tracker:
|
|
29
|
+
self.fps_tracker[window_name] = {"start_time": time.time(), "frame_count": 0}
|
|
34
30
|
|
|
35
|
-
|
|
36
|
-
self.
|
|
31
|
+
self.fps_tracker[window_name]["frame_count"] += 1
|
|
32
|
+
elapsed_time = time.time() - self.fps_tracker[window_name]["start_time"]
|
|
33
|
+
fps = self.fps_tracker[window_name]["frame_count"] / max(elapsed_time, 1e-5)
|
|
37
34
|
|
|
38
|
-
|
|
39
|
-
|
|
35
|
+
if window_name not in self.windows:
|
|
36
|
+
self.windows[window_name] = True
|
|
37
|
+
|
|
38
|
+
# Make a copy to avoid modifying the original frame from multiple threads
|
|
39
|
+
display_frame = frame.copy()
|
|
40
|
+
|
|
40
41
|
try:
|
|
41
|
-
cv2.
|
|
42
|
+
cv2.putText(display_frame, f"FPS: {fps:.2f}", (10, 20), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 255), 1)
|
|
43
|
+
cv2.imshow(window_name, display_frame)
|
|
42
44
|
key = cv2.waitKey(1) & 0xFF
|
|
43
45
|
|
|
44
46
|
if key == ord('q'):
|
|
45
|
-
self.
|
|
47
|
+
self._close_window_unsafe(window_name)
|
|
46
48
|
except Exception as e:
|
|
47
49
|
logging.error(f"Error displaying frame for {window_name}: {e}")
|
|
48
50
|
|
|
49
51
|
except Exception as e:
|
|
50
52
|
logging.error(f"Error in show_frame for {window_name}: {e}")
|
|
51
53
|
|
|
54
|
+
def _close_window_unsafe(self, window_name):
|
|
55
|
+
"""Close window without acquiring locks (called when already locked)."""
|
|
56
|
+
if window_name in self.windows:
|
|
57
|
+
try:
|
|
58
|
+
cv2.destroyWindow(window_name)
|
|
59
|
+
except Exception as e:
|
|
60
|
+
logging.error(f"Error closing window {window_name}: {e}")
|
|
61
|
+
del self.windows[window_name]
|
|
62
|
+
|
|
52
63
|
def close_window(self, window_name):
|
|
53
64
|
"""Close specific window."""
|
|
54
|
-
with self.
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
try:
|
|
58
|
-
cv2.destroyWindow(window_name)
|
|
59
|
-
except Exception as e:
|
|
60
|
-
logging.error(f"Error closing window {window_name}: {e}")
|
|
61
|
-
del self.windows[window_name]
|
|
65
|
+
with self._cv_lock:
|
|
66
|
+
with self.lock:
|
|
67
|
+
self._close_window_unsafe(window_name)
|
|
62
68
|
|
|
63
69
|
def is_window_open(self, pipeline_id):
|
|
64
70
|
"""Check if a window is open for a given pipeline."""
|
|
@@ -176,6 +176,7 @@ class PipelineProcessor:
|
|
|
176
176
|
|
|
177
177
|
target_render_fps = 25.0
|
|
178
178
|
target_frame_time = 1.0 / target_render_fps
|
|
179
|
+
last_preview_check = 0
|
|
179
180
|
|
|
180
181
|
try:
|
|
181
182
|
while self.running:
|
|
@@ -186,22 +187,24 @@ class PipelineProcessor:
|
|
|
186
187
|
if frame is None:
|
|
187
188
|
if not self._handle_frame_failure(video_manager, worker_source_id):
|
|
188
189
|
break
|
|
189
|
-
# no frame this tick—just continue (the streamer will repeat last good frame)
|
|
190
190
|
time.sleep(0.04)
|
|
191
191
|
continue
|
|
192
192
|
|
|
193
|
-
# successful frame
|
|
194
193
|
self.consecutive_frame_failures = 0
|
|
195
|
-
self.last_successful_frame_time =
|
|
194
|
+
self.last_successful_frame_time = loop_start
|
|
196
195
|
self.frame_counter += 1
|
|
197
196
|
|
|
198
|
-
|
|
197
|
+
# Check preview status less frequently
|
|
198
|
+
if loop_start - last_preview_check >= self.preview_check_interval:
|
|
199
|
+
self._check_and_update_rtmp_streaming()
|
|
200
|
+
last_preview_check = loop_start
|
|
201
|
+
|
|
202
|
+
should_draw = self.rtmp_streaming_active or self.debug_flag
|
|
199
203
|
|
|
200
204
|
if should_draw:
|
|
201
|
-
# draw annotations
|
|
202
205
|
try:
|
|
203
|
-
|
|
204
|
-
|
|
206
|
+
frame_to_draw = frame.copy()
|
|
207
|
+
self.frame_drawer.draw_polygons(frame_to_draw)
|
|
205
208
|
drawn_frame = self.frame_drawer.draw_frame(
|
|
206
209
|
frame_to_draw,
|
|
207
210
|
self.tracked_objects_render,
|
|
@@ -214,26 +217,19 @@ class PipelineProcessor:
|
|
|
214
217
|
else:
|
|
215
218
|
drawn_frame = frame
|
|
216
219
|
|
|
217
|
-
# debug snapshot if requested
|
|
218
220
|
if self.debug_flag:
|
|
219
221
|
tracked_objects_render = self._process_frame(frame)
|
|
220
222
|
try:
|
|
223
|
+
debug_frame = frame.copy()
|
|
221
224
|
self.debug_repo.update_debug_entries_by_pipeline_id(
|
|
222
225
|
self.pipeline_id,
|
|
223
|
-
self.frame_drawer.draw_frame(
|
|
226
|
+
self.frame_drawer.draw_frame(debug_frame, tracked_objects_render),
|
|
224
227
|
tracked_objects_render
|
|
225
228
|
)
|
|
226
229
|
except Exception as e:
|
|
227
230
|
logging.warning(f"Debug save failed: {e}")
|
|
228
231
|
self.debug_flag = False
|
|
229
232
|
|
|
230
|
-
# Check if RTMP streaming should be active based on preview requests
|
|
231
|
-
current_time = time.time()
|
|
232
|
-
if current_time - self.last_preview_check_time >= self.preview_check_interval:
|
|
233
|
-
self._check_and_update_rtmp_streaming()
|
|
234
|
-
self.last_preview_check_time = current_time
|
|
235
|
-
|
|
236
|
-
# Push frame to RTMP stream if preview is active
|
|
237
233
|
if self.rtmp_streaming_active:
|
|
238
234
|
if self.rtmp_streamer is None:
|
|
239
235
|
try:
|
|
@@ -254,35 +250,19 @@ class PipelineProcessor:
|
|
|
254
250
|
except Exception:
|
|
255
251
|
pass
|
|
256
252
|
self.rtmp_streamer = None
|
|
257
|
-
|
|
258
|
-
# Stop RTMP streaming if preview is no longer active
|
|
259
|
-
if self.rtmp_streamer is not None:
|
|
260
|
-
try:
|
|
261
|
-
logging.info(f"🛑 Stopping RTMP streamer for pipeline {pipeline_id} (preview expired)")
|
|
262
|
-
self.rtmp_streamer.stop_stream()
|
|
263
|
-
except Exception as e:
|
|
264
|
-
logging.error(f"❌ Error stopping RTMP streamer: {e}")
|
|
265
|
-
finally:
|
|
266
|
-
self.rtmp_streamer = None
|
|
267
|
-
|
|
268
|
-
# feed detection worker with latest-only behavior
|
|
269
|
-
if self.detection_thread and self.detection_thread.is_alive():
|
|
253
|
+
elif self.rtmp_streamer is not None:
|
|
270
254
|
try:
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
try:
|
|
278
|
-
self.frame_queue.put_nowait(frame)
|
|
279
|
-
except queue.Full:
|
|
280
|
-
pass
|
|
255
|
+
logging.info(f"🛑 Stopping RTMP streamer for pipeline {pipeline_id} (preview expired)")
|
|
256
|
+
self.rtmp_streamer.stop_stream()
|
|
257
|
+
except Exception as e:
|
|
258
|
+
logging.error(f"❌ Error stopping RTMP streamer: {e}")
|
|
259
|
+
finally:
|
|
260
|
+
self.rtmp_streamer = None
|
|
281
261
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
262
|
+
# Only feed frames to detection queue if detection processor is active
|
|
263
|
+
if self.detection_processor is not None:
|
|
264
|
+
if not self.frame_queue.full():
|
|
265
|
+
self.frame_queue.put_nowait(frame)
|
|
286
266
|
|
|
287
267
|
loop_elapsed = time.time() - loop_start
|
|
288
268
|
sleep_time = max(0.001, target_frame_time - loop_elapsed)
|
|
@@ -328,7 +308,13 @@ class PipelineProcessor:
|
|
|
328
308
|
|
|
329
309
|
while self.running:
|
|
330
310
|
try:
|
|
331
|
-
|
|
311
|
+
# Calculate how long to wait for next detection
|
|
312
|
+
current_time = time.time()
|
|
313
|
+
time_since_last_detection = current_time - last_detection_time
|
|
314
|
+
time_until_next_detection = max(0.1, self.detection_interval - time_since_last_detection)
|
|
315
|
+
|
|
316
|
+
# Wait for frame with timeout aligned to detection interval
|
|
317
|
+
frame = self.frame_queue.get(block=True, timeout=time_until_next_detection)
|
|
332
318
|
|
|
333
319
|
# Check for poison pill (None = stop signal)
|
|
334
320
|
if frame is None:
|
|
@@ -353,9 +339,14 @@ class PipelineProcessor:
|
|
|
353
339
|
except queue.Empty:
|
|
354
340
|
pass
|
|
355
341
|
|
|
356
|
-
# Respect detection interval
|
|
342
|
+
# Respect detection interval - skip if too soon
|
|
357
343
|
if (current_time - last_detection_time) < self.detection_interval:
|
|
344
|
+
# Sleep the remaining time instead of busy-waiting
|
|
345
|
+
remaining_time = self.detection_interval - (current_time - last_detection_time)
|
|
346
|
+
if remaining_time > 0.01:
|
|
347
|
+
time.sleep(remaining_time)
|
|
358
348
|
continue
|
|
349
|
+
|
|
359
350
|
last_detection_time = current_time
|
|
360
351
|
|
|
361
352
|
if self.detection_processor is None or frame is None or frame.size == 0:
|
|
@@ -384,6 +375,7 @@ class PipelineProcessor:
|
|
|
384
375
|
)
|
|
385
376
|
|
|
386
377
|
except queue.Empty:
|
|
378
|
+
# Timeout occurred - this is normal, just continue to next iteration
|
|
387
379
|
pass
|
|
388
380
|
except Exception as e:
|
|
389
381
|
logging.error(f"❌ Error in detection thread for pipeline {pipeline_id}: {e}", exc_info=True)
|
|
@@ -307,6 +307,7 @@ class RTMPStreamer:
|
|
|
307
307
|
if not self.is_active():
|
|
308
308
|
raise BrokenPipeError("FFmpeg process is not active")
|
|
309
309
|
self._ffmpeg_process.stdin.write(frame.tobytes())
|
|
310
|
+
self._ffmpeg_process.stdin.flush() # Ensure data is sent immediately
|
|
310
311
|
except (BrokenPipeError, OSError) as e:
|
|
311
312
|
|
|
312
313
|
# Check if this failure was from a HW encoder
|
|
@@ -356,23 +357,20 @@ class RTMPStreamer:
|
|
|
356
357
|
'-framerate', str(self.fps), '-i', '-',
|
|
357
358
|
]
|
|
358
359
|
|
|
360
|
+
# Add encoder and encoder-specific parameters
|
|
359
361
|
cmd.extend(encoder_args)
|
|
360
362
|
|
|
363
|
+
# Common video parameters
|
|
361
364
|
cmd.extend([
|
|
362
|
-
'-
|
|
365
|
+
'-pix_fmt', 'yuv420p',
|
|
363
366
|
'-b:v', f"{self.bitrate}k", '-maxrate', f"{self.bitrate}k", '-bufsize', f"{self.bitrate*2}k",
|
|
364
367
|
'-g', str(self.fps * 2), '-keyint_min', str(self.fps),
|
|
365
|
-
'-force_key_frames', 'expr:gte(t,n_forced*1)',
|
|
366
|
-
'-
|
|
368
|
+
'-force_key_frames', 'expr:gte(t,n_forced*1)',
|
|
369
|
+
'-an', # No audio
|
|
370
|
+
'-flvflags', 'no_duration_filesize',
|
|
371
|
+
'-f', 'flv', self.rtmp_url,
|
|
367
372
|
])
|
|
368
373
|
|
|
369
|
-
if encoder_name == "libx264":
|
|
370
|
-
cmd.extend([
|
|
371
|
-
"-preset", "ultrafast",
|
|
372
|
-
"-tune", "zerolatency",
|
|
373
|
-
"-x264-params", "open_gop=0:aud=1:repeat-headers=1:nal-hrd=cbr",
|
|
374
|
-
])
|
|
375
|
-
|
|
376
374
|
return cmd, encoder_name
|
|
377
375
|
|
|
378
376
|
def _select_ffmpeg_encoder(self, force_cpu: bool = False) -> Tuple[List[str], str]:
|
|
@@ -381,26 +379,74 @@ class RTMPStreamer:
|
|
|
381
379
|
Will force CPU if force_cpu is True.
|
|
382
380
|
"""
|
|
383
381
|
if force_cpu:
|
|
384
|
-
return [
|
|
382
|
+
return [
|
|
383
|
+
"-c:v", "libx264",
|
|
384
|
+
"-preset", "ultrafast",
|
|
385
|
+
"-tune", "zerolatency",
|
|
386
|
+
"-profile:v", "main",
|
|
387
|
+
], "libx264"
|
|
385
388
|
|
|
386
389
|
force_encoder = os.environ.get("RTMP_ENCODER", "").lower()
|
|
387
390
|
|
|
388
391
|
if force_encoder == "cpu" or force_encoder == "libx264":
|
|
389
|
-
return [
|
|
392
|
+
return [
|
|
393
|
+
"-c:v", "libx264",
|
|
394
|
+
"-preset", "ultrafast",
|
|
395
|
+
"-tune", "zerolatency",
|
|
396
|
+
"-profile:v", "main",
|
|
397
|
+
], "libx264"
|
|
390
398
|
elif force_encoder == "nvenc":
|
|
391
|
-
return [
|
|
399
|
+
return [
|
|
400
|
+
"-c:v", "h264_nvenc",
|
|
401
|
+
"-preset", "p1", # p1 = fastest, p7 = slowest
|
|
402
|
+
"-tune", "ull", # ultra-low latency
|
|
403
|
+
"-rc:v", "cbr", # constant bitrate for streaming
|
|
404
|
+
"-rc-lookahead", "0", # disable lookahead for lower latency
|
|
405
|
+
"-delay", "0", # zero delay
|
|
406
|
+
"-zerolatency", "1", # enable zero latency mode
|
|
407
|
+
"-profile:v", "main",
|
|
408
|
+
"-gpu", "0", # Use first GPU
|
|
409
|
+
], "h264_nvenc"
|
|
392
410
|
|
|
393
411
|
if self._platform.is_jetson():
|
|
394
|
-
# Jetson-specific encoder
|
|
395
|
-
return [
|
|
412
|
+
# Jetson-specific encoder with optimizations
|
|
413
|
+
return [
|
|
414
|
+
"-c:v", "h264_nvenc",
|
|
415
|
+
"-preset", "p1",
|
|
416
|
+
"-tune", "ull",
|
|
417
|
+
"-rc:v", "cbr",
|
|
418
|
+
"-rc-lookahead", "0",
|
|
419
|
+
"-delay", "0",
|
|
420
|
+
"-zerolatency", "1",
|
|
421
|
+
"-profile:v", "main",
|
|
422
|
+
], "h264_nvenc"
|
|
396
423
|
|
|
397
424
|
if sys.platform == "darwin":
|
|
398
|
-
return [
|
|
425
|
+
return [
|
|
426
|
+
"-c:v", "h264_videotoolbox",
|
|
427
|
+
"-profile:v", "main",
|
|
428
|
+
"-realtime", "1",
|
|
429
|
+
], "h264_videotoolbox"
|
|
399
430
|
|
|
400
431
|
has_nvidia = (os.environ.get("NVIDIA_VISIBLE_DEVICES") is not None or
|
|
401
432
|
os.path.exists("/proc/driver/nvidia/version"))
|
|
402
433
|
|
|
403
434
|
if has_nvidia:
|
|
404
|
-
return [
|
|
435
|
+
return [
|
|
436
|
+
"-c:v", "h264_nvenc",
|
|
437
|
+
"-preset", "p1", # p1 = fastest preset
|
|
438
|
+
"-tune", "ull", # ultra-low latency
|
|
439
|
+
"-rc:v", "cbr", # constant bitrate
|
|
440
|
+
"-rc-lookahead", "0", # disable lookahead
|
|
441
|
+
"-delay", "0", # zero delay
|
|
442
|
+
"-zerolatency", "1", # zero latency mode
|
|
443
|
+
"-profile:v", "main",
|
|
444
|
+
"-gpu", "0", # Use first GPU
|
|
445
|
+
], "h264_nvenc"
|
|
405
446
|
|
|
406
|
-
return [
|
|
447
|
+
return [
|
|
448
|
+
"-c:v", "libx264",
|
|
449
|
+
"-preset", "ultrafast",
|
|
450
|
+
"-tune", "zerolatency",
|
|
451
|
+
"-profile:v", "main",
|
|
452
|
+
], "libx264"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: nedo-vision-worker-core
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.1
|
|
4
4
|
Summary: Nedo Vision Worker Core Library for AI Vision Processing
|
|
5
5
|
Author-email: Willy Achmat Fauzi <willy.achmat@gmail.com>
|
|
6
6
|
Maintainer-email: Willy Achmat Fauzi <willy.achmat@gmail.com>
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/cli.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{nedo_vision_worker_core-0.3.9 → nedo_vision_worker_core-0.4.1}/nedo_vision_worker_core/doctor.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|