00001 
00002 
00003 
00004 
00005 
00006 
00007 
00008 
00009 
00010 
00011 
00012 
00013 
00014 
00015 
00016 
00017 
00018 
00019 
00020 
00021 
00022 
00023 
00024 
00025 
00026 
00027 
00028 
00029 
00030 
00031 
00032 
00033 
00034 
00035 #include <opencv2/core/core.hpp>
00036 #include <opencv2/imgproc/imgproc.hpp>
00037 #include <opencv2/highgui/highgui.hpp>
00038 
00039 
00040 #include <vector>
00041 
00042 namespace cpl_visual_features
00043 {
00044 class LinkEdges
00045 {
00046  public:
00047   static std::vector<std::vector<cv::Point> > edgeLink(cv::Mat& edge_img_raw,
00048                                         unsigned int min_length=1,
00049                                         bool use_displays = false)
00050   {
00051     
00052     cv::Mat edge_img(edge_img_raw.size(), CV_8UC1, cv::Scalar(0));
00053     for (int r = 0; r < edge_img.rows; ++r)
00054     {
00055       for (int c = 0; c < edge_img.cols; ++c)
00056       {
00057         if (edge_img_raw.at<float>(r,c) != 0.0)
00058         {
00059           edge_img.at<uchar>(r,c) = 1;
00060         }
00061       }
00062     }
00063 
00064     
00065     removeIsolatedPixels(edge_img);
00066     edge_img = thinEdges(edge_img);
00067     
00068     edge_img.convertTo(edge_img_raw, CV_32FC1);
00069 
00070     
00071     cv::Mat ends;
00072     cv::Mat junctions;
00073     findEndsJunctions(edge_img, ends, junctions);
00074 
00075     
00076     cv::Mat edge_img_f;
00077     edge_img.convertTo(edge_img_f, CV_32FC1);
00078     std::vector<std::vector<cv::Point> > edges;
00079     int edge_no = 0;
00080     for (int r = 0; r < edge_img.rows; ++r)
00081     {
00082       for (int c = 0; c < edge_img.cols; ++c)
00083       {
00084         if (edge_img_f.at<float>(r,c) == 1)
00085         {
00086           std::vector<cv::Point> b = trackEdge(edge_img_f, r, c, edge_no++, junctions);
00087           
00088           if (b.size() < min_length) continue;
00089           edges.push_back(b);
00090         }
00091       }
00092     }
00093     edge_img_f = -1*edge_img_f;
00094 
00095     if (use_displays)
00096     {
00097       ROS_DEBUG_STREAM("Found " << edges.size() << " edges ");
00098       cv::Mat edge_disp_img(edge_img.size(), CV_32FC3, cv::Scalar(0.0,0.0,0.0));
00099       for (unsigned int i = 0; i < edges.size(); ++i)
00100       {
00101         cv::Vec3f rand_color;
00102         rand_color[0] = static_cast<float>(rand())/RAND_MAX;
00103         rand_color[1] = static_cast<float>(rand())/RAND_MAX;
00104         rand_color[2] = static_cast<float>(rand())/RAND_MAX;
00105 
00106         for (unsigned int j = 0; j < edges[i].size(); ++j)
00107         {
00108           edge_disp_img.at<cv::Vec3f>(edges[i][j].y, edges[i][j].x) = rand_color;
00109         }
00110       }
00111       cv::imshow("linked edges", edge_disp_img);
00112     }
00113 
00114     return edges;
00115   }
00116 
00117  protected:
00118   static void removeIsolatedPixels(cv::Mat& img)
00119   {
00120     
00121     cv::Mat singles(img.size(), CV_8UC1, cv::Scalar(0));
00122     cv::Mat point_finder_filter(3, 3, CV_8UC1, cv::Scalar(1));
00123     cv::filter2D(img, singles, singles.depth(), point_finder_filter);
00124 
00125     
00126     for (int r = 0; r < img.rows; ++r)
00127     {
00128       for (int c = 0; c < img.cols; ++c)
00129       {
00130         if (singles.at<uchar>(r,c) == 1)
00131         {
00132           img.at<uchar>(r,c) = 0;
00133         }
00134       }
00135     }
00136   }
00137 
00138   static cv::Mat thinEdges(cv::Mat img)
00139   {
00140     cv::Mat skel(img.size(), CV_8UC1, cv::Scalar(0));
00141     cv::Mat temp(img.size(), CV_8UC1);
00142     cv::Mat eroded;
00143     cv::Mat element = cv::getStructuringElement(cv::MORPH_CROSS, cv::Size(3,3));
00144     do
00145     {
00146       cv::erode(img, eroded, element);
00147       cv::dilate(eroded, temp, element);
00148       cv::subtract(img, temp, temp);
00149       cv::bitwise_or(skel, temp, skel);
00150       eroded.copyTo(img);
00151     } while (!(cv::norm(img) == 0));
00152     removeIsolatedPixels(skel);
00153     return skel;
00154   }
00155 
00156   static void findEndsJunctions(cv::Mat& edge_img, cv::Mat& ends,
00157                                 cv::Mat& junctions)
00158   {
00159     ends.create(edge_img.size(), CV_8UC1);
00160     junctions.create(edge_img.size(), CV_8UC1);
00161     for (int r = 0; r < edge_img.rows; ++r)
00162     {
00163       for (int c = 0; c < edge_img.cols; ++c)
00164       {
00165         if (edge_img.at<uchar>(r,c))
00166         {
00167           int crossings = getCrossings(edge_img, r, c);
00168           if (crossings >= 6)
00169           {
00170             junctions.at<uchar>(r,c) = 1;
00171           }
00172           else if (crossings == 2)
00173           {
00174             ends.at<uchar>(r,c) = 1;
00175           }
00176         }
00177       }
00178     }
00179   }
00180 
00181   static int getCrossings(cv::Mat& edge_img, const int r, const int c)
00182   {
00183     cv::Mat a(1,8,CV_8SC1, cv::Scalar(0));
00184     cv::Mat b(1,8,CV_8SC1, cv::Scalar(0));
00185     a.at<char>(0,0) = edge_img.at<uchar>(r-1,c-1);
00186     a.at<char>(0,1) = edge_img.at<uchar>(r-1,c);
00187     a.at<char>(0,2) = edge_img.at<uchar>(r-1,c+1);
00188     a.at<char>(0,3) = edge_img.at<uchar>(r,c+1);
00189     a.at<char>(0,4) = edge_img.at<uchar>(r+1,c+1);
00190     a.at<char>(0,5) = edge_img.at<uchar>(r+1,c);
00191     a.at<char>(0,6) = edge_img.at<uchar>(r+1,c-1);
00192     a.at<char>(0,7) = edge_img.at<uchar>(r,c-1);
00193 
00194     b.at<char>(0,0) = edge_img.at<uchar>(r-1,c);
00195     b.at<char>(0,1) = edge_img.at<uchar>(r-1,c+1);
00196     b.at<char>(0,2) = edge_img.at<uchar>(r,c+1);
00197     b.at<char>(0,3) = edge_img.at<uchar>(r+1,c+1);
00198     b.at<char>(0,4) = edge_img.at<uchar>(r+1,c);
00199     b.at<char>(0,5) = edge_img.at<uchar>(r+1,c-1);
00200     b.at<char>(0,6) = edge_img.at<uchar>(r,c-1);
00201     b.at<char>(0,7) = edge_img.at<uchar>(r-1,c-1);
00202     return cv::sum(cv::abs(a-b))[0];
00203   }
00204 
00205   enum PtStatus
00206   {
00207     NO_POINT,
00208     THERE_IS_A_POINT,
00209     LAST_POINT
00210   };
00211 
00212   static std::vector<cv::Point> trackEdge(cv::Mat& edge_img, int r_start, int c_start,
00213                             int edge_no, cv::Mat& junctions)
00214   {
00215     std::vector<cv::Point> b;
00216     b.push_back(cv::Point(c_start, r_start));
00217     edge_img.at<float>(r_start, c_start) = -edge_no;
00218     int r = r_start;
00219     int c = c_start;
00220     PtStatus status = nextPoint(edge_img, r, c, edge_no, junctions);
00221 
00222     while (status != NO_POINT)
00223     {
00224       b.push_back(cv::Point(c, r));
00225       edge_img.at<float>(r,c) = -edge_no;
00226       if (status == LAST_POINT)
00227       {
00228         status = NO_POINT;
00229       }
00230       else
00231       {
00232         status = nextPoint(edge_img, r, c, edge_no, junctions);
00233       }
00234     }
00235 
00236     if (isJunction(junctions,cv::Point(c_start, r_start)))
00237     {
00238       std::reverse(b.begin(), b.end());
00239       
00240       status = nextPoint(edge_img, r_start, c_start, edge_no, junctions);
00241 
00242       while (status != NO_POINT)
00243       {
00244         b.push_back(cv::Point(c, r));
00245         edge_img.at<float>(r,c) = -edge_no;
00246         if (status == LAST_POINT)
00247         {
00248           status = NO_POINT;
00249         }
00250         else
00251         {
00252           status = nextPoint(edge_img, r, c, edge_no, junctions);
00253         }
00254       }
00255     }
00256 
00257     
00258     if (b.size() >= 4)
00259     {
00260       const int end = b.size() -1;
00261       if (abs(b[0].x - b[end].x) <= 1 && abs(b[0].y - b[end].y) <= 1)
00262       {
00263         b.push_back(b[0]);
00264       }
00265     }
00266     return b;
00267   }
00268 
00269   static PtStatus nextPoint(cv::Mat& edge_img, int& r_start, int& c_start,
00270                             int edge_no, cv::Mat& junctions)
00271   {
00272     
00273     for (int r = std::max(0, r_start-1); r <= std::min(r_start+1, edge_img.rows-1); ++r)
00274     {
00275       for (int c = std::max(0, c_start-1); c <= std::min(c_start+1, edge_img.cols-1); ++c)
00276       {
00277         if (isJunction(junctions, r, c) && edge_img.at<float>(r,c) != -edge_no)
00278         {
00279           r_start = r;
00280           c_start = c;
00281           return LAST_POINT;
00282         }
00283       }
00284     }
00285 
00286     bool check_flag = false;
00287     int backup_r = 0;
00288     int backup_c = 0;
00289     for (int r = std::max(0, r_start-1); r <= std::min(r_start+1, edge_img.rows-1); ++r)
00290     {
00291       for (int c = std::max(0, c_start-1); c <= std::min(c_start+1, edge_img.cols-1); ++c)
00292       {
00293         
00294         if (r == r_start && c == c_start) continue;
00295         if (edge_img.at<float>(r,c) == 1)
00296         {
00297           if (neighborSum(edge_img, r, c, edge_no) < 2)
00298           {
00299             r_start = r;
00300             c_start = c;
00301             return THERE_IS_A_POINT;
00302           }
00303           else
00304           {
00305             check_flag = true;
00306             backup_r = r;
00307             backup_c = c;
00308           }
00309         }
00310       }
00311     }
00312     if (check_flag)
00313     {
00314       r_start = backup_r;
00315       c_start = backup_c;
00316       return THERE_IS_A_POINT;
00317     }
00318 
00319     
00320     r_start = 0;
00321     c_start = 0;
00322     return NO_POINT;
00323   }
00324 
00325   static int neighborSum(cv::Mat& edge_img, int r_seed, int c_seed, int edge_no)
00326   {
00327     int ns = 0;
00328     for (int r = std::max(0, r_seed-1); r <= std::min(r_seed+1, edge_img.rows-1); ++r)
00329     {
00330       for (int c = std::max(0, c_seed-1); c <= std::min(c_seed+1, edge_img.cols-1); ++c)
00331       {
00332         if (r == r_seed && c == c_seed) continue;
00333         if (edge_img.at<float>(r,c) == -edge_no) ++ns;
00334       }
00335     }
00336     return ns;
00337   }
00338 
00339 
00340   static bool isJunction(cv::Mat& junctions, cv::Point p)
00341   {
00342     return (junctions.at<float>(p.y, p.x)==1);
00343   }
00344 
00345   static bool isJunction(cv::Mat& junctions, int r, int c)
00346   {
00347     return (junctions.at<float>(r, c)==1);
00348   }
00349 };
00350 };