00001 #include <ros/ros.h>
00002 #include <image_transport/image_transport.h>
00003 #include <opencv/cv.h>
00004 #include <opencv/highgui.h>
00005 #include <cv_bridge/CvBridge.h>
00006
00007 #include <image_geometry/pinhole_camera_model.h>
00008
00009 #include <pcl_ros/transforms.h>
00010 #include <tf/transform_listener.h>
00011 #include <tf/transform_broadcaster.h>
00012 #include <angles/angles.h>
00013 #include <sensor_msgs/PointCloud.h>
00014
00015 #include <boost/thread/mutex.hpp>
00016
00017
00018 #include <pcl/ros/conversions.h>
00019 #include <sensor_msgs/point_cloud_conversion.h>
00020 #include <pcl/point_types.h>
00021 #include <pcl/io/pcd_io.h>
00022
00023 #include "pcl/filters/extract_indices.h"
00024 #include "pcl/segmentation/extract_clusters.h"
00025 #include "pcl/kdtree/kdtree_flann.h"
00026
00027 #include <boost/algorithm/string.hpp>
00028 #include <boost/filesystem.hpp>
00029 #include "pcl/common/common.h"
00030
00031 #include <geometry_msgs/PolygonStamped.h>
00032 typedef pcl::PointXYZ Point;
00033 typedef pcl::KdTree<Point>::Ptr KdTreePtr;
00034
00035 class PointCloudToImageProjector
00036 {
00037
00038 ros::NodeHandle nh_;
00039 image_transport::Publisher image_pub_;
00040 image_transport::CameraSubscriber image_sub_;
00041 ros::Subscriber cloud_sub_;
00042 ros::Publisher polygon_pub_;
00043
00044 tf::TransformListener tf_listener_;
00045
00046 image_transport::ImageTransport it_;
00047 sensor_msgs::CvBridge bridge_;
00048 std::string input_image_topic_, input_cloud_topic_, output_cluster_topic_, output_image_topic_, output_polygon_points_topic_;
00049
00050 pcl::PointCloud<pcl::PointXYZ> cloud_in_, cloud_in_tranformed_;
00051 IplImage* image_;
00052 boost::mutex cloud_lock_;
00053 std::vector <pcl::PointCloud<pcl::PointXYZ> > cloud_queue_, cluster_queue_;
00054
00055 image_geometry::PinholeCameraModel cam_model_;
00056 int offset_, nr_clusters_;
00057 pcl::EuclideanClusterExtraction<Point> cluster_;
00058 int object_cluster_min_size_, counter_, count_images_;
00059 double object_cluster_tolerance_;
00060 KdTreePtr clusters_tree_;
00061 bool cluster_cloud_, save_data_;
00062 std::string image_cluster_name_, location_;
00063 pcl::PCDWriter pcd_writer_;
00064 pcl::PointXYZ point_center_, point_min_, point_max_;
00065 geometry_msgs::PolygonStamped polygon_points_;
00066
00067 public:
00068 PointCloudToImageProjector(ros::NodeHandle &n)
00069 : nh_(n), it_(nh_)
00070 {
00071 nh_.param("input_image_topic", input_image_topic_, std::string("/wide_stereo/right/image_rect_color"));
00072 nh_.param("input_cloud_topic", input_cloud_topic_, std::string("/laser_table_detector/extract_object_clusters/output"));
00073 nh_.param("output_cluster_topic", output_cluster_topic_, std::string("output_cluster"));
00074 nh_.param("output_image_topic", output_image_topic_, std::string("image_with_projected_cluster"));
00075 nh_.param("output_polygon_points_topic", output_polygon_points_topic_, std::string("polygon_points"));
00076 nh_.param("offset", offset_, 0);
00077 nh_.param("object_cluster_min_size", object_cluster_min_size_, 100);
00078 nh_.param("object_cluster_tolerance", object_cluster_tolerance_, 0.03);
00079 nh_.param("cluster_cloud", cluster_cloud_, false);
00080 nh_.param("image_cluster_name", image_cluster_name_, std::string(""));
00081 nh_.param("location", location_, std::string("island"));
00082 nh_.param("save_data", save_data_, false);
00083 nh_.param("nr_clusters_", nr_clusters_, 1);
00084 counter_ = count_images_ = 0;
00085 image_sub_ = it_.subscribeCamera(input_image_topic_, 1, &PointCloudToImageProjector::image_cb, this);
00086 cloud_sub_= nh_.subscribe (input_cloud_topic_, 10, &PointCloudToImageProjector::cloud_cb, this);
00087 image_pub_ = it_.advertise(output_image_topic_, 100);
00088 polygon_pub_ = nh_.advertise<geometry_msgs::PolygonStamped>(output_polygon_points_topic_, 1);
00089 clusters_tree_ = boost::make_shared<pcl::KdTreeFLANN<Point> > ();
00090 clusters_tree_->setEpsilon (1);
00091 }
00092
00093 void cluster_cloud (pcl::PointCloud<pcl::PointXYZ> &cloud_in, std::vector<pcl::PointCloud<pcl::PointXYZ> > &cluster_queue)
00094 {
00095 std::vector<pcl::PointIndices> clusters;
00096 cluster_.setInputCloud (boost::make_shared<pcl::PointCloud<pcl::PointXYZ> >(cloud_in));
00097 cluster_.setClusterTolerance (object_cluster_tolerance_);
00098 cluster_.setMinClusterSize (object_cluster_min_size_);
00099
00100 cluster_.setSearchMethod (clusters_tree_);
00101 cluster_.extract (clusters);
00102
00103 ROS_INFO("[PointCloudToImageProjector: ] Found clusters: %ld", clusters.size());
00104
00105 cluster_queue.resize(clusters.size());
00106 for (unsigned int i = 0; i < clusters.size(); i++)
00107 {
00108 pcl::copyPointCloud (cloud_in, clusters[i], cluster_queue[i]);
00109 }
00110 }
00111
00112 void cloud_cb (const sensor_msgs::PointCloud2ConstPtr& pc)
00113 {
00114 unsigned long size = pc->height*pc->width;
00115 ROS_INFO("[PointCloudToImageProjector: ] cloud received with points: %ld in frame %s",
00116 size, pc->header.frame_id.c_str());
00117 pcl::fromROSMsg(*pc, cloud_in_);
00118 boost::mutex::scoped_lock lock(cloud_lock_);
00119 cloud_queue_.push_back(cloud_in_);
00120 counter_++;
00121 }
00122
00123
00124 void image_cb(const sensor_msgs::ImageConstPtr& image_msg, const sensor_msgs::CameraInfoConstPtr& info_msg)
00125 {
00126
00127
00128 if (!cloud_queue_.empty())
00129 {
00130 image_ = NULL;
00131 try
00132 {
00133 image_ = bridge_.imgMsgToCv(image_msg, "rgb8");
00134 }
00135 catch (sensor_msgs::CvBridgeException& ex) {
00136 ROS_ERROR("[PointCloudToImageProjector:] Failed to convert image");
00137 return;
00138 }
00139 cam_model_.fromCameraInfo(info_msg);
00140 for (unsigned long i = 0; i < cloud_queue_.size(); i++)
00141 {
00142 if (cluster_cloud_)
00143 {
00144 cluster_cloud (cloud_queue_[i], cluster_queue_);
00145 for (unsigned long j = 0; j < cluster_queue_.size(); j++)
00146 {
00147 process(cluster_queue_[j], image_);
00148 }
00149 cluster_queue_.clear();
00150 }
00151 else
00152 {
00153 process(cloud_queue_[i], image_);
00154 }
00155 }
00156 cloud_queue_.clear();
00157 }
00158 }
00159
00160
00161
00162
00163
00164
00165
00166
00167 void process (pcl::PointCloud<pcl::PointXYZ> &cloud_in, IplImage *image)
00168 {
00169
00170 if (cloud_in.width == 1 && cloud_in.height == 1)
00171 {
00172
00173
00174
00175
00176
00177 IplImage * token = cvCreateImage(cvSize(2, 480), image->depth, image->nChannels);
00178 cvZero(token);
00179 image_pub_.publish(bridge_.cvToImgMsg(token));
00180 ROS_INFO("[PointCloudToImageProjector:] Published TOKEN Image");
00181 cvReleaseImage(&token);
00182 return;
00183 }
00184 pcl_ros::transformPointCloud(cam_model_.tfFrame(), cloud_in, cloud_in_tranformed_, tf_listener_);
00185 ROS_INFO("[PointCloudToImageProjector:] cloud transformed to %s", cam_model_.tfFrame().c_str());
00186
00187 CvMemStorage* stor = cvCreateMemStorage (0);
00188 CvSeq* seq = cvCreateSeq (CV_SEQ_ELTYPE_POINT, sizeof (CvSeq), sizeof (CvPoint), stor);
00189 if (cloud_in_tranformed_.points.size() == 0)
00190 {
00191 ROS_WARN("[PointCloudToImageProjector:] Point cloud points with size 0!!! Returning.");
00192 return;
00193 }
00194
00195 polygon_points_.polygon.points.clear();
00196 geometry_msgs::Point32 tmp_pt;
00197 for (unsigned long i = 0; i < cloud_in_tranformed_.points.size(); i++)
00198 {
00199 cv::Point3d pt_cv(cloud_in_tranformed_.points[i].x, cloud_in_tranformed_.points[i].y,
00200 cloud_in_tranformed_.points[i].z);
00201 cv::Point2d uv;
00202 cam_model_.project3dToPixel(pt_cv, uv);
00203 ROS_DEBUG_STREAM("points projected: " << uv.x << " " << uv.y);
00204
00205 if (uv.x < 0 || uv.x > image->width)
00206 {
00207
00208 continue;
00209 }
00210 if (uv.y < 0 || uv.y > image->height)
00211 {
00212
00213 continue;
00214 }
00215
00216
00217 CvPoint pt;
00218 pt.x = uv.x;
00219 pt.y = uv.y;
00220 tmp_pt.x = uv.x;
00221 tmp_pt.y = uv.y;
00222 tmp_pt.z = 0.0;
00223 polygon_points_.polygon.points.push_back(tmp_pt);
00224 cvSeqPush( seq, &pt );
00225 }
00226 polygon_points_.header.stamp = ros::Time::now();
00227 polygon_points_.header.seq = 0;
00228 polygon_pub_.publish(polygon_points_);
00229 ROS_INFO("Polygon with %ld points published", polygon_points_.polygon.points.size());
00230
00231 CvRect rect = cvBoundingRect(seq);
00232
00233 ROS_DEBUG_STREAM("rect: " << rect.x << " " << rect.y << " " << rect.width << " " << rect.height);
00234
00235 if (rect.width == 0 || rect.height == 0)
00236 {
00237 ROS_WARN("[PointCloudToImageProjector:] Rectangle's width = height = 0. This is impossible. Skipping ROI extraction.");
00238 return;
00239 }
00240 CvPoint pt1, pt2;
00241 pt1.x = rect.x;
00242
00243 if ((rect.x + (rect.width + 0.1*rect.width)) > image->width)
00244 {
00245 rect.width = image->width + - rect.x;
00246 }
00247 else
00248 rect.width = rect.width + 0.1 * rect.width;
00249
00250 pt2.x = rect.x + rect.width;
00251 pt1.y = rect.y;
00252
00253
00254 if ((rect.y + (rect.height + 0.1*rect.height)) > image->height)
00255 {
00256 rect.height = image->height + - rect.y;
00257 }
00258 else
00259 rect.height = rect.height + 0.1 * rect.height;
00260 pt2.y = rect.y+rect.height;
00261
00262
00263
00264
00265
00266
00267 rect.x = rect.x - offset_;
00268 rect.y = rect.y - offset_;
00269 rect.height = rect.height + 2*offset_;
00270 rect.width = rect.width + 2*offset_;
00271 IplImage *subimage;
00272 cvSetImageROI(image,rect);
00273
00274 subimage = cvCreateImage( cvSize(rect.width, rect.height), image->depth, image->nChannels );
00275 cvCopy(image, subimage);
00276 cvResetImageROI(image);
00277 image_pub_.publish(bridge_.cvToImgMsg(subimage, "rgb8"));
00278
00279 if (save_data_)
00280 {
00281 char counter_char[100];
00282 sprintf (counter_char, "%04d", counter_);
00283 std::stringstream ss;
00284 std::stringstream ss_position;
00285 getMinMax3D (cloud_in, point_min_, point_max_);
00286
00287 point_center_.x = (point_max_.x + point_min_.x)/2;
00288 point_center_.y = (point_max_.y + point_min_.y)/2;
00289 point_center_.z = (point_max_.z + point_min_.z)/2;
00290 ss_position << point_center_.x << "_" << point_center_.y << "_" << point_center_.z;
00291
00292 ss << ros::Time::now();
00293 image_cluster_name_ = location_ + "/" + std::string(counter_char) + "/" + "NAME" + "_" + ss.str() + "_" + ss_position.str();
00294
00295 boost::filesystem::path outpath (location_ + "/" + std::string(counter_char));
00296 if (!boost::filesystem::exists (outpath))
00297 {
00298 if (!boost::filesystem::create_directories (outpath))
00299 {
00300 ROS_ERROR ("Error creating directory %s.", (location_ + "/" + std::string(counter_char)).c_str ());
00301
00302 }
00303 ROS_INFO ("Creating directory %s", (location_ + "/" + std::string(counter_char)).c_str ());
00304 }
00305
00306 pcd_writer_.write (image_cluster_name_ + ".pcd", cloud_in_tranformed_, true);
00307 cv::imwrite(image_cluster_name_ + ".png", subimage);
00308 }
00309 ROS_INFO("[PointCloudToImageProjector:] image with size %d %d published to %s", subimage->width, subimage->height, output_image_topic_.c_str());
00310 cvReleaseImage(&subimage);
00311 }
00312 };
00313
00314 int main(int argc, char** argv)
00315 {
00316 ros::init(argc, argv, "pointcloud_to_image_projector_opencv_node");
00317 ros::NodeHandle n("~");
00318 PointCloudToImageProjector pp(n);
00319 ros::spin();
00320
00321 }