Skip to content

Commit 97a7083

Browse files
m-barkerjws-1
andauthored
Detect-skills-debugging (#211)
* fix: incorrect remapping keys for handover * fix: remove speech server in launch file * feat: add visualisation of detect skill * feat: add visualisation of detect 3D detections * feat: add publishing of 3D polygons * feat: add publishing of markers for detect 3D skill * feat: add publishing of text label of markerrs * Update common/helpers/markers/src/markers/__init__.py Co-authored-by: Jared Swift <jared.swift@kcl.ac.uk> --------- Co-authored-by: Jared Swift <jared.swift@kcl.ac.uk>
1 parent 2743ef3 commit 97a7083

File tree

7 files changed

+133
-32
lines changed

7 files changed

+133
-32
lines changed

common/helpers/markers/src/markers/__init__.py

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
from collections import defaultdict
77

8-
from typing import Union
8+
from typing import Union, Optional
99

1010
publisher_counts = defaultdict(int)
1111

@@ -16,12 +16,13 @@ def create_marker(
1616
r: float = 0.0,
1717
g: float = 1.0,
1818
b: float = 0.0,
19+
name: Optional[str] = None,
1920
):
2021
marker_msg = Marker()
22+
marker_msg.type = Marker.SPHERE
2123
marker_msg.header.frame_id = point_stamped.header.frame_id
2224
marker_msg.header.stamp = point_stamped.header.stamp
2325
marker_msg.id = idx
24-
marker_msg.type = Marker.SPHERE
2526
marker_msg.action = Marker.ADD
2627
marker_msg.pose.position = point_stamped.point
2728
marker_msg.pose.orientation.w = 1.0
@@ -32,6 +33,10 @@ def create_marker(
3233
marker_msg.color.r = r
3334
marker_msg.color.g = g
3435
marker_msg.color.b = b
36+
37+
if name is not None:
38+
marker_msg.type = Marker.TEXT_VIEW_FACING
39+
marker_msg.text = name
3540
return marker_msg
3641

3742

@@ -42,11 +47,19 @@ def create_and_publish_marker(
4247
r: float = 0.0,
4348
g: float = 1.0,
4449
b: float = 0.0,
50+
name: Optional[str] = None,
4551
):
4652
if idx is None:
4753
global publisher_counts
4854
idx = publisher_counts[publisher]
4955
publisher_counts[publisher] += 1
50-
5156
marker_msg = create_marker(point_stamped, idx, r, g, b)
5257
publisher.publish(marker_msg)
58+
rospy.sleep(2) # Needed to prevent markers from being overwritten
59+
if name is not None:
60+
name_location = point_stamped.point
61+
name_location.z += 0.1
62+
idx = publisher_counts[publisher]
63+
publisher_counts[publisher] += 1
64+
marker_name_msg = create_marker(point_stamped, idx, r, g, b, name)
65+
publisher.publish(marker_name_msg)

common/vision/lasr_vision_deepface/requirements.txt

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
absl-py==2.1.0 # via tensorboard, tensorflow
22
astunparse==1.6.3 # via tensorflow
33
beautifulsoup4==4.12.3 # via gdown
4-
blinker==1.8.1 # via flask
4+
blinker==1.8.2 # via flask
55
cachetools==5.3.3 # via google-auth
6-
certifi==2024.2.2 # via requests
6+
certifi==2024.6.2 # via requests
77
charset-normalizer==3.3.2 # via requests
88
click==8.1.7 # via flask
99
deepface==0.0.91 # via -r requirements.in
@@ -12,17 +12,17 @@ fire==0.6.0 # via deepface
1212
flask==3.0.3 # via deepface
1313
flatbuffers==1.12 # via tensorflow
1414
gast==0.4.0 # via tensorflow
15-
gdown==5.1.0 # via deepface, retina-face
15+
gdown==5.2.0 # via deepface, retina-face
1616
google-auth==2.29.0 # via google-auth-oauthlib, tensorboard
1717
google-auth-oauthlib==0.4.6 # via tensorboard
1818
google-pasta==0.2.0 # via tensorflow
19-
grpcio==1.63.0 # via tensorboard, tensorflow
19+
grpcio==1.64.1 # via tensorboard, tensorflow
2020
gunicorn==22.0.0 # via deepface
2121
h5py==3.11.0 # via tensorflow
2222
idna==3.7 # via requests
2323
importlib-metadata==7.1.0 # via flask, markdown
2424
itsdangerous==2.2.0 # via flask
25-
jinja2==3.1.3 # via flask
25+
jinja2==3.1.4 # via flask
2626
keras==2.9.0 # via deepface, mtcnn, tensorflow
2727
keras-preprocessing==1.1.2 # via tensorflow
2828
libclang==18.1.1 # via tensorflow
@@ -31,7 +31,7 @@ markupsafe==2.1.5 # via jinja2, werkzeug
3131
mtcnn==0.1.1 # via deepface
3232
numpy==1.24.4 # via -r requirements.in, deepface, h5py, keras-preprocessing, opencv-python, opt-einsum, pandas, retina-face, tensorboard, tensorflow
3333
oauthlib==3.2.2 # via requests-oauthlib
34-
opencv-python==4.9.0.80 # via deepface, mtcnn, retina-face
34+
opencv-python==4.10.0.82 # via deepface, mtcnn, retina-face
3535
opt-einsum==3.3.0 # via tensorflow
3636
packaging==24.0 # via gunicorn, tensorflow
3737
pandas==2.0.3 # via deepface
@@ -42,7 +42,7 @@ pyasn1-modules==0.4.0 # via google-auth
4242
pysocks==1.7.1 # via requests
4343
python-dateutil==2.9.0.post0 # via pandas
4444
pytz==2024.1 # via pandas
45-
requests[socks]==2.31.0 # via deepface, gdown, requests-oauthlib, tensorboard
45+
requests[socks]==2.32.3 # via deepface, gdown, requests-oauthlib, tensorboard
4646
requests-oauthlib==2.0.0 # via google-auth-oauthlib
4747
retina-face==0.0.17 # via deepface
4848
rsa==4.9 # via google-auth
@@ -56,13 +56,13 @@ tensorflow-estimator==2.9.0 # via tensorflow
5656
tensorflow-io-gcs-filesystem==0.34.0 # via tensorflow
5757
termcolor==2.4.0 # via fire, tensorflow
5858
tqdm==4.66.4 # via deepface, gdown
59-
typing-extensions==4.11.0 # via tensorflow
59+
typing-extensions==4.12.1 # via tensorflow
6060
tzdata==2024.1 # via pandas
6161
urllib3==2.2.1 # via requests
62-
werkzeug==3.0.2 # via flask, tensorboard
62+
werkzeug==3.0.3 # via flask, tensorboard
6363
wheel==0.43.0 # via astunparse, tensorboard
6464
wrapt==1.16.0 # via tensorflow
65-
zipp==3.18.1 # via importlib-metadata
65+
zipp==3.19.1 # via importlib-metadata
6666

6767
# The following packages are considered to be unsafe in a requirements file:
6868
# setuptools

common/vision/lasr_vision_msgs/msg/Detection.msg

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ string name
55
float32 confidence
66

77
# Bounding box mask defined in pixel-space
8+
# X and Y are the midpoints of the bounding box.
89
int32[] xywh
910

1011
# Segmentation mask defined in pixel-space

skills/src/lasr_skills/detect.py

Lines changed: 49 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
#!/usr/bin/env python3
2-
2+
import cv2
3+
import cv2_img
34
import rospy
45
import smach
56
from sensor_msgs.msg import Image
6-
77
from lasr_vision_msgs.srv import YoloDetection
88

99
from typing import List, Union
@@ -13,10 +13,11 @@ class Detect(smach.State):
1313
def __init__(
1414
self,
1515
image_topic: str = "/xtion/rgb/image_raw",
16-
model: str = "yolov8n.pt",
16+
model: str = "yolov8x.pt",
1717
filter: Union[List[str], None] = None,
1818
confidence: float = 0.5,
1919
nms: float = 0.3,
20+
debug_publisher: str = "/skills/detect/debug",
2021
):
2122
smach.State.__init__(
2223
self,
@@ -30,16 +31,58 @@ def __init__(
3031
self.nms = nms
3132
self.yolo = rospy.ServiceProxy("/yolov8/detect", YoloDetection)
3233
self.yolo.wait_for_service()
34+
self.debug_pub = rospy.Publisher(debug_publisher, Image, queue_size=1)
3335

3436
def execute(self, userdata):
3537
img_msg = rospy.wait_for_message(self.image_topic, Image)
38+
img_cv2 = cv2_img.msg_to_cv2_img(img_msg)
3639
try:
3740
result = self.yolo(img_msg, self.model, self.confidence, self.nms)
38-
result.detected_objects = [
39-
det for det in result.detected_objects if det.name in self.filter
40-
]
41+
if len(self.filter) > 0:
42+
result.detected_objects = [
43+
det for det in result.detected_objects if det.name in self.filter
44+
]
4145
userdata.detections = result
46+
47+
# Annotate the image with the detected objects
48+
for det in result.detected_objects:
49+
x, y, w, h = det.xywh[0], det.xywh[1], det.xywh[2], det.xywh[3]
50+
cv2.rectangle(
51+
img_cv2,
52+
(x - (w // 2), y - (h // 2)),
53+
(x + (w // 2), y + (h // 2)),
54+
(0, 255, 0),
55+
2,
56+
)
57+
cv2.putText(
58+
img_cv2,
59+
f"{det.name} ({det.confidence:.2f})",
60+
(x - 50, y - (h // 2) - 10),
61+
cv2.FONT_HERSHEY_SIMPLEX,
62+
0.9,
63+
(0, 255, 0),
64+
2,
65+
)
66+
67+
self.debug_pub.publish(cv2_img.cv2_img_to_msg(img_cv2))
68+
4269
return "succeeded"
4370
except rospy.ServiceException as e:
4471
rospy.logwarn(f"Unable to perform inference. ({str(e)})")
4572
return "failed"
73+
74+
75+
if __name__ == "__main__":
76+
rospy.init_node("detect")
77+
while not rospy.is_shutdown():
78+
detect = Detect(
79+
image_topic="/usb_cam/image_raw",
80+
)
81+
sm = smach.StateMachine(outcomes=["succeeded", "failed"])
82+
with sm:
83+
smach.StateMachine.add(
84+
"DETECT",
85+
detect,
86+
transitions={"succeeded": "succeeded", "failed": "failed"},
87+
)
88+
sm.execute()

skills/src/lasr_skills/detect_3d.py

100644100755
Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
1+
#!/usr/bin/env python3
12
import rospy
23
import smach
4+
import numpy as np
35

46
from sensor_msgs.msg import PointCloud2
7+
from visualization_msgs.msg import Marker
8+
from geometry_msgs.msg import PointStamped, Point
59
from lasr_vision_msgs.srv import YoloDetection3D
10+
from markers import create_and_publish_marker
611

712
from typing import List, Union
813

@@ -11,10 +16,11 @@ class Detect3D(smach.State):
1116
def __init__(
1217
self,
1318
depth_topic: str = "/xtion/depth_registered/points",
14-
model: str = "yolov8n-seg.pt",
19+
model: str = "yolov8x-seg.pt",
1520
filter: Union[List[str], None] = None,
1621
confidence: float = 0.5,
1722
nms: float = 0.3,
23+
debug_publisher: str = "/skills/detect3d/debug",
1824
):
1925
smach.State.__init__(
2026
self,
@@ -28,16 +34,43 @@ def __init__(
2834
self.nms = nms
2935
self.yolo = rospy.ServiceProxy("/yolov8/detect3d", YoloDetection3D)
3036
self.yolo.wait_for_service()
37+
self.debug_pub = rospy.Publisher(debug_publisher, Marker, queue_size=1)
3138

3239
def execute(self, userdata):
3340
pcl_msg = rospy.wait_for_message(self.depth_topic, PointCloud2)
3441
try:
3542
result = self.yolo(pcl_msg, self.model, self.confidence, self.nms)
36-
result.detected_objects = [
37-
det for det in result.detected_objects if det.name in self.filter
38-
]
43+
if len(self.filter) > 0:
44+
result.detected_objects = [
45+
det for det in result.detected_objects if det.name in self.filter
46+
]
3947
userdata.detections_3d = result
48+
49+
for det in result.detected_objects:
50+
point_stamped = PointStamped()
51+
point_stamped.header.frame_id = "map"
52+
point_stamped.point = det.point
53+
rospy.loginfo(f"Detected point: {point_stamped}")
54+
if np.isnan(det.point.x).any():
55+
rospy.loginfo(f"No depth detected, object likely too far away")
56+
continue
57+
create_and_publish_marker(self.debug_pub, point_stamped, name=det.name)
58+
4059
return "succeeded"
4160
except rospy.ServiceException as e:
4261
rospy.logwarn(f"Unable to perform inference. ({str(e)})")
4362
return "failed"
63+
64+
65+
if __name__ == "__main__":
66+
rospy.init_node("detect")
67+
while not rospy.is_shutdown():
68+
detect = Detect3D()
69+
sm = smach.StateMachine(outcomes=["succeeded", "failed"])
70+
with sm:
71+
smach.StateMachine.add(
72+
"DETECT",
73+
detect,
74+
transitions={"succeeded": "succeeded", "failed": "failed"},
75+
)
76+
sm.execute()

skills/src/lasr_skills/detect_3d_in_area.py

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,40 @@
11
import smach
2-
2+
import rospy
33
from lasr_skills import Detect3D
4-
54
from typing import List, Union
65

6+
from geometry_msgs.msg import Polygon, Point, Point32
77
from shapely.geometry import Point
88
from shapely.geometry.polygon import Polygon
99

1010

1111
class Detect3DInArea(smach.StateMachine):
1212
class FilterDetections(smach.State):
13-
def __init__(self, area_polygon: Polygon):
13+
def __init__(
14+
self,
15+
area_polygon: Polygon,
16+
debug_publisher: str = "/skills/detect3d_in_area/debug",
17+
):
1418
smach.State.__init__(
1519
self,
1620
outcomes=["succeeded", "failed"],
1721
input_keys=["detections_3d"],
1822
output_keys=["detections_3d"],
1923
)
2024
self.area_polygon = area_polygon
25+
self.debug_publisher = rospy.Publisher(
26+
debug_publisher, Polygon, queue_size=1
27+
)
2128

2229
def execute(self, userdata):
2330
detected_objects = userdata["detections_3d"].detected_objects
24-
31+
# publish polygon for debugging
32+
polygon_msg = Polygon()
33+
polygon_msg.points = [
34+
Point32(x=point.x, y=point.y, z=point.z)
35+
for point in self.area_polygon.exterior.coords
36+
]
37+
self.debug_publisher.publish(polygon_msg)
2538
satisfied_points = [
2639
self.area_polygon.contains(Point(object.point.x, object.point.y))
2740
for object in detected_objects
@@ -39,7 +52,7 @@ def __init__(
3952
self,
4053
area_polygon: Polygon,
4154
depth_topic: str = "/xtion/depth_registered/points",
42-
model: str = "yolov8n-seg.pt",
55+
model: str = "yolov8x-seg.pt",
4356
filter: Union[List[str], None] = None,
4457
confidence: float = 0.5,
4558
nms: float = 0.3,

skills/src/lasr_skills/detect_gesture.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ def __init__(
2020
self,
2121
gesture_to_detect: Optional[str] = None,
2222
buffer_width: int = 50,
23+
debug_publisher: str = "/skills/gesture_detection/debug",
2324
):
2425
"""Optionally stores the gesture to detect. If None, it will infer the gesture from the keypoints."""
2526
smach.State.__init__(
@@ -28,12 +29,9 @@ def __init__(
2829
input_keys=["img_msg"],
2930
output_keys=["gesture_detected"],
3031
)
31-
self.debug = debug
3232
self.gesture_to_detect = gesture_to_detect
3333
self.body_pix_client = rospy.ServiceProxy("/bodypix/detect", BodyPixDetection)
34-
self.debug_publisher = rospy.Publisher(
35-
"/gesture_detection/debug", Image, queue_size=1
36-
)
34+
self.debug_publisher = rospy.Publisher(debug_publisher, Image, queue_size=1)
3735
self.buffer_width = buffer_width
3836

3937
def execute(self, userdata):

0 commit comments

Comments
 (0)