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
00042 #include <ros/ros.h>
00043 #include "opencv_apps/nodelet.h"
00044 #include <image_transport/image_transport.h>
00045 #include <cv_bridge/cv_bridge.h>
00046 #include <sensor_msgs/image_encodings.h>
00047
00048 #include <opencv2/highgui/highgui.hpp>
00049 #include <opencv2/imgproc/imgproc.hpp>
00050
00051 #include <dynamic_reconfigure/server.h>
00052 #include "opencv_apps/GoodfeatureTrackConfig.h"
00053 #include "opencv_apps/Point2D.h"
00054 #include "opencv_apps/Point2DArrayStamped.h"
00055
00056 namespace opencv_apps
00057 {
00058 class GoodfeatureTrackNodelet : public opencv_apps::Nodelet
00059 {
00060 image_transport::Publisher img_pub_;
00061 image_transport::Subscriber img_sub_;
00062 image_transport::CameraSubscriber cam_sub_;
00063 ros::Publisher msg_pub_;
00064
00065 boost::shared_ptr<image_transport::ImageTransport> it_;
00066
00067 typedef opencv_apps::GoodfeatureTrackConfig Config;
00068 typedef dynamic_reconfigure::Server<Config> ReconfigureServer;
00069 Config config_;
00070 boost::shared_ptr<ReconfigureServer> reconfigure_server_;
00071
00072 int queue_size_;
00073 bool debug_view_;
00074 ros::Time prev_stamp_;
00075
00076 std::string window_name_;
00077 static bool need_config_update_;
00078
00079 int max_corners_;
00080
00081 void reconfigureCallback(Config& new_config, uint32_t level)
00082 {
00083 config_ = new_config;
00084 max_corners_ = config_.max_corners;
00085 }
00086
00087 const std::string& frameWithDefault(const std::string& frame, const std::string& image_frame)
00088 {
00089 if (frame.empty())
00090 return image_frame;
00091 return frame;
00092 }
00093
00094 void imageCallbackWithInfo(const sensor_msgs::ImageConstPtr& msg, const sensor_msgs::CameraInfoConstPtr& cam_info)
00095 {
00096 doWork(msg, cam_info->header.frame_id);
00097 }
00098
00099 void imageCallback(const sensor_msgs::ImageConstPtr& msg)
00100 {
00101 doWork(msg, msg->header.frame_id);
00102 }
00103
00104 static void trackbarCallback(int , void* )
00105 {
00106 need_config_update_ = true;
00107 }
00108
00109 void doWork(const sensor_msgs::ImageConstPtr& msg, const std::string& input_frame_from_msg)
00110 {
00111
00112 try
00113 {
00114
00115 cv::Mat frame = cv_bridge::toCvShare(msg, sensor_msgs::image_encodings::BGR8)->image;
00116
00117
00118 opencv_apps::Point2DArrayStamped corners_msg;
00119 corners_msg.header = msg->header;
00120
00121
00122 cv::Mat src_gray;
00123 int max_trackbar = 100;
00124
00125 if (frame.channels() > 1)
00126 {
00127 cv::cvtColor(frame, src_gray, cv::COLOR_BGR2GRAY);
00128 }
00129 else
00130 {
00131 src_gray = frame;
00132 cv::cvtColor(src_gray, frame, cv::COLOR_GRAY2BGR);
00133 }
00134
00135 if (debug_view_)
00136 {
00138 cv::namedWindow(window_name_, cv::WINDOW_AUTOSIZE);
00139 cv::createTrackbar("Max corners", window_name_, &max_corners_, max_trackbar, trackbarCallback);
00140 if (need_config_update_)
00141 {
00142 config_.max_corners = max_corners_;
00143 reconfigure_server_->updateConfig(config_);
00144 need_config_update_ = false;
00145 }
00146 }
00147
00149 if (max_corners_ < 1)
00150 {
00151 max_corners_ = 1;
00152 }
00153
00155 std::vector<cv::Point2f> corners;
00156 double quality_level = 0.01;
00157 double min_distance = 10;
00158 int block_size = 3;
00159 bool use_harris_detector = false;
00160 double k = 0.04;
00161
00162 cv::RNG rng(12345);
00163
00165 cv::goodFeaturesToTrack(src_gray, corners, max_corners_, quality_level, min_distance, cv::Mat(), block_size,
00166 use_harris_detector, k);
00167
00169 NODELET_INFO_STREAM("** Number of corners detected: " << corners.size());
00170 int r = 4;
00171 for (const cv::Point2f& corner : corners)
00172 {
00173 cv::circle(frame, corner, r, cv::Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255)), -1, 8,
00174 0);
00175 }
00176
00177
00178 if (debug_view_)
00179 {
00180 cv::imshow(window_name_, frame);
00181 int c = cv::waitKey(1);
00182 }
00183
00184
00185 for (const cv::Point2f& i : corners)
00186 {
00187 opencv_apps::Point2D corner;
00188 corner.x = i.x;
00189 corner.y = i.y;
00190 corners_msg.points.push_back(corner);
00191 }
00192
00193 sensor_msgs::Image::Ptr out_img = cv_bridge::CvImage(msg->header, "bgr8", frame).toImageMsg();
00194 img_pub_.publish(out_img);
00195 msg_pub_.publish(corners_msg);
00196 }
00197 catch (cv::Exception& e)
00198 {
00199 NODELET_ERROR("Image processing error: %s %s %s %i", e.err.c_str(), e.func.c_str(), e.file.c_str(), e.line);
00200 }
00201
00202 prev_stamp_ = msg->header.stamp;
00203 }
00204
00205 void subscribe()
00206 {
00207 NODELET_DEBUG("Subscribing to image topic.");
00208 if (config_.use_camera_info)
00209 cam_sub_ = it_->subscribeCamera("image", queue_size_, &GoodfeatureTrackNodelet::imageCallbackWithInfo, this);
00210 else
00211 img_sub_ = it_->subscribe("image", queue_size_, &GoodfeatureTrackNodelet::imageCallback, this);
00212 }
00213
00214 void unsubscribe()
00215 {
00216 NODELET_DEBUG("Unsubscribing from image topic.");
00217 img_sub_.shutdown();
00218 cam_sub_.shutdown();
00219 }
00220
00221 public:
00222 virtual void onInit()
00223 {
00224 Nodelet::onInit();
00225 it_ = boost::shared_ptr<image_transport::ImageTransport>(new image_transport::ImageTransport(*nh_));
00226
00227 pnh_->param("queue_size", queue_size_, 3);
00228 pnh_->param("debug_view", debug_view_, false);
00229 if (debug_view_)
00230 {
00231 always_subscribe_ = true;
00232 }
00233 prev_stamp_ = ros::Time(0, 0);
00234
00235 window_name_ = "Image";
00236 max_corners_ = 23;
00237
00238 reconfigure_server_ = boost::make_shared<dynamic_reconfigure::Server<Config> >(*pnh_);
00239 dynamic_reconfigure::Server<Config>::CallbackType f =
00240 boost::bind(&GoodfeatureTrackNodelet::reconfigureCallback, this, _1, _2);
00241 reconfigure_server_->setCallback(f);
00242
00243 img_pub_ = advertiseImage(*pnh_, "image", 1);
00244 msg_pub_ = advertise<opencv_apps::Point2DArrayStamped>(*pnh_, "corners", 1);
00245
00246 onInitPostProcess();
00247 }
00248 };
00249 bool GoodfeatureTrackNodelet::need_config_update_ = false;
00250 }
00251
00252 namespace goodfeature_track
00253 {
00254 class GoodfeatureTrackNodelet : public opencv_apps::GoodfeatureTrackNodelet
00255 {
00256 public:
00257 virtual void onInit()
00258 {
00259 ROS_WARN("DeprecationWarning: Nodelet goodfeature_track/goodfeature_track is deprecated, "
00260 "and renamed to opencv_apps/goodfeature_track.");
00261 opencv_apps::GoodfeatureTrackNodelet::onInit();
00262 }
00263 };
00264 }
00265
00266 #include <pluginlib/class_list_macros.h>
00267 PLUGINLIB_EXPORT_CLASS(opencv_apps::GoodfeatureTrackNodelet, nodelet::Nodelet);
00268 PLUGINLIB_EXPORT_CLASS(goodfeature_track::GoodfeatureTrackNodelet, nodelet::Nodelet);