image_rotate_nodelet.cpp
Go to the documentation of this file.
00001 /*********************************************************************
00002 * Software License Agreement (BSD License)
00003 *
00004 *  Copyright (c) 2014, JSK Lab.
00005 *                2008, Willow Garage, Inc.
00006 *  All rights reserved.
00007 * 
00008 *  Redistribution and use in source and binary forms, with or without
00009 *  modification, are permitted provided that the following conditions
00010 *  are met:
00011 * 
00012 *   * Redistributions of source code must retain the above copyright
00013 *     notice, this list of conditions and the following disclaimer.
00014 *   * Redistributions in binary form must reproduce the above
00015 *     copyright notice, this list of conditions and the following
00016 *     disclaimer in the documentation and/or other materials provided
00017 *     with the distribution.
00018 *   * Neither the name of the Willow Garage nor the names of its
00019 *     contributors may be used to endorse or promote products derived
00020 *     from this software without specific prior written permission.
00021 * 
00022 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00023 *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00024 *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00025 *  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00026 *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00027 *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00028 *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00029 *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
00030 *  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00031 *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
00032 *  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00033 *  POSSIBILITY OF SUCH DAMAGE.
00034 *********************************************************************/
00035 
00036 /********************************************************************
00037 * image_rotate_nodelet.cpp
00038 * this is a forked version of image_rotate.
00039 * this image_rotate_nodelet supports:
00040 *  1) nodelet
00041 *  2) tf and tf2
00042 *********************************************************************/
00043 
00044 #include <ros/ros.h>
00045 #include <nodelet/nodelet.h>
00046 
00047 #include <tf/transform_listener.h>
00048 #include <tf/transform_broadcaster.h>
00049 #include <tf2_ros/buffer_client.h>
00050 #include <eigen_conversions/eigen_msg.h>
00051 #include <tf_conversions/tf_eigen.h>
00052 #include <image_rotate/ImageRotateConfig.h>
00053 #include <geometry_msgs/Vector3Stamped.h>
00054 #include <image_transport/image_transport.h>
00055 #include <cv_bridge/cv_bridge.h>
00056 #include <opencv2/imgproc/imgproc.hpp>
00057 #include <dynamic_reconfigure/server.h>
00058 #include <math.h>
00059 
00060 namespace image_rotate {
00061 class ImageRotateNodelet : public nodelet::Nodelet
00062 {
00063   bool use_tf2_;
00064   boost::shared_ptr<tf::TransformListener> tf_sub_;
00065   tf::TransformBroadcaster tf_pub_;
00066   boost::shared_ptr<tf2::BufferClient> tf2_client_;
00067   image_rotate::ImageRotateConfig config_;
00068   dynamic_reconfigure::Server<image_rotate::ImageRotateConfig> srv;
00069 
00070   image_transport::Publisher img_pub_;
00071   image_transport::Subscriber img_sub_;
00072   image_transport::CameraSubscriber cam_sub_;
00073 
00074   tf::Stamped<tf::Vector3> target_vector_;
00075   tf::Stamped<tf::Vector3> source_vector_;
00076     
00077   boost::shared_ptr<image_transport::ImageTransport> it_;
00078   ros::NodeHandle nh_;
00079 
00080   int subscriber_count_;
00081   double angle_;
00082   ros::Time prev_stamp_;
00083 
00084   void setupTFListener()
00085   {
00086     if (use_tf2_) {
00087       // shutdown tf_sub_
00088       if (tf_sub_) {
00089         tf_sub_.reset();
00090       }
00091     }
00092     else {
00093       if(!tf_sub_) {
00094         tf_sub_.reset(new tf::TransformListener());
00095       }
00096     }
00097   }
00098   
00099   void reconfigureCallback(image_rotate::ImageRotateConfig &new_config, uint32_t level)
00100   {
00101     config_ = new_config;
00102     target_vector_.setValue(config_.target_x, config_.target_y, config_.target_z);
00103     source_vector_.setValue(config_.source_x, config_.source_y, config_.source_z);
00104     if (subscriber_count_)
00105     { // @todo Could do this without an interruption at some point.
00106       unsubscribe();
00107       subscribe();
00108     }
00109     if (use_tf2_ != config_.use_tf2) {
00110       use_tf2_ = config_.use_tf2;
00111       setupTFListener();
00112     }
00113   }
00114 
00115   const std::string &frameWithDefault(const std::string &frame, const std::string &image_frame)
00116   {
00117     if (frame.empty())
00118       return image_frame;
00119     return frame;
00120   }
00121 
00122   void imageCallbackWithInfo(const sensor_msgs::ImageConstPtr& msg, const sensor_msgs::CameraInfoConstPtr& cam_info)
00123   {
00124     do_work(msg, cam_info->header.frame_id);
00125   }
00126   
00127   void imageCallback(const sensor_msgs::ImageConstPtr& msg)
00128   {
00129     do_work(msg, msg->header.frame_id);
00130   }
00131 
00132   void transformVector(const std::string& input_frame_id, const ros::Time& target_time,
00133                        const std::string& source_frame_id, const ros::Time& time,
00134                        const std::string& fixed_frame_id,
00135                        const tf::Stamped<tf::Vector3>& input_vector,
00136                        tf::Stamped<tf::Vector3>& target_vector,
00137                        const ros::Duration& duration)
00138   {
00139     if (use_tf2_) {
00140       geometry_msgs::TransformStamped trans
00141         = tf2_client_->lookupTransform(input_frame_id, source_frame_id,
00142                                        target_time, duration);
00143       // geometry_msgs -> eigen -> tf
00144       Eigen::Affine3d transform_eigen;
00145       tf::transformMsgToEigen(trans.transform, transform_eigen);
00146       tf::StampedTransform transform_tf; // convert trans to tfStampedTransform
00147       tf::transformEigenToTF(transform_eigen, transform_tf);
00148       tf::Vector3 origin = tf::Vector3(0, 0, 0);
00149       tf::Vector3 end = input_vector;
00150       tf::Vector3 output = (transform_tf * end) - (transform_tf * origin);
00151       target_vector.setData(output);
00152       target_vector.stamp_ = input_vector.stamp_;
00153       target_vector.frame_id_ = input_frame_id;
00154     }
00155     else {
00156       tf_sub_->waitForTransform(input_frame_id, target_time,
00157                                 source_frame_id, time,
00158                                 fixed_frame_id, duration);
00159       tf_sub_->transformVector(input_frame_id, target_time, input_vector,
00160                                fixed_frame_id, target_vector);
00161     }
00162   }
00163   
00164   void do_work(const sensor_msgs::ImageConstPtr& msg, const std::string input_frame_from_msg)
00165   {
00166     try
00167     {
00168       std::string input_frame_id = frameWithDefault(config_.input_frame_id, input_frame_from_msg);
00169 
00170       // Transform the target vector into the image frame.
00171       target_vector_.stamp_ = msg->header.stamp;
00172       target_vector_.frame_id_ = frameWithDefault(config_.target_frame_id, input_frame_id);
00173       tf::Stamped<tf::Vector3> target_vector_transformed;
00174       transformVector(input_frame_id, msg->header.stamp,
00175                       target_vector_.frame_id_, target_vector_.stamp_,
00176                       input_frame_id, target_vector_, target_vector_transformed,
00177                       ros::Duration(0.2));
00178 
00179       // Transform the source vector into the image frame.
00180       source_vector_.stamp_ = msg->header.stamp;
00181       source_vector_.frame_id_ = frameWithDefault(config_.source_frame_id, input_frame_id);
00182       tf::Stamped<tf::Vector3> source_vector_transformed;
00183       transformVector(input_frame_id, msg->header.stamp,
00184                       source_vector_.frame_id_, source_vector_.stamp_,
00185                       input_frame_id, source_vector_, source_vector_transformed,
00186                       ros::Duration(0.01));
00187 
00188       // NODELET_INFO("target: %f %f %f", target_vector_.x(), target_vector_.y(), target_vector_.z());
00189       // NODELET_INFO("target_transformed: %f %f %f", target_vector_transformed.x(), target_vector_transformed.y(), target_vector_transformed.z());
00190       // NODELET_INFO("source: %f %f %f", source_vector_.x(), source_vector_.y(), source_vector_.z());
00191       // NODELET_INFO("source_transformed: %f %f %f", source_vector_transformed.x(), source_vector_transformed.y(), source_vector_transformed.z());
00192 
00193       // Calculate the angle of the rotation.
00194       double angle = angle_;
00195       if ((target_vector_transformed.x()    != 0 || target_vector_transformed.y()    != 0) &&
00196           (source_vector_transformed.x() != 0 || source_vector_transformed.y() != 0))
00197       {
00198         angle = atan2(target_vector_transformed.y(), target_vector_transformed.x());
00199         angle -= atan2(source_vector_transformed.y(), source_vector_transformed.x());
00200       }
00201 
00202       // Rate limit the rotation.
00203       if (config_.max_angular_rate == 0)
00204         angle_ = angle;
00205       else
00206       {
00207         double delta = fmod(angle - angle_, 2.0 * M_PI);
00208         if (delta > M_PI)
00209           delta -= 2.0 * M_PI;
00210         else if (delta < - M_PI)
00211           delta += 2.0 * M_PI;
00212 
00213         double max_delta = config_.max_angular_rate * (msg->header.stamp - prev_stamp_).toSec();
00214         if (delta > max_delta)
00215           delta = max_delta;
00216         else if (delta < -max_delta)
00217           delta = - max_delta;
00218 
00219         angle_ += delta;
00220       }
00221       angle_ = fmod(angle_, 2.0 * M_PI);
00222     }
00223     catch (tf::TransformException &e)
00224     {
00225       NODELET_ERROR("Transform error: %s", e.what());
00226     }
00227 
00228     //NODELET_INFO("angle: %f", 180 * angle_ / M_PI);
00229 
00230     // Publish the transform.
00231     tf::StampedTransform transform;
00232     transform.setOrigin(tf::Vector3(0.0, 0.0, 0.0));
00233     transform.setRotation(tf::Quaternion(tf::Vector3(0.0, 0.0, 1.0), angle_));
00234     transform.frame_id_ = msg->header.frame_id;
00235     transform.child_frame_id_ = frameWithDefault(config_.output_frame_id, msg->header.frame_id + "_rotated");
00236     transform.stamp_ = msg->header.stamp;
00237     tf_pub_.sendTransform(transform);
00238 
00239     // Transform the image.
00240     try
00241     {
00242       // Convert the image into something opencv can handle.
00243       cv::Mat in_image = cv_bridge::toCvShare(msg, msg->encoding)->image;
00244 
00245       // Compute the output image size.
00246       int max_dim = in_image.cols > in_image.rows ? in_image.cols : in_image.rows;
00247       int min_dim = in_image.cols < in_image.rows ? in_image.cols : in_image.rows;
00248       int noblack_dim = min_dim / sqrt(2);
00249       int diag_dim = sqrt(in_image.cols*in_image.cols + in_image.rows*in_image.rows);
00250       int out_size;
00251       int candidates[] = { noblack_dim, min_dim, max_dim, diag_dim, diag_dim }; // diag_dim repeated to simplify limit case.
00252       int step = config_.output_image_size;
00253       out_size = candidates[step] + (candidates[step + 1] - candidates[step]) * (config_.output_image_size - step);
00254       //NODELET_INFO("out_size: %d", out_size);
00255 
00256       // Compute the rotation matrix.
00257       cv::Mat rot_matrix = cv::getRotationMatrix2D(cv::Point2f(in_image.cols / 2.0, in_image.rows / 2.0), 180 * angle_ / M_PI, 1);
00258       cv::Mat translation = rot_matrix.col(2);
00259       rot_matrix.at<double>(0, 2) += (out_size - in_image.cols) / 2.0;
00260       rot_matrix.at<double>(1, 2) += (out_size - in_image.rows) / 2.0;
00261 
00262       // Do the rotation
00263       cv::Mat out_image;
00264       cv::warpAffine(in_image, out_image, rot_matrix, cv::Size(out_size, out_size));
00265 
00266       // Publish the image.
00267       sensor_msgs::Image::Ptr out_img = cv_bridge::CvImage(msg->header, msg->encoding, out_image).toImageMsg();
00268       out_img->header.frame_id = transform.child_frame_id_;
00269       img_pub_.publish(out_img);
00270     }
00271     catch (cv::Exception &e)
00272     {
00273       NODELET_ERROR("Image processing error: %s %s %s %i", e.err.c_str(), e.func.c_str(), e.file.c_str(), e.line);
00274     }
00275 
00276     prev_stamp_ = msg->header.stamp;
00277   }
00278 
00279   void subscribe()
00280   {
00281     NODELET_DEBUG("Subscribing to image topic.");
00282     if (config_.use_camera_info && config_.input_frame_id.empty())
00283       cam_sub_ = it_->subscribeCamera("image", 3, &ImageRotateNodelet::imageCallbackWithInfo, this);
00284     else
00285       img_sub_ = it_->subscribe("image", 3, &ImageRotateNodelet::imageCallback, this);
00286   }
00287 
00288   void unsubscribe()
00289   {
00290       NODELET_DEBUG("Unsubscribing from image topic.");
00291       img_sub_.shutdown();
00292       cam_sub_.shutdown();
00293   }
00294 
00295   void connectCb(const image_transport::SingleSubscriberPublisher& ssp)
00296   {
00297     if (subscriber_count_++ == 0) {
00298       subscribe();
00299     }
00300   }
00301 
00302   void disconnectCb(const image_transport::SingleSubscriberPublisher&)
00303   {
00304     subscriber_count_--;
00305     if (subscriber_count_ == 0) {
00306       unsubscribe();
00307     }
00308   }
00309 
00310 public:
00311   virtual void onInit()
00312   {
00313     nh_ = getNodeHandle();
00314     it_ = boost::shared_ptr<image_transport::ImageTransport>(new image_transport::ImageTransport(nh_));
00315     tf2_client_.reset(new tf2::BufferClient("tf2_buffer_server", 100, ros::Duration(0.2)));
00316     subscriber_count_ = 0;
00317     angle_ = 0;
00318     prev_stamp_ = ros::Time(0, 0);
00319     image_transport::SubscriberStatusCallback connect_cb    = boost::bind(&ImageRotateNodelet::connectCb, this, _1);
00320     image_transport::SubscriberStatusCallback disconnect_cb = boost::bind(&ImageRotateNodelet::disconnectCb, this, _1);
00321     img_pub_ = image_transport::ImageTransport(ros::NodeHandle(nh_, "rotated")).advertise("image", 1, connect_cb, disconnect_cb);
00322 
00323     dynamic_reconfigure::Server<image_rotate::ImageRotateConfig>::CallbackType f =
00324       boost::bind(&ImageRotateNodelet::reconfigureCallback, this, _1, _2);
00325     srv.setCallback(f);
00326   }
00327 };
00328 }
00329 #include <pluginlib/class_list_macros.h>
00330 PLUGINLIB_EXPORT_CLASS(image_rotate::ImageRotateNodelet, nodelet::Nodelet);


image_rotate
Author(s): Blaise Gassend
autogenerated on Mon Oct 6 2014 00:45:47