00001 #include <ros/ros.h>
00002 #include <ros/package.h>
00003 #include <nodelet/nodelet.h>
00004 #include <cv_bridge/cv_bridge.h>
00005 #include <opencv2/core/core.hpp>
00006 #include <opencv2/imgproc/imgproc.hpp>
00007 #include <dynamic_reconfigure/server.h>
00008 #include <sensor_msgs/image_encodings.h>
00009 #include <pluginlib/class_list_macros.h>
00010 #include <image_transport/image_transport.h>
00011 #include <nifti_image_proc/ProcessingConfig.h>
00012
00013
00014 #define MIN_INTENSITY 0
00015 #define MAX_INTENSITY 255
00016 #define LOOKUP_SIZE 256
00017
00018 namespace nifti_image_proc {
00019
00023 class ImageProcessingNodelet: public nodelet::Nodelet {
00024 private:
00026 int min_intensity;
00028 int max_intensity;
00030 double gamma;
00032 double percent_saturated;
00033 boost::shared_ptr<image_transport::ImageTransport> it;
00034 image_transport::Publisher cameraPub;
00035 image_transport::Subscriber cameraSub;
00036 nifti_image_proc::ProcessingConfig processingConfig;
00037 boost::shared_ptr<dynamic_reconfigure::Server<nifti_image_proc::ProcessingConfig> > server;
00038 dynamic_reconfigure::Server<nifti_image_proc::ProcessingConfig>::CallbackType f;
00039 public:
00040 ImageProcessingNodelet();
00041 void onInit();
00042 void processingCallback(nifti_image_proc::ProcessingConfig &config, uint32_t level);
00043 void cameraCallback(const sensor_msgs::ImageConstPtr& msg);
00044 cv::Mat cumulativeHist(cv::Mat hist);
00045 };
00046
00047 ImageProcessingNodelet::ImageProcessingNodelet():
00048 min_intensity(MIN_INTENSITY),
00049 max_intensity(MAX_INTENSITY),
00050 gamma(1.0),
00051 percent_saturated(5.0) {
00052 }
00053
00054 void ImageProcessingNodelet::onInit() {
00055 NODELET_INFO("Initializing ImageProcessingNodelet...");
00056 #ifdef USE_MT_NODE_HANDLE
00057 ros::NodeHandle &nh = getMTNodeHandle();
00058 ros::NodeHandle &pnh = getMTPrivateNodeHandle();
00059 #else
00060 ros::NodeHandle &nh = getNodeHandle();
00061 ros::NodeHandle &pnh = getPrivateNodeHandle();
00062 #endif
00063
00064 pnh.param("min_intensity", min_intensity, min_intensity);
00065 pnh.param("max_intensity", max_intensity, max_intensity);
00066 pnh.param("gamma", gamma, gamma);
00067 pnh.param("percent_saturated", percent_saturated, percent_saturated);
00068
00069 f = boost::bind(&ImageProcessingNodelet::processingCallback, this, _1, _2);
00070 server.reset(new dynamic_reconfigure::Server<nifti_image_proc::ProcessingConfig>(pnh));
00071 server->setCallback(f);
00072
00073 it.reset(new image_transport::ImageTransport(nh));
00074 cameraSub = it->subscribe("image", 5, &ImageProcessingNodelet::cameraCallback, this);
00075 cameraPub = it->advertise("processed_image", 5);
00076 NODELET_INFO("ImageProcessingNodelet initialized.");
00077 }
00078
00079 void ImageProcessingNodelet::processingCallback(nifti_image_proc::ProcessingConfig &config, uint32_t level) {
00080 processingConfig = config;
00081 }
00082
00087 void ImageProcessingNodelet::cameraCallback(const sensor_msgs::ImageConstPtr& msg) {
00088
00089 if (cameraPub.getNumSubscribers() <= 0) {
00090 return;
00091 }
00092
00093 cv_bridge::CvImagePtr cv_ptr;
00094 try {
00095 cv_ptr = cv_bridge::toCvCopy(msg, sensor_msgs::image_encodings::BGR8);
00096 } catch (cv_bridge::Exception& e) {
00097 ROS_ERROR("cv_bridge exception: %s", e.what());
00098 return;
00099 }
00100
00101 gamma = processingConfig.gamma;
00102 percent_saturated = processingConfig.percent_saturated;
00103 double width = cv_ptr->image.cols, height = cv_ptr->image.rows;
00104
00105 if (percent_saturated != 0.0) {
00106 cv::Mat grey;
00107 cv::cvtColor(cv_ptr->image, grey, CV_BGR2GRAY);
00108 cv::Mat hist;
00109
00110 int histSize = 256;
00111 float range[] = { 0, 256 };
00112 const float* histRange = { range };
00113 calcHist(&grey, 1, 0, cv::Mat(), hist, 1, &histSize, &histRange, true, false);
00114
00115 hist /= width * height;
00116 hist *= 100;
00117 hist = cumulativeHist(hist);
00118
00119 int l_min = 255, l_max = 0, sum_min = 0.0, sum_max = 0.0;
00120 for (; l_min >= 0; l_min--) {
00121 if (hist.at<float>(l_min) <= percent_saturated / 2.0) {
00122 break;
00123 }
00124 }
00125 for (; l_max < 256; l_max++) {
00126 if (100 - hist.at<float>(l_max) <= percent_saturated / 2.0) {
00127 break;
00128 }
00129 }
00130 min_intensity = l_min;
00131 max_intensity = l_max;
00132 } else {
00133 min_intensity = processingConfig.min_intensity;
00134 max_intensity = processingConfig.max_intensity;
00135 }
00136
00137 cv::Mat lookup(1, LOOKUP_SIZE, CV_8U);
00138 for (int i = 0; i < LOOKUP_SIZE; i++) {
00139 if (i < min_intensity) {
00140 lookup.at<uchar>(i) = MIN_INTENSITY;
00141 } else if (i > max_intensity) {
00142 lookup.at<uchar>(i) = MAX_INTENSITY;
00143 } else {
00144 lookup.at<uchar>(i) = static_cast<uchar>(255.0
00145 * pow(((double) (i - min_intensity) / (double) (max_intensity - min_intensity)), 1 / gamma) + 0.5);
00146 }
00147 }
00148
00149 cv::Mat result;
00150 cv::LUT(cv_ptr->image, lookup, result);
00151
00152 cv_bridge::CvImagePtr image_ptr(new cv_bridge::CvImage);
00153 image_ptr->header = msg->header;
00154
00155
00156 image_ptr->encoding = sensor_msgs::image_encodings::BGR8;
00157 image_ptr->image = result;
00158
00159 cameraPub.publish(image_ptr->toImageMsg());
00160 }
00161
00167 cv::Mat ImageProcessingNodelet::cumulativeHist(cv::Mat hist) {
00168 cv::Mat cumulative(hist.size(), hist.type());
00169 float sum = 0.0;
00170 for (int i = 0; i < 256; i++) {
00171 sum += hist.at<float>(i);
00172 cumulative.at<float>(i) = sum;
00173 }
00174 return cumulative;
00175 }
00176
00177 }
00178
00179 PLUGINLIB_DECLARE_CLASS(nifti_image_proc, image_processing_nodelet,
00180 nifti_image_proc::ImageProcessingNodelet, nodelet::Nodelet)