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 {
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
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
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
00090
00091
00092
00093
00094
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
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
00130
00131
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
00141 try
00142 {
00143
00144 cv::Mat in_image = cv_bridge::toCvShare(msg, msg->encoding)->image;
00145
00146
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 };
00153 int step = config_.output_image_size;
00154 out_size = candidates[step] + (candidates[step + 1] - candidates[step]) * (config_.output_image_size - step);
00155
00156
00157
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
00164 cv::Mat out_image;
00165 cv::warpAffine(in_image, out_image, rot_matrix, cv::Size(out_size, out_size));
00166
00167
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 }