Skip to content

Commit eb4bbe3

Browse files
committed
Refactor shared stats and skeleton parsing
Move `ProcessorStats` from `dlc_processor` into `services.inference.base` so it can be imported as a shared inference type, and update dependent imports/tests accordingly. Add a typed `SkeletonPacket` protocol plus `resolve_packet_skeleton()` to consistently resolve packet-provided skeleton metadata into a `ResolvedSkeleton` only when required fields are present.
1 parent 3e5ab2d commit eb4bbe3

5 files changed

Lines changed: 59 additions & 29 deletions

File tree

‎dlclivegui/display/skeleton.py‎

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
from dataclasses import dataclass
66
from enum import Enum, auto
7+
from typing import Protocol
78

89
import cv2
910
import numpy as np
@@ -85,11 +86,6 @@ def resolve_skeleton(
8586
)
8687

8788

88-
# ###################### #
89-
# Skeleton I/O #
90-
# ###################### #
91-
92-
9389
def skeleton_definition_from_metadata(
9490
*,
9591
identifier: str,
@@ -126,6 +122,39 @@ def skeleton_definition_from_metadata(
126122
)
127123

128124

125+
# ###################### #
126+
# Skeleton I/O #
127+
# ###################### #
128+
129+
130+
class SkeletonPacket(Protocol):
131+
keypoint_names: list[str] | None
132+
skeleton_id: str | None
133+
skeleton_edges: tuple[tuple[str, str], ...] | None
134+
135+
136+
def resolve_packet_skeleton(
137+
packet: SkeletonPacket,
138+
) -> ResolvedSkeleton | None:
139+
"""Resolve skeleton metadata supplied by a pose packet."""
140+
if not packet.keypoint_names:
141+
return None
142+
143+
if not packet.skeleton_id or not packet.skeleton_edges:
144+
return None
145+
146+
definition = skeleton_definition_from_metadata(
147+
identifier=packet.skeleton_id,
148+
display_name=packet.skeleton_id,
149+
edges=packet.skeleton_edges,
150+
)
151+
152+
return resolve_skeleton(
153+
definition,
154+
packet.keypoint_names,
155+
)
156+
157+
129158
# ###################### #
130159
# Rendering outcomes #
131160
# ###################### #

‎dlclivegui/services/dlc_processor.py‎

Lines changed: 1 addition & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
import time
1010
from collections import deque
1111
from contextlib import contextmanager
12-
from dataclasses import dataclass
1312
from typing import Any
1413

1514
import numpy as np
@@ -27,7 +26,7 @@
2726
from dlclivegui.utils.stats import WorkerTimingStats
2827
from dlclivegui.utils.utils import format_thread_stack
2928

30-
from .inference.base import PoseBackends, PosePacket, PoseResult, PoseSource, WorkerState
29+
from .inference.base import PoseBackends, PosePacket, PoseResult, PoseSource, ProcessorStats, WorkerState
3130

3231
logger = logging.getLogger(__name__)
3332
STOP_WORKER_TIMEOUT = 10.0 # # seconds to wait in STOPPING state before scheduling background reaping
@@ -93,27 +92,6 @@ def validate_pose_array(
9392
return arr
9493

9594

96-
@dataclass
97-
class ProcessorStats:
98-
"""Statistics for DLC processor performance."""
99-
100-
frames_enqueued: int = 0
101-
frames_processed: int = 0
102-
frames_dropped: int = 0
103-
queue_size: int = 0
104-
processing_fps: float = 0.0
105-
average_latency: float = 0.0
106-
last_latency: float = 0.0
107-
# Profiling metrics
108-
avg_queue_wait: float = 0.0
109-
avg_inference_time: float = 0.0
110-
avg_signal_emit_time: float = 0.0
111-
avg_total_process_time: float = 0.0
112-
# Separated timing for GPU vs socket processor
113-
avg_gpu_inference_time: float = 0.0 # Pure model inference
114-
avg_processor_overhead: float = 0.0 # Socket processor overhead
115-
116-
11795
class DLCLiveProcessor(QObject):
11896
"""Background pose estimation using DLCLive with queue-based threading."""
11997

‎dlclivegui/services/inference/base.py‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,3 +42,24 @@ class PoseResult:
4242
pose: np.ndarray | None
4343
timestamp: float
4444
packet: PosePacket | None = None
45+
46+
47+
@dataclass
48+
class ProcessorStats:
49+
"""Statistics for DLC processor performance."""
50+
51+
frames_enqueued: int = 0
52+
frames_processed: int = 0
53+
frames_dropped: int = 0
54+
queue_size: int = 0
55+
processing_fps: float = 0.0
56+
average_latency: float = 0.0
57+
last_latency: float = 0.0
58+
# Profiling metrics
59+
avg_queue_wait: float = 0.0
60+
avg_inference_time: float = 0.0
61+
avg_signal_emit_time: float = 0.0
62+
avg_total_process_time: float = 0.0
63+
# Separated timing for GPU vs socket processor
64+
avg_gpu_inference_time: float = 0.0 # Pure model inference
65+
avg_processor_overhead: float = 0.0 # Socket processor overhead

‎dlclivegui/utils/stats.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from typing import TYPE_CHECKING
88

99
if TYPE_CHECKING:
10-
from dlclivegui.services.dlc_processor import ProcessorStats
10+
from dlclivegui.services.inference.base import ProcessorStats
1111

1212

1313
@dataclass

‎tests/services/test_dlc_processor.py‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
from dlclivegui.processors.processor_utils import ProcessorSpec
1313
from dlclivegui.services.dlc_processor import (
1414
DLCLiveProcessor,
15+
)
16+
from dlclivegui.services.inference.base import (
1517
ProcessorStats,
1618
WorkerState,
1719
)

0 commit comments

Comments
 (0)