├── LICENSE
├── launch
└── rtsp_camera.launch
├── .gitignore
├── package.xml
├── src
└── rtsp_driver_node
├── README.md
└── CMakeLists.txt
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Andrea F Daniele
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/launch/rtsp_camera.launch:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # Distribution / packaging
10 | .Python
11 | env/
12 | build/
13 | develop-eggs/
14 | dist/
15 | downloads/
16 | eggs/
17 | .eggs/
18 | lib/
19 | lib64/
20 | parts/
21 | sdist/
22 | var/
23 | wheels/
24 | *.egg-info/
25 | .installed.cfg
26 | *.egg
27 |
28 | # PyInstaller
29 | # Usually these files are written by a python script from a template
30 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
31 | *.manifest
32 | *.spec
33 |
34 | # Installer logs
35 | pip-log.txt
36 | pip-delete-this-directory.txt
37 |
38 | # Unit test / coverage reports
39 | htmlcov/
40 | .tox/
41 | .coverage
42 | .coverage.*
43 | .cache
44 | nosetests.xml
45 | coverage.xml
46 | *.cover
47 | .hypothesis/
48 |
49 | # Translations
50 | *.mo
51 | *.pot
52 |
53 | # Django stuff:
54 | *.log
55 | local_settings.py
56 |
57 | # Flask stuff:
58 | instance/
59 | .webassets-cache
60 |
61 | # Scrapy stuff:
62 | .scrapy
63 |
64 | # Sphinx documentation
65 | docs/_build/
66 |
67 | # PyBuilder
68 | target/
69 |
70 | # Jupyter Notebook
71 | .ipynb_checkpoints
72 |
73 | # pyenv
74 | .python-version
75 |
76 | # celery beat schedule file
77 | celerybeat-schedule
78 |
79 | # SageMath parsed files
80 | *.sage.py
81 |
82 | # dotenv
83 | .env
84 |
85 | # virtualenv
86 | .venv
87 | venv/
88 | ENV/
89 |
90 | # Spyder project settings
91 | .spyderproject
92 | .spyproject
93 |
94 | # Rope project settings
95 | .ropeproject
96 |
97 | # mkdocs documentation
98 | /site
99 |
100 | # mypy
101 | .mypy_cache/
102 |
--------------------------------------------------------------------------------
/package.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | rtsp_ros_driver
4 | 0.0.0
5 | The rtsp_ros_driver package
6 |
7 |
8 |
9 |
10 | Andrea F. Daniele
11 |
12 |
13 |
14 |
15 |
16 | MIT
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 | catkin
52 | cv_bridge
53 | rospy
54 | sensor_msgs
55 | cv_bridge
56 | rospy
57 | sensor_msgs
58 | cv_bridge
59 | rospy
60 | sensor_msgs
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
--------------------------------------------------------------------------------
/src/rtsp_driver_node:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env python2
2 |
3 | import os
4 | import cv2
5 | import sys
6 | import time
7 | import rospy
8 | from cv_bridge import CvBridge
9 | from sensor_msgs.msg import Image, CameraInfo
10 | from camera_info_manager import *
11 | from threading import Thread
12 | from time import sleep
13 |
14 |
15 | verbose = False
16 | camera_frame = ''
17 | camera_info_manager = None
18 | camera_info_publisher = None
19 | is_shutdown = False
20 | async_info_publisher = False
21 | async_info_publisher_freq = 30
22 |
23 | def camera_info_publisher_fcn(_):
24 | global camera_frame, camera_info_manager, camera_info_publisher, async_info_publisher_freq, is_shutdown
25 | wait_time = 1.0 / async_info_publisher_freq
26 | while True:
27 | if is_shutdown or rospy.is_shutdown():
28 | return
29 | # publish camera calibration
30 | camera_info_msg = camera_info_manager.getCameraInfo()
31 | camera_info_msg.header.frame_id = camera_frame
32 | camera_info_msg.header.stamp = rospy.Time.now()
33 | camera_info_publisher.publish( camera_info_msg )
34 | # wait for 1 second
35 | sleep(wait_time)
36 |
37 |
38 | if __name__ == '__main__':
39 | # initialize ROS node
40 | print("Initializing ROS node... ", end=' ')
41 | rospy.init_node('rtsp_camera_driver_node')
42 | print("Done!")
43 |
44 | # get ROS parameters
45 | resource = rospy.get_param('~rtsp_resource')
46 | camera_name = rospy.get_param('~camera_name')
47 | camera_frame = rospy.get_param('~camera_frame')
48 | image_raw_topic = rospy.get_param('~image_raw_topic')
49 | camera_info_topic = rospy.get_param('~camera_info_topic')
50 |
51 | # check the environment variable ROS_HOME
52 | if( 'ROS_HOME' not in os.environ ):
53 | rospy.logerr("The environment variable ROS_HOME is not set. Please set it before you run this node. It should be pointing to your `catkin_ws` directory.")
54 | exit(0)
55 |
56 | # open RTSP stream
57 | cap = cv2.VideoCapture(resource)
58 | if not cap.isOpened():
59 | rospy.logerr("Error opening resource `%s`. Please check." % resource)
60 | exit(0)
61 | rospy.loginfo("Resource successfully opened")
62 |
63 | # create publishers
64 | image_pub = rospy.Publisher(image_raw_topic, Image, queue_size=1)
65 | camera_info_publisher = rospy.Publisher(camera_info_topic, CameraInfo, queue_size=1)
66 |
67 | # initialize ROS_CV_Bridge
68 | ros_cv_bridge = CvBridge()
69 |
70 | # initialize Camera Info Manager
71 | camera_info_manager = CameraInfoManager(cname=camera_name, namespace=camera_name)
72 | camera_info_manager.loadCameraInfo()
73 | if not camera_info_manager.isCalibrated():
74 | rospy.logwarn("No calibration found for the current camera")
75 |
76 | # if async_info_publisher, create and launch async publisher node
77 | if async_info_publisher:
78 | camera_info_pub = Thread(target=camera_info_publisher_fcn, args=(camera_info_manager, ))
79 | camera_info_pub.start()
80 |
81 | # initialize variables
82 | print("Correctly opened resource, starting to publish feed.")
83 | rval, cv_image = cap.read()
84 | last_t = time.time()
85 | last_print_t = time.time()
86 | t_buffer = []
87 |
88 | # process frames
89 | while rval:
90 | # get new frame
91 | rval, cv_image = cap.read()
92 | # handle Ctrl-C
93 | key = cv2.waitKey(20)
94 | if rospy.is_shutdown() or key == 27 or key == 1048603:
95 | break
96 | # convert CV image to ROS message
97 | image_msg = ros_cv_bridge.cv2_to_imgmsg(cv_image, encoding="bgr8")
98 | image_msg.header.frame_id = camera_frame
99 | image_msg.header.stamp = rospy.Time.now()
100 | image_pub.publish( image_msg )
101 | # publish camera calibration in case of sync publisher
102 | if not async_info_publisher:
103 | camera_info_msg = camera_info_manager.getCameraInfo()
104 | camera_info_msg.header.frame_id = camera_frame
105 | camera_info_msg.header.stamp = image_msg.header.stamp
106 | camera_info_publisher.publish( camera_info_msg )
107 | # compute frequency
108 | cur_t = time.time()
109 | t_buffer.append( cur_t - last_t )
110 | # print frequency (if verbose)
111 | if cur_t - last_print_t > 1:
112 | wait_avg_sec = float(sum(t_buffer))/float(len(t_buffer))
113 | hz = 1. / wait_avg_sec
114 | if verbose: rospy.loginfo('Streaming @ %.1f Hz' % hz)
115 | last_print_t = cur_t
116 | last_t = cur_t
117 |
118 | # stop thread
119 | if async_info_publisher:
120 | is_shutdown = True
121 | camera_info_pub.join()
122 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # rtsp_ros_driver
2 |
3 | This ROS package contains a driver node that reads frames from an RTSP video stream (e.g., IP Camera) and publishes them out as [sensor_msgs/Image](http://docs.ros.org/api/sensor_msgs/html/msg/Image.html) ROS messages.
4 |
5 |
6 | # Configuration
7 |
8 | `rtsp_ros_driver` implements the ROS [camera_info_manager](http://wiki.ros.org/camera_info_manager_py) interface.
9 | This means that the driver node can seamlessly handle camera calibration files. `rtsp_ros_driver` stores camera
10 | calibration files in the directory `$ROS_HOME/camera_info/`. Export the environment variable `$ROS_HOME` to point to
11 | your `catkin_ws` directory.
12 |
13 | ```
14 | export ROS_HOME=
15 | ```
16 |
17 |
18 | # Tutorial
19 |
20 | You can launch the `rtsp_ros_driver` main node by running:
21 |
22 | ```
23 | roslaunch rtsp_ros_driver rtsp_camera.launch
24 | ```
25 |
26 |
27 | # Launch files
28 |
29 | ## rtsp_camera.launch
30 | Launches the main node of the package (i.e., `rtsp_driver_node`). Unlike the `rtsp_driver_node` node, this launch file allows us to specify hostname, username, password, and other parameters about the camera separately. These parameters will be combined into a standard URL format and sent to the `rtsp_driver_node` node.
31 |
32 | ### Subscribed topics
33 | None.
34 |
35 | ### Published topics
36 | Identical to the `rtsp_driver_node` node (see below).
37 |
38 | ### Arguments
39 |
40 | #### Mandatory arguments
41 |
42 | _hostname_
43 |
44 | (`string`)
45 |
46 |
47 | Hostname or IP of the RTSP camera.
48 |
49 |
50 | _username_
51 |
52 | (`string`)
53 |
54 |
55 | Username for the RTSP camera.
56 |
57 |
58 | _password_
59 |
60 | (`string`)
61 |
62 |
63 | Password for the RTSP camera.
64 |
65 |
66 | _stream_
67 |
68 | (`string`)
69 |
70 |
71 | Name of the video stream published by the RTSP camera.
72 |
73 |
74 |
75 | #### Optional arguments
76 |
77 | _camera\_name_
78 |
79 | (`string`, default: _rtsp\_camera_)
80 |
81 |
82 | Namespace of the camera.
83 |
84 |
85 | _camera\_frame_
86 |
87 | (`string`, default: _rtsp\_camera\_link_)
88 |
89 |
90 | Name of the camera reference frame.
91 |
92 |
93 | _image\_raw\_topic_
94 |
95 | (`string`, default: _~image\_raw_)
96 |
97 |
98 | Name of the output image topic.
99 |
100 |
101 | _camera\_info\_topic_
102 |
103 | (`string`, default: _~camera\_info_)
104 |
105 |
106 | Name of the output camera info topic.
107 |
108 |
109 | _port_
110 |
111 | (`int`, default: _554_)
112 |
113 |
114 | Port of the RTSP camera.
115 |
116 |
117 |
118 |
119 | # Nodes
120 |
121 | ## rtsp_driver_node
122 | Main node of the package, it is responsible for reading frames from the given RTSP video stream source and publishing them out as [sensor_msgs/Image](http://docs.ros.org/api/sensor_msgs/html/msg/Image.html) ROS messages.
123 |
124 | ### Subscribed topics
125 | None.
126 |
127 | ### Published topics
128 |
129 | _//_
130 |
131 | ([sensor_msgs/Image](http://docs.ros.org/api/sensor_msgs/html/msg/Image.html))
132 |
133 |
134 | Raw image stream from the camera.
135 |
136 |
137 | _//_
138 |
139 | ([sensor_msgs/CameraInfo](http://docs.ros.org/api/sensor_msgs/html/msg/CameraInfo.html))
140 |
141 |
142 | Camera metadata.
143 |
144 |
145 | ### Parameters
146 |
147 | #### Mandatory parameters
148 |
149 | ~_rtsp\_resource_
150 |
151 | (`string`)
152 |
153 |
154 | RTSP URL to the camera string.
155 |
156 |
157 |
158 | #### Optional parameters
159 |
160 | ~_camera\_name_
161 |
162 | (`string`, default: _rtsp\_camera_)
163 |
164 |
165 | Namespace of the camera.
166 |
167 |
168 | ~_camera\_frame_
169 |
170 | (`string`, default: _rtsp\_camera\_link_)
171 |
172 |
173 | Name of the camera reference frame.
174 |
175 |
176 | ~_image\_raw\_topic_
177 |
178 | (`string`, default: _~image\_raw_)
179 |
180 |
181 | Name of the output image topic.
182 |
183 |
184 | ~_camera\_info\_topic_
185 |
186 | (`string`, default: _~camera\_info_)
187 |
188 |
189 | Name of the output camera info topic.
190 |
191 |
--------------------------------------------------------------------------------
/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 2.8.3)
2 | project(rtsp_ros_driver)
3 |
4 | ## Compile as C++11, supported in ROS Kinetic and newer
5 | # add_compile_options(-std=c++11)
6 |
7 | ## Find catkin macros and libraries
8 | ## if COMPONENTS list like find_package(catkin REQUIRED COMPONENTS xyz)
9 | ## is used, also find other catkin packages
10 | find_package(catkin REQUIRED COMPONENTS
11 | cv_bridge
12 | rospy
13 | sensor_msgs
14 | )
15 |
16 | ## System dependencies are found with CMake's conventions
17 | # find_package(Boost REQUIRED COMPONENTS system)
18 |
19 |
20 | ## Uncomment this if the package has a setup.py. This macro ensures
21 | ## modules and global scripts declared therein get installed
22 | ## See http://ros.org/doc/api/catkin/html/user_guide/setup_dot_py.html
23 | # catkin_python_setup()
24 |
25 | ################################################
26 | ## Declare ROS messages, services and actions ##
27 | ################################################
28 |
29 | ## To declare and build messages, services or actions from within this
30 | ## package, follow these steps:
31 | ## * Let MSG_DEP_SET be the set of packages whose message types you use in
32 | ## your messages/services/actions (e.g. std_msgs, actionlib_msgs, ...).
33 | ## * In the file package.xml:
34 | ## * add a build_depend tag for "message_generation"
35 | ## * add a build_depend and a run_depend tag for each package in MSG_DEP_SET
36 | ## * If MSG_DEP_SET isn't empty the following dependency has been pulled in
37 | ## but can be declared for certainty nonetheless:
38 | ## * add a run_depend tag for "message_runtime"
39 | ## * In this file (CMakeLists.txt):
40 | ## * add "message_generation" and every package in MSG_DEP_SET to
41 | ## find_package(catkin REQUIRED COMPONENTS ...)
42 | ## * add "message_runtime" and every package in MSG_DEP_SET to
43 | ## catkin_package(CATKIN_DEPENDS ...)
44 | ## * uncomment the add_*_files sections below as needed
45 | ## and list every .msg/.srv/.action file to be processed
46 | ## * uncomment the generate_messages entry below
47 | ## * add every package in MSG_DEP_SET to generate_messages(DEPENDENCIES ...)
48 |
49 | ## Generate messages in the 'msg' folder
50 | # add_message_files(
51 | # FILES
52 | # Message1.msg
53 | # Message2.msg
54 | # )
55 |
56 | ## Generate services in the 'srv' folder
57 | # add_service_files(
58 | # FILES
59 | # Service1.srv
60 | # Service2.srv
61 | # )
62 |
63 | ## Generate actions in the 'action' folder
64 | # add_action_files(
65 | # FILES
66 | # Action1.action
67 | # Action2.action
68 | # )
69 |
70 | ## Generate added messages and services with any dependencies listed here
71 | # generate_messages(
72 | # DEPENDENCIES
73 | # sensor_msgs
74 | # )
75 |
76 | ################################################
77 | ## Declare ROS dynamic reconfigure parameters ##
78 | ################################################
79 |
80 | ## To declare and build dynamic reconfigure parameters within this
81 | ## package, follow these steps:
82 | ## * In the file package.xml:
83 | ## * add a build_depend and a run_depend tag for "dynamic_reconfigure"
84 | ## * In this file (CMakeLists.txt):
85 | ## * add "dynamic_reconfigure" to
86 | ## find_package(catkin REQUIRED COMPONENTS ...)
87 | ## * uncomment the "generate_dynamic_reconfigure_options" section below
88 | ## and list every .cfg file to be processed
89 |
90 | ## Generate dynamic reconfigure parameters in the 'cfg' folder
91 | # generate_dynamic_reconfigure_options(
92 | # cfg/DynReconf1.cfg
93 | # cfg/DynReconf2.cfg
94 | # )
95 |
96 | ###################################
97 | ## catkin specific configuration ##
98 | ###################################
99 | ## The catkin_package macro generates cmake config files for your package
100 | ## Declare things to be passed to dependent projects
101 | ## INCLUDE_DIRS: uncomment this if your package contains header files
102 | ## LIBRARIES: libraries you create in this project that dependent projects also need
103 | ## CATKIN_DEPENDS: catkin_packages dependent projects also need
104 | ## DEPENDS: system dependencies of this project that dependent projects also need
105 | catkin_package(
106 | # INCLUDE_DIRS include
107 | # LIBRARIES rtsp_ros_driver
108 | # CATKIN_DEPENDS cv_bridge rospy sensor_msgs
109 | # DEPENDS system_lib
110 | )
111 |
112 | ###########
113 | ## Build ##
114 | ###########
115 |
116 | ## Specify additional locations of header files
117 | ## Your package locations should be listed before other locations
118 | include_directories(
119 | # include
120 | ${catkin_INCLUDE_DIRS}
121 | )
122 |
123 | ## Declare a C++ library
124 | # add_library(${PROJECT_NAME}
125 | # src/${PROJECT_NAME}/rtsp_ros_driver.cpp
126 | # )
127 |
128 | ## Add cmake target dependencies of the library
129 | ## as an example, code may need to be generated before libraries
130 | ## either from message generation or dynamic reconfigure
131 | # add_dependencies(${PROJECT_NAME} ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
132 |
133 | ## Declare a C++ executable
134 | ## With catkin_make all packages are built within a single CMake context
135 | ## The recommended prefix ensures that target names across packages don't collide
136 | # add_executable(${PROJECT_NAME}_node src/rtsp_ros_driver_node.cpp)
137 |
138 | ## Rename C++ executable without prefix
139 | ## The above recommended prefix causes long target names, the following renames the
140 | ## target back to the shorter version for ease of user use
141 | ## e.g. "rosrun someones_pkg node" instead of "rosrun someones_pkg someones_pkg_node"
142 | # set_target_properties(${PROJECT_NAME}_node PROPERTIES OUTPUT_NAME node PREFIX "")
143 |
144 | ## Add cmake target dependencies of the executable
145 | ## same as for the library above
146 | # add_dependencies(${PROJECT_NAME}_node ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
147 |
148 | ## Specify libraries to link a library or executable target against
149 | # target_link_libraries(${PROJECT_NAME}_node
150 | # ${catkin_LIBRARIES}
151 | # )
152 |
153 | #############
154 | ## Install ##
155 | #############
156 |
157 | # all install targets should use catkin DESTINATION variables
158 | # See http://ros.org/doc/api/catkin/html/adv_user_guide/variables.html
159 |
160 | ## Mark executable scripts (Python etc.) for installation
161 | ## in contrast to setup.py, you can choose the destination
162 | # install(PROGRAMS
163 | # scripts/my_python_script
164 | # DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
165 | # )
166 |
167 | ## Mark executables and/or libraries for installation
168 | # install(TARGETS ${PROJECT_NAME} ${PROJECT_NAME}_node
169 | # ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
170 | # LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
171 | # RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
172 | # )
173 |
174 | ## Mark cpp header files for installation
175 | # install(DIRECTORY include/${PROJECT_NAME}/
176 | # DESTINATION ${CATKIN_PACKAGE_INCLUDE_DESTINATION}
177 | # FILES_MATCHING PATTERN "*.h"
178 | # PATTERN ".svn" EXCLUDE
179 | # )
180 |
181 | ## Mark other files for installation (e.g. launch and bag files, etc.)
182 | # install(FILES
183 | # # myfile1
184 | # # myfile2
185 | # DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}
186 | # )
187 |
188 | #############
189 | ## Testing ##
190 | #############
191 |
192 | ## Add gtest based cpp test target and link libraries
193 | # catkin_add_gtest(${PROJECT_NAME}-test test/test_rtsp_ros_driver.cpp)
194 | # if(TARGET ${PROJECT_NAME}-test)
195 | # target_link_libraries(${PROJECT_NAME}-test ${PROJECT_NAME})
196 | # endif()
197 |
198 | ## Add folders to be run by python nosetests
199 | # catkin_add_nosetests(test)
200 |
--------------------------------------------------------------------------------