|
#!/usr/bin/env python3
|
|
|
|
import sys
|
|
import cv2
|
|
import numpy as np
|
|
import math
|
|
|
|
import rospy
|
|
|
|
from std_msgs.msg import Header
|
|
from sensor_msgs.msg import Image
|
|
from sensor_msgs.msg import CameraInfo
|
|
from sensor_msgs import point_cloud2
|
|
from sensor_msgs.msg import PointCloud2, PointField
|
|
from visualization_msgs.msg import Marker
|
|
from visualization_msgs.msg import MarkerArray
|
|
from geometry_msgs.msg import Point
|
|
from scipy.spatial.transform import Rotation
|
|
|
|
import ros_numpy
|
|
import message_filters
|
|
|
|
import struct
|
|
|
|
import body_landmark_detector_python3 as bl
|
|
|
|
import detection_ros_markers_python3 as dr
|
|
import detection_2d_to_3d_python3 as d2
|
|
|
|
|
|
class DetectionNode:
|
|
def __init__(self, detector, default_marker_name, node_name,
|
|
topic_base_name, fit_plane, min_box_side_m=None,
|
|
max_box_side_m=None, modify_3d_detections=None):
|
|
self.rgb_image = None
|
|
self.rgb_image_timestamp = None
|
|
self.depth_image = None
|
|
self.depth_image_timestamp = None
|
|
self.camera_info = None
|
|
self.all_points = []
|
|
self.publish_marker_point_clouds = True
|
|
|
|
self.detector = detector
|
|
|
|
self.marker_collection = dr.DetectionBoxMarkerCollection(default_marker_name)
|
|
|
|
self.landmark_color_dict = self.detector.get_landmark_color_dict()
|
|
self.topic_base_name = topic_base_name
|
|
self.node_name = node_name
|
|
self.fit_plane = fit_plane
|
|
self.min_box_side_m = min_box_side_m
|
|
self.max_box_side_m = max_box_side_m
|
|
self.modify_3d_detections = modify_3d_detections
|
|
self.image_count = 0
|
|
|
|
|
|
def image_callback(self, ros_rgb_image, ros_depth_image, rgb_camera_info):
|
|
self.rgb_image = ros_numpy.numpify(ros_rgb_image)
|
|
self.rgb_image_timestamp = ros_rgb_image.header.stamp
|
|
self.depth_image = ros_numpy.numpify(ros_depth_image)
|
|
self.depth_image_timestamp = ros_depth_image.header.stamp
|
|
self.camera_info = rgb_camera_info
|
|
self.image_count = self.image_count + 1
|
|
|
|
# OpenCV expects bgr images, but numpify by default returns rgb images.
|
|
self.rgb_image = cv2.cvtColor(self.rgb_image, cv2.COLOR_RGB2BGR)
|
|
|
|
# Copy the depth image to avoid a change to the depth image
|
|
# during the update.
|
|
time_diff = self.rgb_image_timestamp - self.depth_image_timestamp
|
|
time_diff = abs(time_diff.to_sec())
|
|
if time_diff > 0.0001:
|
|
print('WARNING: The rgb image and the depth image were not taken at the same time.')
|
|
print(' The time difference between their timestamps =', closest_time_diff, 's')
|
|
|
|
# Rotate the image by 90deg to account for camera
|
|
# orientation. In the future, this may be performed at the
|
|
# image source.
|
|
detection_box_image = cv2.rotate(self.rgb_image, cv2.ROTATE_90_CLOCKWISE)
|
|
|
|
debug_input = False
|
|
if debug_input:
|
|
print('DetectionNode.image_callback: received an image!')
|
|
print('DetectionNode.image_callback: detection_box_image.shape =', detection_box_image.shape)
|
|
cv2.imwrite('./output_images/deep_learning_input_' + str(self.image_count).zfill(4) + '.png', detection_box_image)
|
|
|
|
debug_output = False
|
|
detections_2d, output_image = self.detector.apply_to_image(detection_box_image, draw_output=debug_output)
|
|
if debug_output:
|
|
print('DetectionNode.image_callback: processed image with deep network!')
|
|
print('DetectionNode.image_callback: output_image.shape =', output_image.shape)
|
|
cv2.imwrite('./output_images/deep_learning_output_' + str(self.image_count).zfill(4) + '.png', output_image)
|
|
|
|
detections_3d = d2.detections_2d_to_3d(detections_2d, self.rgb_image, self.camera_info, self.depth_image, fit_plane=self.fit_plane, min_box_side_m=self.min_box_side_m, max_box_side_m=self.max_box_side_m)
|
|
|
|
if self.modify_3d_detections is not None:
|
|
detections_3d = self.modify_3d_detections(detections_3d)
|
|
|
|
self.marker_collection.update(detections_3d, self.rgb_image_timestamp)
|
|
|
|
marker_array = self.marker_collection.get_ros_marker_array(self.landmark_color_dict)
|
|
include_axes = True
|
|
include_z_axes = False
|
|
axes_array = None
|
|
axes_scale = 4.0
|
|
if include_axes or include_z_axes:
|
|
axes_array = self.marker_collection.get_ros_axes_array(include_z_axes, include_axes, axes_scale=axes_scale)
|
|
|
|
if self.publish_marker_point_clouds:
|
|
for marker in self.marker_collection:
|
|
marker_points = marker.get_marker_point_cloud()
|
|
self.add_point_array_to_point_cloud(marker_points)
|
|
publish_plane_points = False
|
|
if publish_plane_points:
|
|
plane_points = marker.get_plane_fit_point_cloud()
|
|
self.add_point_array_to_point_cloud(plane_points)
|
|
self.publish_point_cloud()
|
|
self.visualize_markers_pub.publish(marker_array)
|
|
if axes_array is not None:
|
|
self.visualize_axes_pub.publish(axes_array)
|
|
|
|
|
|
def add_to_point_cloud(self, x_mat, y_mat, z_mat, mask):
|
|
points = [[x, y, z] for x, y, z, m in zip(x_mat.flatten(), y_mat.flatten(), z_mat.flatten(), mask.flatten()) if m > 0]
|
|
self.all_points.extend(points)
|
|
|
|
def add_point_array_to_point_cloud(self, point_array):
|
|
if point_array is not None:
|
|
self.all_points.extend(list(point_array))
|
|
|
|
def publish_point_cloud(self):
|
|
header = Header()
|
|
header.frame_id = '/camera_color_optical_frame'
|
|
header.stamp = rospy.Time.now()
|
|
fields = [PointField('x', 0, PointField.FLOAT32, 1),
|
|
PointField('y', 4, PointField.FLOAT32, 1),
|
|
PointField('z', 8, PointField.FLOAT32, 1),
|
|
PointField('rgba', 12, PointField.UINT32, 1)]
|
|
r = 255
|
|
g = 0
|
|
b = 0
|
|
a = 128
|
|
rgba = struct.unpack('I', struct.pack('BBBB', b, g, r, a))[0]
|
|
points = [[x, y, z, rgba] for x, y, z in self.all_points]
|
|
point_cloud = point_cloud2.create_cloud(header, fields, points)
|
|
self.visualize_point_cloud_pub.publish(point_cloud)
|
|
self.all_points = []
|
|
|
|
def main(self):
|
|
rospy.init_node(self.node_name)
|
|
name = rospy.get_name()
|
|
rospy.loginfo("{0} started".format(name))
|
|
|
|
self.rgb_topic_name = '/camera/color/image_raw' #'/camera/infra1/image_rect_raw'
|
|
self.rgb_image_subscriber = message_filters.Subscriber(self.rgb_topic_name, Image)
|
|
|
|
self.depth_topic_name = '/camera/aligned_depth_to_color/image_raw'
|
|
self.depth_image_subscriber = message_filters.Subscriber(self.depth_topic_name, Image)
|
|
|
|
self.camera_info_subscriber = message_filters.Subscriber('/camera/color/camera_info', CameraInfo)
|
|
|
|
self.synchronizer = message_filters.TimeSynchronizer([self.rgb_image_subscriber, self.depth_image_subscriber, self.camera_info_subscriber], 10)
|
|
self.synchronizer.registerCallback(self.image_callback)
|
|
|
|
self.visualize_markers_pub = rospy.Publisher('/' + self.topic_base_name + '/marker_array', MarkerArray, queue_size=1)
|
|
self.visualize_axes_pub = rospy.Publisher('/' + self.topic_base_name + '/axes', MarkerArray, queue_size=1)
|
|
self.visualize_point_cloud_pub = rospy.Publisher('/' + self.topic_base_name + '/point_cloud2', PointCloud2, queue_size=1)
|
|
|