$search
00001 00059 //ros includes 00060 #include <ros/console.h> 00061 //cob includes 00062 #include "cob_3d_mapping_common/polygon.h" 00063 //pcl includes 00064 #include <pcl/point_cloud.h> 00065 #include <pcl/point_types.h> 00066 #ifdef PCL_VERSION_COMPARE 00067 #include <pcl/common/transforms.h> 00068 #else 00069 #include <pcl/common/common.h> 00070 #include <pcl/common/transform.h> 00071 #include <pcl/registration/transforms.h> 00072 #endif 00073 #include <pcl/common/centroid.h> 00074 #include <pcl/common/eigen.h> 00075 00076 //boost includes 00077 #include <boost/shared_ptr.hpp> 00078 00079 //custom definitons 00080 #define MOD(a,b) ( ((a%b)+b)%b ) 00081 00082 namespace cob_3d_mapping 00083 { 00084 // NON MEMBER FUNCTIONS 00085 00093 void 00094 getPointOnPolygon(const Eigen::Vector3f &normal,double d,Eigen::Vector3f &point) 00095 { 00096 float value=fabs(normal(0)); 00097 int direction=0; 00098 00099 if(fabs(normal(1))>value) 00100 { 00101 00102 direction=1; 00103 value=fabs(normal(1)); 00104 } 00105 00106 if(fabs(normal(2))>value) 00107 00108 { 00109 direction=2; 00110 value=fabs(normal(2)); 00111 } 00112 point << 0,0,0; 00113 point(direction)=-d/normal(direction); 00114 } 00115 00124 void 00125 getCoordinateSystemOnPlane(const Eigen::Vector3f &normal, 00126 Eigen::Vector3f &u, Eigen::Vector3f &v) 00127 { 00128 v = normal.unitOrthogonal (); 00129 u = normal.cross (v); 00130 } 00131 00132 00136 void 00137 copyGpcStructure(const gpc_polygon* source, gpc_polygon* dest) 00138 { 00139 dest->num_contours = source->num_contours; 00140 dest->hole = (int*)malloc(source->num_contours*sizeof(int)); 00141 dest->contour = (gpc_vertex_list*)malloc(source->num_contours*sizeof(gpc_vertex_list)); 00142 for(int j=0; j<source->num_contours; ++j) 00143 { 00144 dest->hole[j] = source->hole[j]; 00145 dest->contour[j].num_vertices = source->contour[j].num_vertices; 00146 dest->contour[j].vertex = (gpc_vertex*)malloc(source->contour[j].num_vertices*sizeof(gpc_vertex)); 00147 00148 for(int k=0; k<source->contour[j].num_vertices; ++k) 00149 { 00150 dest->contour[j].vertex[k].x = source->contour[j].vertex[k].x; 00151 dest->contour[j].vertex[k].y = source->contour[j].vertex[k].y; 00152 } 00153 } 00154 } 00155 00163 void 00164 smoothGpcStructure(const gpc_polygon* gpc_in, gpc_polygon* gpc_out) 00165 { 00166 copyGpcStructure(gpc_in, gpc_out); 00167 float weight_data = 0.5, weight_smooth = 0.01, tolerance = 1.0f; 00168 float change = tolerance; 00169 00170 for(int j=0; j<gpc_in->num_contours; ++j) 00171 { 00172 int num_iteration=0; 00173 int l = gpc_in->contour[j].num_vertices; // length 00174 gpc_vertex* pn = gpc_out->contour[j].vertex; // new polygon 00175 gpc_vertex* po = gpc_in->contour[j].vertex; // old polygon 00176 00177 while(change >= tolerance && num_iteration<=5) 00178 { 00179 change = 0.0f; 00180 for(int k=0; k<l; ++k) 00181 { 00182 //if( !k%10 ) continue; 00183 // do x value: 00184 float before = pn[k].x; 00185 pn[k].x += weight_data * (po[k].x - pn[k].x); 00186 pn[k].x += weight_smooth * (pn[ MOD(k-1,l) ].x + pn[ (k+1)%l ].x - (2.0f * pn[k].x)); 00187 pn[k].x += 0.5f * weight_smooth * (2.0f * pn[ MOD(k-1,l) ].x - pn[ MOD(k-2,l) ].x - pn[k].x); 00188 pn[k].x += 0.5f * weight_smooth * (2.0f * pn[ (k+1)%l ].x - pn[ (k+2)%l ].x - pn[k].x); 00189 change += fabs(before - pn[k].x); 00190 00191 // do y value: 00192 before = pn[k].y; 00193 pn[k].y += weight_data * (po[k].y - pn[k].y); 00194 pn[k].y += weight_smooth * (pn[ MOD(k-1,l) ].y + pn[ (k+1)%l ].y - (2.0f * pn[k].y)); 00195 pn[k].y += 0.5f * weight_smooth * (2.0f * pn[ MOD(k-1,l) ].y - pn[ MOD(k-2,l) ].y - pn[k].y); 00196 pn[k].y += 0.5f * weight_smooth * (2.0f * pn[ (k+1)%l ].y - pn[ (k+2)%l ].y - pn[k].y); 00197 change += fabs(before - pn[k].y); 00198 } 00199 ++num_iteration; 00200 } 00201 ROS_DEBUG_STREAM( "Needed " << num_iteration << " iterations for polygon of size " << l ); 00202 } 00203 } 00204 00205 00206 //##########methods for instantiation############## 00207 void 00208 Polygon::computeAttributes(const Eigen::Vector3f &new_normal, const Eigen::Vector4f& new_centroid) 00209 { 00210 this->d = new_centroid.head(3).dot(new_normal); 00211 if (this->d > 0) { this->normal = -new_normal; d = -d; } 00212 else { this->normal = new_normal; } 00213 centroid = new_centroid; 00214 00215 pcl::getTransformationFromTwoUnitVectorsAndOrigin( 00216 this->normal.unitOrthogonal(),this->normal,this->centroid.head(3),this->transform_from_world_to_plane); 00217 } 00218 00219 void Polygon::transform2tf(const Eigen::Affine3f& trafo) 00220 { 00221 //transform contours 00222 this->TransformContours(trafo); 00223 00224 // transform parameters 00225 Eigen::Vector3f tf_normal = trafo.rotation() *this->normal; 00226 this->normal =tf_normal; 00227 Eigen::Vector3f tf_centroid3f = this->centroid.head(3); 00228 tf_centroid3f = trafo * tf_centroid3f; 00229 this->centroid.head(3) = tf_centroid3f; 00230 this->computeAttributes(this->normal,this->centroid); 00231 } 00232 00233 00234 void 00235 Polygon::smoothPolygon() 00236 { 00237 gpc_polygon *before, *after; 00238 this->getGpcStructure(this->transform_from_world_to_plane, before); 00239 smoothGpcStructure(before,after); 00240 this->applyGpcStructure(this->transform_from_world_to_plane.inverse(), after); 00241 gpc_free_polygon(before); 00242 gpc_free_polygon(after); 00243 } 00244 00245 00246 //###########methods for merging################## 00247 00248 00249 void 00250 Polygon::getMergeCandidates(const std::vector<Polygon::Ptr>& poly_vec, std::vector<int>& intersections) const 00251 { 00252 for(size_t i=0; i< poly_vec.size(); ++i) 00253 { 00254 if(this->hasSimilarParametersWith(poly_vec[i]) && this->isIntersectedWith(poly_vec[i])) intersections.push_back(i); 00255 } 00256 } 00257 00258 bool 00259 Polygon::isIntersectedWith(const Polygon::Ptr& poly) const 00260 { 00261 gpc_polygon *gpc_res = new gpc_polygon(); 00262 this->getIntersection(poly,gpc_res); 00263 bool res = (gpc_res->num_contours != 0); 00264 gpc_free_polygon(gpc_res); 00265 return (res); 00266 } 00267 00268 00269 void 00270 Polygon::getIntersection(const Polygon::Ptr& poly, gpc_polygon* gpc_intersection) const 00271 { 00272 gpc_polygon *gpc_poly = new gpc_polygon(), *gpc_here = new gpc_polygon(); 00273 00274 this->getGpcStructure(poly->transform_from_world_to_plane, gpc_here); 00275 poly->getGpcStructure(poly->transform_from_world_to_plane, gpc_poly); 00276 gpc_polygon_clip(GPC_INT, gpc_here, gpc_poly, gpc_intersection); 00277 gpc_free_polygon(gpc_poly); 00278 gpc_free_polygon(gpc_here); 00279 } 00280 00281 bool 00282 Polygon::getContourOverlap(const Polygon::Ptr& poly, float& rel_overlap, int& abs_overlap) const 00283 { 00284 gpc_polygon *gpc_a = new gpc_polygon(), *gpc_b = new gpc_polygon(); 00285 gpc_polygon *gpc_res_int = new gpc_polygon(), *gpc_res_union = new gpc_polygon(); 00286 this->getGpcStructure(poly->transform_from_world_to_plane, gpc_a); 00287 poly->getGpcStructure(poly->transform_from_world_to_plane, gpc_b); 00288 gpc_polygon_clip(GPC_INT, gpc_a, gpc_b, gpc_res_int); 00289 gpc_polygon_clip(GPC_UNION, gpc_a, gpc_b, gpc_res_union); 00290 00291 int i_int = -1, i_union = -1; 00292 for(size_t i=0;i<gpc_res_int->num_contours;++i) { if(!gpc_res_int->hole[i]) { i_int = i; break; } } 00293 for(size_t i=0;i<gpc_res_union->num_contours;++i) { if(!gpc_res_union->hole[i]) { i_union = i; break; } } 00294 if(i_int == -1 || i_union == -1) return false; 00295 int overlap = 0; 00296 float d_th = pow( 0.01, 2 ); 00297 for(size_t i=0;i<gpc_res_int->contour[i_int].num_vertices; ++i) 00298 { 00299 gpc_vertex* pv_int = &gpc_res_int->contour[i_int].vertex[i]; 00300 for(size_t j=0;j<gpc_res_union->contour[i_union].num_vertices; ++j) 00301 { 00302 if( pow(gpc_res_union->contour[i_union].vertex[j].x - pv_int->x, 2) + 00303 pow(gpc_res_union->contour[i_union].vertex[j].y - pv_int->y, 2) < d_th ) 00304 { 00305 ++overlap; 00306 break; 00307 } 00308 } 00309 } 00310 rel_overlap = (float)overlap/(float)gpc_res_int->contour[i_int].num_vertices; 00311 abs_overlap = overlap; 00312 ROS_DEBUG_STREAM("Overlap: " << overlap << "/"<<gpc_res_int->contour[i_int].num_vertices << " -> " 00313 << (float)overlap/(float)gpc_res_int->contour[i_int].num_vertices ); 00314 gpc_free_polygon(gpc_a); 00315 gpc_free_polygon(gpc_b); 00316 gpc_free_polygon(gpc_res_int); 00317 gpc_free_polygon(gpc_res_union); 00318 return true; 00319 } 00320 00321 00322 float 00323 Polygon::computeSimilarity(const Polygon::Ptr& poly) const 00324 { 00325 float normal = (fabs(poly->normal.dot(this->normal)) - this->merge_settings_.angle_thresh) / 00326 (1-this->merge_settings_.angle_thresh); 00327 float d = fabs(fabs((this->centroid-poly->centroid).head(3).dot(this->normal))-this->merge_settings_.d_thresh) / 00328 this->merge_settings_.d_thresh; 00329 float overlap = 0.0; 00330 int abs_overlap; 00331 this->getContourOverlap(poly, overlap, abs_overlap); 00332 return ( 3.0f / (1.0f / normal + 1.0f / d + 1.0f / overlap) ); 00333 } 00334 00335 void 00336 Polygon::merge(std::vector<Polygon::Ptr>& poly_vec) 00337 { 00338 Polygon::Ptr p_average= Polygon::Ptr(new Polygon); 00339 this->applyWeighting(poly_vec,p_average); 00340 this->merge_union(poly_vec,p_average); 00341 this->assignWeight(); 00342 this->assignID(poly_vec); 00343 } 00344 00345 void 00346 Polygon::merge_union(std::vector<Polygon::Ptr>& poly_vec, Polygon::Ptr& p_average) 00347 { 00348 gpc_polygon *gpc_C = new gpc_polygon, *smoothed = new gpc_polygon; 00349 this->getGpcStructure(p_average->transform_from_world_to_plane, gpc_C); 00350 00351 for(size_t i=0;i<poly_vec.size();++i) 00352 { 00353 gpc_polygon *gpc_B = new gpc_polygon; 00354 poly_vec[i]->getGpcStructure(p_average->transform_from_world_to_plane, gpc_B); 00355 gpc_polygon_clip(GPC_UNION, gpc_B, gpc_C, gpc_C); 00356 gpc_free_polygon(gpc_B); 00357 } 00358 00359 // fill in parameters for "this" polygon 00360 this->transform_from_world_to_plane = p_average->transform_from_world_to_plane; 00361 this->d = p_average->d; 00362 this->normal = p_average->normal; 00363 this->centroid = p_average->centroid; 00364 if(this->merged<9) { this->merged = p_average->merged; } 00365 else { this->merged = 9; } 00366 this->merge_weight_ = p_average->merge_weight_; 00367 copyGpcStructure(gpc_C, smoothed); 00368 smoothGpcStructure(gpc_C, smoothed); 00369 this->applyGpcStructure(p_average->transform_from_world_to_plane.inverse(), smoothed); 00370 gpc_free_polygon(gpc_C); 00371 gpc_free_polygon(smoothed); 00372 } 00373 00374 00375 00376 void 00377 Polygon::assignWeight() 00378 { 00379 if (std::strcmp(merge_settings_.weighting_method.c_str(), "COUNTER")== 0) 00380 { 00381 merge_weight_=merged; 00382 } 00383 else if (std::strcmp(merge_settings_.weighting_method.c_str(), "AREA")== 0) 00384 { 00385 double area = computeArea3d(); 00386 merge_weight_=(merged*area); 00387 } 00388 } 00389 00390 void 00391 Polygon::assignID(const std::vector<Polygon::Ptr>& poly_vec) 00392 { 00393 unsigned int tmp_id=poly_vec[0]->id; 00394 for(size_t i=0;i<poly_vec.size();++i) 00395 { 00396 if(poly_vec[i]->id<tmp_id)tmp_id=poly_vec[i]->id; 00397 } 00398 this->id=tmp_id; 00399 } 00400 00401 void 00402 Polygon::applyWeighting(const std::vector<Polygon::Ptr>& poly_vec, Polygon::Ptr & p_average) 00403 { 00404 Eigen::Vector3f average_normal=normal*merge_weight_; 00405 Eigen::Vector4f average_centroid=centroid*merge_weight_; 00406 double average_d=d*merge_weight_; 00407 double sum_w=merge_weight_; 00408 int sum_merged=merged; 00409 00410 for(int i=0 ; i< (int) poly_vec.size();i++) 00411 { 00412 Polygon& p_map1 =*(poly_vec[i]); 00413 00414 if(normal.dot(p_map1.normal)<0) 00415 { 00416 average_normal += p_map1.merge_weight_* -p_map1.normal; 00417 } 00418 else 00419 { 00420 average_normal += p_map1.merge_weight_* p_map1.normal; 00421 } 00422 average_centroid += p_map1.merge_weight_* p_map1.centroid; 00423 average_d +=p_map1.merge_weight_ * p_map1.d; 00424 sum_w += p_map1.merge_weight_; 00425 sum_merged += p_map1.merged; 00426 } 00427 00428 average_normal=average_normal/sum_w; 00429 average_centroid=average_centroid/sum_w; 00430 average_d=average_d/sum_w; 00431 average_normal.normalize(); 00432 00433 if (sum_merged < 9) 00434 { 00435 p_average->merged=sum_merged; 00436 } 00437 else 00438 { 00439 p_average->merged=9; 00440 } 00441 p_average->computeAttributes(average_normal,average_centroid); 00442 } 00443 00444 void 00445 Polygon::getGpcStructure(const Eigen::Affine3f& external_trafo, gpc_polygon* gpc_p) const 00446 { 00447 // get transformed contours 00448 std::vector< std::vector <Eigen::Vector3f> > transformed_contours; 00449 this->getTransformedContours(external_trafo, transformed_contours); 00450 00451 gpc_p->num_contours = contours.size(); 00452 gpc_p->hole = (int*)malloc(contours.size()*sizeof(int)); 00453 gpc_p->contour = (gpc_vertex_list*)malloc(contours.size()*sizeof(gpc_vertex_list)); 00454 for(size_t j=0; j<contours.size(); ++j) 00455 { 00456 gpc_p->hole[j] = holes[j]; 00457 gpc_p->contour[j].num_vertices = contours[j].size(); 00458 gpc_p->contour[j].vertex = (gpc_vertex*)malloc(gpc_p->contour[j].num_vertices*sizeof(gpc_vertex)); 00459 00460 for(size_t k=0; k<contours[j].size(); ++k) 00461 { 00462 Eigen::Vector3f point_trans = transformed_contours[j][k]; 00463 gpc_p->contour[j].vertex[k].x = point_trans(0); 00464 gpc_p->contour[j].vertex[k].y = point_trans(1); 00465 } 00466 } 00467 } 00468 00469 00470 void 00471 Polygon::applyGpcStructure(const Eigen::Affine3f& external_trafo, const gpc_polygon* gpc_p) 00472 { 00473 // clear contours, at the end gpc_C contains everything we need! 00474 this->contours.clear(); 00475 this->holes.clear(); 00476 for(int j=0; j<gpc_p->num_contours; ++j) 00477 { 00478 this->contours.push_back(std::vector<Eigen::Vector3f>()); 00479 this->holes.push_back(gpc_p->hole[j]); 00480 float last_x = 0.0, last_y=0.0; 00481 for (int k=0; k<gpc_p->contour[j].num_vertices; ++k) 00482 { 00483 // check if points are too close to each other (so remove similar points) 00484 if (fabs(gpc_p->contour[j].vertex[k].x - last_x) < 0.01 && fabs(gpc_p->contour[j].vertex[k].y - last_y) < 0.01) 00485 continue; 00486 last_x = gpc_p->contour[j].vertex[k].x; 00487 last_y = gpc_p->contour[j].vertex[k].y; 00488 this->contours.back().push_back(external_trafo * Eigen::Vector3f(last_x,last_y,0)); 00489 } 00490 00491 if (this->contours.back().size() <= 2) // drop empty contour lists 00492 { 00493 this->contours.pop_back(); 00494 this->holes.pop_back(); 00495 } 00496 } 00497 } 00498 00499 00500 //#######methods for calculation##################### 00501 00502 void 00503 Polygon::computeCentroid() 00504 { 00505 //find largest non-hole contour 00506 unsigned int idx = 0; 00507 for (unsigned int i = 0; i < contours.size (); i++) 00508 { 00509 int max_pts = 0; 00510 if(!holes[i]) 00511 { 00512 if((int)contours[i].size()>(int)max_pts) 00513 { 00514 max_pts = contours[i].size(); 00515 idx = i; 00516 } 00517 } 00518 } 00519 pcl::PointCloud<pcl::PointXYZ> poly_cloud; 00520 for (unsigned int j = 0; j < contours[idx].size () ; j++) 00521 { 00522 pcl::PointXYZ p; 00523 p.x = contours[idx][j][0]; 00524 p.y = contours[idx][j][1]; 00525 p.z = contours[idx][j][2]; 00526 poly_cloud.push_back(p); 00527 } 00528 pcl::compute3DCentroid(poly_cloud,centroid); 00529 } 00530 00531 00532 double 00533 Polygon::computeArea() const 00534 { 00535 double xi, xi_1, yi, yi_1, area=0; 00536 double sum; 00537 for (unsigned int i = 0; i < contours.size (); i++) 00538 { 00539 if(holes[i]) continue; 00540 sum = 0; 00541 //area_[i] = 0; 00542 for (unsigned int j = 0; j < contours[i].size (); j++) 00543 { 00544 xi = contours[i][j][0]; 00545 yi = contours[i][j][1]; 00546 if (j == (contours[i].size ()) - 1) 00547 { 00548 xi_1 = contours[i][0][0]; 00549 yi_1 = contours[i][0][1]; 00550 } 00551 else 00552 { 00553 xi_1 = contours[i][j + 1][0]; 00554 yi_1 = contours[i][j + 1][1]; 00555 } 00556 sum = sum + (xi * yi_1 - xi_1 * yi); 00557 00558 } 00559 area += fabs (sum / 2); 00560 } 00561 return area; 00562 } 00563 00564 00565 00566 double 00567 Polygon::computeArea3d() const 00568 { 00569 double area=0; 00570 for (size_t i=0; i<contours.size(); ++i) 00571 { 00572 Eigen::Vector3f vec_sum(Eigen::Vector3f::Zero()); 00573 00574 for (int j=0; j<(int)contours[i].size()-1; ++j) 00575 vec_sum += contours[i][j].cross(contours[i][j+1]); 00576 00577 vec_sum += contours[ i ][ contours[i].size()-1 ].cross(contours[i][0]); 00578 area += 0.5 * normal.dot(vec_sum); 00579 00580 // special cases for holes can be ignored, since their vertex orientation is opposite to the 00581 // outer most contour. Therefore the their area sign are different. 00582 00583 //if(holes[i]) area -= 0.5d * fabs(normal.dot(temp_vec)); 00584 //else area += 0.5d * fabs(normal.dot(temp_vec)); 00585 } 00586 return std::fabs(area); 00587 } 00588 00589 void 00590 Polygon::getTransformationFromPlaneToWorld( 00591 const Eigen::Vector3f &normal, 00592 const Eigen::Vector3f &origin, 00593 Eigen::Affine3f &transformation) const 00594 { 00595 Eigen::Vector3f u, v; 00596 getCoordinateSystemOnPlane(normal, u, v); 00597 pcl::getTransformationFromTwoUnitVectorsAndOrigin(v, normal, origin, transformation); 00598 transformation = transformation.inverse(); 00599 } 00600 00601 void 00602 Polygon::getTransformationFromPlaneToWorld( 00603 const Eigen::Vector3f z_axis, 00604 const Eigen::Vector3f &normal, 00605 const Eigen::Vector3f &origin, 00606 Eigen::Affine3f &transformation) 00607 { 00608 this->normal = normal; 00609 pcl::getTransformationFromTwoUnitVectorsAndOrigin(z_axis, normal, origin, transformation); 00610 transformation = transformation.inverse(); 00611 } 00612 00613 void 00614 Polygon::TransformContours(const Eigen::Affine3f& trafo) 00615 { 00616 00617 for(size_t j=0; j<contours.size(); j++) 00618 { 00619 for(size_t k=0; k<contours[j].size(); k++) 00620 { 00621 contours[j][k] = trafo * contours[j][k]; 00622 } 00623 } 00624 } 00625 00626 void 00627 Polygon::getTransformedContours(const Eigen::Affine3f& trafo, std::vector< std::vector<Eigen::Vector3f> >& new_contours) const 00628 { 00629 new_contours.resize(contours.size()); 00630 for(size_t j=0; j<contours.size(); j++) 00631 { 00632 new_contours[j].resize(contours[j].size()); 00633 for(size_t k=0; k<contours[j].size(); k++) 00634 { 00635 new_contours[j][k] = trafo*contours[j][k]; 00636 } 00637 } 00638 } 00639 00640 void 00641 Polygon::computePoseAndBoundingBox(Eigen::Affine3f& pose, Eigen::Vector4f& min_pt, Eigen::Vector4f& max_pt) 00642 { 00643 pcl::PointCloud<pcl::PointXYZ> poly_cloud; 00644 unsigned int idx = 0; 00645 for (unsigned int j = 0; j < contours[idx].size () ; j++) 00646 { 00647 pcl::PointXYZ p; 00648 p.x = contours[idx][j][0]; 00649 p.y = contours[idx][j][1]; 00650 p.z = contours[idx][j][2]; 00651 poly_cloud.push_back(p); 00652 } 00653 Eigen::Matrix3f cov; 00654 pcl::computeCovarianceMatrix (poly_cloud, centroid, cov); 00655 EIGEN_ALIGN16 Eigen::Vector3f eigen_values; 00656 EIGEN_ALIGN16 Eigen::Matrix3f eigen_vectors; 00657 pcl::eigen33 (cov, eigen_vectors, eigen_values); 00658 pcl::getTransformationFromTwoUnitVectorsAndOrigin(eigen_vectors.col(1),eigen_vectors.col(0),centroid.head(3),pose); 00659 00660 pcl::PointCloud<pcl::PointXYZ> cloud_trans; 00661 pcl::transformPointCloud(poly_cloud, cloud_trans, pose); 00662 pcl::getMinMax3D(cloud_trans, min_pt, max_pt); 00663 } 00664 00665 //#############debugging methods####################### 00666 00667 void Polygon::debug_output(std::string name) 00668 { 00669 std::string path = "/home/goa-tz/debug/dbg/"; 00670 path.append(name.c_str()); 00671 std::ofstream os(path.c_str()); 00672 00673 for (int i = 0; i < (int) this->contours.size(); ++i) 00674 { 00675 for (int j = 0; j < (int) this->contours[i].size(); ++j) 00676 { 00677 os << contours[i][j]<<std::endl; 00678 } 00679 } 00680 00681 os.close(); 00682 } 00683 00684 }//namespace