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
00012
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
00068 cv::getDerivKernels(dy_kernel_, dx_kernel_, 1, 0, CV_SCHARR, true, CV_32F);
00069
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
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
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
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
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
00115 cv::Mat known_bg_pixels;
00116 color_img_lab.copyTo(known_bg_pixels, known_bg_mask);
00117
00118
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
00130 int cur_idx = num_nodes++;
00131 nt.addNode(r, c, cur_idx);
00132 int test_idx = nt.getIdx(r,c);
00133
00134
00135 if (c > 0 && much_larger_mask.at<uchar>(r, c-1) > 0)
00136 {
00137 num_edges++;
00138 }
00139 if (r > 0)
00140 {
00141
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
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
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, 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
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, 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
00257 g->maxflow(false);
00258
00259 cv::Mat segs = convertFlowToMat(g, nt, color_img.rows, color_img.cols);
00260
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
00270
00271 cv::imshow("Arm segment", segment_arm);
00272 cv::imshow("Background segment", segment_bg);
00273 cv::imshow("Table mask", table_mask);
00274
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
00281
00282
00283 cv::minMaxLoc(bg_weights, &min_val, &max_val);
00284 cv::imshow("bg weights", (bg_weights-min_val)/(max_val-min_val));
00285
00286
00287
00288
00289
00290
00291
00292
00293
00294
00295
00296
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
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
00346
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
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
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)
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
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 };