00001 #include <ros/ros.h>
00002 #include <nodelet/nodelet.h>
00003 #include <image_transport/image_transport.h>
00004 #include <sensor_msgs/image_encodings.h>
00005 #include <dynamic_reconfigure/server.h>
00006 #include <image_proc/CropDecimateConfig.h>
00007
00008 namespace image_proc {
00009
00010 class CropDecimateNodelet : public nodelet::Nodelet
00011 {
00012
00013 boost::shared_ptr<image_transport::ImageTransport> it_in_, it_out_;
00014 image_transport::CameraSubscriber sub_;
00015 int queue_size_;
00016
00017 boost::mutex connect_mutex_;
00018 image_transport::CameraPublisher pub_;
00019
00020
00021 boost::recursive_mutex config_mutex_;
00022 typedef image_proc::CropDecimateConfig Config;
00023 typedef dynamic_reconfigure::Server<Config> ReconfigureServer;
00024 boost::shared_ptr<ReconfigureServer> reconfigure_server_;
00025 Config config_;
00026
00027 virtual void onInit();
00028
00029 void connectCb();
00030
00031 void imageCb(const sensor_msgs::ImageConstPtr& image_msg,
00032 const sensor_msgs::CameraInfoConstPtr& info_msg);
00033
00034 void configCb(Config &config, uint32_t level);
00035 };
00036
00037 void CropDecimateNodelet::onInit()
00038 {
00039 ros::NodeHandle& nh = getNodeHandle();
00040 ros::NodeHandle& private_nh = getPrivateNodeHandle();
00041 ros::NodeHandle nh_in (nh, "camera");
00042 ros::NodeHandle nh_out(nh, "camera_out");
00043 it_in_ .reset(new image_transport::ImageTransport(nh_in));
00044 it_out_.reset(new image_transport::ImageTransport(nh_out));
00045
00046
00047 private_nh.param("queue_size", queue_size_, 5);
00048
00049
00050 reconfigure_server_.reset(new ReconfigureServer(config_mutex_, private_nh));
00051 ReconfigureServer::CallbackType f = boost::bind(&CropDecimateNodelet::configCb, this, _1, _2);
00052 reconfigure_server_->setCallback(f);
00053
00054
00055 image_transport::SubscriberStatusCallback connect_cb = boost::bind(&CropDecimateNodelet::connectCb, this);
00056 ros::SubscriberStatusCallback connect_cb_info = boost::bind(&CropDecimateNodelet::connectCb, this);
00057
00058 boost::lock_guard<boost::mutex> lock(connect_mutex_);
00059 pub_ = it_out_->advertiseCamera("image_raw", 1, connect_cb, connect_cb, connect_cb_info, connect_cb_info);
00060 }
00061
00062
00063 void CropDecimateNodelet::connectCb()
00064 {
00065 boost::lock_guard<boost::mutex> lock(connect_mutex_);
00066 if (pub_.getNumSubscribers() == 0)
00067 sub_.shutdown();
00068 else if (!sub_)
00069 sub_ = it_in_->subscribeCamera("image_raw", queue_size_, &CropDecimateNodelet::imageCb, this);
00070 }
00071
00072 void CropDecimateNodelet::imageCb(const sensor_msgs::ImageConstPtr& image_msg,
00073 const sensor_msgs::CameraInfoConstPtr& info_msg)
00074 {
00077
00078 Config config;
00079 {
00080 boost::lock_guard<boost::recursive_mutex> lock(config_mutex_);
00081 config = config_;
00082 }
00083
00085 config.x_offset = (config.x_offset / 2) * 2;
00086 config.y_offset = (config.y_offset / 2) * 2;
00087
00088 int max_width = image_msg->width - config.x_offset;
00089 int max_height = image_msg->height - config.y_offset;
00090 int width = config.width;
00091 int height = config.height;
00092 if (width == 0 || width > max_width)
00093 width = max_width;
00094 if (height == 0 || height > max_height)
00095 height = max_height;
00096
00097
00098 if (config.decimation_x == 1 &&
00099 config.decimation_y == 1 &&
00100 config.x_offset == 0 &&
00101 config.y_offset == 0 &&
00102 width == (int)image_msg->width &&
00103 height == (int)image_msg->height)
00104 {
00105 pub_.publish(image_msg, info_msg);
00106 return;
00107 }
00108
00110 if (sensor_msgs::image_encodings::bitDepth(image_msg->encoding) != 8)
00111 {
00112 NODELET_ERROR_THROTTLE(2, "Only 8-bit encodings are currently supported");
00113 return;
00114 }
00115
00116
00117 sensor_msgs::CameraInfoPtr out_info = boost::make_shared<sensor_msgs::CameraInfo>(*info_msg);
00118 int binning_x = std::max((int)info_msg->binning_x, 1);
00119 int binning_y = std::max((int)info_msg->binning_y, 1);
00120 out_info->binning_x = binning_x * config.decimation_x;
00121 out_info->binning_y = binning_y * config.decimation_y;
00122 out_info->roi.x_offset += config.x_offset * binning_x;
00123 out_info->roi.y_offset += config.y_offset * binning_y;
00124 out_info->roi.height = height * binning_y;
00125 out_info->roi.width = width * binning_x;
00126
00127 if (width != (int)image_msg->width || height != (int)image_msg->height)
00128 out_info->roi.do_rectify = true;
00129
00130
00131 sensor_msgs::ImagePtr out_image = boost::make_shared<sensor_msgs::Image>();
00132 out_image->header = image_msg->header;
00133 out_image->height = height / config.decimation_y;
00134 out_image->width = width / config.decimation_x;
00135
00136
00137 if (config.decimation_x == 1 && config.decimation_y == 1)
00138 {
00139
00140 int num_channels = sensor_msgs::image_encodings::numChannels(image_msg->encoding);
00141 out_image->encoding = image_msg->encoding;
00142 out_image->step = out_image->width * num_channels;
00143 out_image->data.resize(out_image->height * out_image->step);
00144
00145 const uint8_t* input_buffer = &image_msg->data[config.y_offset*image_msg->step + config.x_offset*num_channels];
00146 uint8_t* output_buffer = &out_image->data[0];
00147 for (int y = 0; y < (int)out_image->height; ++y)
00148 {
00149 memcpy(output_buffer, input_buffer, out_image->step);
00150 input_buffer += image_msg->step;
00151 output_buffer += out_image->step;
00152 }
00153 }
00154 else if (sensor_msgs::image_encodings::isMono(image_msg->encoding))
00155 {
00156
00157 out_image->encoding = sensor_msgs::image_encodings::MONO8;
00158 out_image->step = out_image->width;
00159 out_image->data.resize(out_image->height * out_image->step);
00160
00161
00162 int input_step = config.decimation_y * image_msg->step;
00163 int input_skip = config.decimation_x;
00164 const uint8_t* input_row = &image_msg->data[config.y_offset*image_msg->step + config.x_offset];
00165 uint8_t* output_buffer = &out_image->data[0];
00166
00167
00168 for (int y = 0; y < (int)out_image->height; ++y, input_row += input_step)
00169 {
00170 const uint8_t* input_buffer = input_row;
00171 for (int x = 0; x < (int)out_image->width; ++x, input_buffer += input_skip, ++output_buffer)
00172 *output_buffer = *input_buffer;
00173 }
00174 }
00175 else
00176 {
00177
00178 out_image->encoding = sensor_msgs::image_encodings::BGR8;
00179 out_image->step = out_image->width * 3;
00180 out_image->data.resize(out_image->height * out_image->step);
00181
00182 if (sensor_msgs::image_encodings::isBayer(image_msg->encoding))
00183 {
00184 if (config.decimation_x % 2 != 0 || config.decimation_y % 2 != 0)
00185 {
00186 NODELET_ERROR_THROTTLE(2, "Odd decimation not supported for Bayer images");
00187 return;
00188 }
00189
00190
00191 int R, G1, G2, B;
00192 if (image_msg->encoding == sensor_msgs::image_encodings::BAYER_RGGB8)
00193 {
00194 R = 0;
00195 G1 = 1;
00196 G2 = image_msg->step;
00197 B = image_msg->step + 1;
00198 }
00199 else if (image_msg->encoding == sensor_msgs::image_encodings::BAYER_BGGR8)
00200 {
00201 B = 0;
00202 G1 = 1;
00203 G2 = image_msg->step;
00204 R = image_msg->step + 1;
00205 }
00206 else if (image_msg->encoding == sensor_msgs::image_encodings::BAYER_GBRG8)
00207 {
00208 G1 = 0;
00209 B = 1;
00210 R = image_msg->step;
00211 G2 = image_msg->step + 1;
00212 }
00213 else if (image_msg->encoding == sensor_msgs::image_encodings::BAYER_GRBG8)
00214 {
00215 G1 = 0;
00216 R = 1;
00217 B = image_msg->step;
00218 G2 = image_msg->step + 1;
00219 }
00220 else
00221 {
00222 NODELET_ERROR_THROTTLE(2, "Unrecognized Bayer encoding '%s'", image_msg->encoding.c_str());
00223 return;
00224 }
00225
00226
00227 int input_step = config.decimation_y * image_msg->step;
00228 int input_skip = config.decimation_x;
00229 const uint8_t* input_row = &image_msg->data[config.y_offset*image_msg->step + config.x_offset];
00230 uint8_t* output_buffer = &out_image->data[0];
00231
00232
00233 for (int y = 0; y < (int)out_image->height; ++y, input_row += input_step)
00234 {
00235 const uint8_t* input_buffer = input_row;
00236 for (int x = 0; x < (int)out_image->width; ++x, input_buffer += input_skip, output_buffer += 3)
00237 {
00238 output_buffer[0] = input_buffer[B];
00239 output_buffer[1] = (input_buffer[G1] + input_buffer[G2]) / 2;
00240 output_buffer[2] = input_buffer[R];
00241 }
00242 }
00243 }
00244 else
00245 {
00246
00247 int R, G, B, channels;
00248 if (image_msg->encoding == sensor_msgs::image_encodings::BGR8)
00249 {
00250 B = 0;
00251 G = 1;
00252 R = 2;
00253 channels = 3;
00254 }
00255 else if (image_msg->encoding == sensor_msgs::image_encodings::RGB8)
00256 {
00257 R = 0;
00258 G = 1;
00259 B = 2;
00260 channels = 3;
00261 }
00262 else if (image_msg->encoding == sensor_msgs::image_encodings::BGRA8)
00263 {
00264 B = 0;
00265 G = 1;
00266 R = 2;
00267 channels = 4;
00268 }
00269 else if (image_msg->encoding == sensor_msgs::image_encodings::RGBA8)
00270 {
00271 R = 0;
00272 G = 1;
00273 B = 2;
00274 channels = 4;
00275 }
00276 else
00277 {
00278 NODELET_ERROR_THROTTLE(2, "Unsupported encoding '%s'", image_msg->encoding.c_str());
00279 return;
00280 }
00281
00282
00283 int input_step = config.decimation_y * image_msg->step;
00284 int input_skip = config.decimation_x * channels;
00285 const uint8_t* input_row = &image_msg->data[config.y_offset*image_msg->step + config.x_offset*channels];
00286 uint8_t* output_buffer = &out_image->data[0];
00287
00288
00289 for (int y = 0; y < (int)out_image->height; ++y, input_row += input_step)
00290 {
00291 const uint8_t* input_buffer = input_row;
00292 for (int x = 0; x < (int)out_image->width; ++x, input_buffer += input_skip, output_buffer += 3)
00293 {
00294 output_buffer[0] = input_buffer[B];
00295 output_buffer[1] = input_buffer[G];
00296 output_buffer[2] = input_buffer[R];
00297 }
00298 }
00299
00300 }
00301 }
00302
00303 pub_.publish(out_image, out_info);
00304 }
00305
00306 void CropDecimateNodelet::configCb(Config &config, uint32_t level)
00307 {
00308 config_ = config;
00309 }
00310
00311 }
00312
00313
00314 #include <pluginlib/class_list_macros.h>
00315 PLUGINLIB_DECLARE_CLASS(image_proc, crop_decimate, image_proc::CropDecimateNodelet, nodelet::Nodelet)