color_histogram_sliding_matcher.cpp
Go to the documentation of this file.
1 #include <ros/ros.h>
2 #include <opencv2/opencv.hpp>
4 #include <rospack/rospack.h>
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <cv_bridge/cv_bridge.h>
11 #include <boost/thread/mutex.hpp>
12 #include <dynamic_reconfigure/server.h>
14 #include <sensor_msgs/Image.h>
15 #include <sensor_msgs/CameraInfo.h>
16 #include <geometry_msgs/PolygonStamped.h>
17 #include <geometry_msgs/PointStamped.h>
18 #include <jsk_recognition_msgs/Rect.h>
19 #include <jsk_perception/ColorHistogramSlidingMatcherConfig.h>
20 #include <jsk_recognition_msgs/BoundingBoxArray.h>
25 
26 using namespace std;
27 using namespace ros;
28 
30 {
32  boost::mutex _mutex;
38 
40  ros::Publisher _pubBestPolygon; //for jsk_pcl_ros/src/pointcloud_screenpoint_nodelet.cpp
44  std::vector< cv::Mat > template_images;
45  std::vector< std::vector<unsigned int> > template_vecs;
46  std::vector< std::vector< std::vector<unsigned int> > >hsv_integral;
47  int standard_height, standard_width;
56  inline double calc_coe(std::vector< unsigned int > &a, std::vector<unsigned int> &b){
57  unsigned long sum_a=0, sum_b=0;
58  double coe=0;
59  for(int k=0, size=a.size(); k<size; ++k){
60  sum_a += a[k]; sum_b+= b[k];
61  }
62  for(int k=0; k<256; ++k){
63  coe +=std::min((double)a[k]/sum_a, (double)b[k]/sum_b);
64  }
65  return coe;
66  }
67  typedef struct BOX{
68  int x, y, dx, dy;
69  double coefficient;
70  BOX(int _x, int _y, int _dx, int _dy){
71  x=_x, y=_y, dx=_dx, dy=_dy;
72  }
73  BOX(int _x, int _y, int _dx, int _dy, double _coefficient){
74  x=_x, y=_y, dx=_dx, dy=_dy, coefficient = _coefficient;
75  }
76  } box;
77  inline bool point_in_box(int x, int y, box a_box){
78  return a_box.x <= x && a_box.x+a_box.dx >= x && a_box.y <= y && a_box.y+a_box.dy >= y;
79  }
80  inline bool in_box(int x, int y, int dx, int dy, box a_box){
81  return point_in_box(x, y, a_box) || point_in_box(x+dx, y, a_box) || point_in_box(x, y+dy, a_box) || point_in_box(x+dx, y+dy, a_box);
82  }
83  inline bool in_boxes(int x, int y, int dx, int dy, std::vector<box> boxes){
84  for (int i=0; i<boxes.size(); ++i){
85  if(in_box(x, y, dx, dy, boxes[i])){
86  return true;
87  }
88  }
89  return false;
90  }
91 public:
92  MatcherNode() : _it(_node)
93  , _subImage( _it, "image", 1)
94  , _subInfo(_node, "camera_info", 1)
95  , sync( SyncPolicy(10), _subImage, _subInfo)
96  {
97  _pubBestRect = _node.advertise< jsk_recognition_msgs::Rect >("best_rect", 1);
98  _pubBestPolygon = _node.advertise< geometry_msgs::PolygonStamped >("best_polygon", 1);
99  _pubBestPoint = _node.advertise< geometry_msgs::PointStamped >("rough_point", 1);
100  _pubBestBoundingBox = _node.advertise< jsk_recognition_msgs::BoundingBoxArray >("best_box", 1);
101  _debug_pub = _it.advertise("debug_image", 1);
102  // _subImage = _it.subscribe("image", 1, &MatcherNode::image_cb, this);
103 
104  sync.registerCallback( boost::bind (&MatcherNode::image_cb , this, _1, _2) );
105 
106 
107  ros::NodeHandle local_nh("~");
108  std::string template_filename;
109  std::string default_template_file_name;
110  try {
111 #ifdef ROSPACK_EXPORT
113  rospack::Package *p = rp.get_pkg("jsk_perception");
114  if (p!=NULL) default_template_file_name = p->path + std::string("/sample/opencv-logo2.png");
115 #else
116  rospack::Rospack rp;
117  std::vector<std::string> search_path;
118  rp.getSearchPathFromEnv(search_path);
119  rp.crawl(search_path, 1);
120  std::string path;
121  if (rp.find("jsk_perception",path)==true) default_template_file_name = path + std::string("/sample/opencv-logo2.png");
122 #endif
123  } catch (std::runtime_error &e) {}
124  local_nh.param("template_filename", template_filename, default_template_file_name);
125  local_nh.param("standard_width", standard_width, 12);
126  local_nh.param("standard_height", standard_height, 12);
127  local_nh.param("best_window_estimation_method", best_window_estimation_method, 1);
128  local_nh.param("coefficient_threshold", coefficient_thre, 0.5);
129  local_nh.param("coefficient_threshold_for_best_window", coefficient_thre_for_best_window, 0.67);
130  local_nh.param("sliding_factor", sliding_factor, 1.0);
131  local_nh.param("show_result", show_result_, true);
132  local_nh.param("pub_debug_image", pub_debug_image_, true);
133  local_nh.param("object_width", template_width, 0.06);
134  local_nh.param("object_height", template_height, 0.0739);
135 
136  cv::Mat template_image=cv::imread(template_filename);
137  if(template_add(template_image))template_images.push_back(template_image);
138  }
139  bool template_add(cv::Mat template_image){
140  if(template_image.cols==0){
141  ROS_INFO("template_error size==0");
142  return false;
143  }
144  cv::Mat template_hsv_image;
145  cv::cvtColor(template_image, template_hsv_image, CV_BGR2HSV);
146  std::vector<unsigned int> template_vec(256);
147  for (int k=0; k<256; ++k){
148  template_vec[k]=0;
149  }
150  for (int i = 0; i<template_image.rows; ++i){
151  for (int j = 0; j<template_image.cols; ++j) {
152  uchar h=template_hsv_image.data[i*template_hsv_image.step+j*3+0];
153  uchar s=template_hsv_image.data[i*template_hsv_image.step+j*3+1];
154  int index_h=0;
155  int index_s=0;
156  while(h>15)
157  {
158  h-=16;++index_h;
159  }
160  while(s>15)
161  {
162  s-=16;++index_s;
163  }
164  ++template_vec[index_h*16 + index_s];
165  }
166  }
167  template_vecs.push_back(template_vec);
168  ROS_INFO("template vec was set successfully");
169  return true;
170  }
171 
172  void image_cb(const sensor_msgs::ImageConstPtr& msg_ptr, const sensor_msgs::CameraInfoConstPtr& info_ptr)
173  {
174  boost::mutex::scoped_lock lock(_mutex);
175  if(template_vecs.size()==0){
176  ROS_INFO("template vec is empty");
177  return;
178  }
179  cv_bridge::CvImagePtr cv_ptr;
180  try{
181  cv_ptr = cv_bridge::toCvCopy(msg_ptr, "bgr8");
182  }
183  catch (cv_bridge::Exception& e){
184  ROS_ERROR("cv_bridge exception: %s", e.what());
185  return;
186  }
187  cv::Mat image=cv_ptr->image.clone();
188  ROS_INFO("mat made");
189  if(hsv_integral.size()==0||(image.cols!=hsv_integral.size() && image.rows!=hsv_integral[0].size())){
190  ROS_INFO("before memory size was changed");
191  hsv_integral.resize(image.cols);
192  for(int i=0; i<image.cols; ++i){
193  hsv_integral[i].resize(image.rows);
194  for(int j=0; j<image.rows; ++j){
195  hsv_integral[i][j].resize(255);
196  }
197  }
198  ROS_INFO("memory size was changed");
199  }
200  cv::Mat hsv_image;
201  cv::cvtColor(image, hsv_image, CV_BGR2HSV);
202  unsigned char hsv_mat[image.cols][image.rows];
203  for (size_t i = 0, image_rows=image.rows; i<image_rows; ++i){
204  for (size_t j = 0, image_cols=image.cols; j<image_cols; ++j) {
205  uchar h=hsv_image.data[i*hsv_image.step+j*3+0];
206  uchar s=hsv_image.data[i*hsv_image.step+j*3+1];
207  int index_h=0;
208  int index_s=0;
209  while(h>15)
210  {
211  h-=16;++index_h;
212  }
213  while(s>15)
214  {
215  s-=16;++index_s;
216  }
217  hsv_mat[j][i] = index_h*16+index_s;
218  }
219  }
220 
221 #ifdef _OPENMP
222 #pragma omp parallel for
223 #endif
224  for (size_t k =0; k<16*16; ++k){
225  if (k == hsv_mat[0][0]){
226  hsv_integral[0][0][k]=1;
227  }
228  else{
229  hsv_integral[0][0][k]=0;
230  }
231  for (size_t j=1, image_cols=image.cols; j<image_cols; ++j){
232  if(k == hsv_mat[j][0]){
233  hsv_integral[j][0][k] = hsv_integral[j-1][0][k]+1;
234  }
235  else{
236  hsv_integral[j][0][k] = hsv_integral[j-1][0][k];
237  }
238  }
239  for (int i=1,image_rows=image.rows; i<image_rows; ++i){
240  unsigned int s=0;
241  for (int j=0, image_cols=image.cols; j<image_cols; ++j){
242  if(k == hsv_mat[j][i]){
243  ++s;
244  }
245  hsv_integral[j][i][k] = hsv_integral[j][i-1][k]+ s;
246  }
247  }
248  }
249  ROS_INFO("integral histogram made");
250  for(size_t template_index=0; template_index<template_vecs.size(); ++template_index){
251  std::vector<unsigned int> template_vec = template_vecs[template_index];
252  double max_coe = 0;
253  int index_i=-1, index_j=-1 ,index_width_d, index_height_d;
254  float max_scale=-1;
255  // for(float scale=1; scale<image.cols/32; scale*=1.2){
256  std::vector<box> boxes;
257  for(float scale=image.cols/32; scale > 1; scale/=1.2){
258  int dot = (int)(scale*2*sliding_factor);
259  if(dot<1) dot = 1;
260  int width_d = (int)(scale*standard_width);
261  int height_d = (int)(scale*standard_height);
262  for(size_t i=0, image_rows=image.rows; i+height_d < image_rows; i+=dot){
263  for(size_t j=0, image_cols=image.cols; j+width_d < image_cols; j+=dot){
264  //if in box continue;
265  std::vector <unsigned int> vec(256);
266  for(size_t k=0; k<256; ++k){
267  vec[k] = hsv_integral[j+width_d][i+height_d][k]
268  - hsv_integral[j][i+height_d][k]
269  - hsv_integral[j+width_d][i][k]
270  + hsv_integral[j][i][k];
271  }
272  double temp_coe = calc_coe(template_vec, vec);
273  //if larger than 0.7
274  if(temp_coe > coefficient_thre){
275  boxes.push_back(box(j, i, width_d, height_d, temp_coe));
276 
277  if (temp_coe > max_coe){
278  max_coe = temp_coe;
279  index_i=i; index_j=j; max_scale = scale;
280  index_width_d=width_d; index_height_d=height_d;
281  }
282  }
283  }
284  }
285  }
286  ROS_INFO("max_coefficient:%f", max_coe);
287  if(boxes.size() == 0 || max_coe < coefficient_thre_for_best_window){
288  ROS_INFO("no objects found");
289  if(show_result_){
290  cv::imshow("result", image);
291  cv::imshow("template", template_images[template_index]);
292  cv::waitKey(20);
293  }
294  if(pub_debug_image_){
295  cv_bridge::CvImage out_msg;
296  out_msg.header = msg_ptr->header;
297  out_msg.encoding = "bgr8";
298  out_msg.image = image;
299  _debug_pub.publish(out_msg.toImageMsg());
300  }
301  return;
302  }
303  if(best_window_estimation_method==0){
304  }
305  //expand box
306  if(best_window_estimation_method==1){
307  box best_large_box(index_j, index_i, index_width_d, index_height_d, 0);
308  max_coe = 0;
309  int temp_width = 0;
310  bool large_found=false;
311  for(size_t i=0; i<boxes.size(); ++i){
312  if(temp_width!=boxes[i].dx){
313  if(large_found) break;
314  temp_width = boxes[i].dx;
315  }
316  if(in_box(index_j, index_i, index_width_d, index_height_d, boxes[i])){
317  large_found=true;
318  if(max_coe < boxes[i].coefficient){
319  best_large_box = boxes[i];
320  max_coe = boxes[i].coefficient;
321  }
322  }
323  }
324  index_j = best_large_box.x; index_i = best_large_box.y;
325  max_scale = best_large_box.dx/standard_width;
326  }
327  if(best_window_estimation_method==2){
328  max_coe = 0;
329  std::vector<box> boxes_temp;
330  for(size_t i=0; i<boxes.size(); ++i){
331  box box_temp = boxes[i];
332  if(in_boxes(box_temp.x, box_temp.y, box_temp.dx, box_temp.dy, boxes_temp)){
333  continue;
334  }
335  boxes_temp.push_back(box_temp);
336  if(box_temp.coefficient > max_coe){
337  max_coe = box_temp.coefficient;
338  index_j = box_temp.x, index_j=box_temp.y;
339  max_scale = box_temp.dx/standard_width;
340  }
341  }
342  }
343  // best result;
344 
345  cv::rectangle(image, cv::Point(index_j, index_i), cv::Point(index_j+(max_scale*standard_width), index_i+(max_scale*standard_height)), cv::Scalar(0, 0, 200), 3, 4);
346  jsk_recognition_msgs::Rect rect;
347  rect.x=index_j, rect.y=index_i, rect.width=(int)(max_scale*standard_width); rect.height=(int)(max_scale*standard_height);
348  _pubBestRect.publish(rect);
349  geometry_msgs::PolygonStamped polygon_msg;
350  polygon_msg.header = msg_ptr->header;
351  polygon_msg.polygon.points.resize(2);
352  polygon_msg.polygon.points[0].x=index_j;
353  polygon_msg.polygon.points[0].y=index_i;
354  polygon_msg.polygon.points[1].x=(int)(max_scale*standard_width)+index_j;
355  polygon_msg.polygon.points[1].y=(int)(max_scale*standard_height)+index_i;
356  _pubBestPolygon.publish(polygon_msg);
357  //calc position from 2D camera model
359  pcam.fromCameraInfo(*info_ptr);
360  cv::Point2f corners2d[4] = {cv::Point2f(index_j, index_i),
361  cv::Point2f((int)(max_scale*standard_width)+index_j, index_i),
362  cv::Point2f((int)(max_scale*standard_width)+index_j, (int)(max_scale*standard_height)+index_i),
363  cv::Point2f(index_j, (int)(max_scale*standard_height)+index_i)
364  };
365  cv::Mat corners2d_mat(cv::Size(4, 1), CV_32FC2, corners2d);
366  cv::Point3f corners3d[4] = {cv::Point3f(-template_height/2,-template_width/2,0),
367  cv::Point3f(-template_height/2,template_width/2,0),
368  cv::Point3f(template_height/2,template_width/2,0),
369  cv::Point3f(template_height/2,-template_width/2,0)
370  };
371  cv::Mat corners3d_mat (cv::Size(4, 1), CV_32FC3, corners3d);
372  double fR3[3], fT3[3];
373  cv::Mat rvec(3, 1, CV_64FC1, fR3);
374  cv::Mat tvec(3, 1, CV_64FC1, fT3);
375  cv::Mat zero_distortion_mat = cv::Mat::zeros(4, 1, CV_64FC1);
376 
377  cv::solvePnP (corners3d_mat, corners2d_mat,
378  pcam.intrinsicMatrix(),
379  zero_distortion_mat,//if unrectified: pcam.distortionCoeffs()
380  rvec, tvec);
381  geometry_msgs::PointStamped best_point;
382  best_point.point.x = fT3[0];
383  best_point.point.y = fT3[1];
384  best_point.point.z = fT3[2];
385  best_point.header = msg_ptr->header;
386  _pubBestPoint.publish(best_point);
387  jsk_recognition_msgs::BoundingBoxArray box_array_msg;
388  jsk_recognition_msgs::BoundingBox box_msg;
389  box_array_msg.header = box_msg.header = msg_ptr->header;
390  box_msg.pose.position = best_point.point;
391  box_msg.pose.orientation.x = box_msg.pose.orientation.y = box_msg.pose.orientation.z = 0;
392  box_msg.pose.orientation.w = 1;
393  box_msg.dimensions.x = box_msg.dimensions.z = template_width;
394  box_msg.dimensions.y = template_height;
395  box_array_msg.boxes.push_back(box_msg);
396  _pubBestBoundingBox.publish(box_array_msg);
397  //cv::solvePnP ();
398  if(show_result_){
399  cv::imshow("result", image);
400  cv::imshow("template", template_images[template_index]);
401  cv::waitKey(20);
402  }
403  if(pub_debug_image_){
404  cv_bridge::CvImage out_msg;
405  out_msg.header = msg_ptr->header;
406  out_msg.encoding = "bgr8";
407  out_msg.image = image;
408  _debug_pub.publish(out_msg.toImageMsg());
409  }
410  }
411  }
412  void dyn_conf_callback(jsk_perception::ColorHistogramSlidingMatcherConfig &config, uint32_t level){
413  boost::mutex::scoped_lock lock(_mutex);
414  standard_height = config.standard_height;
415  standard_width = config.standard_width;
416  coefficient_thre = config.coefficient_threshold;
417  sliding_factor = config.sliding_factor;
418  coefficient_thre_for_best_window = config.coefficient_threshold_for_best_window;
419  best_window_estimation_method = config.best_window_estimation_method;
420  if(level==1){ // size changed
421  }
422  }
423 };
424 
425 inline double calc_coe(unsigned int * a, unsigned int* b){
426  unsigned long sum_a=0, sum_b=0;
427  double coe=0;
428  for(int k=0; k<256; ++k){
429  sum_a += a[k]; sum_b+= b[k];
430  }
431  for(int k=0; k<256; ++k){
432  coe +=std::min((double)a[k]/sum_a, (double)b[k]/sum_b);
433  }
434  return coe;
435 }
436 
437 
438 int main(int argc, char **argv){
439  ros::init (argc, argv, "ColorHistogramSlidingMatcher");
440 
441  MatcherNode matcher;
442 
443  dynamic_reconfigure::Server<jsk_perception::ColorHistogramSlidingMatcherConfig> server;
444  dynamic_reconfigure::Server<jsk_perception::ColorHistogramSlidingMatcherConfig>::CallbackType f;
445  f = boost::bind(&MatcherNode::dyn_conf_callback, &matcher, _1, _2);
446  server.setCallback(f);
447  ros::spin();
448  return 0;
449 }
rospack::Rospack rp
BOX(int _x, int _y, int _dx, int _dy)
const cv::Matx33d & intrinsicMatrix() const
f
BOX(int _x, int _y, int _dx, int _dy, double _coefficient)
message_filters::sync_policies::ExactTime< sensor_msgs::Image, sensor_msgs::CameraInfo > SyncPolicy
path
void publish(const boost::shared_ptr< M > &message) const
image_transport::Publisher _debug_pub
void image_cb(const sensor_msgs::ImageConstPtr &msg_ptr, const sensor_msgs::CameraInfoConstPtr &info_ptr)
ROSCPP_DECL void init(int &argc, char **argv, const std::string &name, uint32_t options=0)
std::string encoding
Publisher advertise(const std::string &base_topic, uint32_t queue_size, bool latch=false)
message_filters::Subscriber< sensor_msgs::CameraInfo > _subInfo
image_transport::SubscriberFilter _subImage
std::vector< std::vector< std::vector< unsigned int > > > hsv_integral
T dot(T *pf1, T *pf2, int length)
server
image_transport::ImageTransport _it
ROSCPP_DECL void spin(Spinner &spinner)
std::vector< std::vector< unsigned int > > template_vecs
Connection registerCallback(C &callback)
bool in_boxes(int x, int y, int dx, int dy, std::vector< box > boxes)
void publish(const sensor_msgs::Image &message) const
#define ROS_INFO(...)
bool fromCameraInfo(const sensor_msgs::CameraInfo &msg)
bool param(const std::string &param_name, T &param_val, const T &default_val) const
CvImagePtr toCvCopy(const sensor_msgs::ImageConstPtr &source, const std::string &encoding=std::string())
short s
bool point_in_box(int x, int y, box a_box)
x
Publisher advertise(const std::string &topic, uint32_t queue_size, bool latch=false)
bool getSearchPathFromEnv(std::vector< std::string > &sp)
bool template_add(cv::Mat template_image)
void dyn_conf_callback(jsk_perception::ColorHistogramSlidingMatcherConfig &config, uint32_t level)
mutex_t * lock
int main(int argc, char **argv)
message_filters::Synchronizer< SyncPolicy > sync
void crawl(std::vector< std::string > search_path, bool force)
bool in_box(int x, int y, int dx, int dy, box a_box)
std::vector< cv::Mat > template_images
#define NULL
bool find(const std::string &name, std::string &path)
double calc_coe(std::vector< unsigned int > &a, std::vector< unsigned int > &b)
#define ROS_ERROR(...)
double calc_coe(unsigned int *a, unsigned int *b)
sensor_msgs::ImagePtr toImageMsg() const
std_msgs::Header header


jsk_perception
Author(s): Manabu Saito, Ryohei Ueda
autogenerated on Mon May 3 2021 03:03:27