image_rotate.cpp
Go to the documentation of this file.
00001 #include <ros/ros.h>
00002 #include <tf/transform_listener.h>
00003 #include <tf/transform_broadcaster.h>
00004 #include <image_rotate/ImageRotateConfig.h>
00005 #include <geometry_msgs/Vector3Stamped.h>
00006 #include <image_transport/image_transport.h>
00007 #include <cv_bridge/cv_bridge.h>
00008 #include <opencv2/imgproc/imgproc.hpp>
00009 #include <dynamic_reconfigure/server.h>
00010 #include <math.h>
00011 
00012 class ImageRotater
00013 {
00014   tf::TransformListener tf_sub_;
00015   tf::TransformBroadcaster tf_pub_;
00016 
00017   image_rotate::ImageRotateConfig config_;
00018   dynamic_reconfigure::Server<image_rotate::ImageRotateConfig> srv;
00019 
00020   image_transport::Publisher img_pub_;
00021   image_transport::Subscriber img_sub_;
00022   image_transport::CameraSubscriber cam_sub_;
00023 
00024   tf::Stamped<tf::Vector3> target_vector_;
00025   tf::Stamped<tf::Vector3> source_vector_;
00026     
00027   image_transport::ImageTransport it_;
00028   ros::NodeHandle nh_;
00029 
00030   int subscriber_count_;
00031   double angle_;
00032   ros::Time prev_stamp_;
00033 
00034   void reconfigureCallback(image_rotate::ImageRotateConfig &new_config, uint32_t level)
00035   {
00036     config_ = new_config;
00037     target_vector_.setValue(config_.target_x, config_.target_y, config_.target_z);
00038     source_vector_.setValue(config_.source_x, config_.source_y, config_.source_z);
00039     if (subscriber_count_)
00040     { // @todo Could do this without an interruption at some point.
00041       unsubscribe();
00042       subscribe();
00043     }
00044   }
00045 
00046   const std::string &frameWithDefault(const std::string &frame, const std::string &image_frame)
00047   {
00048     if (frame.empty())
00049       return image_frame;
00050     return frame;
00051   }
00052 
00053   void imageCallbackWithInfo(const sensor_msgs::ImageConstPtr& msg, const sensor_msgs::CameraInfoConstPtr& cam_info)
00054   {
00055     do_work(msg, cam_info->header.frame_id);
00056   }
00057   
00058   void imageCallback(const sensor_msgs::ImageConstPtr& msg)
00059   {
00060     do_work(msg, msg->header.frame_id);
00061   }
00062 
00063   void do_work(const sensor_msgs::ImageConstPtr& msg, const std::string input_frame_from_msg)
00064   {
00065     try
00066     {
00067       std::string input_frame_id = frameWithDefault(config_.input_frame_id, input_frame_from_msg);
00068 
00069       // Transform the target vector into the image frame.
00070       target_vector_.stamp_ = msg->header.stamp;
00071       target_vector_.frame_id_ = frameWithDefault(config_.target_frame_id, input_frame_id);
00072       tf::Stamped<tf::Vector3> target_vector_transformed;
00073       tf_sub_.waitForTransform(input_frame_id, msg->header.stamp,
00074                                target_vector_.frame_id_, target_vector_.stamp_,
00075                                input_frame_id, ros::Duration(0.2));
00076       tf_sub_.transformVector(input_frame_id, msg->header.stamp, target_vector_,
00077                               input_frame_id, target_vector_transformed);
00078 
00079       // Transform the source vector into the image frame.
00080       source_vector_.stamp_ = msg->header.stamp;
00081       source_vector_.frame_id_ = frameWithDefault(config_.source_frame_id, input_frame_id);
00082       tf::Stamped<tf::Vector3> source_vector_transformed;
00083       tf_sub_.waitForTransform(input_frame_id, msg->header.stamp,
00084                                source_vector_.frame_id_, source_vector_.stamp_,
00085                                input_frame_id, ros::Duration(0.01));
00086       tf_sub_.transformVector(input_frame_id, msg->header.stamp, source_vector_,
00087                               input_frame_id, source_vector_transformed);
00088 
00089       //ROS_INFO("target: %f %f %f", target_vector_.x(), target_vector_.y(), target_vector_.z());
00090       //ROS_INFO("target_transformed: %f %f %f", target_vector_transformed.x(), target_vector_transformed.y(), target_vector_transformed.z());
00091       //ROS_INFO("source: %f %f %f", source_vector_.x(), source_vector_.y(), source_vector_.z());
00092       //ROS_INFO("source_transformed: %f %f %f", source_vector_transformed.x(), source_vector_transformed.y(), source_vector_transformed.z());
00093 
00094       // Calculate the angle of the rotation.
00095       double angle = angle_;
00096       if ((target_vector_transformed.x()    != 0 || target_vector_transformed.y()    != 0) &&
00097           (source_vector_transformed.x() != 0 || source_vector_transformed.y() != 0))
00098       {
00099         angle = atan2(target_vector_transformed.y(), target_vector_transformed.x());
00100         angle -= atan2(source_vector_transformed.y(), source_vector_transformed.x());
00101       }
00102 
00103       // Rate limit the rotation.
00104       if (config_.max_angular_rate == 0)
00105         angle_ = angle;
00106       else
00107       {
00108         double delta = fmod(angle - angle_, 2.0 * M_PI);
00109         if (delta > M_PI)
00110           delta -= 2.0 * M_PI;
00111         else if (delta < - M_PI)
00112           delta += 2.0 * M_PI;
00113 
00114         double max_delta = config_.max_angular_rate * (msg->header.stamp - prev_stamp_).toSec();
00115         if (delta > max_delta)
00116           delta = max_delta;
00117         else if (delta < -max_delta)
00118           delta = - max_delta;
00119 
00120         angle_ += delta;
00121       }
00122       angle_ = fmod(angle_, 2.0 * M_PI);
00123     }
00124     catch (tf::TransformException &e)
00125     {
00126       ROS_ERROR("Transform error: %s", e.what());
00127     }
00128 
00129     //ROS_INFO("angle: %f", 180 * angle_ / M_PI);
00130 
00131     // Publish the transform.
00132     tf::StampedTransform transform;
00133     transform.setOrigin(tf::Vector3(0.0, 0.0, 0.0));
00134     transform.setRotation(tf::Quaternion(tf::Vector3(0.0, 0.0, 1.0), angle_));
00135     transform.frame_id_ = msg->header.frame_id;
00136     transform.child_frame_id_ = frameWithDefault(config_.output_frame_id, msg->header.frame_id + "_rotated");
00137     transform.stamp_ = msg->header.stamp;
00138     tf_pub_.sendTransform(transform);
00139 
00140     // Transform the image.
00141     try
00142     {
00143       // Convert the image into something opencv can handle.
00144       cv::Mat in_image = cv_bridge::toCvShare(msg, msg->encoding)->image;
00145 
00146       // Compute the output image size.
00147       int max_dim = in_image.cols > in_image.rows ? in_image.cols : in_image.rows;
00148       int min_dim = in_image.cols < in_image.rows ? in_image.cols : in_image.rows;
00149       int noblack_dim = min_dim / sqrt(2);
00150       int diag_dim = sqrt(in_image.cols*in_image.cols + in_image.rows*in_image.rows);
00151       int out_size;
00152       int candidates[] = { noblack_dim, min_dim, max_dim, diag_dim, diag_dim }; // diag_dim repeated to simplify limit case.
00153       int step = config_.output_image_size;
00154       out_size = candidates[step] + (candidates[step + 1] - candidates[step]) * (config_.output_image_size - step);
00155       //ROS_INFO("out_size: %d", out_size);
00156 
00157       // Compute the rotation matrix.
00158       cv::Mat rot_matrix = cv::getRotationMatrix2D(cv::Point2f(in_image.cols / 2.0, in_image.rows / 2.0), 180 * angle_ / M_PI, 1);
00159       cv::Mat translation = rot_matrix.col(2);
00160       rot_matrix.at<double>(0, 2) += (out_size - in_image.cols) / 2.0;
00161       rot_matrix.at<double>(1, 2) += (out_size - in_image.rows) / 2.0;
00162 
00163       // Do the rotation
00164       cv::Mat out_image;
00165       cv::warpAffine(in_image, out_image, rot_matrix, cv::Size(out_size, out_size));
00166 
00167       // Publish the image.
00168       sensor_msgs::Image::Ptr out_img = cv_bridge::CvImage(msg->header, msg->encoding, out_image).toImageMsg();
00169       out_img->header.frame_id = transform.child_frame_id_;
00170       img_pub_.publish(out_img);
00171     }
00172     catch (cv::Exception &e)
00173     {
00174       ROS_ERROR("Image processing error: %s %s %s %i", e.err.c_str(), e.func.c_str(), e.file.c_str(), e.line);
00175     }
00176 
00177     prev_stamp_ = msg->header.stamp;
00178   }
00179 
00180   void subscribe()
00181   {
00182     ROS_DEBUG("Subscribing to image topic.");
00183     if (config_.use_camera_info && config_.input_frame_id.empty())
00184       cam_sub_ = it_.subscribeCamera("image", 3, &ImageRotater::imageCallbackWithInfo, this);
00185     else
00186       img_sub_ = it_.subscribe("image", 3, &ImageRotater::imageCallback, this);
00187   }
00188 
00189   void unsubscribe()
00190   {
00191       ROS_DEBUG("Unsubscribing from image topic.");
00192       img_sub_.shutdown();
00193       cam_sub_.shutdown();
00194   }
00195 
00196   void connectCb(const image_transport::SingleSubscriberPublisher& ssp)
00197   {
00198     if (subscriber_count_++ == 0) {
00199       subscribe();
00200     }
00201   }
00202 
00203   void disconnectCb(const image_transport::SingleSubscriberPublisher&)
00204   {
00205     subscriber_count_--;
00206     if (subscriber_count_ == 0) {
00207       unsubscribe();
00208     }
00209   }
00210 
00211 public:
00212   ImageRotater(ros::NodeHandle nh = ros::NodeHandle()) : it_(nh), nh_(nh), subscriber_count_(0), angle_(0), prev_stamp_(0, 0)
00213   {
00214     image_transport::SubscriberStatusCallback connect_cb    = boost::bind(&ImageRotater::connectCb, this, _1);
00215     image_transport::SubscriberStatusCallback disconnect_cb = boost::bind(&ImageRotater::disconnectCb, this, _1);
00216     img_pub_ = image_transport::ImageTransport(ros::NodeHandle(nh_, "rotated")).advertise("image", 1, connect_cb, disconnect_cb);
00217 
00218     dynamic_reconfigure::Server<image_rotate::ImageRotateConfig>::CallbackType f =
00219       boost::bind(&ImageRotater::reconfigureCallback, this, _1, _2);
00220     srv.setCallback(f);
00221   }
00222 };
00223 
00224 int main(int argc, char **argv)
00225 {
00226   ros::init(argc, argv, "image_rotate", ros::init_options::AnonymousName);
00227 
00228   ImageRotater ir;
00229   ros::spin();
00230 }


image_rotate
Author(s): Blaise Gassend
autogenerated on Fri Jan 3 2014 11:24:46