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 };