color_histogram_label_match.cpp
Go to the documentation of this file.
1 // -*- mode: c++ -*-
2 /*********************************************************************
3  * Software License Agreement (BSD License)
4  *
5  * Copyright (c) 2015, JSK Lab
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * * Redistributions of source code must retain the above copyright
13  * notice, this list of conditions and the following disclaimer.
14  * * Redistributions in binary form must reproduce the above
15  * copyright notice, this list of conditions and the following
16  * disclaimer in the documentation and/o2r other materials provided
17  * with the distribution.
18  * * Neither the name of the JSK Lab nor the names of its
19  * contributors may be used to endorse or promote products derived
20  * from this software without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
26  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
27  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
28  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
30  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
32  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33  * POSSIBILITY OF SUCH DAMAGE.
34  *********************************************************************/
35 
37 #include <boost/assign.hpp>
39 #include <cv_bridge/cv_bridge.h>
42 
43 namespace jsk_perception
44 {
46  {
47  DiagnosticNodelet::onInit();
48  srv_ = boost::make_shared <dynamic_reconfigure::Server<Config> > (*pnh_);
49  dynamic_reconfigure::Server<Config>::CallbackType f =
50  boost::bind (
52  srv_->setCallback (f);
53  pnh_->param("use_mask", use_mask_, false);
54  pub_debug_ = advertise<sensor_msgs::Image>(
55  *pnh_, "debug", 1);
56  pub_coefficient_image_ = advertise<sensor_msgs::Image>(
57  *pnh_, "output/coefficient_image", 1);
58  pub_result_ = advertise<sensor_msgs::Image>(
59  *pnh_, "output/extracted_region", 1);
61  }
62 
64  {
65  sub_image_.subscribe(*pnh_, "input", 1);
66  sub_label_.subscribe(*pnh_, "input/label", 1);
67  ros::V_string names = boost::assign::list_of("~input")("~input/label");
68  if (use_mask_) {
69  sync_ = boost::make_shared<message_filters::Synchronizer<SyncPolicy> >(100);
70  sub_mask_.subscribe(*pnh_, "input/mask", 1);
71  names.push_back("~input/mask");
72  sync_->connectInput(sub_image_, sub_label_, sub_mask_);
73  sync_->registerCallback(
74  boost::bind(
75  &ColorHistogramLabelMatch::match, this, _1, _2, _3));
76  }
77  else {
78  sync_wo_mask_ = boost::make_shared<message_filters::Synchronizer<SyncPolicyWithoutMask> >(100);
79  sync_wo_mask_->connectInput(sub_image_, sub_label_);
80  sync_wo_mask_->registerCallback(
81  boost::bind(
82  &ColorHistogramLabelMatch::match, this, _1, _2));
83  }
84  sub_histogram_ = pnh_->subscribe(
85  "input/histogram", 1, &ColorHistogramLabelMatch::histogramCallback, this);
86  names.push_back("~input/histogram");
88  }
89 
91  {
94  if (use_mask_) {
96  }
98  }
99 
100  void ColorHistogramLabelMatch::getLabels(const cv::Mat& label,
101  std::vector<int>& keys)
102  {
103  std::map<int, bool> map;
104  for (int j = 0; j < label.rows; j++) {
105  for (int i = 0; i < label.cols; i++) {
106  int label_value = label.at<int>(j, i);
107  if (map.find(label_value) == map.end()) {
108  map[label_value] = true;
109  }
110  }
111  }
112 
113  for (std::map<int, bool>::iterator it = map.begin();
114  it != map.end();
115  ++it) {
116  keys.push_back(it->first);
117  }
118  }
119 
120  void ColorHistogramLabelMatch::getMaskImage(const cv::Mat& label_image,
121  const int label,
122  cv::Mat& mask)
123  {
124  for (int j = 0; j < label_image.rows; j++) {
125  for (int i = 0; i < label_image.cols; i++) {
126  if (label_image.at<int>(j, i) == label) {
127  mask.at<uchar>(j, i) = 255;
128  }
129  }
130  }
131  }
132 
134  const cv::Mat& original_image,
135  const cv::Mat& masked_image)
136  {
137  int original_count = 0;
138  int masked_count = 0;
139  for (int j = 0; j < original_image.rows; j++) {
140  for (int i = 0; i < original_image.cols; i++) {
141  if (original_image.at<uchar>(j, i) != 0) {
142  original_count++;
143  }
144  if (masked_image.at<uchar>(j, i) != 0) {
145  masked_count++;
146  }
147  }
148  }
149  return original_count != masked_count;
150  }
151 
153  const sensor_msgs::Image::ConstPtr& image_msg,
154  const sensor_msgs::Image::ConstPtr& label_msg)
155  {
156  cv::Mat whole_mask = cv::Mat(image_msg->height,
157  image_msg->width, CV_8UC1, cv::Scalar(255));
158  sensor_msgs::Image::ConstPtr mask_msg
159  = cv_bridge::CvImage(image_msg->header,
161  whole_mask).toImageMsg();
162  match(image_msg, label_msg, mask_msg);
163  }
164 
165 
167  const sensor_msgs::Image::ConstPtr& image_msg,
168  const sensor_msgs::Image::ConstPtr& label_msg,
169  const sensor_msgs::Image::ConstPtr& mask_msg)
170  {
171  boost::mutex::scoped_lock lock(mutex_);
172  if (histogram_.empty()) {
173  NODELET_DEBUG("no reference histogram is available");
174  return;
175  }
176 
177  cv::Mat image
178  = cv_bridge::toCvShare(image_msg, image_msg->encoding)->image;
179  cv::Mat label
180  = cv_bridge::toCvShare(label_msg, label_msg->encoding)->image;
181  cv::Mat whole_mask
182  = cv_bridge::toCvShare(mask_msg, mask_msg->encoding)->image;
183 
184  cv::Mat coefficient_image = cv::Mat::zeros(
185  image_msg->height, image_msg->width, CV_32FC1);
186  std::vector<int> labels;
187  getLabels(label, labels);
188 
189  cv::Mat coefficients_heat_image = cv::Mat::zeros(
190  image_msg->height, image_msg->width, CV_8UC3); // BGR8
191  int hist_size = histogram_.cols;
192  float range[] = { min_value_, max_value_ };
193  const float* hist_range = { range };
194  double min_coef = DBL_MAX;
195  double max_coef = - DBL_MAX;
196  for (size_t i = 0; i < labels.size(); i++) {
197  int label_index = labels[i];
198  cv::Mat label_mask = cv::Mat::zeros(label.rows, label.cols, CV_8UC1);
199  getMaskImage(label, label_index, label_mask);
200  double coef = 0.0;
201  // get label_mask & whole_mask and check is it all black or not
202  cv::Mat masked_label;
203  label_mask.copyTo(masked_label, whole_mask);
204  if (isMasked(label_mask, masked_label)) {
205  coef = masked_coefficient_;
206  }
207  else {
208  cv::MatND hist;
209  bool uniform = true; bool accumulate = false;
210  cv::calcHist(&image, 1, 0, label_mask, hist, 1,
211  &hist_size, &hist_range, uniform, accumulate);
212  cv::normalize(hist, hist, 1, hist.rows, cv::NORM_L2, -1,
213  cv::Mat());
214  cv::Mat hist_mat = cv::Mat::zeros(1, hist_size, CV_32FC1);
215  for (size_t j = 0; j < hist_size; j++) {
216  hist_mat.at<float>(0, j) = hist.at<float>(0, j);
217  }
218  //cv::Mat hist_mat = hist;
219 
220  coef = coefficients(hist_mat, histogram_);
221  if (min_coef > coef) {
222  min_coef = coef;
223  }
224  if (max_coef < coef) {
225  max_coef = coef;
226  }
227  }
228  std_msgs::ColorRGBA coef_color = jsk_topic_tools::heatColor(coef);
229  for (size_t j = 0; j < coefficients_heat_image.rows; j++) {
230  for (size_t i = 0; i < coefficients_heat_image.cols; i++) {
231  if (label_mask.at<uchar>(j, i) == 255) {
232  coefficients_heat_image.at<cv::Vec3b>(j, i)
233  = cv::Vec3b(int(coef_color.b * 255),
234  int(coef_color.g * 255),
235  int(coef_color.r * 255));
236  coefficient_image.at<float>(j, i) = coef;
237  }
238  }
239  }
240  }
241  NODELET_INFO("coef: %f - %f", min_coef, max_coef);
243  cv_bridge::CvImage(image_msg->header,
245  coefficients_heat_image).toImageMsg());
247  cv_bridge::CvImage(image_msg->header,
249  coefficient_image).toImageMsg());
250  // apply threshold operation
251  cv::Mat threshold_image = cv::Mat::zeros(
252  coefficient_image.rows, coefficient_image.cols, CV_32FC1);
253  if (threshold_method_ == 0) { // smaller than
254  cv::threshold(coefficient_image, threshold_image, coef_threshold_, 1,
255  cv::THRESH_BINARY_INV);
256  }
257  else if (threshold_method_ == 1) { // greater than
258  cv::threshold(coefficient_image, threshold_image, coef_threshold_, 1,
259  cv::THRESH_BINARY);
260  }
261  else if (threshold_method_ == 2 || threshold_method_ == 3) {
262  // convert image into 8UC to apply otsu' method
263  cv::Mat otsu_image = cv::Mat::zeros(
264  coefficient_image.rows, coefficient_image.cols, CV_8UC1);
265  cv::Mat otsu_result_image = cv::Mat::zeros(
266  coefficient_image.rows, coefficient_image.cols, CV_8UC1);
267  coefficient_image.convertTo(otsu_image, 8, 255.0);
268  cv::threshold(otsu_image, otsu_result_image, coef_threshold_, 255,
269  cv::THRESH_OTSU);
270  // convert it into float image again
271  if (threshold_method_ == 2) {
272  otsu_result_image.convertTo(threshold_image, 32, 1 / 255.0);
273  }
274  else if (threshold_method_ == 3) {
275  otsu_result_image.convertTo(threshold_image, 32, - 1 / 255.0, 1.0);
276  }
277  }
278  cv::Mat threshold_uchar_image = cv::Mat(threshold_image.rows,
279  threshold_image.cols,
280  CV_8UC1);
281  threshold_image.convertTo(threshold_uchar_image, 8, 255.0);
282  // convert image from float to uchar
284  cv_bridge::CvImage(image_msg->header,
286  threshold_uchar_image).toImageMsg());
287  }
288 
290  const cv::Mat& ref_hist,
291  const cv::Mat& target_hist)
292  {
293  if (coefficient_method_ == 0) {
294  return (1.0 + cv::compareHist(ref_hist, target_hist, CV_COMP_CORREL)) / 2.0;
295  }
296  else if (coefficient_method_ == 1) {
297  double x = cv::compareHist(ref_hist, target_hist, CV_COMP_CHISQR);
298  return 1/ (x * x + 1);
299  }
300  else if (coefficient_method_ == 2) {
301  return cv::compareHist(ref_hist, target_hist, CV_COMP_INTERSECT);
302  }
303  else if (coefficient_method_ == 3) {
304  return 1.0 - cv::compareHist(ref_hist, target_hist, CV_COMP_BHATTACHARYYA);
305  }
306  else if (coefficient_method_ == 4 || coefficient_method_ == 5) {
307  cv::Mat ref_sig = cv::Mat::zeros(ref_hist.cols, 2, CV_32FC1);
308  cv::Mat target_sig = cv::Mat::zeros(ref_hist.cols, 2, CV_32FC1);
309  //NODELET_INFO("ref_hist.cols = %d", ref_hist.cols);
310  for (size_t i = 0; i < ref_hist.cols; i++) {
311  ref_sig.at<float>(i, 0) = ref_hist.at<float>(0, i);
312  target_sig.at<float>(i, 0) = target_hist.at<float>(0, i);
313  ref_sig.at<float>(i, 1) = i;
314  target_sig.at<float>(i, 1) = i;
315  }
316  if (coefficient_method_ == 4) {
317  double x = cv::EMD(ref_sig, target_sig, CV_DIST_L1);
318  return 1.0 / (1.0 + x * x);
319  }
320  else {
321  double x = cv::EMD(ref_sig, target_sig, CV_DIST_L2);
322  return 1.0 / (1.0 + x * x);
323  }
324  }
325  else {
326  NODELET_ERROR("unknown coefficiet method: %d", coefficient_method_);
327  return 0;
328  }
329  }
330 
332  const jsk_recognition_msgs::ColorHistogram::ConstPtr& histogram_msg)
333  {
334  boost::mutex::scoped_lock lock(mutex_);
335  //histogram_ = histogram_msg->histogram;
336  histogram_ = cv::Mat(1, histogram_msg->histogram.size(), CV_32FC1);
337  for (size_t i = 0; i < histogram_msg->histogram.size(); i++) {
338  histogram_.at<float>(0, i) = histogram_msg->histogram[i];
339  }
340  cv::normalize(histogram_, histogram_, 1, histogram_.rows, cv::NORM_L2,
341  -1, cv::Mat());
342  }
343 
345  Config &config, uint32_t level)
346  {
347  boost::mutex::scoped_lock lock(mutex_);
348  coefficient_method_ = config.coefficient_method;
349  max_value_ = config.max_value;
350  min_value_ = config.min_value;
351  masked_coefficient_ = config.masked_coefficient;
352  coef_threshold_ = config.coef_threshold;
353  threshold_method_ = config.threshold_method;
354  }
355 
356 }
357 
CvImageConstPtr toCvShare(const sensor_msgs::ImageConstPtr &source, const std::string &encoding=std::string())
std_msgs::ColorRGBA heatColor(double v)
f
message_filters::Subscriber< sensor_msgs::Image > sub_mask_
boost::shared_ptr< dynamic_reconfigure::Server< Config > > srv_
#define NODELET_ERROR(...)
void publish(const boost::shared_ptr< M > &message) const
message_filters::Subscriber< sensor_msgs::Image > sub_label_
virtual void getLabels(const cv::Mat &label, std::vector< int > &labels)
hist
virtual double coefficients(const cv::Mat &ref_hist, const cv::Mat &target_hist)
virtual void getMaskImage(const cv::Mat &label_image, const int label, cv::Mat &mask)
std::vector< std::string > V_string
message_filters::Subscriber< sensor_msgs::Image > sub_image_
virtual void histogramCallback(const jsk_recognition_msgs::ColorHistogram::ConstPtr &histogram_msg)
virtual void match(const sensor_msgs::Image::ConstPtr &image_msg, const sensor_msgs::Image::ConstPtr &label_msg, const sensor_msgs::Image::ConstPtr &mask_msg)
boost::shared_ptr< ros::NodeHandle > pnh_
const std::string TYPE_32FC1
bool warnNoRemap(const std::vector< std::string > names)
virtual void configCallback(Config &config, uint32_t level)
x
boost::shared_ptr< message_filters::Synchronizer< SyncPolicyWithoutMask > > sync_wo_mask_
mutex_t * lock
label
#define NODELET_INFO(...)
void subscribe(ros::NodeHandle &nh, const std::string &topic, uint32_t queue_size, const ros::TransportHints &transport_hints=ros::TransportHints(), ros::CallbackQueueInterface *callback_queue=0)
virtual bool isMasked(const cv::Mat &original_image, const cv::Mat &masked_image)
#define NODELET_DEBUG(...)
sensor_msgs::ImagePtr toImageMsg() const
PLUGINLIB_EXPORT_CLASS(jsk_perception::ColorHistogramLabelMatch, nodelet::Nodelet)
boost::shared_ptr< message_filters::Synchronizer< SyncPolicy > > sync_


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