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
00038
00039
00040
00041
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
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 {
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
00144 Eigen::Affine3d transform_eigen;
00145 tf::transformMsgToEigen(trans.transform, transform_eigen);
00146 tf::StampedTransform transform_tf;
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
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
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
00189
00190
00191
00192
00193
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
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
00229
00230
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
00240 try
00241 {
00242
00243 cv::Mat in_image = cv_bridge::toCvShare(msg, msg->encoding)->image;
00244
00245
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 };
00252 int step = config_.output_image_size;
00253 out_size = candidates[step] + (candidates[step + 1] - candidates[step]) * (config_.output_image_size - step);
00254
00255
00256
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
00263 cv::Mat out_image;
00264 cv::warpAffine(in_image, out_image, rot_matrix, cv::Size(out_size, out_size));
00265
00266
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);