00001 #include "hrl_table_detect/tabletop_segmentor.h"
00002 using namespace std;
00003
00004 namespace hrl_table_detect {
00005
00006 TabletopSegmentor::TabletopSegmentor() : img_trans(nh) {
00007 }
00008
00009
00010 void TabletopSegmentor::onInit() {
00012 nh_priv = ros::NodeHandle("~");
00013 bool run_service;
00014 nh_priv.param<bool>("run_service", run_service, false);
00015 nh_priv.param<double>("scan_width", maxy, 1.5); miny = -maxy;
00016 nh_priv.param<double>("min_scan_depth", minx, 0.3);
00017 nh_priv.param<double>("max_scan_depth", maxx, 3.0);
00018 nh_priv.param<double>("min_table_height", minz, 0.5);
00019 nh_priv.param<double>("max_table_height", maxz, 1.0);
00020 nh_priv.param<double>("height_image_res", resolution, 200);
00021 nh_priv.param<double>("inlier_magnitude", inlier_magnitude, 200);
00022 nh_priv.param<int32_t>("num_edge_dilate", num_edge_dilate, 1);
00023 nh_priv.param<int32_t>("num_closes", num_closes, 1);
00024 nh_priv.param<double>("degree_bins", degree_bins, 0.3);
00025 nh_priv.param<double>("hough_thresh", hough_thresh, 30); hough_thresh *= resolution;
00026 nh_priv.param<double>("theta_gran", theta_gran, 1); theta_gran *= CV_PI/180;
00027 nh_priv.param<double>("rho_gran", rho_gran, 0.1); rho_gran *= resolution;
00028 nh_priv.param<double>("xgran", xgran, 0.1); xgran *= resolution;
00029 nh_priv.param<double>("ygran", ygran, 0.1); ygran *= resolution;
00030 nh_priv.param<double>("obj_min_area", obj_min_area, 0.02); obj_min_area *= resolution;
00031 imgx = (maxx-minx)*resolution;
00032 imgy = (maxy-miny)*resolution;
00034
00035 obj_poses_found = false;
00036 height_img_sum = cv::Mat::zeros(imgx, imgy, CV_32F);
00037 height_img_count = cv::Mat::zeros(imgx, imgy, CV_32F);
00038 height_img_max = cv::Mat::zeros(imgx, imgy, CV_8U);
00039
00040
00041 height_pub = img_trans.advertise("/obj_height_image", 1);
00042 obj_arr_pub = nh.advertise<geometry_msgs::PoseArray>("/table_object_poses", 1);
00043 nh_priv = ros::NodeHandle("~");
00044 if(run_service) {
00045 table_detect_start = nh.advertiseService("/obj_segment_start", &TabletopSegmentor::startCallback, this);
00046 table_detect_stop = nh.advertiseService("/obj_segment_stop", &TabletopSegmentor::stopCallback, this);
00047 table_detect_inst = nh.advertiseService("/obj_segment_inst", &TabletopSegmentor::instCallback, this);
00048 }
00049 else
00050 pc_sub = nh.subscribe("/kinect_head/rgb/points", 1, &TabletopSegmentor::pcCallback, this);
00051 ros::Duration(1.0).sleep();
00052 }
00053
00054 bool TabletopSegmentor::startCallback(hrl_table_detect::DetectTableStart::Request& req,
00055 hrl_table_detect::DetectTableStart::Response& resp) {
00056 obj_poses_found = false;
00057 height_img_sum = cv::Mat::zeros(imgx, imgy, CV_32F);
00058 height_img_count = cv::Mat::zeros(imgx, imgy, CV_32F);
00059 height_img_max = cv::Mat::zeros(imgx, imgy, CV_8U);
00060 pc_sub = nh.subscribe("/kinect_head/rgb/points", 1, &TabletopSegmentor::pcCallback, this);
00061 return true;
00062 }
00063
00064 bool TabletopSegmentor::stopCallback(hrl_table_detect::DetectTableStop::Request& req,
00065 hrl_table_detect::DetectTableStop::Response& resp) {
00066 pc_sub.shutdown();
00067 resp.grasp_points = obj_poses;
00068 return true;
00069 }
00070
00071 bool TabletopSegmentor::instCallback(hrl_table_detect::DetectTableInst::Request& req,
00072 hrl_table_detect::DetectTableInst::Response& resp) {
00073 obj_poses_found = false;
00074 height_img_sum = cv::Mat::zeros(imgx, imgy, CV_32F);
00075 height_img_count = cv::Mat::zeros(imgx, imgy, CV_32F);
00076 height_img_max = cv::Mat::zeros(imgx, imgy, CV_8U);
00077 pc_sub = nh.subscribe("/kinect_head/rgb/points", 1, &TabletopSegmentor::pcCallback, this);
00078 double begin_time = ros::Time::now().toSec();
00079 double now = begin_time;
00080 ros::Rate r(100);
00081 while(ros::ok() && now - begin_time < req.block_time) {
00082 ros::spinOnce();
00083 now = ros::Time::now().toSec();
00084 r.sleep();
00085 }
00086 pc_sub.shutdown();
00087 resp.grasp_points = obj_poses;
00088 return true;
00089 }
00090
00091 bool compind(uint32_t a, uint32_t b, vector<float> v) { return v[a] < v[b]; }
00092
00093 void convCvBox2DPoly(CvBox2D& box, CvPoint**& pts, int*& npts) {
00094 pts = new CvPoint*[1]; pts[0] = new CvPoint[4];
00095 float x = box.center.x, y = box.center.y;
00096 float x1 = box.size.height*cos(box.angle-CV_PI/2), y1 = -box.size.height*sin(box.angle-CV_PI/2);
00097 float x2 = box.size.width*cos(box.angle), y2 = -box.size.width*sin(box.angle);
00098 pts[0][0].x = x+x1+x2; pts[0][0].y = y+y1+y2;
00099 pts[0][1].x = x-x1+x2; pts[0][1].y = y-y1+y2;
00100 pts[0][2].x = x-x1-x2; pts[0][2].y = y-y1-y2;
00101 pts[0][3].x = x+x1-x2; pts[0][3].y = y+y1-y2;
00102 pts[0][4].x = x+x1+x2; pts[0][4].y = y+y1+y2;
00103 npts = new int[1]; npts[0] = 5;
00104 }
00105
00106 void drawCvBox2D(IplImage& img, CvBox2D& box, cv::Scalar color, int width=1) {
00107 CvPoint** rpts; int* npts;
00108 convCvBox2DPoly(box, rpts, npts);
00109 cvPolyLine(&img, rpts, npts, 1, 1, color, width);
00110 delete[] rpts[0]; delete[] rpts; delete[] npts;
00111 }
00112
00113 void fillCvBox2D(IplImage& img, CvBox2D& box, cv::Scalar color) {
00114 CvPoint** rpts; int* npts;
00115 convCvBox2DPoly(box, rpts, npts);
00116 cvFillPoly(&img, rpts, npts, 1, color);
00117 delete[] rpts[0]; delete[] rpts; delete[] npts;
00118 }
00119
00120 void TabletopSegmentor::pcCallback(sensor_msgs::PointCloud2::ConstPtr pc_msg) {
00121 if(!pc_lock.try_lock())
00122 return;
00123
00124 pcl::PointCloud<pcl::PointXYZRGB> pc_full, pc_full_frame;
00125 pcl::fromROSMsg(*pc_msg, pc_full);
00126 string base_frame("/base_link");
00127 ros::Time now = ros::Time::now();
00128 tf_listener.waitForTransform(pc_msg->header.frame_id, base_frame, now, ros::Duration(3.0));
00129 pcl_ros::transformPointCloud(base_frame, pc_full, pc_full_frame, tf_listener);
00130
00131
00132 cv::Mat cur_height_img = cv::Mat::zeros(imgx, imgy, CV_8U);
00133
00134 BOOST_FOREACH(const pcl::PointXYZRGB& pt, pc_full_frame.points) {
00135 if(pt.x != pt.x || pt.y != pt.y || pt.z != pt.z)
00136 continue;
00137 int32_t x, y, z;
00138 x = (pt.x - minx)/(maxx-minx) * imgx;
00139 y = (pt.y - miny)/(maxy-miny) * imgy;
00140 z = (pt.z - minz)/(maxz-minz) * 256;
00141 if(x < 0 || y < 0) continue;
00142 if(x >= imgx || y >= imgy) continue;
00143 if(z < 0 || z >= 256) continue;
00144 if(cur_height_img.at<uint8_t>(x, y) == 0 || cur_height_img.at<uint8_t>(x, y) < (uint8_t) z)
00145 cur_height_img.at<uint8_t>(x, y) = (uint8_t) z;
00146 }
00147 cv::max(height_img_max, cur_height_img, height_img_max);
00148 cv::Mat cur_height_img_flt;
00149 cur_height_img.convertTo(cur_height_img_flt, CV_32F);
00150 height_img_sum += cur_height_img_flt;
00151 cv::Mat cur_count(imgx, imgy, CV_8U);
00152 cur_count = (cur_height_img > 0) / 255;
00153 cv::Mat cur_count_flt(imgx, imgy, CV_32F);
00154 cur_count.convertTo(cur_count_flt, CV_32F);
00155 height_img_count += cur_count_flt;
00156 cv::Mat height_img_avg_flt = height_img_sum / height_img_count;
00157 cv::Mat height_img_avg(imgx, imgy, CV_8U);
00158 height_img_avg_flt.convertTo(height_img_avg, CV_8U);
00159 height_img_avg = height_img_max;
00160
00161 cv::Mat height_hist(256, 1, CV_32F, cv::Scalar(0));
00162 for(uint32_t x=0;x<imgx;x++)
00163 for(uint32_t y=0;y<imgy;y++) {
00164 if(height_img_avg.at<uint8_t>(x,y) == 255)
00165 height_img_avg.at<uint8_t>(x,y) = 0;
00166 if(height_img_avg.at<uint8_t>(x,y) != 0) {
00167 height_hist.at<float>(height_img_avg.at<uint8_t>(x,y), 0)++;
00168 }
00169 }
00171 uint32_t gfiltlen = 25;
00172 float stddev = 256/(maxz-minz) * 0.015;
00173 cv::Mat gauss_filt(gfiltlen, 1, CV_32F, cv::Scalar(0));
00174 for(uint32_t i=0;i<gfiltlen;i++)
00175 gauss_filt.at<float>(i,0) = 0.39894 / stddev * std::exp(-(i-((float)gfiltlen)/2)*(i-((float)gfiltlen)/2)/(2*stddev*stddev));
00176
00177 uint32_t maxval = 0, maxidx = 0;
00178 for(uint32_t i=0;i<256-gfiltlen;i++) {
00179 uint32_t sum = 0;
00180 for(uint32_t j=0;j<gfiltlen;j++)
00181 sum += height_hist.at<float>(i+j,0) * gauss_filt.at<float>(j,0);
00182 if(sum > maxval && i != 0) {
00183 maxval = sum;
00184 maxidx = i+gfiltlen/2;
00185 }
00186 }
00187 int32_t table_height = ((int32_t)maxidx);
00188
00190 cv::Mat height_img_thresh(imgx, imgy, CV_8U);
00191 height_img_thresh = height_img_avg.clone();
00192 for(uint32_t x=0;x<imgx;x++)
00193 for(uint32_t y=0;y<imgy;y++) {
00194 if(std::fabs(table_height - ((int32_t)height_img_thresh.at<uint8_t>(x,y))) < stddev*2)
00195 height_img_thresh.at<uint8_t>(x,y) = 255;
00196 else
00197 height_img_thresh.at<uint8_t>(x,y) = 0;
00198 }
00200 IplImage height_img_thresh_ipl = height_img_thresh;
00201 IplConvKernel* element = cvCreateStructuringElementEx(3, 3, 1, 1, CV_SHAPE_RECT);
00202 cvMorphologyEx(&height_img_thresh_ipl, &height_img_thresh_ipl, NULL, element, CV_MOP_CLOSE, num_closes);
00203
00204
00205 cv::Mat height_img_thresh_blob = height_img_thresh.clone();
00206 IplImage blob_img = height_img_thresh_blob;
00207 CBlobResult blobs = CBlobResult(&blob_img, NULL, 0);
00208
00209 CBlob biggestblob;
00210 blobs.GetNthBlob(CBlobGetArea(), 0, biggestblob);
00211 cv::Mat table_blob(imgx, imgy, CV_8U, cv::Scalar(0)); IplImage table_blob_img = table_blob;
00212 biggestblob.FillBlob(&table_blob_img, cv::Scalar(150));
00213
00214 CvBox2D table_roi = biggestblob.GetEllipse(); table_roi.angle *= CV_PI/180;
00215 cv::Mat table_hull(imgx, imgy, CV_8U, cv::Scalar(0));
00216 IplImage hull_img = table_hull;
00217 fillCvBox2D(hull_img, table_roi, cv::Scalar(255));
00218
00219
00220
00221
00222
00223
00224 cv::Mat above_table(imgx, imgy, CV_8U);
00225 bitwise_and(height_img_max > table_height + stddev*2, table_hull, above_table);
00226 IplImage above_table_img = above_table;
00227
00228 CBlobResult obj_blobs = CBlobResult(&above_table_img, NULL, 0);
00229 obj_blobs.Filter(obj_blobs, B_EXCLUDE, CBlobGetArea(), B_LESS, obj_min_area);
00230 CBlob cur_obj_blob;
00231 vector<float> obj_cents_x, obj_cents_y, obj_cents_r, obj_areas, obj_dists;
00232 vector<uint32_t> obj_inds;
00233 for(int i=0;i<obj_blobs.GetNumBlobs();i++) {
00234 obj_blobs.GetNthBlob(CBlobGetArea(), i, cur_obj_blob);
00235 CvBox2D obj_box = cur_obj_blob.GetEllipse();
00236 obj_cents_x.push_back(obj_box.center.x);
00237 obj_cents_y.push_back(obj_box.center.y);
00238 obj_cents_r.push_back(obj_box.angle * CV_PI/180);
00239 obj_areas.push_back(cur_obj_blob.Area());
00240 obj_dists.push_back((obj_box.center.x-imgx/2)*(obj_box.center.x-imgx/2)+obj_box.center.y*obj_box.center.y);
00241 obj_inds.push_back(i);
00242 }
00243 boost::function<bool(uint32_t, uint32_t)> sortind = boost::bind(&compind, _1, _2, obj_dists);
00244 sort(obj_inds.begin(), obj_inds.end(), sortind);
00245
00246 obj_poses.poses.clear();
00247 for(uint32_t i=0;i<obj_inds.size();i++) {
00248 float posey = obj_cents_x[obj_inds[i]], posex = obj_cents_y[obj_inds[i]];
00249 float poser = -obj_cents_r[obj_inds[i]] + 3*CV_PI/2;
00250 geometry_msgs::Pose cpose;
00251 cpose.position.x = posex/imgx*(maxx-minx) + minx;
00252 cpose.position.y = posey/imgy*(maxy-miny) + miny;
00253 cpose.position.z = table_height/256.0*(maxz-minz) + minz;
00254 btMatrix3x3 quatmat; btQuaternion quat;
00255 quatmat.setEulerZYX(poser, 0 , 0);
00256 quatmat.getRotation(quat);
00257 cpose.orientation.x = quat.x(); cpose.orientation.y = quat.y();
00258 cpose.orientation.z = quat.z(); cpose.orientation.w = quat.w();
00259 CvPoint centerpt; centerpt.y = posex; centerpt.x = posey;
00260 printf("[%d](%f, %f, area: %f)\n", i, posex, posey, obj_areas[obj_inds[i]]);
00261 IplImage height_img_max_ipl = height_img_max;
00262 cvCircle(&height_img_max_ipl, centerpt, 3, cv::Scalar(200), 2);
00263 obj_poses.poses.push_back(cpose);
00264 }
00265 obj_poses.header.stamp = ros::Time::now();
00266 obj_poses.header.frame_id = base_frame;
00267 obj_arr_pub.publish(obj_poses);
00268
00269 cv_bridge::CvImage cvb_height_img;
00270
00271
00272
00273
00274
00275
00276
00277 cvb_height_img.image = above_table;
00278
00279 cvb_height_img.header.stamp = ros::Time::now();
00280 cvb_height_img.header.frame_id = base_frame;
00281 cvb_height_img.encoding = enc::MONO8;
00282 height_pub.publish(cvb_height_img.toImageMsg());
00283 pc_full_frame.header.stamp = ros::Time::now();
00284 pc_full_frame.header.frame_id = base_frame;
00285
00286
00287 if(obj_poses.poses.size() > 0)
00288 obj_poses_found = true;
00289
00290
00291 pc_lock.unlock();
00292 }
00293
00294 };
00295
00296 using namespace hrl_table_detect;
00297
00298 int main(int argc, char **argv)
00299 {
00300 ros::init(argc, argv, "tabletop_segmentation");
00301 TabletopSegmentor ta;
00302 ta.onInit();
00303 ros::spin();
00304 return 0;
00305 }