shows window for distance tracking, only uses keypoints where same keypoint >75% confidence on both feeds
This commit is contained in:
@@ -43,6 +43,9 @@ KEYPOINT_NAMES = [
|
|||||||
'left_knee', 'right_knee', 'left_ankle', 'right_ankle',
|
'left_knee', 'right_knee', 'left_ankle', 'right_ankle',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# Minimum confidence in BOTH camera feeds to include a keypoint in distance calc
|
||||||
|
DIST_THRESHOLD = 0.75
|
||||||
|
|
||||||
|
|
||||||
def draw_keypoints(frame, keypoints, keypoint_scores, threshold=0.3):
|
def draw_keypoints(frame, keypoints, keypoint_scores, threshold=0.3):
|
||||||
"""Draw COCO 17-keypoint skeleton on *frame* (in-place)."""
|
"""Draw COCO 17-keypoint skeleton on *frame* (in-place)."""
|
||||||
@@ -118,6 +121,10 @@ class KeypointTriangulationNode(Node):
|
|||||||
# ── Display state ───────────────────────────────────────────────
|
# ── Display state ───────────────────────────────────────────────
|
||||||
self._left_display = None
|
self._left_display = None
|
||||||
self._right_display = None
|
self._right_display = None
|
||||||
|
self._dist_display = None
|
||||||
|
self.get_logger().info(
|
||||||
|
f'Distance window uses keypoints with >= {DIST_THRESHOLD*100:.0f}% '
|
||||||
|
'confidence in both camera feeds.')
|
||||||
self.create_timer(1.0 / 30.0, self._display_timer_cb)
|
self.create_timer(1.0 / 30.0, self._display_timer_cb)
|
||||||
|
|
||||||
self.get_logger().info(
|
self.get_logger().info(
|
||||||
@@ -371,8 +378,10 @@ class KeypointTriangulationNode(Node):
|
|||||||
|
|
||||||
# Triangulate every mutually-confident keypoint
|
# Triangulate every mutually-confident keypoint
|
||||||
all_points_3d = []
|
all_points_3d = []
|
||||||
|
avg_distances = [] # per-person average distance (75%+ conf kps only)
|
||||||
for lp, rp in matches:
|
for lp, rp in matches:
|
||||||
person_pts = {} # kp_idx -> (xyz, residual)
|
person_pts = {} # kp_idx -> (xyz, residual)
|
||||||
|
high_conf_dists = []
|
||||||
for kp_idx in range(17):
|
for kp_idx in range(17):
|
||||||
if (lp['scores'][kp_idx] <= self._threshold or
|
if (lp['scores'][kp_idx] <= self._threshold or
|
||||||
rp['scores'][kp_idx] <= self._threshold):
|
rp['scores'][kp_idx] <= self._threshold):
|
||||||
@@ -387,14 +396,41 @@ class KeypointTriangulationNode(Node):
|
|||||||
pt3d, residual = self._triangulate(d1, d2)
|
pt3d, residual = self._triangulate(d1, d2)
|
||||||
if pt3d is not None and residual < self._max_residual:
|
if pt3d is not None and residual < self._max_residual:
|
||||||
person_pts[kp_idx] = (pt3d, residual)
|
person_pts[kp_idx] = (pt3d, residual)
|
||||||
|
# Only count toward distance if both scores >= DIST_THRESHOLD
|
||||||
|
if (lp['scores'][kp_idx] >= DIST_THRESHOLD and
|
||||||
|
rp['scores'][kp_idx] >= DIST_THRESHOLD):
|
||||||
|
high_conf_dists.append(np.linalg.norm(pt3d))
|
||||||
|
|
||||||
if person_pts:
|
if person_pts:
|
||||||
all_points_3d.append(person_pts)
|
all_points_3d.append(person_pts)
|
||||||
|
avg_distances.append(
|
||||||
|
float(np.mean(high_conf_dists)) if high_conf_dists else None)
|
||||||
|
|
||||||
# Publish 3D markers
|
# Publish 3D markers
|
||||||
self._marker_pub.publish(
|
self._marker_pub.publish(
|
||||||
self._build_markers(all_points_3d, left_msg.header.stamp))
|
self._build_markers(all_points_3d, left_msg.header.stamp))
|
||||||
|
|
||||||
|
# Build distance display window
|
||||||
|
row_h = 220
|
||||||
|
frame_h = max(400, 140 + row_h * max(len(avg_distances), 1))
|
||||||
|
dist_frame = np.zeros((frame_h, 1600, 3), dtype=np.uint8)
|
||||||
|
cv2.putText(dist_frame, 'Avg distance (>=75% conf keypoints)',
|
||||||
|
(20, 70), cv2.FONT_HERSHEY_SIMPLEX, 2.0, (180, 180, 180), 3)
|
||||||
|
if avg_distances:
|
||||||
|
for i, d in enumerate(avg_distances):
|
||||||
|
if d is not None:
|
||||||
|
txt = f'Person {i + 1}: {d:.2f} m'
|
||||||
|
color = (100, 255, 100)
|
||||||
|
else:
|
||||||
|
txt = f'Person {i + 1}: -- (no 75%+ kps)'
|
||||||
|
color = (80, 80, 200)
|
||||||
|
cv2.putText(dist_frame, txt, (20, 140 + i * row_h),
|
||||||
|
cv2.FONT_HERSHEY_SIMPLEX, 4.5, color, 8)
|
||||||
|
else:
|
||||||
|
cv2.putText(dist_frame, 'No people detected', (20, 200),
|
||||||
|
cv2.FONT_HERSHEY_SIMPLEX, 4.0, (100, 100, 100), 6)
|
||||||
|
self._dist_display = dist_frame
|
||||||
|
|
||||||
# Draw 2D keypoints on display frames
|
# Draw 2D keypoints on display frames
|
||||||
if left_people:
|
if left_people:
|
||||||
draw_keypoints(
|
draw_keypoints(
|
||||||
@@ -429,6 +465,8 @@ class KeypointTriangulationNode(Node):
|
|||||||
cv2.imshow('Left - Keypoints', self._left_display)
|
cv2.imshow('Left - Keypoints', self._left_display)
|
||||||
if self._right_display is not None:
|
if self._right_display is not None:
|
||||||
cv2.imshow('Right - Keypoints', self._right_display)
|
cv2.imshow('Right - Keypoints', self._right_display)
|
||||||
|
if self._dist_display is not None:
|
||||||
|
cv2.imshow('Distance (75%+ confidence)', self._dist_display)
|
||||||
|
|
||||||
key = cv2.waitKey(1) & 0xFF
|
key = cv2.waitKey(1) & 0xFF
|
||||||
if key == ord('q'):
|
if key == ord('q'):
|
||||||
|
|||||||
Reference in New Issue
Block a user