48 #include <image_rotate/ImageRotateConfig.h> 49 #include <geometry_msgs/Vector3Stamped.h> 52 #include <opencv2/imgproc/imgproc.hpp> 53 #include <dynamic_reconfigure/server.h> 65 dynamic_reconfigure::Server<image_rotate::ImageRotateConfig>
srv;
84 target_vector_.vector.x = config_.target_x;
85 target_vector_.vector.y = config_.target_y;
86 target_vector_.vector.z = config_.target_z;
88 source_vector_.vector.x = config_.source_x;
89 source_vector_.vector.y = config_.source_y;
90 source_vector_.vector.z = config_.source_z;
91 if (subscriber_count_)
98 const std::string &
frameWithDefault(
const std::string &frame,
const std::string &image_frame)
107 do_work(msg, cam_info->header.frame_id);
112 do_work(msg, msg->header.frame_id);
115 void do_work(
const sensor_msgs::ImageConstPtr& msg,
const std::string input_frame_from_msg)
119 std::string input_frame_id =
frameWithDefault(config_.input_frame_id, input_frame_from_msg);
120 std::string target_frame_id =
frameWithDefault(config_.target_frame_id, input_frame_from_msg);
121 std::string source_frame_id =
frameWithDefault(config_.source_frame_id, input_frame_from_msg);
124 target_vector_.header.stamp = msg->header.stamp;
125 target_vector_.header.frame_id = target_frame_id;
126 geometry_msgs::Vector3Stamped target_vector_transformed;
127 geometry_msgs::TransformStamped transform = tf_buffer_.
lookupTransform(target_frame_id, input_frame_id, msg->header.stamp, msg->header.stamp-prev_stamp_);
131 source_vector_.header.stamp = msg->header.stamp;
132 source_vector_.header.frame_id = source_frame_id;
133 geometry_msgs::Vector3Stamped source_vector_transformed;
134 transform = tf_buffer_.
lookupTransform(source_frame_id, input_frame_id, msg->header.stamp, msg->header.stamp-prev_stamp_);
144 if ((target_vector_transformed.vector.x != 0 || target_vector_transformed.vector.y != 0) &&
145 (source_vector_transformed.vector.x != 0 || source_vector_transformed.vector.y != 0))
147 angle =
atan2(target_vector_transformed.vector.y, target_vector_transformed.vector.x);
148 angle -=
atan2(source_vector_transformed.vector.y, source_vector_transformed.vector.x);
152 if (config_.max_angular_rate == 0)
156 double delta = fmod(angle - angle_, 2.0 * M_PI);
159 else if (delta < - M_PI)
162 double max_delta = config_.max_angular_rate * (msg->header.stamp -
prev_stamp_).toSec();
163 if (delta > max_delta)
165 else if (delta < -max_delta)
170 angle_ = fmod(angle_, 2.0 * M_PI);
180 geometry_msgs::TransformStamped transform;
181 transform.transform.translation.x = 0;
182 transform.transform.translation.y = 0;
183 transform.transform.translation.z = 0;
185 transform.header.frame_id = msg->header.frame_id;
186 transform.child_frame_id =
frameWithDefault(config_.output_frame_id, msg->header.frame_id +
"_rotated");
187 transform.header.stamp = msg->header.stamp;
197 int max_dim = in_image.cols > in_image.rows ? in_image.cols : in_image.rows;
198 int min_dim = in_image.cols < in_image.rows ? in_image.cols : in_image.rows;
199 int noblack_dim = min_dim /
sqrt(2);
200 int diag_dim =
sqrt(in_image.cols*in_image.cols + in_image.rows*in_image.rows);
202 int candidates[] = { noblack_dim, min_dim, max_dim, diag_dim, diag_dim };
203 int step = config_.output_image_size;
204 out_size = candidates[step] + (candidates[step + 1] - candidates[step]) * (config_.output_image_size - step);
208 cv::Mat rot_matrix = cv::getRotationMatrix2D(cv::Point2f(in_image.cols / 2.0, in_image.rows / 2.0), 180 * angle_ / M_PI, 1);
209 cv::Mat translation = rot_matrix.col(2);
210 rot_matrix.at<
double>(0, 2) += (out_size - in_image.cols) / 2.0;
211 rot_matrix.at<
double>(1, 2) += (out_size - in_image.rows) / 2.0;
215 cv::warpAffine(in_image, out_image, rot_matrix, cv::Size(out_size, out_size));
219 out_img->header.frame_id = transform.child_frame_id;
222 catch (cv::Exception &e)
224 NODELET_ERROR(
"Image processing error: %s %s %s %i", e.err.c_str(), e.func.c_str(), e.file.c_str(), e.line);
227 prev_stamp_ = msg->header.stamp;
233 if (config_.use_camera_info && config_.input_frame_id.empty())
248 if (subscriber_count_++ == 0) {
256 if (subscriber_count_ == 0) {
266 subscriber_count_ = 0;
274 dynamic_reconfigure::Server<image_rotate::ImageRotateConfig>::CallbackType
f =
CvImageConstPtr toCvShare(const sensor_msgs::ImageConstPtr &source, const std::string &encoding=std::string())
const std::string & frameWithDefault(const std::string &frame, const std::string &image_frame)
#define NODELET_ERROR(...)
TF2SIMD_FORCE_INLINE tf2Scalar angle(const Quaternion &q1, const Quaternion &q2)
void connectCb(const image_transport::SingleSubscriberPublisher &ssp)
virtual geometry_msgs::TransformStamped lookupTransform(const std::string &target_frame, const std::string &source_frame, const ros::Time &time, const ros::Duration timeout) const
Publisher advertise(const std::string &base_topic, uint32_t queue_size, bool latch=false)
boost::function< void(const SingleSubscriberPublisher &)> SubscriberStatusCallback
void doTransform(const T &data_in, T &data_out, const geometry_msgs::TransformStamped &transform)
void disconnectCb(const image_transport::SingleSubscriberPublisher &)
PLUGINLIB_EXPORT_CLASS(image_rotate::ImageRotateNodelet, nodelet::Nodelet)
void do_work(const sensor_msgs::ImageConstPtr &msg, const std::string input_frame_from_msg)
void reconfigureCallback(image_rotate::ImageRotateConfig &new_config, uint32_t level)
image_transport::CameraSubscriber cam_sub_
boost::shared_ptr< tf2_ros::TransformListener > tf_sub_
void publish(const sensor_msgs::Image &message) const
tf2_ros::TransformBroadcaster tf_pub_
geometry_msgs::Vector3Stamped source_vector_
image_transport::Subscriber img_sub_
boost::shared_ptr< image_transport::ImageTransport > it_
INLINE Rall1d< T, V, S > sqrt(const Rall1d< T, V, S > &arg)
ros::NodeHandle & getNodeHandle() const
geometry_msgs::Vector3Stamped target_vector_
tf2_ros::Buffer tf_buffer_
INLINE Rall1d< T, V, S > atan2(const Rall1d< T, V, S > &y, const Rall1d< T, V, S > &x)
void convert(const A &a, B &b)
image_rotate::ImageRotateConfig config_
void imageCallback(const sensor_msgs::ImageConstPtr &msg)
image_transport::Publisher img_pub_
void imageCallbackWithInfo(const sensor_msgs::ImageConstPtr &msg, const sensor_msgs::CameraInfoConstPtr &cam_info)
dynamic_reconfigure::Server< image_rotate::ImageRotateConfig > srv
#define NODELET_DEBUG(...)
sensor_msgs::ImagePtr toImageMsg() const