edges.h
Go to the documentation of this file.
00001 /*********************************************************************
00002  * Software License Agreement (BSD License)
00003  *
00004  *  Copyright (c) 2012, Georgia Institute of Technology
00005  *  All rights reserved.
00006  *
00007  *  Redistribution and use in source and binary forms, with or without
00008  *  modification, are permitted provided that the following conditions
00009  *  are met:
00010  *
00011  *   * Redistributions of source code must retain the above copyright
00012  *     notice, this list of conditions and the following disclaimer.
00013  *   * Redistributions in binary form must reproduce the above
00014  *     copyright notice, this list of conditions and the following
00015  *     disclaimer in the documentation and/or other materials provided
00016  *     with the distribution.
00017  *   * Neither the name of the Georgia Institute of Technology nor the names of
00018  *     its contributors may be used to endorse or promote products derived
00019  *     from this software without specific prior written permission.
00020  *
00021  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00022  *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00023  *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00024  *  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00025  *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00026  *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00027  *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00028  *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
00029  *  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00030  *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
00031  *  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00032  *  POSSIBILITY OF SUCH DAMAGE.
00033  *********************************************************************/
00034 // OpenCV
00035 #include <opencv2/core/core.hpp>
00036 #include <opencv2/imgproc/imgproc.hpp>
00037 #include <opencv2/highgui/highgui.hpp>
00038 
00039 // STL
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     // binarize image
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     // Clean up edge image
00065     removeIsolatedPixels(edge_img);
00066     edge_img = thinEdges(edge_img);
00067     // NOTE: Here we change the input image to be the cleaned up edge image
00068     edge_img.convertTo(edge_img_raw, CV_32FC1);
00069 
00070     // Find locations of edge intersections
00071     cv::Mat ends;
00072     cv::Mat junctions;
00073     findEndsJunctions(edge_img, ends, junctions);
00074 
00075     // Join edge pixels
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           // Remove short edges
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     // Find single pixel locations
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     // Remove pixels with filter score 1
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       // TODO: Should this call in recursively and just extend b?
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     // check for loops and close them
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     // Check if any neighbors are junction locations with other lines
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         // Skip the current pixel
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     // Set return values
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 };


cpl_visual_features
Author(s): Tucker Hermans
autogenerated on Wed Nov 27 2013 11:52:35