histogram_segmentation.cpp
Go to the documentation of this file.
00001 /*
00002  * Software License Agreement (Modified BSD License)
00003  *
00004  *  Copyright (c) 2012, PAL Robotics, S.L.
00005  *  All rights reserved.
00006  *
00007  *  Redistribution and use in source and binary forms, with or without
00008  *  modification, are permitted provided that the following conditions
00009  *  are met:
00010  *
00011  *   * Redistributions of source code must retain the above copyright
00012  *     notice, this list of conditions and the following disclaimer.
00013  *   * Redistributions in binary form must reproduce the above
00014  *     copyright notice, this list of conditions and the following
00015  *     disclaimer in the documentation and/or other materials provided
00016  *     with the distribution.
00017  *   * Neither the name of PAL Robotics, S.L. nor the names of its
00018  *     contributors may be used to endorse or promote products derived
00019  *     from this software without specific prior written permission.
00020  *
00021  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00022  *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00023  *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00024  *  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00025  *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00026  *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00027  *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00028  *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
00029  *  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00030  *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
00031  *  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00032  *  POSSIBILITY OF SUCH DAMAGE.
00033  */
00034 
00045 // PAL headers
00046 #include <pal_vision_segmentation/HistogramSegmentConfig.h>
00047 #include <pal_vision_segmentation/image_processing.h>
00048 #include <pal_vision_segmentation/histogram.h>
00049 
00050 // ROS headers
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 // OpenCV headers
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 // Boost headers
00064 #include <boost/filesystem.hpp>
00065 
00066 
00067 /***Variables used in callbacks***/
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 /***end of callback section***/
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   //update header of cv::Mat L from imgL:
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; //ros::Time::now();
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; //ros::Time::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; //ros::Time::now();
00172             image_pub.publish(*masked_msg.toImageMsg());
00173         }
00174 
00175         //DEBUG
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) ) //if the given path is not a folder
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);            //all those bin values > threshold are set to 255, otherwise 0
00208   }
00209   else //otherwise, it should be a folder
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       //pal_vision_util::calcHSVHist(images, hist);
00226       std::vector<cv::MatND> hists;
00227       //compute a separate histogram for each image
00228       pal_vision_util::calcHSVHist(images, hists);
00229 
00230       //create a new histogram where every bin takes the maximum value if any of the histogram's bin value is
00231       //larger than the threshold
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"); //initialize with a default name
00259     ros::NodeHandle nh("~"); //use node name as sufix of the namespace
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 }


pal_vision_segmentation
Author(s): Bence Magyar
autogenerated on Fri Aug 28 2015 11:57:00