---
# System prepended metadata

title: OpenVINO ROS2 Python Demo

---

# 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()
```

