lk_flow_nodelet.cpp
Go to the documentation of this file.
1 /*********************************************************************
2 * Software License Agreement (BSD License)
3 *
4 * Copyright (c) 2014, Kei Okada.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * * Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * * Redistributions in binary form must reproduce the above
14 * copyright notice, this list of conditions and the following
15 * disclaimer in the documentation and/or other materials provided
16 * with the distribution.
17 * * Neither the name of the Kei Okada nor the names of its
18 * contributors may be used to endorse or promote products derived
19 * from this software without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 * POSSIBILITY OF SUCH DAMAGE.
33 *********************************************************************/
34 
35 // https://github.com/Itseez/opencv/blob/2.4/samples/cpp/lk_demo.cpp
40 #include <ros/ros.h>
41 #include "opencv_apps/nodelet.h"
44 #include <cv_bridge/cv_bridge.h>
46 
47 #include <opencv2/highgui/highgui.hpp>
48 #include <opencv2/imgproc/imgproc.hpp>
49 #include <opencv2/video/tracking.hpp>
50 
51 #include <dynamic_reconfigure/server.h>
52 #include "std_srvs/Empty.h"
53 #include "opencv_apps/LKFlowConfig.h"
54 #include "opencv_apps/FlowArrayStamped.h"
55 
56 namespace opencv_apps
57 {
58 class LKFlowNodelet : public opencv_apps::Nodelet
59 {
67 
69 
70  typedef opencv_apps::LKFlowConfig Config;
71  typedef dynamic_reconfigure::Server<Config> ReconfigureServer;
74 
75  int queue_size_;
76  bool debug_view_;
78 
79  std::string window_name_;
80  static bool need_config_update_;
81 
82  int MAX_COUNT;
83  bool needToInit;
84  bool nightMode;
85  cv::Point2f point;
86  bool addRemovePt;
87  cv::Mat gray, prevGray;
88  std::vector<cv::Point2f> points[2];
89 
91  int min_distance_;
92  int block_size_;
93  float harris_k_;
94 
95  void reconfigureCallback(Config& new_config, uint32_t level)
96  {
97  config_ = new_config;
98  quality_level_ = config_.quality_level;
99  min_distance_ = config_.min_distance;
100  block_size_ = config_.block_size;
101  harris_k_ = config_.harris_k;
102  }
103 
104  const std::string& frameWithDefault(const std::string& frame, const std::string& image_frame)
105  {
106  if (frame.empty())
107  return image_frame;
108  return frame;
109  }
110 
111  void imageCallbackWithInfo(const sensor_msgs::ImageConstPtr& msg, const sensor_msgs::CameraInfoConstPtr& cam_info)
112  {
113  doWork(msg, cam_info->header.frame_id);
114  }
115 
116  void imageCallback(const sensor_msgs::ImageConstPtr& msg)
117  {
118  doWork(msg, msg->header.frame_id);
119  }
120 #if 0
121  static void onMouse( int event, int x, int y, int /*flags*/, void* /*param*/ )
122  {
123  if( event == CV_EVENT_LBUTTONDOWN )
124  {
125  point = Point2f((float)x, (float)y);
126  addRemovePt = true;
127  }
128  }
129 #endif
130  static void trackbarCallback(int /*unused*/, void* /*unused*/)
131  {
133  }
134 
135  void doWork(const sensor_msgs::ImageConstPtr& msg, const std::string& input_frame_from_msg)
136  {
137  // Work on the image.
138  try
139  {
140  // Convert the image into something opencv can handle.
142 
143  // Messages
144  opencv_apps::FlowArrayStamped flows_msg;
145  flows_msg.header = msg->header;
146 
148  {
150  cv::namedWindow(window_name_, cv::WINDOW_AUTOSIZE);
151 
152  cv::createTrackbar("Min Distance", window_name_, &min_distance_, 100, trackbarCallback);
153  cv::createTrackbar("Block Size", window_name_, &block_size_, 100, trackbarCallback);
154 
155  // cv::setMouseCallback( window_name_, onMouse, 0 );
157  {
158  reconfigure_server_->updateConfig(config_);
159  config_.quality_level = quality_level_;
160  config_.min_distance = min_distance_;
161  config_.block_size = block_size_;
162  config_.harris_k = harris_k_;
163  need_config_update_ = false;
164  }
165  }
166 
167  // Do the work
168  cv::TermCriteria termcrit(cv::TermCriteria::MAX_ITER + cv::TermCriteria::EPS, 20, 0.03);
169  cv::Size sub_pix_win_size(10, 10), win_size(31, 31);
170 
171  if (image.channels() > 1)
172  {
173  cv::cvtColor(image, gray, cv::COLOR_BGR2GRAY);
174  }
175  else
176  {
177  image.copyTo(gray);
178  }
179 
181  image = cv::Scalar::all(0);
182 
183  if (needToInit)
184  {
185  // automatic initialization
186  cv::goodFeaturesToTrack(gray, points[1], MAX_COUNT, quality_level_, min_distance_, cv::Mat(), block_size_,
187  false, harris_k_);
188  cv::cornerSubPix(gray, points[1], sub_pix_win_size, cv::Size(-1, -1), termcrit);
189  addRemovePt = false;
190  }
191  else if (!points[0].empty())
192  {
193  std::vector<uchar> status;
194  std::vector<float> err;
195  if (prevGray.empty())
196  gray.copyTo(prevGray);
197  cv::calcOpticalFlowPyrLK(prevGray, gray, points[0], points[1], status, err, win_size, 3, termcrit, 0, 0.001);
198  size_t i;
199  for (i = 0; i < points[1].size(); i++)
200  {
201  if (addRemovePt)
202  {
203  if (cv::norm(point - points[1][i]) <= 5)
204  {
205  addRemovePt = false;
206  continue;
207  }
208  }
209 
210  opencv_apps::Flow flow_msg;
211  opencv_apps::Point2D point_msg;
212  opencv_apps::Point2D velocity_msg;
213  if (status[i])
214  {
215  cv::circle(image, points[1][i], 3, cv::Scalar(0, 255, 0), -1, 8);
216  cv::line(image, points[1][i], points[0][i], cv::Scalar(0, 255, 0), 1, 8, 0);
217  point_msg.x = points[1][i].x;
218  point_msg.y = points[1][i].y;
219  velocity_msg.x = points[1][i].x - points[0][i].x;
220  velocity_msg.y = points[1][i].y - points[0][i].y;
221  }
222  flow_msg.point = point_msg;
223  flow_msg.velocity = velocity_msg;
224  flows_msg.status.push_back(status[i]);
225  flows_msg.flow.push_back(flow_msg);
226  }
227  }
228 
229  if (addRemovePt && points[1].size() < (size_t)MAX_COUNT)
230  {
231  std::vector<cv::Point2f> tmp;
232  tmp.push_back(point);
233  cv::cornerSubPix(gray, tmp, win_size, cv::Size(-1, -1), termcrit);
234  points[1].push_back(tmp[0]);
235  addRemovePt = false;
236  }
237 
238  needToInit = false;
239  if (debug_view_)
240  {
241  cv::imshow(window_name_, image);
242 
243  char c = (char)cv::waitKey(1);
244  // if( c == 27 )
245  // break;
246  switch (c)
247  {
248  case 'r':
249  needToInit = true;
250  break;
251  case 'c':
252  points[0].clear();
253  points[1].clear();
254  break;
255  case 'n':
256  nightMode = !nightMode;
257  break;
258  }
259  }
260  std::swap(points[1], points[0]);
261  cv::swap(prevGray, gray);
262 
263  // Publish the image.
264  sensor_msgs::Image::Ptr out_img = cv_bridge::CvImage(msg->header, "bgr8", image).toImageMsg();
265  img_pub_.publish(out_img);
266  msg_pub_.publish(flows_msg);
267  }
268  catch (cv::Exception& e)
269  {
270  NODELET_ERROR("Image processing error: %s %s %s %i", e.err.c_str(), e.func.c_str(), e.file.c_str(), e.line);
271  }
272 
273  prev_stamp_ = msg->header.stamp;
274  }
275 
276  bool initializePointsCb(std_srvs::Empty::Request& request, std_srvs::Empty::Response& response)
277  {
278  needToInit = true;
279  return true;
280  }
281 
282  bool deletePointsCb(std_srvs::Empty::Request& request, std_srvs::Empty::Response& response)
283  {
284  points[0].clear();
285  points[1].clear();
286  return true;
287  }
288 
289  bool toggleNightModeCb(std_srvs::Empty::Request& request, std_srvs::Empty::Response& response)
290  {
291  nightMode = !nightMode;
292  return true;
293  }
294 
295  void subscribe() // NOLINT(modernize-use-override)
296  {
297  NODELET_DEBUG("Subscribing to image topic.");
298  if (config_.use_camera_info)
299  cam_sub_ = it_->subscribeCamera("image", queue_size_, &LKFlowNodelet::imageCallbackWithInfo, this);
300  else
301  img_sub_ = it_->subscribe("image", queue_size_, &LKFlowNodelet::imageCallback, this);
302  }
303 
304  void unsubscribe() // NOLINT(modernize-use-override)
305  {
306  NODELET_DEBUG("Unsubscribing from image topic.");
307  img_sub_.shutdown();
308  cam_sub_.shutdown();
309  }
310 
311 public:
312  virtual void onInit() // NOLINT(modernize-use-override)
313  {
314  Nodelet::onInit();
316 
317  pnh_->param("queue_size", queue_size_, 3);
318  pnh_->param("debug_view", debug_view_, false);
319  if (debug_view_)
320  {
321  always_subscribe_ = true;
322  }
323  prev_stamp_ = ros::Time(0, 0);
324 
325  window_name_ = "LK Demo";
326  MAX_COUNT = 500;
327  needToInit = true;
328  nightMode = false;
329  addRemovePt = false;
330 
331  reconfigure_server_ = boost::make_shared<dynamic_reconfigure::Server<Config> >(*pnh_);
332  dynamic_reconfigure::Server<Config>::CallbackType f =
334  reconfigure_server_->setCallback(f);
335 
336  img_pub_ = advertiseImage(*pnh_, "image", 1);
337  msg_pub_ = advertise<opencv_apps::FlowArrayStamped>(*pnh_, "flows", 1);
338  initialize_points_service_ = pnh_->advertiseService("initialize_points", &LKFlowNodelet::initializePointsCb, this);
339  delete_points_service_ = pnh_->advertiseService("delete_points", &LKFlowNodelet::deletePointsCb, this);
340  toggle_night_mode_service_ = pnh_->advertiseService("toggle_night_mode", &LKFlowNodelet::toggleNightModeCb, this);
341 
342  NODELET_INFO("Hot keys: ");
343  NODELET_INFO("\tESC - quit the program");
344  NODELET_INFO("\tr - auto-initialize tracking");
345  NODELET_INFO("\tc - delete all the points");
346  NODELET_INFO("\tn - switch the \"night\" mode on/off");
347  // NODELET_INFO("To add/remove a feature point click it");
348 
350  }
351 };
353 } // namespace opencv_apps
354 
355 namespace lk_flow
356 {
358 {
359 public:
360  virtual void onInit() // NOLINT(modernize-use-override)
361  {
362  ROS_WARN("DeprecationWarning: Nodelet lk_flow/lk_flow is deprecated, "
363  "and renamed to opencv_apps/lk_flow.");
365  }
366 };
367 } // namespace lk_flow
368 
369 #ifdef USE_PLUGINLIB_CLASS_LIST_MACROS_H
371 #else
373 #endif
opencv_apps::LKFlowNodelet::cam_sub_
image_transport::CameraSubscriber cam_sub_
Definition: lk_flow_nodelet.cpp:126
lk_flow
Definition: lk_flow_nodelet.cpp:355
nodelet.h
opencv_apps::LKFlowNodelet::prevGray
cv::Mat prevGray
Definition: lk_flow_nodelet.cpp:151
NODELET_ERROR
#define NODELET_ERROR(...)
opencv_apps::LKFlowNodelet::trackbarCallback
static void trackbarCallback(int, void *)
Definition: lk_flow_nodelet.cpp:194
PLUGINLIB_EXPORT_CLASS
PLUGINLIB_EXPORT_CLASS(opencv_apps::LKFlowNodelet, nodelet::Nodelet)
opencv_apps::Nodelet::onInitPostProcess
virtual void onInitPostProcess()
Post processing of initialization of nodelet. You need to call this method in order to use always_sub...
Definition: nodelet.cpp:77
ros::Publisher
image_encodings.h
image_transport::ImageTransport
opencv_apps::LKFlowNodelet::img_sub_
image_transport::Subscriber img_sub_
Definition: lk_flow_nodelet.cpp:125
boost::shared_ptr< image_transport::ImageTransport >
cv_bridge::CvImage::toImageMsg
sensor_msgs::ImagePtr toImageMsg() const
lk_flow::LKFlowNodelet::onInit
virtual void onInit()
Initialize nodehandles nh_ and pnh_. Subclass should call this method in its onInit method.
Definition: lk_flow_nodelet.cpp:360
lk_flow::LKFlowNodelet
Definition: lk_flow_nodelet.cpp:357
ros.h
opencv_apps::LKFlowNodelet::toggleNightModeCb
bool toggleNightModeCb(std_srvs::Empty::Request &request, std_srvs::Empty::Response &response)
Definition: lk_flow_nodelet.cpp:353
opencv_apps::LKFlowNodelet::reconfigure_server_
boost::shared_ptr< ReconfigureServer > reconfigure_server_
Definition: lk_flow_nodelet.cpp:137
opencv_apps::LKFlowNodelet::harris_k_
float harris_k_
Definition: lk_flow_nodelet.cpp:157
boost::placeholders::_1
boost::arg< 1 > _1
Definition: nodelet.cpp:44
opencv_apps::LKFlowNodelet::initializePointsCb
bool initializePointsCb(std_srvs::Empty::Request &request, std_srvs::Empty::Response &response)
Definition: lk_flow_nodelet.cpp:340
opencv_apps::Nodelet
Nodelet to automatically subscribe/unsubscribe topics according to subscription of advertised topics.
Definition: nodelet.h:90
opencv_apps::LKFlowNodelet::initialize_points_service_
ros::ServiceServer initialize_points_service_
Definition: lk_flow_nodelet.cpp:128
opencv_apps::LKFlowNodelet::needToInit
bool needToInit
Definition: lk_flow_nodelet.cpp:147
opencv_apps::LKFlowNodelet::imageCallbackWithInfo
void imageCallbackWithInfo(const sensor_msgs::ImageConstPtr &msg, const sensor_msgs::CameraInfoConstPtr &cam_info)
Definition: lk_flow_nodelet.cpp:175
opencv_apps::LKFlowNodelet::quality_level_
float quality_level_
Definition: lk_flow_nodelet.cpp:154
opencv_apps::LKFlowNodelet::Config
opencv_apps::LKFlowConfig Config
Definition: lk_flow_nodelet.cpp:134
opencv_apps::LKFlowNodelet::reconfigureCallback
void reconfigureCallback(Config &new_config, uint32_t level)
Definition: lk_flow_nodelet.cpp:159
opencv_apps::LKFlowNodelet
Definition: lk_flow_nodelet.cpp:90
opencv_apps::LKFlowNodelet::need_config_update_
static bool need_config_update_
Definition: lk_flow_nodelet.cpp:144
ros::Publisher::publish
void publish(const boost::shared_ptr< M > &message) const
opencv_apps::LKFlowNodelet::block_size_
int block_size_
Definition: lk_flow_nodelet.cpp:156
f
f
opencv_apps::LKFlowNodelet::min_distance_
int min_distance_
Definition: lk_flow_nodelet.cpp:155
ros::ServiceServer
opencv_apps::LKFlowNodelet::img_pub_
image_transport::Publisher img_pub_
Definition: lk_flow_nodelet.cpp:124
image_transport::Subscriber
opencv_apps::Nodelet::advertiseImage
image_transport::Publisher advertiseImage(ros::NodeHandle &nh, const std::string &topic, int queue_size)
Advertise an image topic and watch the publisher. Publishers which are created by this method....
Definition: nodelet.h:201
class_list_macros.h
opencv_apps::LKFlowNodelet::gray
cv::Mat gray
Definition: lk_flow_nodelet.cpp:151
opencv_apps::LKFlowNodelet::window_name_
std::string window_name_
Definition: lk_flow_nodelet.cpp:143
image_transport::CameraSubscriber
opencv_apps::LKFlowNodelet::msg_pub_
ros::Publisher msg_pub_
Definition: lk_flow_nodelet.cpp:127
image_transport::CameraSubscriber::shutdown
void shutdown()
opencv_apps::LKFlowNodelet::deletePointsCb
bool deletePointsCb(std_srvs::Empty::Request &request, std_srvs::Empty::Response &response)
Definition: lk_flow_nodelet.cpp:346
opencv_apps::LKFlowNodelet::addRemovePt
bool addRemovePt
Definition: lk_flow_nodelet.cpp:150
boost::placeholders::_2
boost::arg< 2 > _2
Definition: nodelet.cpp:45
opencv_apps::LKFlowNodelet::config_
Config config_
Definition: lk_flow_nodelet.cpp:136
opencv_apps::Nodelet::always_subscribe_
bool always_subscribe_
A flag to disable watching mechanism and always subscribe input topics. It can be specified via ~alwa...
Definition: nodelet.h:300
ROS_WARN
#define ROS_WARN(...)
opencv_apps::LKFlowNodelet::doWork
void doWork(const sensor_msgs::ImageConstPtr &msg, const std::string &input_frame_from_msg)
Definition: lk_flow_nodelet.cpp:199
image_transport::Publisher::publish
void publish(const sensor_msgs::Image &message) const
opencv_apps
Demo code to calculate moments.
Definition: nodelet.h:68
opencv_apps::LKFlowNodelet::it_
boost::shared_ptr< image_transport::ImageTransport > it_
Definition: lk_flow_nodelet.cpp:132
opencv_apps::LKFlowNodelet::nightMode
bool nightMode
Definition: lk_flow_nodelet.cpp:148
image_transport.h
opencv_apps::LKFlowNodelet::imageCallback
void imageCallback(const sensor_msgs::ImageConstPtr &msg)
Definition: lk_flow_nodelet.cpp:180
opencv_apps::LKFlowNodelet::MAX_COUNT
int MAX_COUNT
Definition: lk_flow_nodelet.cpp:146
NODELET_INFO
#define NODELET_INFO(...)
opencv_apps::LKFlowNodelet::subscribe
void subscribe()
This method is called when publisher is subscribed by other nodes. Set up subscribers in this method.
Definition: lk_flow_nodelet.cpp:359
opencv_apps::LKFlowNodelet::frameWithDefault
const std::string & frameWithDefault(const std::string &frame, const std::string &image_frame)
Definition: lk_flow_nodelet.cpp:168
nodelet::Nodelet
ros::Time
opencv_apps::LKFlowNodelet::point
cv::Point2f point
Definition: lk_flow_nodelet.cpp:149
image_transport::Publisher
cv_bridge.h
opencv_apps::LKFlowNodelet::ReconfigureServer
dynamic_reconfigure::Server< Config > ReconfigureServer
Definition: lk_flow_nodelet.cpp:135
cv_bridge::CvImage
class_list_macros.hpp
opencv_apps::LKFlowNodelet::toggle_night_mode_service_
ros::ServiceServer toggle_night_mode_service_
Definition: lk_flow_nodelet.cpp:130
opencv_apps::LKFlowNodelet::queue_size_
int queue_size_
Definition: lk_flow_nodelet.cpp:139
sensor_msgs::image_encodings::BGR8
const std::string BGR8
opencv_apps::LKFlowNodelet::delete_points_service_
ros::ServiceServer delete_points_service_
Definition: lk_flow_nodelet.cpp:129
opencv_apps::LKFlowNodelet::onInit
virtual void onInit()
Initialize nodehandles nh_ and pnh_. Subclass should call this method in its onInit method.
Definition: lk_flow_nodelet.cpp:376
cv_bridge::toCvShare
CvImageConstPtr toCvShare(const sensor_msgs::Image &source, const boost::shared_ptr< void const > &tracked_object, const std::string &encoding=std::string())
opencv_apps::LKFlowNodelet::unsubscribe
void unsubscribe()
This method is called when publisher is unsubscribed by other nodes. Shut down subscribers in this me...
Definition: lk_flow_nodelet.cpp:368
opencv_apps::LKFlowNodelet::prev_stamp_
ros::Time prev_stamp_
Definition: lk_flow_nodelet.cpp:141
opencv_apps::Nodelet::nh_
boost::shared_ptr< ros::NodeHandle > nh_
Shared pointer to nodehandle.
Definition: nodelet.h:272
opencv_apps::LKFlowNodelet::debug_view_
bool debug_view_
Definition: lk_flow_nodelet.cpp:140
opencv_apps::LKFlowNodelet::points
std::vector< cv::Point2f > points[2]
Definition: lk_flow_nodelet.cpp:152
opencv_apps::Nodelet::onInit
virtual void onInit()
Initialize nodehandles nh_ and pnh_. Subclass should call this method in its onInit method.
Definition: nodelet.cpp:60
opencv_apps::Nodelet::pnh_
boost::shared_ptr< ros::NodeHandle > pnh_
Shared pointer to private nodehandle.
Definition: nodelet.h:277
NODELET_DEBUG
#define NODELET_DEBUG(...)
image_transport::Subscriber::shutdown
void shutdown()


opencv_apps
Author(s): Kei Okada
autogenerated on Fri May 23 2025 02:49:49