# 8. Write a ROS Subscriber

In this part, you are going to implement an image subscriber which is "the Photocopier" in my example. This subscriber is simply continuously reading images from the topic `image_raw` (that you created from [chapter 7](https://robodev.blog/write-a-ros-publisher) and displaying them on your screen. Of course, you can do much more than just view the images (like putting the images to some machine learning algorithms that you are going to do in the next chapter) or you can even view the image right from the publisher itself but we should start somewhere, right? This image subscriber (and publisher) is only an example to show you how different processes in ROS communicate with each other.

# The Code

Let's create a new Python script in the [*my\_cam*](https://github.com/TrinhNC/my_cam) folder and name it `image_subscriber.py`. If you already did the [last chapter](https://robodev.blog/write-a-ros-publisher), the code below should look familiar because the structure is quite the same. Again, I highly recommend you follow the explanation part below and type by yourself instead of copy&paste.

```python
#!/usr/bin/python3

import rospy
import cv2
from cv_bridge import CvBridge, CvBridgeError
from sensor_msgs.msg import Image

def callback(image_msg):
    """This function is called to handle the subscribed messages

    Args:
        image_msg (Image): message type Image from sensor_msgs
    """
    try:
        cv_image = bridge.imgmsg_to_cv2(image_msg)
        cv2.imshow('ROS Image Subscriber', cv_image)
        cv2.waitKey(10)
    except CvBridgeError as error:
        print(error)

if __name__=="__main__":
    bridge = CvBridge()
    rospy.init_node("image_subscriber", anonymous=True)
    print("Subscribe images from topic /image_raw ...")
    
    image_subcriber = rospy.Subscriber("image_raw", Image, callback)
    
    try:
        # spin() simply keeps python from exiting until this node is stopped
        rospy.spin()
    except KeyboardInterrupt:
        print("Shutting down!")
```

# The Code Explain

The first part of the code is exactly the same as in the [*image\_publisher.py*](https://github.com/TrinhNC/my_cam/blob/main/src/image_publisher.py). It is basically to import all the necessary libraries.

```python
#!/usr/bin/python3

import rospy
import cv2
from cv_bridge import CvBridge, CvBridgeError
from sensor_msgs.msg import Image
```

Next, a *callback* function is created but I want to explain first the main function and come to this *callback* later. In the main, similar to the publisher, you need to create a CvBridge object and initialize a node whose name is `"image_subscriber"`.

```python
if __name__=="__main__":
    bridge = CvBridge()
    rospy.init_node("image_subscriber", anonymous=True)
    print("Subscribe images from topic /image_raw ...")
```

Then, the subscriber is created by using the function `rospy.Subscriber("image_raw", Image, callback)` in which `image_raw` is the topic you want to subscribe to, `Image` is the data type of `image_raw` and `callback` is a function that will be invoked when new messages are received. Finally, `rospy.spin()` is called to keep your node from exiting until the node has been stopped (by *Ctrl + C* for instance).

```python
    image_subcriber = rospy.Subscriber("image_raw", Image, callback)
    try:
        # spin() simply keeps python from exiting until this node is stopped
        rospy.spin()
    except KeyboardInterrupt:
        print("Shutting down!")
```

The `callback` function is defined always with the incoming message as the first argument.

```python
def callback(image_msg):
```

Inside the `callback`, you first convert the ROS image message `image_msg` to OpenCV format (here I named it `cv_image`) using the `imgmsg_to_cv2` function. Then, you can display the image using the `cv2.imshow` function which takes first the window name (here `ROS Image Subscriber`) as the first argument and the image `cv_image` as the second. Finally, `cv2.waitKey(10)` is used to delay for given milliseconds (here 10ms) or until any key is pressed before switching to the next frame.

```python
    try:
        cv_image = bridge.imgmsg_to_cv2(image_msg)
        cv2.imshow('ROS Image Subscriber', cv_image)
        cv2.waitKey(10)
    except CvBridgeError as error:
        print(error)
```

# Run the Subscriber

In order to start the script above from a terminal, you should make it executable by either right-click on the script and selecting **Properties &gt; Permissions** and check **Allow executing file as program**, or using the command `sudo chmod +x image_subscriber.py`. Then, open 3 terminals or one terminator with 3 sub-terminals as below.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1671896323053/73438b68-d55c-4331-8fc0-16f368b42232.jpeg align="center")

In the first terminal, run ROS Master

```bash
roscore
```

In the second, start the publisher (remember to [connect the webcam to your virtual machine](https://robodev.blog/write-a-ros-publisher#heading-setup-camera-in-vmware)).

```bash
rosrun my_cam image_publisher.py
```

In the third, start the subscriber:

```bash
rosrun my_cam image_subscriber.py
```

After that, you should see a window with the title *ROS Image Subscriber* showing your webcam view like what I have in the photo above (the cute cow is actually one of my assistants :)).

After completing chapters 7 and 8, you now know how to write a ROS publisher and subscriber. You also know how to exploit the images from a camera which is one of the most popular sensors used in robotics. Well done! In the next chapter, as promised, I will help you use one of the available tools to deploy the images and get some useful information instead of just viewing them on screen. Meet you there!

# Reference

1. Cover photo: [http://wiki.ros.org/jade](http://wiki.ros.org/jade)
