00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037 #include <ros/ros.h>
00038 #include <nodelet/nodelet.h>
00039 #include <dynamic_reconfigure/server.h>
00040 #include <image_transport/image_transport.h>
00041 #include <camera_info_manager/camera_info_manager.h>
00042 #include <opencv2/highgui/highgui.hpp>
00043 #include <opencv2/imgproc/imgproc.hpp>
00044 #include <cv_bridge/cv_bridge.h>
00045 #include <sstream>
00046 #include <stdexcept>
00047 #include <boost/filesystem.hpp>
00048 #include <boost/assign/list_of.hpp>
00049 #include <boost/thread/thread.hpp>
00050 #include <queue>
00051 #include <mutex>
00052 #include <video_stream_opencv/VideoStreamConfig.h>
00053
00054 namespace fs = boost::filesystem;
00055
00056 namespace video_stream_opencv {
00057
00058 class VideoStreamNodelet: public nodelet::Nodelet {
00059 protected:
00060 boost::shared_ptr<ros::NodeHandle> nh, pnh;
00061 image_transport::CameraPublisher pub;
00062 boost::shared_ptr<dynamic_reconfigure::Server<VideoStreamConfig> > dyn_srv;
00063 std::mutex q_mutex, s_mutex;
00064 std::queue<cv::Mat> framesQueue;
00065 cv::Mat frame;
00066 boost::shared_ptr<cv::VideoCapture> cap;
00067 std::string video_stream_provider;
00068 std::string video_stream_provider_type;
00069 std::string camera_name;
00070 std::string camera_info_url;
00071 std::string frame_id;
00072 double set_camera_fps;
00073 double fps;
00074 int max_queue_size;
00075 bool loop_videofile;
00076 int subscriber_num;
00077 int width_target;
00078 int height_target;
00079 bool flip_horizontal;
00080 bool flip_vertical;
00081 bool capture_thread_running;
00082 bool reopen_on_read_failure;
00083 boost::thread capture_thread;
00084 ros::Timer publish_timer;
00085 sensor_msgs::CameraInfo cam_info_msg;
00086
00087
00088
00089 virtual sensor_msgs::CameraInfo get_default_camera_info_from_image(sensor_msgs::ImagePtr img){
00090 sensor_msgs::CameraInfo cam_info_msg;
00091 cam_info_msg.header.frame_id = img->header.frame_id;
00092
00093 cam_info_msg.height = img->height;
00094 cam_info_msg.width = img->width;
00095 NODELET_INFO_STREAM("The image width is: " << img->width);
00096 NODELET_INFO_STREAM("The image height is: " << img->height);
00097
00098 cam_info_msg.distortion_model = "plumb_bob";
00099
00100 cam_info_msg.D.resize(5, 0.0);
00101
00102 cam_info_msg.K = boost::assign::list_of(1.0) (0.0) (img->width/2.0)
00103 (0.0) (1.0) (img->height/2.0)
00104 (0.0) (0.0) (1.0);
00105
00106 cam_info_msg.R = boost::assign::list_of (1.0) (0.0) (0.0)
00107 (0.0) (1.0) (0.0)
00108 (0.0) (0.0) (1.0);
00109
00110 cam_info_msg.P = boost::assign::list_of (1.0) (0.0) (img->width/2.0) (0.0)
00111 (0.0) (1.0) (img->height/2.0) (0.0)
00112 (0.0) (0.0) (1.0) (0.0);
00113 return cam_info_msg;
00114 }
00115
00116
00117 virtual void do_capture() {
00118 NODELET_DEBUG("Capture thread started");
00119 cv::Mat frame;
00120 ros::Rate camera_fps_rate(set_camera_fps);
00121
00122 int frame_counter = 0;
00123
00124 capture_thread_running = true;
00125 while (nh->ok() && capture_thread_running && subscriber_num > 0) {
00126 if (!cap->isOpened()) {
00127 NODELET_WARN("Waiting for device...");
00128 cv::waitKey(100);
00129 continue;
00130 }
00131 if (!cap->read(frame)) {
00132 NODELET_ERROR("Could not capture frame");
00133 if (reopen_on_read_failure) {
00134 NODELET_WARN("trying to reopen the device");
00135 unsubscribe();
00136 subscribe();
00137 }
00138 }
00139
00140 frame_counter++;
00141 if (video_stream_provider_type == "videofile")
00142 {
00143 camera_fps_rate.sleep();
00144 }
00145 if (video_stream_provider_type == "videofile" &&
00146 frame_counter == cap->get(CV_CAP_PROP_FRAME_COUNT))
00147 {
00148 if (loop_videofile)
00149 {
00150 cap->open(video_stream_provider);
00151 frame_counter = 0;
00152 }
00153 else {
00154 NODELET_INFO("Reached the end of frames");
00155 break;
00156 }
00157 }
00158
00159 if(!frame.empty()) {
00160 std::lock_guard<std::mutex> g(q_mutex);
00161
00162 if (framesQueue.size() < max_queue_size) {
00163 framesQueue.push(frame.clone());
00164 }
00165
00166 else {
00167 framesQueue.pop();
00168 framesQueue.push(frame.clone());
00169 }
00170 }
00171 }
00172 NODELET_DEBUG("Capture thread finished");
00173 }
00174
00175 virtual void do_publish(const ros::TimerEvent& event) {
00176 bool is_new_image = false;
00177 sensor_msgs::ImagePtr msg;
00178 std_msgs::Header header;
00179 header.frame_id = frame_id;
00180 {
00181 std::lock_guard<std::mutex> g(q_mutex);
00182 if (!framesQueue.empty()){
00183 frame = framesQueue.front();
00184 framesQueue.pop();
00185 is_new_image = true;
00186 }
00187 }
00188
00189
00190 if(!frame.empty()) {
00191
00192
00193
00194 if (is_new_image){
00195 if (flip_horizontal && flip_vertical)
00196 cv::flip(frame, frame, -1);
00197 else if (flip_horizontal)
00198 cv::flip(frame, frame, 1);
00199 else if (flip_vertical)
00200 cv::flip(frame, frame, 0);
00201 }
00202 msg = cv_bridge::CvImage(header, "bgr8", frame).toImageMsg();
00203
00204 if (cam_info_msg.distortion_model == ""){
00205 NODELET_WARN_STREAM("No calibration file given, publishing a reasonable default camera info.");
00206 cam_info_msg = get_default_camera_info_from_image(msg);
00207
00208 }
00209
00210 pub.publish(*msg, cam_info_msg, ros::Time::now());
00211 }
00212 }
00213
00214 virtual void subscribe() {
00215 ROS_DEBUG("Subscribe");
00216 cap.reset(new cv::VideoCapture);
00217 try {
00218 int device_num = std::stoi(video_stream_provider);
00219 NODELET_INFO_STREAM("Opening VideoCapture with provider: /dev/video" << device_num);
00220 cap->open(device_num);
00221 } catch (std::invalid_argument &ex) {
00222 NODELET_INFO_STREAM("Opening VideoCapture with provider: " << video_stream_provider);
00223 cap->open(video_stream_provider);
00224 if (!cap->isOpened()) {
00225 NODELET_FATAL_STREAM("Invalid 'video_stream_provider': " << video_stream_provider);
00226 return;
00227 }
00228 }
00229 NODELET_INFO_STREAM("Video stream provider type detected: " << video_stream_provider_type);
00230
00231 double reported_camera_fps;
00232
00233
00234 reported_camera_fps = cap->get(CV_CAP_PROP_FPS);
00235 if (reported_camera_fps > 0.0)
00236 NODELET_INFO_STREAM("Camera reports FPS: " << reported_camera_fps);
00237 else
00238 NODELET_INFO_STREAM("Backend can't provide camera FPS information");
00239
00240 cap->set(CV_CAP_PROP_FPS, set_camera_fps);
00241 if(!cap->isOpened()){
00242 NODELET_ERROR_STREAM("Could not open the stream.");
00243 return;
00244 }
00245 if (width_target != 0 && height_target != 0){
00246 cap->set(CV_CAP_PROP_FRAME_WIDTH, width_target);
00247 cap->set(CV_CAP_PROP_FRAME_HEIGHT, height_target);
00248 }
00249
00250 try {
00251 capture_thread = boost::thread(
00252 boost::bind(&VideoStreamNodelet::do_capture, this));
00253 publish_timer = nh->createTimer(
00254 ros::Duration(1.0 / fps), &VideoStreamNodelet::do_publish, this);
00255 } catch (std::exception& e) {
00256 NODELET_ERROR_STREAM("Failed to start capture thread: " << e.what());
00257 }
00258 }
00259
00260 virtual void unsubscribe() {
00261 ROS_DEBUG("Unsubscribe");
00262 publish_timer.stop();
00263 capture_thread_running = false;
00264 capture_thread.join();
00265 cap.reset();
00266 }
00267
00268 virtual void connectionCallbackImpl() {
00269 std::lock_guard<std::mutex> lock(s_mutex);
00270 if (subscriber_num == 0) {
00271 subscriber_num++;
00272 subscribe();
00273 }
00274 }
00275
00276 virtual void disconnectionCallbackImpl() {
00277 std::lock_guard<std::mutex> lock(s_mutex);
00278 bool always_subscribe = false;
00279 pnh->getParamCached("always_subscribe", always_subscribe);
00280 if (video_stream_provider == "videofile" || always_subscribe) {
00281 return;
00282 }
00283
00284 subscriber_num--;
00285 if (subscriber_num == 0) {
00286 unsubscribe();
00287 }
00288 }
00289
00290 virtual void connectionCallback(const image_transport::SingleSubscriberPublisher&) {
00291 connectionCallbackImpl();
00292 }
00293
00294 virtual void infoConnectionCallback(const ros::SingleSubscriberPublisher&) {
00295 connectionCallbackImpl();
00296 }
00297
00298 virtual void disconnectionCallback(const image_transport::SingleSubscriberPublisher&) {
00299 disconnectionCallbackImpl();
00300 }
00301
00302 virtual void infoDisconnectionCallback(const ros::SingleSubscriberPublisher&) {
00303 disconnectionCallbackImpl();
00304 }
00305
00306 virtual void configCallback(VideoStreamConfig& config, uint32_t level) {
00307 NODELET_DEBUG("configCallback");
00308 bool need_resubscribe = false;
00309
00310 if (camera_name != config.camera_name ||
00311 camera_info_url != config.camera_info_url ||
00312 frame_id != config.frame_id) {
00313 camera_name = config.camera_name;
00314 camera_info_url = config.camera_info_url;
00315 frame_id = config.frame_id;
00316 NODELET_INFO_STREAM("Camera name: " << camera_name);
00317 NODELET_INFO_STREAM("Provided camera_info_url: '" << camera_info_url << "'");
00318 NODELET_INFO_STREAM("Publishing with frame_id: " << frame_id);
00319 camera_info_manager::CameraInfoManager cam_info_manager(*nh, camera_name, camera_info_url);
00320
00321 cam_info_msg = cam_info_manager.getCameraInfo();
00322 cam_info_msg.header.frame_id = frame_id;
00323 }
00324
00325 if (set_camera_fps != config.set_camera_fps ||
00326 fps != config.fps) {
00327 if (config.fps > config.set_camera_fps) {
00328 NODELET_WARN_STREAM("Asked to publish at 'fps' (" << config.fps
00329 << ") which is higher than the 'set_camera_fps' (" << config.set_camera_fps <<
00330 "), we can't publish faster than the camera provides images.");
00331 config.fps = config.set_camera_fps;
00332 }
00333 set_camera_fps = config.set_camera_fps;
00334 fps = config.fps;
00335 NODELET_INFO_STREAM("Setting camera FPS to: " << set_camera_fps);
00336 NODELET_INFO_STREAM("Throttling to fps: " << fps);
00337 need_resubscribe = true;
00338 }
00339
00340 if (max_queue_size != config.buffer_queue_size) {
00341 max_queue_size = config.buffer_queue_size;
00342 NODELET_INFO_STREAM("Setting buffer size for capturing frames to: " << max_queue_size);
00343 }
00344
00345 if (flip_horizontal != config.flip_horizontal ||
00346 flip_vertical != config.flip_vertical) {
00347 flip_horizontal = config.flip_horizontal;
00348 flip_vertical = config.flip_vertical;
00349 NODELET_INFO_STREAM("Flip horizontal image is: " << ((flip_horizontal)?"true":"false"));
00350 NODELET_INFO_STREAM("Flip vertical image is: " << ((flip_vertical)?"true":"false"));
00351 }
00352
00353 if (width_target != config.width ||
00354 height_target != config.height) {
00355 width_target = config.width;
00356 height_target = config.height;
00357 if (width_target != 0 && height_target != 0) {
00358 NODELET_INFO_STREAM("Forced image width is: " << width_target);
00359 NODELET_INFO_STREAM("Forced image height is: " << height_target);
00360 }
00361 need_resubscribe = true;
00362 }
00363
00364 if (cap && cap->isOpened()) {
00365 cap->set(CV_CAP_PROP_BRIGHTNESS, config.brightness);
00366 cap->set(CV_CAP_PROP_CONTRAST, config.contrast);
00367 cap->set(CV_CAP_PROP_HUE, config.hue);
00368 cap->set(CV_CAP_PROP_SATURATION, config.saturation);
00369 if (config.auto_exposure) {
00370 cap->set(CV_CAP_PROP_AUTO_EXPOSURE, 0.75);
00371 config.exposure = 0.5;
00372 } else {
00373 cap->set(CV_CAP_PROP_AUTO_EXPOSURE, 0.25);
00374 cap->set(CV_CAP_PROP_EXPOSURE, config.exposure);
00375 }
00376 }
00377
00378 loop_videofile = config.loop_videofile;
00379 reopen_on_read_failure = config.reopen_on_read_failure;
00380
00381 if (subscriber_num > 0 && need_resubscribe) {
00382 unsubscribe();
00383 subscribe();
00384 }
00385 }
00386
00387 virtual void onInit() {
00388 nh.reset(new ros::NodeHandle(getNodeHandle()));
00389 pnh.reset(new ros::NodeHandle(getPrivateNodeHandle()));
00390 subscriber_num = 0;
00391
00392
00393 pnh->param<std::string>("video_stream_provider", video_stream_provider, "0");
00394
00395 try {
00396 int device_num = std::stoi(video_stream_provider);
00397 video_stream_provider_type ="videodevice";
00398 } catch (std::invalid_argument &ex) {
00399 if (video_stream_provider.find("http://") != std::string::npos ||
00400 video_stream_provider.find("https://") != std::string::npos){
00401 video_stream_provider_type = "http_stream";
00402 }
00403 else if(video_stream_provider.find("rtsp://") != std::string::npos){
00404 video_stream_provider_type = "rtsp_stream";
00405 }
00406 else{
00407 fs::file_type file_type = fs::status(fs::path(video_stream_provider)).type();
00408 switch (file_type) {
00409 case fs::file_type::character_file:
00410 case fs::file_type::block_file:
00411 video_stream_provider_type = "videodevice";
00412 break;
00413 case fs::file_type::regular_file:
00414 video_stream_provider_type = "videofile";
00415 break;
00416 default:
00417 video_stream_provider_type = "unknown";
00418 }
00419 }
00420 }
00421
00422
00423 dyn_srv = boost::make_shared<dynamic_reconfigure::Server<VideoStreamConfig> >(*pnh);
00424 auto f = boost::bind(&VideoStreamNodelet::configCallback, this, _1, _2);
00425 dyn_srv->setCallback(f);
00426
00427 subscriber_num = 0;
00428 image_transport::SubscriberStatusCallback connect_cb =
00429 boost::bind(&VideoStreamNodelet::connectionCallback, this, _1);
00430 ros::SubscriberStatusCallback info_connect_cb =
00431 boost::bind(&VideoStreamNodelet::infoConnectionCallback, this, _1);
00432 image_transport::SubscriberStatusCallback disconnect_cb =
00433 boost::bind(&VideoStreamNodelet::disconnectionCallback, this, _1);
00434 ros::SubscriberStatusCallback info_disconnect_cb =
00435 boost::bind(&VideoStreamNodelet::infoDisconnectionCallback, this, _1);
00436 pub = image_transport::ImageTransport(*nh).advertiseCamera(
00437 "image_raw", 1,
00438 connect_cb, connect_cb,
00439 info_connect_cb, info_connect_cb,
00440 ros::VoidPtr(), false);
00441 }
00442
00443 virtual ~VideoStreamNodelet() {
00444 if (subscriber_num > 0)
00445 subscriber_num = 0;
00446 unsubscribe();
00447 }
00448 };
00449 }
00450
00451 #include <pluginlib/class_list_macros.h>
00452 PLUGINLIB_EXPORT_CLASS(video_stream_opencv::VideoStreamNodelet, nodelet::Nodelet)