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
00045
00046 #include <pal_vision_segmentation/HistogramSegmentConfig.h>
00047 #include <pal_vision_segmentation/image_processing.h>
00048 #include <pal_vision_segmentation/histogram.h>
00049
00050
00051 #include <ros/ros.h>
00052 #include <image_transport/image_transport.h>
00053 #include <cv_bridge/cv_bridge.h>
00054 #include <sensor_msgs/image_encodings.h>
00055 #include <dynamic_reconfigure/server.h>
00056
00057
00058 #include <opencv2/core/core.hpp>
00059 #include <opencv2/imgproc/imgproc.hpp>
00060 #include <opencv2/highgui/highgui.hpp>
00061 #include <opencv2/video/video.hpp>
00062
00063
00064 #include <boost/filesystem.hpp>
00065
00066
00067
00068 image_transport::Publisher mask_pub;
00069 int threshold;
00070 int dilate_iterations;
00071 int dilate_size;
00072 int erode_iterations;
00073 int erode_size;
00074 int dark_pixels_threshold;
00075 cv::Mat image_rect;
00076 image_transport::Publisher image_pub;
00077 image_transport::Publisher debug_pub;
00078 cv::MatND target_hist;
00079
00080
00081
00089 void removeDarkPixels(const cv::Mat& originalImg,
00090 cv::Mat& binaryImg,
00091 int threshold = 10)
00092 {
00093 pal_vision_util::Image imgOriginalImg(originalImg), imgL, img_a, img_b;
00094
00095 pal_vision_util::getLab(imgOriginalImg, imgL, img_a, img_b);
00096
00097
00098 cv::Mat L;
00099 L = cv::cvarrToMat(imgL.getIplImg());
00100
00101 cv::Mat L_thresholded;
00102 L_thresholded = L.clone();
00103 cv::threshold(L, L_thresholded, threshold, 255, cv::THRESH_BINARY);
00104 cv::bitwise_and(binaryImg, L_thresholded, binaryImg);
00105 }
00106
00107 void imageCb(const sensor_msgs::ImageConstPtr& msg)
00108 {
00109 if(image_pub.getNumSubscribers() > 0 ||
00110 mask_pub.getNumSubscribers() > 0 ||
00111 debug_pub.getNumSubscribers() > 0)
00112 {
00113 cv_bridge::CvImagePtr cv_ptr;
00114 try
00115 {
00116 cv_ptr = cv_bridge::toCvCopy(msg, sensor_msgs::image_encodings::BGR8);
00117 }
00118 catch (cv_bridge::Exception& e)
00119 {
00120 ROS_ERROR("cv_bridge exception: %s", e.what());
00121 return;
00122 }
00123
00124 image_rect = cv_ptr->image;
00125
00126 cv::Mat backProject;
00127 pal_vision_util::backProject(image_rect, target_hist, threshold, backProject);
00128
00129 if ( dark_pixels_threshold > 0 )
00130 removeDarkPixels(image_rect, backProject, dark_pixels_threshold);
00131
00132 cv::Mat mask, tmp1;
00133
00134 if(dilate_iterations == 0 && erode_iterations == 0)
00135 mask = backProject;
00136
00137 if(dilate_iterations > 0)
00138 {
00139 cv::dilate(backProject, erode_iterations == 0 ? mask: tmp1,
00140 cv::Mat::ones(dilate_size, dilate_size, CV_8UC1),
00141 cv::Point(-1, -1), dilate_iterations);
00142 }
00143
00144 if(erode_iterations > 0)
00145 {
00146 cv::erode(dilate_iterations == 0 ? backProject : tmp1, mask,
00147 cv::Mat::ones(erode_size, erode_size, CV_8UC1),
00148 cv::Point(-1, -1), erode_iterations);
00149 }
00150
00151 ros::Time now = msg->header.stamp;
00152
00153 if(mask_pub.getNumSubscribers() > 0)
00154 {
00155 cv_bridge::CvImage mask_msg;
00156 mask_msg.header = msg->header;
00157 mask_msg.header.stamp = now;
00158 mask_msg.encoding = sensor_msgs::image_encodings::MONO8;
00159 mask_msg.image = mask;
00160 mask_pub.publish(mask_msg.toImageMsg());
00161 }
00162
00163 if(image_pub.getNumSubscribers() > 0)
00164 {
00165 cv::Mat masked;
00166 image_rect.copyTo(masked, mask);
00167 cv_bridge::CvImage masked_msg;
00168 masked_msg.header = msg->header;
00169 masked_msg.encoding = sensor_msgs::image_encodings::BGR8;
00170 masked_msg.image = masked;
00171 masked_msg.header.stamp = now;
00172 image_pub.publish(*masked_msg.toImageMsg());
00173 }
00174
00175
00176 if(debug_pub.getNumSubscribers() > 0)
00177 {
00178 cv_bridge::CvImage debug_msg;
00179 debug_msg.header = msg->header;
00180 debug_msg.encoding = sensor_msgs::image_encodings::MONO8;
00181 debug_msg.image = backProject;
00182 debug_pub.publish(*debug_msg.toImageMsg());
00183 }
00184 }
00185 }
00186
00187 void reconf_callback(pal_vision_segmentation::HistogramSegmentConfig &config, uint32_t level)
00188 {
00189 threshold = config.threshold;
00190 dilate_iterations = config.dilate_iterations;
00191 dilate_size = config.dilate_size;
00192 erode_iterations = config.erode_iterations;
00193 erode_size = config.erode_size;
00194 dark_pixels_threshold = config.dark_pixels_threshold;
00195 }
00196
00197 void computeHistogramFromFile(const std::string& template_path, cv::MatND& hist)
00198 {
00199 if ( !boost::filesystem::is_directory(template_path) )
00200 {
00201 if ( !boost::filesystem::is_regular_file(template_path) )
00202 throw std::runtime_error("The given path is not a file: " + template_path);
00203
00204 cv::Mat raw_template = cv::imread(template_path);
00205 pal_vision_util::calcHSVHist(raw_template, hist);
00206 cv::normalize(hist, hist, 0, 255, cv::NORM_MINMAX, -1, cv::Mat());
00207 cv::threshold(hist, hist, 128, 255, cv::THRESH_BINARY);
00208 }
00209 else
00210 {
00211 boost::filesystem::directory_iterator end_iter;
00212 std::vector<cv::Mat> images;
00213 for( boost::filesystem::directory_iterator dir_iter(template_path) ; dir_iter != end_iter ; ++dir_iter)
00214 {
00215 if ( boost::filesystem::is_regular_file(dir_iter->status()) )
00216 {
00217 std::string fullPathFileName = dir_iter->path().string();
00218 cv::Mat raw_template = cv::imread(fullPathFileName);
00219 if ( !raw_template.empty() )
00220 images.push_back(raw_template);
00221 }
00222 }
00223 if ( !images.empty() )
00224 {
00225
00226 std::vector<cv::MatND> hists;
00227
00228 pal_vision_util::calcHSVHist(images, hists);
00229
00230
00231
00232 for (int h = 0; h < hists[0].rows; ++h)
00233 for (int s = 0; s < hists[0].cols; ++s)
00234 {
00235 unsigned int i = 0;
00236 bool found = false;
00237 while ( i < hists.size() && !found )
00238 {
00239 if ( hists[i].at<float>(h, s) > threshold )
00240 {
00241 hists[0].at<float>(h, s) = 255;
00242 found = true;
00243 }
00244 ++i;
00245 }
00246 if ( !found )
00247 hists[0].at<float>(h, s) = 0;
00248 }
00249 hist = hists[0].clone();
00250 }
00251 else
00252 throw std::runtime_error("No images found in the given path: " + template_path);
00253 }
00254 }
00255
00256 int main(int argc, char *argv[] )
00257 {
00258 ros::init(argc, argv, "histogram_segmentation");
00259 ros::NodeHandle nh("~");
00260 nh.param<int>("threshold", threshold, 254);
00261 nh.param<int>("dilate_iterations", dilate_iterations, 9);
00262 nh.param<int>("dilate_size", dilate_size, 7);
00263 nh.param<int>("erode_iterations", erode_iterations, 0);
00264 nh.param<int>("erode_size", erode_size, 3);
00265 nh.param<int>("dark_pixels_threshold", dark_pixels_threshold, 15);
00266
00267 if(argc < 2)
00268 {
00269 ROS_ERROR("Histogram segmentation needs a template to search for. Please provide it as a command line argument.");
00270 return -1;
00271 }
00272
00273 std::string template_path(argv[1]);
00274 ROS_INFO_STREAM("Computing histogram from: " << template_path);
00275
00276 computeHistogramFromFile( template_path, target_hist );
00277
00278 image_transport::ImageTransport it(nh);
00279 image_transport::Subscriber rect_sub = it.subscribe("/image", 1, &imageCb);
00280 image_pub = it.advertise("image_masked", 1);
00281 mask_pub = it.advertise("mask", 1);
00282 debug_pub = it.advertise("debug",1);
00283
00284 dynamic_reconfigure::Server<pal_vision_segmentation::HistogramSegmentConfig> server;
00285 dynamic_reconfigure::Server<pal_vision_segmentation::HistogramSegmentConfig>::CallbackType f;
00286 f = boost::bind(&reconf_callback, _1, _2);
00287 server.setCallback(f);
00288
00289 ros::spin();
00290 }