00001 #include "hrl_object_fetching/tabletop_detector.h"
00002 using namespace std;
00003
00004 namespace hrl_object_fetching {
00005
00006 TabletopDetector::TabletopDetector() : img_trans(nh) {
00007 }
00008
00009
00010 void TabletopDetector::onInit() {
00011 pc_pub = nh.advertise<pcl::PointCloud<pcl::PointXYZRGB> >("/table_detection_vis", 1);
00012 height_pub = img_trans.advertise("/height_image", 1);
00013 pose_arr_pub = nh.advertise<geometry_msgs::PoseArray>("/table_approach_poses", 1);
00014 nh_priv = ros::NodeHandle("~");
00015 bool run_service;
00016 nh_priv.param<bool>("run_service", run_service, false);
00017 if(run_service)
00018 table_detect_service = nh.advertiseService("/table_detect", &TabletopDetector::srvCallback, this);
00019 else
00020 pc_sub = nh.subscribe("/kinect_head/rgb/points", 1, &TabletopDetector::pcCallback, this);
00021 ros::Duration(1.0).sleep();
00022 }
00023
00024 bool TabletopDetector::srvCallback(hrl_object_fetching::DetectTable::Request& req,
00025 hrl_object_fetching::DetectTable::Response& resp) {
00026 pc_sub = nh.subscribe("/kinect_head/rgb/points", 1, &TabletopDetector::pcCallback, this);
00027 grasp_points_found = false;
00028 while(ros::ok()) {
00029 ros::spinOnce();
00030 if(grasp_points_found) {
00031 resp.grasp_points = grasp_points;
00032 pc_sub.shutdown();
00033 return true;
00034 }
00035 ros::Duration(0.1).sleep();
00036 }
00037 return false;
00038 }
00039
00040 bool compind(int a, int b, vector<float> v) { return std::fabs(v[a]-CV_PI/2) < std::fabs(v[b]-CV_PI/2); }
00041
00042 void TabletopDetector::pcCallback(sensor_msgs::PointCloud2::ConstPtr pc_msg) {
00043
00044 pcl::PointCloud<pcl::PointXYZRGB> pc_full, pc_full_frame;
00045 pcl::fromROSMsg(*pc_msg, pc_full);
00046 string head_frame("/head_pan_link");
00047 string base_frame("/base_link");
00048 ros::Time now = ros::Time::now();
00049 tf_listener.waitForTransform(pc_msg->header.frame_id, head_frame, now, ros::Duration(3.0));
00050 pcl_ros::transformPointCloud(head_frame, pc_full, pc_full_frame, tf_listener);
00051
00052
00053 tf_listener.waitForTransform(base_frame, head_frame, now, ros::Duration(3.0));
00054 tf::StampedTransform base_head_trans;
00055 tf_listener.lookupTransform(base_frame, head_frame, now, base_head_trans);
00056 float z_diff = base_head_trans.getOrigin().z();
00057
00058 float minx = 0.3, maxx = 3.0, miny = -1.5, maxy = 1.5;
00059 float minz = 0.6 - z_diff, maxz = 1.0 - z_diff;
00060 float resolution = 200;
00061 uint32_t imgx = (maxx-minx)*resolution;
00062 uint32_t imgy = (maxy-miny)*resolution;
00063 cv::Mat height_img(imgx, imgy, CV_8U, cv::Scalar(0));
00064 cv_bridge::CvImage cvb_height_img;
00065
00066 BOOST_FOREACH(const pcl::PointXYZRGB& pt, pc_full_frame.points) {
00067 if(pt.x != pt.x || pt.y != pt.y || pt.z != pt.z)
00068 continue;
00069 int32_t x, y, z;
00070 x = (pt.x - minx)/(maxx-minx) * imgx;
00071 y = (pt.y - miny)/(maxy-miny) * imgy;
00072 z = (pt.z - minz)/(maxz-minz) * 256;
00073 if(x < 0 || y < 0) continue;
00074 if(x >= imgx || y >= imgy) continue;
00075 if(z < 0 || z >= 256) continue;
00076 if(height_img.at<uint8_t>(x, y) == 0 || height_img.at<uint8_t>(x, y) < (uint8_t) z)
00077 height_img.at<uint8_t>(x, y) = (uint8_t) z;
00078 }
00079
00080 cv::Mat height_hist(256, 1, CV_32F, cv::Scalar(0));
00081 for(uint32_t x=0;x<imgx;x++)
00082 for(uint32_t y=0;y<imgy;y++) {
00083 if(height_img.at<uint8_t>(x,y) == 255)
00084 height_img.at<uint8_t>(x,y) = 0;
00085 if(height_img.at<uint8_t>(x,y) != 0) {
00086 height_hist.at<float>(height_img.at<uint8_t>(x,y), 0)++;
00087 }
00088 }
00089 uint32_t gfiltlen = 25;
00090 float stddev = 6;
00091 cv::Mat gauss_filt(gfiltlen, 1, CV_32F, cv::Scalar(0));
00092 for(uint32_t i=0;i<gfiltlen;i++)
00093 gauss_filt.at<float>(i,0) = 0.39894 / stddev * std::exp(-(i-((float)gfiltlen)/2)*(i-((float)gfiltlen)/2)/(2*stddev*stddev));
00094
00095 uint32_t maxval = 0, maxidx = 0;
00096 for(uint32_t i=0;i<256-gfiltlen;i++) {
00097 uint32_t sum = 0;
00098 for(uint32_t j=0;j<gfiltlen;j++)
00099 sum += height_hist.at<float>(i+j,0) * gauss_filt.at<float>(j,0);
00100 if(sum > maxval) {
00101 maxval = sum;
00102 maxidx = i+gfiltlen/2;
00103 }
00104 }
00105 int32_t table_height = ((int32_t)maxidx);
00106
00107 cv::Mat height_img_thresh = height_img.clone();
00108 for(uint32_t x=0;x<imgx;x++)
00109 for(uint32_t y=0;y<imgy;y++) {
00110 if(std::fabs(table_height - ((int32_t)height_img_thresh.at<uint8_t>(x,y))) < stddev*2)
00111 height_img_thresh.at<uint8_t>(x,y) = 255;
00112 else
00113 height_img_thresh.at<uint8_t>(x,y) = 0;
00114 }
00115
00116 cv::Mat height_morph(imgx, imgy, CV_8U, cv::Scalar(0));
00117 cv::Mat tmp_img(imgx, imgy, CV_8U, cv::Scalar(0));
00118 IplImage t1 = height_img_thresh; IplImage t2 = height_morph;
00119 IplConvKernel* element = cvCreateStructuringElementEx(3, 3, 1, 1, CV_SHAPE_RECT);
00120 cvMorphologyEx(&t1, &t2, NULL, element, CV_MOP_CLOSE);
00121 cvMorphologyEx(&t2, &t2, NULL, element, CV_MOP_OPEN, 2);
00122 cv::Mat table_edge = height_morph.clone();
00123
00124
00125 double sumx = 0, sumy = 0, suma = 0;
00126 for(uint32_t y=0;y<imgy;y++) {
00127 bool first_found = false;
00128 for(uint32_t x=0;x<imgx;x++) {
00129 if(height_morph.at<uint8_t>(x,y) == 255) {
00130 sumx += x;
00131 sumy += y;
00132 suma ++;
00133 }
00134 if(first_found) {
00135 table_edge.at<uint8_t>(x,y) = 0;
00136 continue;
00137 }
00138 if(table_edge.at<uint8_t>(x,y) == 255)
00139 first_found = true;
00140 }
00141 }
00142 double centerx = sumx/suma, centery = sumy/suma;
00143 double moment11 = 0, moment12 = 0, moment21 = 0, moment22 = 0;
00144 for(uint32_t y=0;y<imgx;y++) {
00145 for(uint32_t x=0;x<imgx;x++) {
00146 if(height_morph.at<uint8_t>(x,y) == 255) {
00147 moment11 += (x - centerx) * (x - centerx);
00148 moment12 += (y - centery) * (x - centerx);
00149 moment21 += (x - centerx) * (y - centery);
00150 moment22 += (y - centery) * (y - centery);
00151 }
00152 }
00153 }
00154 double teig1 = (moment11 + moment22) / 2;
00155 double teig2 = sqrt((moment11 + moment22) * (moment11 + moment22) -
00156 4*(moment11*moment22 - moment12*moment12))/2;
00157 double eig1 = teig1 + teig2, eig2 = teig1 - teig2;
00158 double sign1 = 1, sign2 = 1;
00159 if((moment11 - eig1) * (moment12) < 0)
00160 sign1 = -1;
00161 if((moment11 - eig2) * (moment12) < 0)
00162 sign2 = -1;
00163 double evec1 = sign1 * sqrt((moment22 - eig1) / (moment11 - eig1));
00164 double evec2 = sign2 * sqrt((moment22 - eig2) / (moment11 - eig2));
00165 double m1 = sqrt(1+evec1*evec1), m2 = sqrt(1+evec2*evec2);
00166 double mag = 200 * sqrt(eig1) / suma;
00167 double comp1x = mag * sqrt(eig2/eig1) * evec1/m1, comp1y = sqrt(eig2/eig1) * mag /m1;
00168 double comp2x = mag * evec2/m2, comp2y = mag /m2;
00169 CvPoint** rpts = new CvPoint*[1]; rpts[0] = new CvPoint[4];
00170 rpts[0][0].y = centerx+comp1y+comp2y; rpts[0][0].x = centery+comp1x+comp2x;
00171 rpts[0][1].y = centerx+comp1y-comp2y; rpts[0][1].x = centery+comp1x-comp2x;
00172 rpts[0][2].y = centerx-comp1y-comp2y; rpts[0][2].x = centery-comp1x-comp2x;
00173 rpts[0][3].y = centerx-comp1y+comp2y; rpts[0][3].x = centery-comp1x+comp2x;
00174 rpts[0][4].y = centerx+comp1y+comp2y; rpts[0][4].x = centery+comp1x+comp2x;
00175 int npts[1] = {5};
00176 cvPolyLine(&t2, rpts, npts, 1, 1, cv::Scalar(180), 2);
00177
00178
00179 cv::Mat table_hull(imgx, imgy, CV_8U, cv::Scalar(0));
00180 IplImage ipl_hull = table_hull;
00181 cvFillPoly(&ipl_hull, rpts, npts, 1, cv::Scalar(255));
00182
00183 cv::Mat obj_img(imgx, imgy, CV_8U, cv::Scalar(0));
00184 std::vector<int32_t> xfeats, yfeats, zfeats;
00185 double sumobjx = 0, sumobjy = 0, sumobja = 0;
00186 for(uint32_t y=0;y<imgx;y++)
00187 for(uint32_t x=0;x<imgx;x++)
00188 if(table_hull.at<uint8_t>(x,y) == 255 && height_morph.at<uint8_t>(x,y) == 0
00189 && height_img.at<uint8_t>(x,y) > table_height + stddev*2) {
00190 obj_img.at<uint8_t>(x,y) = height_img.at<uint8_t>(x,y);
00191 sumobjx += x; sumobjy += y; sumobja ++;
00192
00193
00194 }
00195 double objcentx = sumobjx/sumobja, objcenty = sumobjy/sumobja;
00196
00197 CvMemStorage* storage = cvCreateMemStorage(0);
00198 CvSeq* lines = 0;
00199 cv::Mat lines_img = height_morph.clone();
00200 IplImage lines_img_ipl = lines_img;
00201 IplImage table_edge_ipl = table_edge;
00202 cvMorphologyEx(&table_edge_ipl, &table_edge_ipl, NULL, element, CV_MOP_DILATE, 1);
00203 lines = cvHoughLines2(&table_edge_ipl, storage, CV_HOUGH_STANDARD, 1, 3*CV_PI/180, 60, 0, 0);
00204 vector<float> theta_bins, rho_bins;
00205 vector<uint32_t> ind_bins;
00206 for(uint32_t i=0; i < lines->total; i++) {
00207 float* line = (float*)cvGetSeqElem(lines, i);
00208 float rho = line[0];
00209 float theta = line[1];
00210 theta_bins.push_back(theta);
00211 rho_bins.push_back(rho);
00212 ind_bins.push_back(i);
00213
00214 double a = cos(theta), b = sin(theta);
00215 double x0 = a*rho, y0 = b*rho;
00216 CvPoint pt1, pt2;
00217 a = cos(theta+CV_PI/2); b = sin(theta+CV_PI/2);
00218 x0 = objcenty; y0 = objcentx;
00219 pt1.x = cvRound(x0 + 1000*(-b));
00220 pt1.y = cvRound(y0 + 1000*(a));
00221 pt2.x = cvRound(x0 - 1000*(-b));
00222 pt2.y = cvRound(y0 - 1000*(a));
00223 cvLine(&lines_img_ipl, pt1, pt2, cv::Scalar(100), 2, 8 );
00224 }
00225
00226
00227 boost::function<bool(int, int)> sortind = boost::bind(&compind, _1, _2, theta_bins);
00228 sort(ind_bins.begin(), ind_bins.end(), sortind);
00229 vector<float> posesx, posesy, poses_theta;
00230 for(uint32_t i=0;i<ind_bins.size();i++) {
00231 double theta = theta_bins[ind_bins[i]];
00232 double rho = rho_bins[ind_bins[i]];
00233 double a1 = cos(theta-CV_PI/2), b1 = sin(theta-CV_PI/2);
00234 double a2 = cos(theta-CV_PI), b2 = sin(theta-CV_PI);
00235 double vvcl = a2*b1-b2*a1, deltpx = cos(theta)*rho-objcenty, deltpy = sin(theta)*rho-objcentx;
00236 double pvcr = deltpx*b1 - deltpy*a1;
00237 double t = pvcr/vvcl;
00238 double posey = objcenty + t*a2, posex = objcentx + t*b2;
00239 printf("\naPose %d: (t: %f, %f, %f)[%f, %f](%f, %f)[%f, %f](1 %f, %f)(2 %f, %f)[theta %f, %f]\n", i, t, posex, posey, t*a2, t*b2, a1*rho, b1*rho, objcentx, objcenty, a1, b1, a2, b2, theta, rho);
00240 if(posex == posex && posey == posey &&
00241 posex >= 0 && posey >= 0 &&
00242 posex < imgx && posey < imgy) {
00243 posesx.push_back(posex); posesy.push_back(posey); poses_theta.push_back(theta);
00244 }
00245
00246 }
00247 vector<float> retposesx, retposesy;
00248 float xgrand = 0.1 * resolution, ygrand = 0.1 * resolution;
00249 grasp_points.poses.clear();
00250 for(uint32_t i=0;i<posesx.size();i++) {
00251 bool same_found = false;
00252 for(int32_t j=((int)retposesx.size())-1;j>=0;j--) {
00253 if(fabs(posesx[i] - retposesx[j]) < xgrand &&
00254 fabs(posesy[i] - retposesy[j]) < ygrand) {
00255
00256
00257 same_found = true;
00258 }
00259 }
00260 if(!same_found) {
00261 retposesx.push_back(posesx[i]);
00262 retposesy.push_back(posesy[i]);
00263 geometry_msgs::Pose cpose;
00264 cpose.position.x = posesx[i]/imgx*(maxx-minx) + minx;
00265 cpose.position.y = posesy[i]/imgy*(maxy-miny) + miny;
00266 cpose.position.z = table_height/256.0*(maxz-minz) + minz;
00267 btMatrix3x3 quatmat; btQuaternion quat;
00268 quatmat.setEulerZYX(-poses_theta[i] - CV_PI/2, 0 , 0);
00269 quatmat.getRotation(quat);
00270 btQuaternion head_quat(base_head_trans.getRotation().x(),
00271 base_head_trans.getRotation().y(),
00272 base_head_trans.getRotation().z(),
00273 base_head_trans.getRotation().w());
00274
00275
00276
00277 cpose.orientation.x = quat.x(); cpose.orientation.y = quat.y();
00278 cpose.orientation.z = quat.z(); cpose.orientation.w = quat.w();
00279 grasp_points.poses.push_back(cpose);
00280 }
00281 }
00282 grasp_points.header.stamp = ros::Time::now();
00283 grasp_points.header.frame_id = head_frame;
00284 pose_arr_pub.publish(grasp_points);
00285
00286
00287 printf("\nCenter (%f, %f)\n", objcentx, objcenty);
00288 for(uint32_t i=0;i<retposesx.size();i++) {
00289
00290 printf("\nPose %d: (%f, %f)\n", i, retposesx[i], retposesy[i]);
00291
00292 CvPoint centerpt; centerpt.x = retposesy[i]; centerpt.y = retposesx[i];
00293 cvCircle(&lines_img_ipl, centerpt, 3, cv::Scalar(200), 2);
00294 }
00295
00296
00297
00298
00299
00300
00301
00302
00303
00304
00305
00306
00307
00308
00309
00310
00311
00312
00313
00314
00315
00316 cv::Mat table_edge2 = table_edge.clone();
00317 IplImage table_edge_ipl2 = table_edge2;
00318 cvMorphologyEx(&table_edge_ipl2, &table_edge_ipl2, NULL, element, CV_MOP_DILATE, 3);
00319
00320 BOOST_FOREACH(const pcl::PointXYZRGB& pt, pc_full_frame.points) {
00321 if(pt.x != pt.x || pt.y != pt.y || pt.z != pt.z)
00322 continue;
00323 int32_t x, y, z;
00324 x = (pt.x - minx)/(maxx-minx) * imgx;
00325 y = (pt.y - miny)/(maxy-miny) * imgy;
00326 z = (pt.z - minz)/(maxz-minz) * 256;
00327 if(x < 0 || y < 0) continue;
00328 if(x >= imgx || y >= imgy) continue;
00329 if(z < 0 || z >= 256) continue;
00330 if(height_morph.at<uint8_t>(x,y) == 255 &&
00331 std::fabs(table_height - z) < stddev*2) {
00332 uint32_t red = 0xFFFF0000;
00333 ((uint32_t*) &pt.rgb)[0] = red;
00334 }
00335 if(table_edge.at<uint8_t>(x,y) == 255 &&
00336 std::fabs(table_height - z) < stddev*4) {
00337 uint32_t blue = 0xFF0000FF;
00338 ((uint32_t*) &pt.rgb)[0] = blue;
00339 }
00340 }
00341
00342
00343
00344
00345 cvb_height_img.image = lines_img;
00346
00347 cvb_height_img.header.stamp = ros::Time::now();
00348 cvb_height_img.header.frame_id = head_frame;
00349 cvb_height_img.encoding = enc::MONO8;
00350 height_pub.publish(cvb_height_img.toImageMsg());
00351 pc_full_frame.header.stamp = ros::Time::now();
00352 pc_full_frame.header.frame_id = head_frame;
00353 pc_pub.publish(pc_full_frame);
00354
00355 if(grasp_points.poses.size() > 0)
00356 grasp_points_found = true;
00357
00358
00359 }
00360
00361 };
00362
00363 using namespace hrl_object_fetching;
00364
00365 int main(int argc, char **argv)
00366 {
00367 ros::init(argc, argv, "tabletop_approach");
00368 TabletopDetector ta;
00369 ta.onInit();
00370 ros::spin();
00371 return 0;
00372 }