# OpenVINO ROS2 Python Demo ## Installing ROS 2 on Ubuntu Linux https://docs.ros.org/en/foxy/Installation/Ubuntu-Install-Debians.html ## ROS2 OpenCV Reference https://automaticaddison.com/getting-started-with-opencv-in-ros-2-foxy-fitzroy-python/ ### Build ```=bash source /opt/ros/foxy/setup.sh source /opt/intel/openvino_2021/bin/setupvars.sh cd ~/dev_ws/src ros2 pkg create --build-type ament_python ros2_openvino_py --dependencies rclpy image_transport cv_bridge sensor_msgs std_msgs python3-opencv cd ~/dev_ws/ rosdep install -i --from-path src --rosdistro foxy -y colcon build --packages-select ros2_openvino_py ``` ### Run Pub ```=bash source ~/dev_ws/install/setup.sh source /opt/intel/openvino_2021/bin/setupvars.sh ros2 run cv_basics img_publisher ``` ### Run Sub ```=bash source ~/dev_ws/install/setup.sh source /opt/intel/openvino_2021/bin/setupvars.sh ros2 run cv_basics img_subscriber ``` ### Directory dev_ws/src/ros2_openvino_py/ ├── ros2_openvino_py │   ├── __init__.py │   ├── webcam_pub.py │   └── webcam_sub.py ├── package.xml ├── resource │   └── ros2_openvino_py ├── setup.cfg ├── setup.py └── test ├── test_copyright.py ├── test_flake8.py └── test_pep257.py ### src/cv_basics/package.xml ```=xml ?xml version="1.0"?> <?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?> <package format="3"> <name>ros2_openvino_py</name> <version>0.0.0</version> <description>TODO: Package description</description> <maintainer email="dlcv@todo.todo">dlcv</maintainer> <license>TODO: License declaration</license> <depend>rclpy</depend> <depend>image_transport</depend> <depend>cv_bridge</depend> <depend>sensor_msgs</depend> <depend>std_msgs</depend> <depend>python3-opencv</depend> <test_depend>ament_copyright</test_depend> <test_depend>ament_flake8</test_depend> <test_depend>ament_pep257</test_depend> <test_depend>python3-pytest</test_depend> <export> <build_type>ament_python</build_type> </export> </package> ``` ### src/ros2_openvino_py/setup.py ```=python from setuptools import setup package_name = 'ros2_openvino_py' setup( name=package_name, version='0.0.0', packages=[package_name], data_files=[ ('share/ament_index/resource_index/packages', ['resource/' + package_name]), ('share/' + package_name, ['package.xml']), ], install_requires=['setuptools'], zip_safe=True, maintainer='dlcv', maintainer_email='dlcv@todo.todo', description='TODO: Package description', license='TODO: License declaration', tests_require=['pytest'], entry_points={ 'console_scripts': [ 'img_publisher = cv_basics.webcam_pub:main', 'img_subscriber = cv_basics.webcam_sub:main', ], }, ) ``` ### src/ros2_openvino_py/ros2_openvino_py/webcam_pub.py ```=python # Basic ROS 2 program to publish real-time streaming # video from your built-in webcam # Author: # - Addison Sears-Collins # - https://automaticaddison.com # Import the necessary libraries import rclpy # Python Client Library for ROS 2 from rclpy.node import Node # Handles the creation of nodes from sensor_msgs.msg import Image # Image is the message type from cv_bridge import CvBridge # Package to convert between ROS and OpenCV Images import cv2 # OpenCV library class ImagePublisher(Node): """ Create an ImagePublisher class, which is a subclass of the Node class. """ def __init__(self): """ Class constructor to set up the node """ # Initiate the Node class's constructor and give it a name super().__init__('image_publisher') # Create the publisher. This publisher will publish an Image # to the video_frames topic. The queue size is 10 messages. self.publisher_ = self.create_publisher(Image, 'video_frames', 10) # We will publish a message every 0.1 seconds timer_period = 0.1 # seconds # Create the timer self.timer = self.create_timer(timer_period, self.timer_callback) # Create a VideoCapture object # The argument '0' gets the default webcam. self.cap = cv2.VideoCapture(0) # Used to convert between ROS and OpenCV images self.br = CvBridge() def timer_callback(self): """ Callback function. This function gets called every 0.1 seconds. """ # Capture frame-by-frame # This method returns True/False as well # as the video frame. ret, frame = self.cap.read() if ret == True: # Publish the image. # The 'cv2_to_imgmsg' method converts an OpenCV # image to a ROS 2 image message self.publisher_.publish(self.br.cv2_to_imgmsg(frame)) # Display the message on the console self.get_logger().info('Publishing video frame') def main(args=None): # Initialize the rclpy library rclpy.init(args=args) # Create the node image_publisher = ImagePublisher() # Spin the node so the callback function is called. rclpy.spin(image_publisher) # Destroy the node explicitly # (optional - otherwise it will be done automatically # when the garbage collector destroys the node object) image_publisher.destroy_node() # Shutdown the ROS client library for Python rclpy.shutdown() if __name__ == '__main__': main() ``` ### src/ros2_openvino_py/ros2_openvino_py/webcam_sub.py ```=python # Basic ROS 2 program to publish real-time streaming # video from your built-in webcam # Author: # - Addison Sears-Collins # - https://automaticaddison.com import rclpy # Python library for ROS 2 from rclpy.node import Node # Handles the creation of nodes from sensor_msgs.msg import Image # Image is the message type from cv_bridge import CvBridge # Package to convert between ROS and OpenCV Images import cv2 # OpenCV library import numpy as np from openvino.inference_engine import IECore # OpenVINO 2021.4 class ImageSubscriber(Node): """ Create an ImageSubscriber class, which is a subclass of the Node class. """ def __init__(self): """ Class constructor to set up the node """ # Initiate the Node class's constructor and give it a name super().__init__('image_subscriber') # Create the subscriber. This subscriber will receive an Image # from the video_frames topic. The queue size is 10 messages. self.subscription = self.create_subscription( Image, 'video_frames', self.listener_callback, 10) self.subscription # prevent unused variable warning # Used to convert between ROS and OpenCV images self.br = CvBridge() # OpenVINO self.threshold = 0.4 self.ie = IECore() # python3 /opt/intel/openvino_2021/deployment_tools/tools/model_downloader/downloader.py --name person-vehicle-bike-detection-2001 -o /home/dlcv/openvino_models self.net = self.ie.read_network(model='/home/dlcv/openvino_models/intel/person-vehicle-bike-detection-2001/FP16-INT8/person-vehicle-bike-detection-2001.xml') self.input_blob = next(iter(self.net.inputs)) self.out_blob = next(iter(self.net.outputs)) batch,channel,height,width = self.net.inputs[self.input_blob].shape self.batch = batch self.channel = channel self.height = height self.width = width def listener_callback(self, data): """ Callback function. """ # Display the message on the console self.get_logger().info('Receiving video frame') # Convert ROS Image message to OpenCV image frame = self.br.imgmsg_to_cv2(data) # OpenVINO image = cv2.resize(frame, (self.width, self.height)) image = image.transpose((2, 0, 1)) # Change data layout from HWC to CHW exec_net = self.ie.load_network(network=self.net, device_name='CPU') res = exec_net.infer(inputs={self.input_blob: image}) size = frame.shape[:2] for detection in res['detection_out'][0][0]: if detection[2] > self.threshold: print("detection[2]:{}".format(detection[2])) xmin = max(int(detection[3]*size[0]), 0) ymin = max(int(detection[4]*size[1]), 0) xmax = min(int(detection[5]*size[0]), size[0]) ymax = min(int(detection[6]*size[1]), size[1]) class_id = int(detection[1]) cv2.rectangle(frame, (xmin, ymin), (xmax, ymax), (255, 0, 0), 2) # Draw # Display image cv2.imshow("Object Detection", frame) cv2.waitKey(1) def main(args=None): # Initialize the rclpy library rclpy.init(args=args) # Create the node image_subscriber = ImageSubscriber() # Spin the node so the callback function is called. rclpy.spin(image_subscriber) # Destroy the node explicitly # (optional - otherwise it will be done automatically # when the garbage collector destroys the node object) image_subscriber.destroy_node() # Shutdown the ROS client library for Python rclpy.shutdown() if __name__ == '__main__': main() ```