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
00035 #define BOOST_PARAMETER_MAX_ARITY 7
00036
00037 #include <limits>
00038 #include "jsk_pcl_ros/linemod.h"
00039 #include <pcl_conversions/pcl_conversions.h>
00040 #include <boost/filesystem.hpp>
00041 #include <boost/format.hpp>
00042 #include <pcl/recognition/color_gradient_modality.h>
00043 #include <pcl/recognition/surface_normal_modality.h>
00044 #include <pcl/filters/extract_indices.h>
00045 #include <pcl/io/pcd_io.h>
00046 #include <jsk_topic_tools/rosparam_utils.h>
00047 #include <glob.h>
00048 #include <boost/algorithm/string/predicate.hpp>
00049 #include <pcl/common/transforms.h>
00050 #include <pcl/range_image/range_image_planar.h>
00051 #include "jsk_pcl_ros/viewpoint_sampler.h"
00052 #include <cv_bridge/cv_bridge.h>
00053 #include <sensor_msgs/image_encodings.h>
00054 #include <pcl/kdtree/kdtree_flann.h>
00055 #include <yaml-cpp/yaml.h>
00056 #include <pcl/common/common.h>
00057 #include "jsk_recognition_utils/pcl_util.h"
00058 #include "jsk_recognition_utils/geo_util.h"
00059
00060 namespace jsk_pcl_ros
00061 {
00062 void LINEMODTrainer::onInit()
00063 {
00064 PCLNodelet::onInit();
00065 pnh_->param("output_file", output_file_, std::string("template"));
00066 pnh_->param("sample_viewpoint", sample_viewpoint_, true);
00067 pnh_->param("sample_viewpoint_angle_step", sample_viewpoint_angle_step_,
00068 40.0);
00069 pnh_->param("sample_viewpoint_angle_min", sample_viewpoint_angle_min_,
00070 -80.0);
00071 pnh_->param("sample_viewpoint_angle_max", sample_viewpoint_angle_max_,
00072 80.0);
00073 pnh_->param("sample_viewpoint_radius_step", sample_viewpoint_radius_step_,
00074 0.2);
00075 pnh_->param("sample_viewpoint_radius_min", sample_viewpoint_radius_min_,
00076 0.4);
00077 pnh_->param("sample_viewpoint_radius_max", sample_viewpoint_radius_max_,
00078 0.8);
00079 if (!sample_viewpoint_) {
00080 sub_input_.subscribe(*pnh_, "input", 1);
00081 sub_indices_.subscribe(*pnh_, "input/indices", 1);
00082 sync_ = boost::make_shared<message_filters::Synchronizer<SyncPolicy> >(100);
00083 sync_->connectInput(sub_input_, sub_indices_);
00084 sync_->registerCallback(boost::bind(&LINEMODTrainer::store,
00085 this, _1, _2));
00086 }
00087 else {
00088 sub_input_nonsync_ = pnh_->subscribe("input", 1,
00089 &LINEMODTrainer::subscribeCloud,
00090 this);
00091 sub_camera_info_nonsync_ = pnh_->subscribe(
00092 "input/info", 1,
00093 &LINEMODTrainer::subscribeCameraInfo,
00094 this);
00095 pub_range_image_ = pnh_->advertise<sensor_msgs::Image>(
00096 "output/range_image", 1);
00097 pub_colored_range_image_ = pnh_->advertise<sensor_msgs::Image>(
00098 "output/colored_range_image", 1);
00099 pub_sample_cloud_ = pnh_->advertise<sensor_msgs::PointCloud2>(
00100 "output/sample_cloud", 1);
00101 }
00102 start_training_srv_
00103 = pnh_->advertiseService(
00104 "start_training", &LINEMODTrainer::startTraining,
00105 this);
00106 clear_data_srv_
00107 = pnh_->advertiseService(
00108 "clear", &LINEMODTrainer::clearData,
00109 this);
00110 }
00111
00112 void LINEMODTrainer::store(
00113 const sensor_msgs::PointCloud2::ConstPtr& cloud_msg,
00114 const PCLIndicesMsg::ConstPtr& indices_msg)
00115 {
00116 boost::mutex::scoped_lock lock(mutex_);
00117 pcl::PointCloud<pcl::PointXYZRGBA>::Ptr cloud
00118 (new pcl::PointCloud<pcl::PointXYZRGBA>);
00119 pcl::fromROSMsg(*cloud_msg, *cloud);
00120 pcl::PointIndices::Ptr indices(new pcl::PointIndices);
00121 pcl_conversions::toPCL(*indices_msg, *indices);
00122 samples_.push_back(cloud);
00123 sample_indices_.push_back(indices);
00124 NODELET_INFO("%lu samples", samples_.size());
00125 }
00126
00127 void LINEMODTrainer::subscribeCloud(
00128 const sensor_msgs::PointCloud2::ConstPtr& cloud_msg)
00129 {
00130 boost::mutex::scoped_lock lock(mutex_);
00131 pcl::PointCloud<pcl::PointXYZRGBA>::Ptr cloud
00132 (new pcl::PointCloud<pcl::PointXYZRGBA>);
00133 pcl::fromROSMsg(*cloud_msg, *cloud);
00134 samples_before_sampling_.push_back(cloud);
00135 NODELET_INFO("%lu samples", samples_.size());
00136 }
00137
00138 void LINEMODTrainer::subscribeCameraInfo(
00139 const sensor_msgs::CameraInfo::ConstPtr& info_msg)
00140 {
00141 boost::mutex::scoped_lock lock(mutex_);
00142 camera_info_ = info_msg;
00143 }
00144
00145 bool LINEMODTrainer::clearData(
00146 std_srvs::Empty::Request& req,
00147 std_srvs::Empty::Response& res)
00148 {
00149 boost::mutex::scoped_lock lock(mutex_);
00150 NODELET_INFO("clearing %lu samples", samples_.size());
00151 samples_.clear();
00152 sample_indices_.clear();
00153 return true;
00154 }
00155
00156 void LINEMODTrainer::organizedPointCloudWithViewPoint(
00157 const Eigen::Affine3f& transform,
00158 pcl::PointCloud<pcl::PointXYZRGBA>::Ptr raw_cloud,
00159 const image_geometry::PinholeCameraModel& model,
00160 pcl::PointCloud<pcl::PointXYZRGBA>::Ptr colored_cloud,
00161 pcl::PointIndices& mask)
00162 {
00163 int width = model.fullResolution().width;
00164 int height = model.fullResolution().height;
00165 double fx = model.fx();
00166 double fy = model.fy();
00167 double cx = model.cx();
00168 double cy = model.cy();
00169 Eigen::Affine3f viewpoint_transform = transform;
00170 Eigen::Affine3f object_transform = viewpoint_transform.inverse();
00171 pcl::PointCloud<pcl::PointXYZRGBA>::Ptr cloud
00172 (new pcl::PointCloud<pcl::PointXYZRGBA>);
00173 pcl::transformPointCloud<pcl::PointXYZRGBA>(
00174 *raw_cloud, *cloud, object_transform);
00175 pcl::RangeImagePlanar range_image;
00176 Eigen::Affine3f dummytrans;
00177 dummytrans.setIdentity();
00178 range_image.createFromPointCloudWithFixedSize(
00179 *cloud, width, height,
00180 cx, cy, fx, fy, dummytrans);
00181 cv::Mat mat(range_image.height, range_image.width, CV_32FC1);
00182 float *tmpf = (float *)mat.ptr();
00183 for(unsigned int i = 0; i < range_image.height * range_image.width; i++) {
00184 tmpf[i] = range_image.points[i].z;
00185 }
00186 std_msgs::Header dummy_header;
00187 dummy_header.stamp = ros::Time::now();
00188 dummy_header.frame_id = camera_info_->header.frame_id;
00189 cv_bridge::CvImage range_bridge(dummy_header,
00190 "32FC1",
00191 mat);
00192 pub_range_image_.publish(range_bridge.toImageMsg());
00193 pcl::KdTreeFLANN<pcl::PointXYZRGBA>::Ptr
00194 kdtree (new pcl::KdTreeFLANN<pcl::PointXYZRGBA>);
00195 kdtree->setInputCloud(cloud);
00196
00197 colored_cloud->width = range_image.width;
00198 colored_cloud->height = range_image.height;
00199 colored_cloud->is_dense = range_image.is_dense;
00200 pcl::PointXYZRGBA nan_point;
00201 nan_point.x = nan_point.y = nan_point.z
00202 = std::numeric_limits<float>::quiet_NaN();
00203 pcl::PointIndices::Ptr mask_indices (new pcl::PointIndices);
00204 colored_cloud->points.resize(range_image.points.size());
00205
00206 cv::Mat colored_image = cv::Mat::zeros(
00207 range_image.height, range_image.width, CV_8UC3);
00208 for (size_t pi = 0; pi < range_image.points.size(); pi++) {
00209 if (std::isnan(range_image.points[pi].x) ||
00210 std::isnan(range_image.points[pi].y) ||
00211 std::isnan(range_image.points[pi].z)) {
00212
00213 colored_cloud->points[pi] = nan_point;
00214 }
00215 else {
00216 pcl::PointXYZRGBA input_point;
00217 input_point.x = range_image.points[pi].x;
00218 input_point.y = range_image.points[pi].y;
00219 input_point.z = range_image.points[pi].z;
00220 std::vector<int> indices;
00221 std::vector<float> distances;
00222 kdtree->nearestKSearch(input_point, 1, indices, distances);
00223 if (indices.size() > 0) {
00224 input_point.rgba = cloud->points[indices[0]].rgba;
00225 }
00226 colored_image.at<cv::Vec3b>(pi / range_image.width,
00227 pi % range_image.width)
00228 = cv::Vec3b(cloud->points[indices[0]].r,
00229 cloud->points[indices[0]].g,
00230 cloud->points[indices[0]].b);
00231 colored_cloud->points[pi] = input_point;
00232 }
00233 }
00234 for (size_t pi = 0; pi < range_image.points.size(); pi++) {
00235 if (!std::isnan(range_image.points[pi].x) &&
00236 !std::isnan(range_image.points[pi].y) &&
00237 !std::isnan(range_image.points[pi].z)) {
00238
00239 mask.indices.push_back(pi);
00240 }
00241 }
00242 cv_bridge::CvImage colored_range_bridge(dummy_header,
00243 sensor_msgs::image_encodings::RGB8,
00244 colored_image);
00245 pub_colored_range_image_.publish(colored_range_bridge.toImageMsg());
00246
00247 sensor_msgs::PointCloud2 ros_sample_cloud;
00248 pcl::toROSMsg(*colored_cloud, ros_sample_cloud);
00249 pcl::PointCloud<pcl::PointXYZRGB> rgb_cloud;
00250 pcl::fromROSMsg(ros_sample_cloud, rgb_cloud);
00251 pcl::toROSMsg(rgb_cloud, ros_sample_cloud);
00252 ros_sample_cloud.header = dummy_header;
00253 pub_sample_cloud_.publish(ros_sample_cloud);
00254 }
00255
00256 void LINEMODTrainer::trainWithViewpointSampling()
00257 {
00258 NODELET_INFO("Start LINEMOD training from %lu samples", samples_before_sampling_.size());
00259 if (!camera_info_) {
00260 NODELET_FATAL("no camera info is available");
00261 return;
00262 }
00263 if (samples_before_sampling_.size() != 1) {
00264 NODELET_FATAL("we expect only one training pointcloud, but it has %lu pointclouds",
00265 samples_before_sampling_.size());
00266 return;
00267 }
00268
00269 image_geometry::PinholeCameraModel model;
00270 model.fromCameraInfo(camera_info_);
00271
00272 if (samples_before_sampling_.size() != 1) {
00273 NODELET_FATAL("we expect only one sample data");
00274 return;
00275 }
00276 ViewpointSampler sampler(sample_viewpoint_angle_step_,
00277 sample_viewpoint_angle_min_,
00278 sample_viewpoint_angle_max_,
00279 sample_viewpoint_radius_step_,
00280 sample_viewpoint_radius_min_,
00281 sample_viewpoint_radius_max_,
00282 150);
00283 std::vector<Eigen::Affine3f> transforms;
00284 transforms.resize(sampler.sampleNum());
00285 for (size_t i = 0; i < sampler.sampleNum(); i++) {
00286 Eigen::Affine3f transform;
00287 sampler.get(transform);
00288 transforms[i] = transform;
00289 sampler.next();
00290 }
00291
00292
00293
00294
00295
00296
00297 pcl::RangeImagePlanar dummy_range_image;
00298
00299 pcl::PointCloud<pcl::PointXYZRGBA>::Ptr raw_cloud
00300 = samples_before_sampling_[0];
00301
00302
00303 std::vector<pcl::ColorGradientModality<pcl::PointXYZRGBA> >
00304 color_modalities (sampler.sampleNum());
00305 std::vector<pcl::SurfaceNormalModality<pcl::PointXYZRGBA> >
00306 surface_norm_modalities (sampler.sampleNum());
00307 std::vector<pcl::MaskMap> mask_maps (sampler.sampleNum());
00308 std::vector<pcl::RegionXY> regions (sampler.sampleNum());
00309 boost::mutex train_mutex;
00310 pcl::LINEMOD linemod;
00311 int counter = 0;
00312 std::vector<Eigen::Affine3f> pose_in_order_of_training;
00313 #ifdef _OPENMP
00314 #pragma omp parallel for
00315 #endif
00316 for (size_t j = 0; j < sampler.sampleNum(); j++) {
00317
00318 pcl::PointCloud<pcl::PointXYZRGBA>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZRGBA>);
00319 pcl::PointIndices::Ptr indices(new pcl::PointIndices);
00320 organizedPointCloudWithViewPoint(transforms[j], raw_cloud, model,
00321 cloud, *indices);
00322
00323 pcl::ColorGradientModality<pcl::PointXYZRGBA> color_modality;
00324 pcl::SurfaceNormalModality<pcl::PointXYZRGBA> surface_norm_mod;
00325 pcl::MaskMap mask_map(model.fullResolution().width,
00326 model.fullResolution().height);
00327 pcl::RegionXY region;
00328 generateLINEMODTrainingData(cloud, indices,
00329 color_modality, surface_norm_mod,
00330 mask_map, region);
00331 std::vector<pcl::QuantizableModality*> modalities(2);
00332 modalities[0] = &color_modality;
00333 modalities[1] = &surface_norm_mod;
00334 std::vector<pcl::MaskMap*> masks(2);
00335 masks[0] = &mask_map;
00336 masks[1] = &mask_map;
00337 {
00338 boost::mutex::scoped_lock lock(train_mutex);
00339 ++counter;
00340 NODELET_INFO("training: %d/%lu", counter, sampler.sampleNum());
00341 linemod.createAndAddTemplate(modalities, masks, region);
00342 pose_in_order_of_training.push_back(transforms[j]);
00343 }
00344 }
00345
00346
00347
00348
00349 std::ofstream linemod_file;
00350 const std::string linemod_file_name = output_file_ + ".linemod";
00351 NODELET_INFO("writing to %s", linemod_file_name.c_str());
00352 linemod_file.open(linemod_file_name.c_str(),
00353 std::ofstream::out | std::ofstream::binary);
00354 linemod.serialize(linemod_file);
00355 linemod_file.close();
00356 const std::string pcd_file_name = output_file_ + ".pcd";
00357 NODELET_INFO("writing to %s", pcd_file_name.c_str());
00358 pcl::PCDWriter writer;
00359 writer.writeBinaryCompressed(pcd_file_name, *raw_cloud);
00360
00361 std::ofstream pose_file;
00362 const std::string pose_file_name = output_file_ + "_poses.yaml";
00363 pose_file.open(pose_file_name.c_str(), std::ofstream::out);
00364 pose_file << "template_poses: [" << std::endl;
00365 for (size_t i = 0; i < pose_in_order_of_training.size(); i++) {
00366 Eigen::Affine3f pose = pose_in_order_of_training[i];
00367 Eigen::Vector3f pos(pose.translation());
00368 Eigen::Quaternionf rot(pose.rotation());
00369 pose_file << "["
00370 << pos[0] << ", " << pos[1] << ", " << pos[2] << ", "
00371 << rot.x() << ", " << rot.y() << ", " << rot.z() << ", " << rot.w() << "]";
00372 if (i != pose_in_order_of_training.size() - 1) {
00373 pose_file << ", " << std::endl;
00374 }
00375 else {
00376 pose_file << "]" << std::endl;
00377 }
00378 }
00379 pose_file.close();
00380 NODELET_INFO("done");
00381 }
00382
00383 void LINEMODTrainer::generateLINEMODTrainingData(
00384 pcl::PointCloud<pcl::PointXYZRGBA>::Ptr cloud,
00385 pcl::PointIndices::Ptr mask,
00386 pcl::ColorGradientModality<pcl::PointXYZRGBA>& color_grad_mod,
00387 pcl::SurfaceNormalModality<pcl::PointXYZRGBA>& surface_norm_mod,
00388 pcl::MaskMap& mask_map,
00389 pcl::RegionXY& region)
00390 {
00391 pcl::PointCloud<pcl::PointXYZRGBA>::Ptr masked_cloud
00392 (new pcl::PointCloud<pcl::PointXYZRGBA>);
00393 pcl::ExtractIndices<pcl::PointXYZRGBA> ex;
00394 ex.setKeepOrganized(true);
00395 ex.setInputCloud(cloud);
00396 ex.setIndices(mask);
00397 ex.filter(*masked_cloud);
00398 color_grad_mod.setInputCloud(masked_cloud);
00399 color_grad_mod.processInputData();
00400 surface_norm_mod.setInputCloud(cloud);
00401 surface_norm_mod.processInputData();
00402 size_t min_x(masked_cloud->width), min_y(masked_cloud->height), max_x(0), max_y(0);
00403 for (size_t j = 0; j < masked_cloud->height; ++j) {
00404 for (size_t i = 0; i < masked_cloud->width; ++i) {
00405 pcl::PointXYZRGBA p
00406 = masked_cloud->points[j * masked_cloud->width + i];
00407 if (!std::isnan(p.x) && !std::isnan(p.y) && !std::isnan(p.z)) {
00408 mask_map(i, j) = 1;
00409 min_x = std::min(min_x, i);
00410 max_x = std::max(max_x, i);
00411 min_y = std::min(min_y, j);
00412 max_y = std::max(max_y, j);
00413 }
00414 else {
00415 mask_map(i, j) = 0;
00416 }
00417 }
00418 }
00419 region.x = static_cast<int>(min_x);
00420 region.y = static_cast<int>(min_y);
00421 region.width = static_cast<int>(max_x - min_x + 1);
00422 region.height = static_cast<int>(max_y - min_y + 1);
00423 }
00424
00425
00426 std::vector<std::string> LINEMODTrainer::trainOneData(
00427 pcl::PointCloud<pcl::PointXYZRGBA>::Ptr cloud,
00428 pcl::PointIndices::Ptr mask,
00429 std::string& tempstr,
00430 int i)
00431 {
00432 pcl::LINEMOD linemod;
00433 pcl::PointCloud<pcl::PointXYZRGBA>::Ptr masked_cloud
00434 (new pcl::PointCloud<pcl::PointXYZRGBA>);
00435 pcl::ExtractIndices<pcl::PointXYZRGBA> ex;
00436 ex.setKeepOrganized(true);
00437 ex.setInputCloud(cloud);
00438 ex.setIndices(mask);
00439 ex.filter(*masked_cloud);
00440 pcl::ColorGradientModality<pcl::PointXYZRGBA> color_grad_mod;
00441 color_grad_mod.setInputCloud(masked_cloud);
00442 color_grad_mod.processInputData();
00443 pcl::SurfaceNormalModality<pcl::PointXYZRGBA> surface_norm_mod;
00444 surface_norm_mod.setInputCloud(masked_cloud);
00445 surface_norm_mod.processInputData();
00446 std::vector<pcl::QuantizableModality*> modalities(2);
00447 modalities[0] = &color_grad_mod;
00448 modalities[1] = &surface_norm_mod;
00449 size_t min_x(masked_cloud->width), min_y(masked_cloud->height), max_x(0), max_y(0);
00450 pcl::MaskMap mask_map(masked_cloud->width, masked_cloud->height);
00451 for (size_t j = 0; j < masked_cloud->height; ++j) {
00452 for (size_t i = 0; i < masked_cloud->width; ++i) {
00453 pcl::PointXYZRGBA p
00454 = masked_cloud->points[j * masked_cloud->width + i];
00455 if (!std::isnan(p.x) && !std::isnan(p.y) && !std::isnan(p.z)) {
00456 mask_map(i, j) = 1;
00457 min_x = std::min(min_x, i);
00458 max_x = std::max(max_x, i);
00459 min_y = std::min(min_y, j);
00460 max_y = std::max(max_y, j);
00461 }
00462 else {
00463 mask_map(i, j) = 0;
00464 }
00465 }
00466 }
00467 std::vector<pcl::MaskMap*> masks(2);
00468 masks[0] = &mask_map;
00469 masks[1] = &mask_map;
00470 pcl::RegionXY region;
00471 region.x = static_cast<int>(min_x);
00472 region.y = static_cast<int>(min_y);
00473 region.width = static_cast<int>(max_x - min_x + 1);
00474 region.height = static_cast<int>(max_y - min_y + 1);
00475 linemod.createAndAddTemplate(modalities, masks, region);
00476
00477 std::vector<std::string> ret;
00478 {
00479
00480 std::stringstream filename_stream;
00481 filename_stream << boost::format("%s/%05d_template.sqmmt") % tempstr % i;
00482 std::string filename = filename_stream.str();
00483 std::cerr << "writing " << filename << std::endl;
00484 std::ofstream file_stream;
00485 file_stream.open(filename.c_str(),
00486 std::ofstream::out | std::ofstream::binary);
00487 linemod.getTemplate(0).serialize(file_stream);
00488 file_stream.close();
00489 ret.push_back(filename);
00490 }
00491 {
00492
00493 std::stringstream filename_stream;
00494 filename_stream << boost::format("%s/%05d_template.pcd") % tempstr % i;
00495 std::string filename = filename_stream.str();
00496 std::cerr << "writing " << filename << std::endl;
00497 pcl::PCDWriter writer;
00498 writer.writeBinaryCompressed(filename, *masked_cloud);
00499 ret.push_back(filename);
00500 }
00501 return ret;
00502 }
00503
00504 void LINEMODTrainer::trainWithoutViewpointSampling()
00505 {
00506 NODELET_INFO("Start LINEMOD training from %lu samples", samples_.size());
00507 boost::filesystem::path temp = boost::filesystem::unique_path();
00508 boost::filesystem::create_directory(temp);
00509 std::string tempstr = temp.native();
00510 NODELET_INFO("mkdir %s", tempstr.c_str());
00511 std::vector<std::string> all_files;
00512 for (size_t i = 0; i < samples_.size(); i++) {
00513 NODELET_INFO("Processing %lu-th data", i);
00514 pcl::PointIndices::Ptr mask = sample_indices_[i];
00515 pcl::PointCloud<pcl::PointXYZRGBA>::Ptr cloud = samples_[i];
00516
00517
00518
00519
00520 }
00521 tar(tempstr, output_file_);
00522 NODELET_INFO("done");
00523 }
00524
00525 void LINEMODTrainer::tar(const std::string& directory, const std::string& output)
00526 {
00527 std::stringstream command_stream;
00528 command_stream << "tar --format=ustar -cf " << output << " " << directory << "/*";
00529 NODELET_INFO("executing %s", command_stream.str().c_str());
00530 int ret = system(command_stream.str().c_str());
00531 }
00532
00533 bool LINEMODTrainer::startTraining(
00534 std_srvs::Empty::Request& req,
00535 std_srvs::Empty::Response& res)
00536 {
00537 boost::mutex::scoped_lock lock(mutex_);
00538 if (sample_viewpoint_) {
00539 trainWithViewpointSampling();
00540 }
00541 else {
00542 trainWithoutViewpointSampling();
00543 }
00544 return true;
00545 }
00546
00547 void LINEMODDetector::onInit()
00548 {
00549 DiagnosticNodelet::onInit();
00550 pnh_->param("template_file", template_file_, std::string("template"));
00551
00552 template_cloud_.reset(new pcl::PointCloud<pcl::PointXYZRGBA>);
00553 pcl::PCDReader reader;
00554 reader.read(template_file_ + ".pcd", *template_cloud_);
00555 const std::string pose_yaml = template_file_ + "_poses.yaml";
00556 YAML::Node doc;
00557 #ifdef USE_OLD_YAML
00558 std::ifstream pose_fin;
00559 pose_fin.open(pose_yaml.c_str(), std::ifstream::in);
00560 YAML::Parser parser(pose_fin);
00561 while (parser.GetNextDocument(doc)) {
00562 const YAML::Node& template_pose_yaml = doc["template_poses"];
00563 for (size_t i = 0; i < template_pose_yaml.size(); i++) {
00564 const YAML::Node& pose = template_pose_yaml[i];
00565 Eigen::Affine3f trans = jsk_recognition_utils::affineFromYAMLNode(pose);
00566 template_poses_.push_back(trans);
00567
00568 pcl::PointCloud<pcl::PointXYZRGBA> transformed_cloud;
00569 pcl::transformPointCloud<pcl::PointXYZRGBA>(
00570 *template_cloud_, transformed_cloud, trans);
00571
00572 Eigen::Vector4f minpt, maxpt;
00573 pcl::getMinMax3D<pcl::PointXYZRGBA>(transformed_cloud, minpt, maxpt);
00574 jsk_recognition_msgs::BoundingBox bbox = jsk_recognition_utils::boundingBoxFromPointCloud(transformed_cloud);
00575
00576 template_bboxes_.push_back(bbox);
00577 }
00578 }
00579 pose_fin.close();
00580 #else
00581
00582 doc = YAML::LoadFile(pose_yaml);
00583 #endif
00584
00585
00586 srv_ = boost::make_shared <dynamic_reconfigure::Server<Config> > (*pnh_);
00587 dynamic_reconfigure::Server<Config>::CallbackType f =
00588 boost::bind (
00589 &LINEMODDetector::configCallback, this, _1, _2);
00590 srv_->setCallback (f);
00591
00592 pub_cloud_ = advertise<sensor_msgs::PointCloud2>(*pnh_, "output", 1);
00593 pub_detect_mask_ = advertise<sensor_msgs::Image>(*pnh_, "output/mask", 1);
00594 pub_pose_ = advertise<geometry_msgs::PoseStamped>(*pnh_, "output/pose", 1);
00595 pub_original_template_cloud_ = advertise<sensor_msgs::PointCloud2>(
00596 *pnh_, "output/template", 1);
00597
00598 onInitPostProcess();
00599 }
00600
00601 void LINEMODDetector::subscribe()
00602 {
00603 sub_cloud_ = pnh_->subscribe("input", 1, &LINEMODDetector::detect, this);
00604 }
00605
00606 void LINEMODDetector::unsubscribe()
00607 {
00608 sub_cloud_.shutdown();
00609 }
00610
00611 void LINEMODDetector::configCallback(Config &config, uint32_t level)
00612 {
00613 boost::mutex::scoped_lock lock(mutex_);
00614 gradient_magnitude_threshold_ = config.gradient_magnitude_threshold;
00615 detection_threshold_ = config.detection_threshold;
00616 color_gradient_mod_.setGradientMagnitudeThreshold(gradient_magnitude_threshold_);
00617 linemod_.setDetectionThreshold(detection_threshold_);
00618
00619
00620 const std::string linemod_file = template_file_ + ".linemod";
00621 std::ifstream linemod_in;
00622 linemod_in.open(linemod_file.c_str(), std::ifstream::in);
00623 linemod_.deserialize(linemod_in);
00624 linemod_in.close();
00625 }
00626
00627 void LINEMODDetector::computeCenterOfTemplate(
00628 pcl::PointCloud<pcl::PointXYZRGBA>::Ptr cloud,
00629 const pcl::SparseQuantizedMultiModTemplate& linemod_template,
00630 const pcl::LINEMODDetection& linemod_detection,
00631 Eigen::Vector3f& center)
00632 {
00633 const size_t start_x = std::max(linemod_detection.x, 0);
00634 const size_t start_y = std::max(linemod_detection.y, 0);
00635 const size_t end_x = std::min(
00636 static_cast<size_t> (start_x + linemod_template.region.width * linemod_detection.scale),
00637 static_cast<size_t> (cloud->width));
00638 const size_t end_y = std::min(
00639 static_cast<size_t> (start_y + linemod_template.region.height * linemod_detection.scale),
00640 static_cast<size_t> (cloud->height));
00641 size_t counter = 0;
00642 for (size_t row_index = start_y; row_index < end_y; ++row_index) {
00643 for (size_t col_index = start_x; col_index < end_x; ++col_index) {
00644 const pcl::PointXYZRGBA & point = (*cloud) (col_index, row_index);
00645 if (pcl_isfinite (point.x) &&
00646 pcl_isfinite (point.y) &&
00647 pcl_isfinite (point.z)) {
00648 center = center + point.getVector3fMap();
00649 ++counter;
00650 }
00651 }
00652 }
00653 center = center / counter;
00654 }
00655
00656 void LINEMODDetector::detect(
00657 const sensor_msgs::PointCloud2::ConstPtr& cloud_msg)
00658 {
00659 NODELET_INFO("detect");
00660 vital_checker_->poke();
00661 boost::mutex::scoped_lock lock(mutex_);
00662 pcl::PointCloud<pcl::PointXYZRGBA>::Ptr
00663 cloud (new pcl::PointCloud<pcl::PointXYZRGBA>);
00664 pcl::fromROSMsg(*cloud_msg, *cloud);
00665
00666 surface_normal_mod_.setInputCloud(cloud);
00667 surface_normal_mod_.processInputData ();
00668 color_gradient_mod_.setInputCloud (cloud);
00669 color_gradient_mod_.processInputData ();
00670 std::vector<pcl::LINEMODDetection> linemod_detections;
00671 std::vector<pcl::QuantizableModality*> modalities;
00672 modalities.push_back(&color_gradient_mod_);
00673 modalities.push_back(&surface_normal_mod_);
00674 linemod_.detectTemplatesSemiScaleInvariant(modalities, linemod_detections,
00675 0.6944444f, 1.44f, 1.2f);
00676 NODELET_INFO("detected %lu result", linemod_detections.size());
00677
00678 if (linemod_detections.size() > 0) {
00679 double max_score = 0;
00680 size_t max_template_id = 0;
00681 double max_scale = 0;
00682 pcl::LINEMODDetection linemod_detection;
00683 for (size_t i = 0; i < linemod_detections.size(); i++) {
00684 const pcl::LINEMODDetection& detection = linemod_detections[i];
00685 if (max_score < detection.score) {
00686 linemod_detection = detection;
00687 max_score = detection.score;
00688 }
00689 }
00690
00691 const pcl::SparseQuantizedMultiModTemplate& linemod_template =
00692 linemod_.getTemplate(linemod_detection.template_id);
00693 Eigen::Vector3f center(0, 0, 0);
00694 computeCenterOfTemplate(
00695 cloud, linemod_template, linemod_detection, center);
00696
00697 cv::Mat detect_mask = cv::Mat::zeros(cloud->width, cloud->height, CV_8UC1);
00698 int scaled_template_width
00699 = linemod_template.region.width * linemod_detection.scale;
00700 int scaled_template_height
00701 = linemod_template.region.height * linemod_detection.scale;
00702 cv::rectangle(
00703 detect_mask,
00704 cv::Point(linemod_detection.x, linemod_detection.y),
00705 cv::Point(linemod_detection.x + scaled_template_width,
00706 linemod_detection.y + scaled_template_height),
00707 cv::Scalar(255), CV_FILLED);
00708 pub_detect_mask_.publish(cv_bridge::CvImage(cloud_msg->header,
00709 "8UC1",
00710 detect_mask).toImageMsg());
00711
00712 jsk_recognition_msgs::BoundingBox bbox = template_bboxes_[linemod_detection.template_id];
00713 pcl::PointCloud<pcl::PointXYZRGBA>::Ptr
00714 result (new pcl::PointCloud<pcl::PointXYZRGBA>);
00715 pcl::transformPointCloud<pcl::PointXYZRGBA>(
00716 *template_cloud_, *result, template_poses_[linemod_detection.template_id]);
00717 Eigen::Vector4f minpt, maxpt;
00718 pcl::getMinMax3D<pcl::PointXYZRGBA>(*result, minpt, maxpt);
00719 Eigen::Vector4f template_center = (minpt + maxpt) / 2;
00720 Eigen::Vector3f translation = center - Eigen::Vector3f(template_center[0],
00721 template_center[1],
00722 template_center[2]);
00723 Eigen::Affine3f pose = template_poses_[linemod_detection.template_id] * Eigen::Translation3f(translation);
00724 geometry_msgs::PoseStamped ros_pose;
00725 tf::poseEigenToMsg(pose, ros_pose.pose);
00726 ros_pose.header = cloud_msg->header;
00727 pub_pose_.publish(ros_pose);
00728 for (size_t i = 0; i < result->points.size(); i++) {
00729 result->points[i].getVector3fMap()
00730 = result->points[i].getVector3fMap() + translation;
00731 }
00732 sensor_msgs::PointCloud2 ros_result;
00733 pcl::toROSMsg(*result, ros_result);
00734 ros_result.header = cloud_msg->header;
00735 pub_cloud_.publish(ros_result);
00736 sensor_msgs::PointCloud2 ros_template;
00737 pcl::toROSMsg(*template_cloud_, ros_template);
00738 ros_template.header = cloud_msg->header;
00739 pub_original_template_cloud_.publish(ros_template);
00740 }
00741 }
00742 }
00743
00744 #include <pluginlib/class_list_macros.h>
00745 PLUGINLIB_EXPORT_CLASS (jsk_pcl_ros::LINEMODTrainer, nodelet::Nodelet);
00746 PLUGINLIB_EXPORT_CLASS (jsk_pcl_ros::LINEMODDetector, nodelet::Nodelet);