arm_obj_segmentation.cpp
Go to the documentation of this file.
00001 #include <tabletop_pushing/arm_obj_segmentation.h>
00002 #include <opencv2/imgproc/imgproc.hpp>
00003 #include <opencv2/highgui/highgui.hpp>
00004 #include <tabletop_pushing/extern/graphcut/energy.h>
00005 #include <tabletop_pushing/extern/graphcut/GCoptimization.h>
00006 
00007 #include <map>
00008 #include <algorithm>
00009 #include <iostream>
00010 
00011 // #define VISUALIZE_GRAPH_WEIGHTS 1
00012 // #define USE_CANNY_EDGES 1
00013 
00014 namespace tabletop_pushing
00015 {
00016 class NodeTable
00017 {
00018  public:
00019   typedef std::map<int, std::map<int, int> >::iterator TableIt;
00020   typedef std::map<int, int>::iterator CellIt;
00021   typedef std::map<int, int> Cell;
00022 
00023   int find(int r, int c)
00024   {
00025     TableIt it = table_.find(r);
00026     if (it == table_.end())
00027     {
00028       return -1;
00029     }
00030     CellIt cit = table_[r].find(c);
00031     if (cit == table_[r].end())
00032     {
00033       return -1;
00034     }
00035     return table_[r][c];
00036   }
00037 
00038   void addNode(int r, int c, int idx)
00039   {
00040     TableIt it = table_.find(r);
00041     if (it == table_.end())
00042     {
00043       Cell cell_r;
00044       cell_r[c] = idx;
00045       table_[r] = cell_r;
00046     }
00047     else
00048     {
00049       table_[r][c] = idx;
00050     }
00051   }
00052 
00053   int getIdx(int r, int c)
00054   {
00055     return table_[r][c];
00056   }
00057  protected:
00058   std::map<int, std::map<int, int> > table_;
00059 };
00060 
00061 ArmObjSegmentation::ArmObjSegmentation(float fg_tied_weight, float bg_tied_weight, float bg_enlarge_size,
00062                                        float arm_enlarge_width, float arm_shrink_width, float sigma, float lambda) :
00063     fg_tied_weight_(fg_tied_weight), bg_tied_weight_(bg_tied_weight), bg_enlarge_size_(bg_enlarge_size),
00064     arm_enlarge_width_(arm_enlarge_width), arm_shrink_width_(arm_shrink_width),
00065     sigma_d_(sigma), pairwise_lambda_(lambda), have_arm_color_model_(false), have_bg_color_model_(false)
00066 {
00067   // Create derivative kernels for edge calculation
00068   cv::getDerivKernels(dy_kernel_, dx_kernel_, 1, 0, CV_SCHARR, true, CV_32F);
00069   // cv::flip(dy_kernel_, dy_kernel_, -1);
00070   cv::transpose(dy_kernel_, dx_kernel_);
00071 }
00072 
00073 
00074 cv::Mat ArmObjSegmentation::segment(cv::Mat& color_img, cv::Mat& depth_img, cv::Mat& self_mask,
00075                                     cv::Mat& table_mask, bool init_color_models)
00076 {
00077 
00078   cv::Mat color_img_lab_uchar(color_img.size(), color_img.type());
00079   cv::Mat color_img_lab(color_img.size(), CV_32FC3);
00080   cv::cvtColor(color_img, color_img_lab_uchar, CV_BGR2HSV);
00081   // cv::cvtColor(color_img, color_img_lab_uchar, CV_BGR2Lab);
00082   color_img_lab_uchar.convertTo(color_img_lab, CV_32FC3, 1.0/255);
00083   cv::Mat tmp_bw(color_img.size(), CV_8UC1);
00084   cv::Mat bw_img(color_img.size(), CV_32FC1);
00085   // Convert to grayscale
00086   cv::cvtColor(color_img, tmp_bw, CV_BGR2GRAY);
00087   tmp_bw.convertTo(bw_img, CV_32FC1, 1.0/255);
00088 
00089 
00090 #ifdef VISUALIZE_GRAPH_WEIGHTS
00091   cv::Mat fg_weights(color_img.size(), CV_32FC1, cv::Scalar(0.0));
00092   cv::Mat fg_tied_weights(color_img.size(), CV_32FC1, cv::Scalar(0.0));
00093   cv::Mat bg_tied_weights(color_img.size(), CV_32FC1, cv::Scalar(0.0));
00094   cv::Mat bg_weights(color_img.size(), CV_32FC1, cv::Scalar(0.0));
00095   cv::Mat wu_weights(color_img.size(), CV_32FC1, cv::Scalar(0.0));
00096   cv::Mat wl_weights(color_img.size(), CV_32FC1, cv::Scalar(0.0));
00097 #endif // VISUALIZE_GRAPH_WEIGHTS
00098 
00099   // TODO: Clean this up once we have stuff working well
00100   cv::Mat inv_self_mask;
00101   cv::bitwise_not(self_mask, inv_self_mask);
00102   cv::Mat much_larger_mask(self_mask.size(), CV_8UC1, cv::Scalar(255));
00103   cv::Mat enlarge_element(bg_enlarge_size_, bg_enlarge_size_, CV_8UC1, cv::Scalar(255));
00104   cv::dilate(inv_self_mask, much_larger_mask, enlarge_element);
00105   cv::Mat larger_mask, known_arm_mask;
00106   cv::Mat arm_band = getArmBand(inv_self_mask, arm_enlarge_width_, arm_shrink_width_, false,
00107                                 larger_mask, known_arm_mask);
00108 
00109   // Get known arm pixels
00110   cv::Mat known_arm_pixels;
00111   color_img_lab.copyTo(known_arm_pixels, known_arm_mask);
00112   cv::Mat known_bg_mask = much_larger_mask - larger_mask;
00113 
00114   // Get known object pixels
00115   cv::Mat known_bg_pixels;
00116   color_img_lab.copyTo(known_bg_pixels, known_bg_mask);
00117 
00118   // Get stats for building graph
00119   int num_nodes = 0;
00120   int num_edges = 0;
00121 
00122   tabletop_pushing::NodeTable nt;
00123   for (int r = 0; r < much_larger_mask.rows; ++r)
00124   {
00125     for (int c = 0; c < much_larger_mask.cols; ++c)
00126     {
00127       if (much_larger_mask.at<uchar>(r,c) > 0)
00128       {
00129         // Add this as another node
00130         int cur_idx = num_nodes++;
00131         nt.addNode(r, c, cur_idx);
00132         int test_idx = nt.getIdx(r,c);
00133         // Check for edges to add
00134         // left edge
00135         if (c > 0 && much_larger_mask.at<uchar>(r, c-1) > 0)
00136         {
00137           num_edges++;
00138         }
00139         if (r > 0)
00140         {
00141           // Up edge
00142           if(much_larger_mask.at<uchar>(r-1,c) > 0)
00143           {
00144             num_edges++;
00145           }
00146         }
00147       }
00148     }
00149   }
00150   if (num_nodes < 1)
00151   {
00152     cv::Mat empty(color_img.size(), CV_8UC1, cv::Scalar(0));
00153     return empty;
00154   }
00155 
00156   if (!have_arm_color_model_)
00157   {
00158     std::cout << "[arm_obj_segmentation]: Building arm color model." << std::endl;
00159     arm_color_model_ = getGMMColorModel(known_arm_pixels, known_arm_mask, 3);
00160     have_arm_color_model_ = true;
00161   }
00162   if(!have_bg_color_model_)
00163   {
00164     std::cout << "[arm_obj_segmentation]: Building bg color model." << std::endl;
00165     bg_color_model_ = getGMMColorModel(known_bg_pixels, known_bg_mask, 5);
00166     have_bg_color_model_ = true;
00167   }
00168 
00169 #ifdef USE_CANNY_EDGES
00170   cv::Mat canny_edges_8bit;
00171   cv::Mat canny_edges;
00172   cv::Canny(tmp_bw, canny_edges_8bit, 120, 250);
00173   canny_edges_8bit.convertTo(canny_edges, CV_32FC1, 1.0/255);
00174 #else
00175   cv::Mat Ix = getXImageDeriv(bw_img);
00176   cv::Mat Iy = getYImageDeriv(bw_img);
00177 #endif
00178   cv::Mat Dx = getXImageDeriv(depth_img);
00179   cv::Mat Dy = getYImageDeriv(depth_img);
00180   tabletop_pushing::GraphType *g;
00181   g = new GraphType(num_nodes, num_edges);
00182   // Populate unary and binary edge weights
00183   for (int r = 0; r < much_larger_mask.rows; ++r)
00184   {
00185     for (int c = 0; c < much_larger_mask.cols; ++c)
00186     {
00187       if (much_larger_mask.at<uchar>(r,c) > 0)
00188       {
00189         int cur_idx = nt.getIdx(r,c);
00190         float fg_weight = 0.0;
00191         float bg_weight = 0.0;
00192         if (known_arm_mask.at<uchar>(r,c) > 0)
00193         {
00194           fg_weight = fg_tied_weight_;
00195           bg_weight = 0.0;
00196         }
00197         else if (known_bg_mask.at<uchar>(r,c) > 0 || table_mask.at<uchar>(r,c) > 0)
00198         {
00199           fg_weight = 0.0;
00200           bg_weight = bg_tied_weight_;
00201         }
00202         else
00203         {
00204           fg_weight = getUnaryWeight(color_img_lab.at<cv::Vec3f>(r,c), arm_color_model_);
00205           bg_weight = getUnaryWeight(color_img_lab.at<cv::Vec3f>(r,c), bg_color_model_)*0.5;
00206         }
00207         g->add_node();
00208         g->add_tweights(cur_idx, fg_weight, bg_weight);
00209 
00210 #ifdef VISUALIZE_GRAPH_WEIGHTS
00211         fg_weights.at<float>(r,c) = fg_weight;
00212         bg_weights.at<float>(r,c) = bg_weight;
00213 #endif // VISUALIZE_GRAPH_WEIGHTS
00214 
00215         // Add left link
00216         if (c > 0 && much_larger_mask.at<uchar>(r, c-1) > 0)
00217         {
00218           int other_idx = nt.getIdx(r, c-1);
00219 #ifdef USE_CANNY_EDGES
00220           float w_l = getEdgeWeightBoundary(canny_edges.at<float>(r,c), Dx.at<float>(r,c),
00221                                             canny_edges.at<float>(r, c-1), Dx.at<float>(r, c-1));
00222 #else // USE_CANNY_EDGES
00223           float w_l = getEdgeWeightBoundary(Ix.at<float>(r,c), Dx.at<float>(r,c),
00224                                             Ix.at<float>(r, c-1), Dx.at<float>(r, c-1));
00225 #endif // USE_CANNY_EDGES
00226           g->add_edge(cur_idx, other_idx, /*capacities*/ w_l, w_l);
00227 
00228 #ifdef VISUALIZE_GRAPH_WEIGHTS
00229           wl_weights.at<float>(r,c) = w_l;
00230 #endif // VISUALIZE_GRAPH_WEIGHTS
00231         }
00232         if (r > 0)
00233         {
00234           // Add up link
00235           if(much_larger_mask.at<uchar>(r-1,c) > 0)
00236           {
00237             int other_idx = nt.getIdx(r-1, c);
00238 #ifdef USE_CANNY_EDGES
00239             float w_u = getEdgeWeightBoundary(canny_edges.at<float>(r, c),   Dy.at<float>(r, c),
00240                                               canny_edges.at<float>(r-1, c), Dy.at<float>(r-1, c));
00241 #else // USE_CANNY_EDGES
00242             float w_u = getEdgeWeightBoundary(Iy.at<float>(r,c), Dx.at<float>(r,c),
00243                                               Iy.at<float>(r, c-1), Dx.at<float>(r, c-1));
00244 #endif // USE_CANNY_EDGES
00245             g->add_edge(cur_idx, other_idx, /*capacities*/ w_u, w_u);
00246 
00247 #ifdef VISUALIZE_GRAPH_WEIGHTS
00248             wu_weights.at<float>(r,c) = w_u;
00249 #endif // VISUALIZE_GRAPH_WEIGHTS
00250           }
00251         }
00252       }
00253     }
00254   }
00255 
00256   // Perform cut
00257   g->maxflow(false);
00258   // Convert output into image
00259   cv::Mat segs = convertFlowToMat(g, nt, color_img.rows, color_img.cols);
00260   // Cleanup
00261   delete g;
00262 
00263   cv::Mat graph_input;
00264   cv::Mat segment_arm;
00265   cv::Mat segment_bg;
00266   color_img.copyTo(graph_input, much_larger_mask);
00267   color_img.copyTo(segment_arm, segs);
00268   color_img.copyTo(segment_bg, (segs == 0));
00269   //cv::imshow("segments", segs);
00270   // cv::imshow("Graph input", graph_input);
00271   cv::imshow("Arm segment", segment_arm);
00272   cv::imshow("Background segment", segment_bg);
00273   cv::imshow("Table mask", table_mask);
00274   // cv::imshow("Known bg mask", known_bg_mask);
00275 
00276 #ifdef VISUALIZE_GRAPH_WEIGHTS
00277   double min_val, max_val;
00278   cv::minMaxLoc(fg_weights, &min_val, &max_val);
00279   cv::imshow("fg weights", (fg_weights-min_val)/(max_val-min_val));
00280   // std::cout << "Max fg weight: " << max_val << std::endl;
00281   // std::cout << "Min fg weight: " << min_val << std::endl;
00282 
00283   cv::minMaxLoc(bg_weights, &min_val, &max_val);
00284   cv::imshow("bg weights", (bg_weights-min_val)/(max_val-min_val));
00285   // std::cout << "Max bg weight: " << max_val << std::endl;
00286   // std::cout << "Min bg weight: " << min_val << std::endl;
00287 
00288   // cv::minMaxLoc(wu_weights, &min_val, &max_val);
00289   // cv::imshow("up weights", wu_weights/max_val);
00290   // std::cout << "Max up weight: " << max_val << std::endl;
00291   // std::cout << "Min up weight: " << min_val << std::endl;
00292 
00293   // cv::minMaxLoc(wl_weights, &min_val, &max_val);
00294   // cv::imshow("left weights", wl_weights/max_val);
00295   // std::cout << "Max left weight: " << max_val << std::endl;
00296   // std::cout << "Min left weight: " << min_val << std::endl;
00297 #endif // VISUALIZE_GRAPH_WEIGHTS
00298 #ifdef USE_CANNY_EDGES
00299   cv::imshow("Canny", canny_edges);
00300 #endif
00301   return segs;
00302 }
00303 
00304 //
00305 // Helper Methods
00306 //
00307 
00308 cv::Mat ArmObjSegmentation::getMorphCross(int img_size, int cross_width)
00309 {
00310   cv::Mat morph_element(img_size, img_size, CV_8UC1, cv::Scalar(0));
00311   int img_d = img_size / 2;
00312   int cross_d = std::max(cross_width/2,1);
00313   for (int r = 0; r < morph_element.rows; ++r)
00314   {
00315     for (int c = 0; c < morph_element.cols; ++c)
00316     {
00317       if (abs(img_d - r) < cross_d || abs(img_d - c) < cross_d)
00318       {
00319         morph_element.at<uchar>(r,c) = 255;
00320       }
00321     }
00322   }
00323   return morph_element;
00324 }
00325 
00326 cv::Mat ArmObjSegmentation::convertFlowToMat(GraphType *g, NodeTable& nt, int R, int C)
00327 {
00328   cv::Mat segs(R,C, CV_8UC1, cv::Scalar(0));
00329   for (int r = 0; r < R; ++r)
00330   {
00331     uchar* seg_row = segs.ptr<uchar>(r);
00332     for (int c = 0; c < C; ++c)
00333     {
00334       int idx = nt.find(r,c);
00335       if (idx < 0) continue;
00336       int label = (g->what_segment(idx) == GraphType::SOURCE);
00337       seg_row[c] = label*255;
00338     }
00339   }
00340   return segs;
00341 }
00342 
00343 float ArmObjSegmentation::getEdgeWeightBoundary(float I0, float d0, float I1, float d1)
00344 {
00345   // float w = pairwise_lambda_*exp(-std::max(fabs(I0)+fabs(d0), fabs(I1)+fabs(d1))/sigma_d_);
00346   // float w = pairwise_lambda_*exp(-std::max(fabs(I0), fabs(I1)));
00347   float w = pairwise_lambda_*exp(-fabs(I0));
00348   return w;
00349 }
00350 
00351 cv::Mat ArmObjSegmentation::getXImageDeriv(cv::Mat& input_img)
00352 {
00353   cv::Mat Ix(input_img.size(), CV_32FC1);
00354   // Get image X derivative
00355   cv::filter2D(input_img, Ix, CV_32F, dx_kernel_);
00356   return Ix;
00357 }
00358 
00359 cv::Mat ArmObjSegmentation::getYImageDeriv(cv::Mat& input_img)
00360 {
00361   cv::Mat Iy(input_img.size(), CV_32FC1);
00362   // Get image Y derivative
00363   cv::filter2D(input_img, Iy, CV_32F, dy_kernel_);
00364   return Iy;
00365 }
00366 
00367 cv::Mat ArmObjSegmentation::getArmBand(cv::Mat& input_mask, int enlarge_width,
00368                                        int shrink_width, bool input_inverted)
00369 {
00370   cv::Mat larger_mask, smaller_mask;
00371   return getArmBand(input_mask, enlarge_width, shrink_width, input_inverted, larger_mask, smaller_mask);
00372 }
00373 
00384 cv::Mat ArmObjSegmentation::getArmBand(cv::Mat& input_mask, int enlarge_width,
00385                                        int shrink_width, bool input_inverted,
00386                                        cv::Mat& larger_mask,  cv::Mat& smaller_mask)
00387 {
00388   cv::Mat mask;
00389   if (input_inverted) // Swap if the arm is negative and the background positive
00390   {
00391     cv::bitwise_not(input_mask, mask);
00392   }
00393   else
00394   {
00395     mask = input_mask;
00396   }
00397 
00398   cv::Mat enlarge_element(enlarge_width, enlarge_width, CV_8UC1, cv::Scalar(255));
00399   cv::Mat shrink_element(shrink_width, shrink_width, CV_8UC1, cv::Scalar(255));
00400   cv::dilate(mask, larger_mask, enlarge_element);
00401   cv::erode(mask, smaller_mask, shrink_element);
00402   cv::Mat arm_band = larger_mask - smaller_mask;
00403   return arm_band;
00404 }
00405 
00406 GMM ArmObjSegmentation::getGMMColorModel(cv::Mat& samples, cv::Mat& mask, int nc)
00407 {
00408   std::vector<cv::Vec3f> pnts;
00409   for (int r = 0; r < samples.rows; ++r)
00410   {
00411     for (int c = 0; c < samples.cols; ++c)
00412     {
00413       if (mask.at<uchar>(r,c) > 0)
00414       {
00415         pnts.push_back(samples.at<cv::Vec3f>(r,c));
00416       }
00417     }
00418   }
00419   GMM color_model(0.0001);
00420   color_model.alloc(nc);
00421   if (pnts.size() > 1)
00422   {
00423     color_model.kmeansInit(pnts, 0.05);
00424     color_model.GmmEm(pnts);
00425     color_model.dispparams();
00426   }
00427   return color_model;
00428 }
00429 
00430 float ArmObjSegmentation::getUnaryWeight(cv::Vec3f sample, GMM& color_model)
00431 {
00432   // return exp(-color_model.grabCutLikelihood(sample));
00433   return color_model.probability(sample);
00434 }
00435 
00436 void ArmObjSegmentation::loadArmColorModel(std::string file_path)
00437 {
00438   arm_color_model_.loadgmm(fs::path(file_path));
00439   have_arm_color_model_ = true;
00440 }
00441 
00442 void ArmObjSegmentation::loadBGColorModel(std::string file_path)
00443 {
00444   bg_color_model_.loadgmm(fs::path(file_path));
00445   have_bg_color_model_ = true;
00446 }
00447 
00448 void ArmObjSegmentation::setBGColorModel(GMM& new_bg_model)
00449 {
00450   bg_color_model_ = new_bg_model;
00451   have_bg_color_model_ = true;
00452 }
00453 
00454 void ArmObjSegmentation::buildBGColorModel(GMM& table_color_model, GMM& obj_color_model)
00455 {
00456   bg_color_model_.alloc(table_color_model.nk + obj_color_model.nk);
00457   int j = 0;
00458   for (int i = 0; i < table_color_model.nk; ++i, ++j)
00459   {
00460     bg_color_model_.kernel[j] = table_color_model.kernel[i];
00461     bg_color_model_.w[j] = table_color_model.w[i];
00462   }
00463   for (int i = 0; i < obj_color_model.nk; ++i, ++j)
00464   {
00465     bg_color_model_.kernel[j] = obj_color_model.kernel[i];
00466     bg_color_model_.w[j] = obj_color_model.w[i];
00467   }
00468   have_bg_color_model_ = true;
00469 }
00470 
00471 }; // namespace tabletop_pushing


tabletop_pushing
Author(s): Tucker Hermans
autogenerated on Wed Nov 27 2013 11:59:43