00001 #include <feature/Detector.h>
00002 #include <feature/ShapeContext.h>
00003 #include <feature/BetaGrid.h>
00004 #include <feature/RangeDetector.h>
00005 #include <feature/CurvatureDetector.h>
00006 #include <feature/NormalBlobDetector.h>
00007 #include <feature/NormalEdgeDetector.h>
00008 #include <feature/RansacFeatureSetMatcher.h>
00009 #include <feature/RansacMultiFeatureSetMatcher.h>
00010 #include <sensorstream/CarmenLog.h>
00011 #include <sensorstream/LogSensorStream.h>
00012 #include <sensorstream/SensorStream.h>
00013 #include <utils/SimpleMinMaxPeakFinder.h>
00014 #include <utils/HistogramDistances.h>
00015
00016 #include <cairo.h>
00017 #include <cairo-pdf.h>
00018 #include <cairo-svg.h>
00019
00020 #include <iostream>
00021 #include <string>
00022 #include <string.h>
00023 #include <sstream>
00024 #include <utility>
00025 #include <sys/stat.h>
00026 #include <sys/types.h>
00027
00028
00029 LogSensorStream m_sensorReference(NULL,NULL);
00030
00031 CurvatureDetector *m_detectorCurvature = NULL;
00032 NormalBlobDetector *m_detectorNormalBlob = NULL;
00033 NormalEdgeDetector *m_detectorNormalEdge = NULL;
00034 RangeDetector *m_detectorRange = NULL;
00035 Detector* m_detector = NULL;
00036
00037 BetaGridGenerator *m_betaGenerator = NULL;
00038 ShapeContextGenerator *m_shapeGenerator = NULL;
00039 DescriptorGenerator *m_descriptor = NULL;
00040
00041 RansacFeatureSetMatcher *m_ransac = NULL;
00042
00043 std::vector< std::vector<InterestPoint *> > m_pointsReference;
00044 std::vector< OrientedPoint2D > m_posesReference;
00045
00046 cairo_t * cairoMap = 0;
00047 cairo_t * cairoOut = 0;
00048
00049 int offsetX = 0, offsetY = 0, border = 10;
00050 double scaleFactor = 1.;
00051 int sizeX = 1024, sizeY = 800;
00052
00053 unsigned int corresp = 9;
00054
00055 double acceptanceSigma = 0.1;
00056
00057 void help(){
00058 std::cerr << "FLIRTLib version 0.9b - authors Gian Diego Tipaldi and Kai O. Arras" << std::endl
00059 << "Usage: ransacLoopClosureDraw -filename <logfile> [options] " << std::endl
00060 << "Options:" << std::endl
00061
00062 << " -filename \t The logfile in CARMEN format to process (mandatory)." << std::endl
00063 << " -corresp \t The number of minimum correspondences to assess a match (default=9)." << std::endl
00064 << " -scale \t The number of scales to consider (default=5)." << std::endl
00065 << " -dmst \t The number of spanning tree for the curvature detector (deafult=2)." << std::endl
00066 << " -window \t The size of the local window for estimating the normal signal (default=3)." << std::endl
00067 << " -detectorType \t The type of detector to use. Available options are (default=0):" << std::endl
00068 << " \t 0 - Curvature detector;" << std::endl
00069 << " \t 1 - Normal edge detector;" << std::endl
00070 << " \t 2 - Normal blob detector;" << std::endl
00071 << " \t 3 - Range detector." << std::endl
00072 << " -descriptorType \t The type of descriptor to use. Available options are (default=0):" << std::endl
00073 << " \t 0 - Beta - grid;" << std::endl
00074 << " \t 1 - Shape context." << std::endl
00075 << " -distanceType \t The distance function to compare the descriptors. Available options are (default=2):" << std::endl
00076 << " \t 0 - Euclidean distance;" << std::endl
00077 << " \t 1 - Chi square distance;" << std::endl
00078 << " \t 2 - Symmetric Chi square distance;" << std::endl
00079 << " \t 3 - Bhattacharyya distance;" << std::endl
00080 << " \t 4 - Kullback Leibler divergence;" << std::endl
00081 << " \t 5 - Jensen Shannon divergence." << std::endl
00082 << " -baseSigma \t The initial standard deviation for the smoothing operator (default=0.2)." << std::endl
00083 << " -sigmaStep \t The incremental step for the scales of the smoothing operator. (default=1.4)." << std::endl
00084 << " -minPeak \t The minimum value for a peak to be detected (default=0.34)." << std::endl
00085 << " -minPeakDistance \t The minimum difference with the neighbors for a peak to be detected (default=0.001)." << std::endl
00086 << " -acceptanceSigma \t The standard deviation of the detection error (default=0.1)." << std::endl
00087 << " -success \t The success probability of RANSAC (default=0.95)." << std::endl
00088 << " -inlier \t The inlier probability of RANSAC (default=0.4)." << std::endl
00089 << " -matchingThreshold \t The threshold two descriptors are considered matched (default=0.4)." << std::endl
00090 << " -strategy \t The strategy for matching two descriptors. Available options are (default=0):" << std::endl
00091 << " \t 0 - Nearest Neighbour. Only the closest match above the threshold is considered;" << std::endl
00092 << " \t 1 - Threshold. All the matches above the threshold are considered." << std::endl
00093 << std::endl
00094 << "The program matches every scan with all the others in the logfile. To work properly, the logfile must" << std::endl
00095 << "be corrected by a SLAM algorithm beforehand (the provided logfiles are already corrected). The program writes" << std::endl
00096 << "one image for each scan in the following directory:" << std::endl
00097 << " <logfile>_<detector>_<descriptor>_<distance>/" << std::endl
00098 << std::endl
00099 << std::endl;
00100 }
00101
00102 void match(unsigned int position)
00103 {
00104
00105
00106 m_sensorReference.seek(position);
00107 cairo_matrix_t m1;
00108 cairo_get_matrix(cairoOut, &m1);
00109 cairo_identity_matrix(cairoOut);
00110 cairo_set_source_surface(cairoOut, cairo_get_target(cairoMap), 0., 0.);
00111 cairo_paint(cairoOut);
00112 cairo_set_matrix(cairoOut, &m1);
00113
00114
00115 std::vector<InterestPoint *> pointsLocal(m_pointsReference[position].size());
00116 const LaserReading* lreadReference = dynamic_cast<const LaserReading*>(m_sensorReference.current());
00117 for(unsigned int j = 0; j < m_pointsReference[position].size(); j++){
00118 InterestPoint * point = new InterestPoint(*m_pointsReference[position][j]);
00119 point->setPosition(lreadReference->getLaserPose().ominus(point->getPosition()));
00120 pointsLocal[j] = point;
00121 }
00122
00123
00124 for(unsigned int i = 0; i < m_pointsReference.size(); i++){
00125 if(i == position) {
00126 continue;
00127 }
00128 OrientedPoint2D transform;
00129 std::vector< std::pair<InterestPoint*, InterestPoint* > > correspondences;
00130 double result = m_ransac->matchSets(m_pointsReference[i], pointsLocal, transform, correspondences);
00131 if(correspondences.size() >= corresp) {
00132 cairo_matrix_t m;
00133 cairo_get_matrix(cairoOut, &m);
00134 cairo_translate(cairoOut, transform.x, transform.y);
00135 cairo_rotate(cairoOut, transform.theta);
00136
00137 cairo_set_source_rgba(cairoOut, 1., 0., 0., 1. - result/(acceptanceSigma * acceptanceSigma * 5.99 * double(pointsLocal.size())));
00138 cairo_move_to(cairoOut, 0., -0.3);
00139 cairo_line_to(cairoOut, 0.6, 0.);
00140 cairo_line_to(cairoOut, 0., 0.3);
00141 cairo_close_path(cairoOut);
00142 cairo_fill(cairoOut);
00143 cairo_set_matrix(cairoOut, &m);
00144 }
00145 }
00146
00147 cairo_matrix_t m;
00148 cairo_get_matrix(cairoOut, &m);
00149 cairo_translate(cairoOut, lreadReference->getLaserPose().x, lreadReference->getLaserPose().y);
00150 cairo_rotate(cairoOut, lreadReference->getLaserPose().theta);
00151 cairo_set_source_rgba(cairoOut, 0., 0., 1., 1.);
00152 cairo_move_to(cairoOut, 0., -0.3);
00153 cairo_line_to(cairoOut, 0.6, 0.);
00154 cairo_line_to(cairoOut, 0., 0.3);
00155 cairo_close_path(cairoOut);
00156 cairo_stroke(cairoOut);
00157 cairo_set_matrix(cairoOut, &m);
00158
00159 }
00160
00161 void detectLog(){
00162 unsigned int i = 0;
00163 unsigned int position = m_sensorReference.tell();
00164 m_sensorReference.seek(0,END);
00165 unsigned int last = m_sensorReference.tell();
00166 m_sensorReference.seek(0);
00167
00168 std::string bar(50, ' ');
00169 bar[0] = '#';
00170 unsigned int progress = 0;
00171
00172 while(!m_sensorReference.end()){
00173 unsigned int currentProgress = (m_sensorReference.tell()*100)/last;
00174 if (progress < currentProgress){
00175 progress = currentProgress;
00176 bar[progress/2] = '#';
00177 std::cout << "\rDetecting points [" << bar << "] " << (m_sensorReference.tell()*100)/last << "%" << std::flush;
00178 }
00179 const LaserReading* lreadReference = dynamic_cast<const LaserReading*>(m_sensorReference.next());
00180 if (lreadReference){
00181 m_detector->detect(*lreadReference, m_pointsReference[i]);
00182 m_posesReference[i] = lreadReference->getLaserPose();
00183 i++;
00184 }
00185 }
00186 m_sensorReference.seek(position);
00187 std::cout << " done." << std::endl;
00188 }
00189
00190 void describeLog(){
00191 unsigned int i = 0;
00192 unsigned int position = m_sensorReference.tell();
00193 m_sensorReference.seek(0,END);
00194 unsigned int last = m_sensorReference.tell();
00195 m_sensorReference.seek(0);
00196
00197 std::string bar(50, ' ');
00198 bar[0] = '#';
00199 unsigned int progress = 0;
00200
00201 while(!m_sensorReference.end()){
00202 unsigned int currentProgress = (m_sensorReference.tell()*100)/last;
00203 if (progress < currentProgress){
00204 progress = currentProgress;
00205 bar[progress/2] = '#';
00206 std::cout << "\rDescribing points [" << bar << "] " << (m_sensorReference.tell()*100)/last << "%" << std::flush;
00207 }
00208 const LaserReading* lreadReference = dynamic_cast<const LaserReading*>(m_sensorReference.next());
00209 if (lreadReference){
00210 for(unsigned int j = 0; j < m_pointsReference[i].size(); j++){
00211 m_pointsReference[i][j]->setDescriptor(m_descriptor->describe(*m_pointsReference[i][j], *lreadReference));
00212 }
00213 i++;
00214 }
00215 }
00216 m_sensorReference.seek(position);
00217 std::cout << " done." << std::endl;
00218 }
00219
00220 void computeBoundingBox(){
00221 double minX = 1e17, minY = 1e17;
00222 double maxX = -1e17, maxY = -1e17;
00223 unsigned int position = m_sensorReference.tell();
00224 m_sensorReference.seek(0,END);
00225 unsigned int last = m_sensorReference.tell();
00226 m_sensorReference.seek(0);
00227
00228 std::string bar(50, ' ');
00229 bar[0] = '#';
00230 unsigned int progress = 0;
00231
00232 while(!m_sensorReference.end()){
00233 unsigned int currentProgress = (m_sensorReference.tell()*100)/last;
00234 if (progress < currentProgress){
00235 progress = currentProgress;
00236 bar[progress/2] = '#';
00237 std::cout << "\rComputing Bounding Box [" << bar << "] " << (m_sensorReference.tell()*100)/last << "%" << std::flush;
00238 }
00239 const LaserReading* lreadReference = dynamic_cast<const LaserReading*>(m_sensorReference.next());
00240 if (lreadReference){
00241 const std::vector<Point2D>& points = lreadReference->getWorldCartesian();
00242 const std::vector<double>& rho = lreadReference->getRho();
00243 for(unsigned int i = 0; i < points.size(); i++){
00244 if(rho[i] >= lreadReference->getMaxRange()) continue;
00245 minX = minX < points[i].x ? minX : points[i].x;
00246 minY = minY < points[i].y ? minY : points[i].y;
00247 maxX = maxX > points[i].x ? maxX : points[i].x;
00248 maxY = maxY > points[i].y ? maxY : points[i].y;
00249 }
00250 }
00251 }
00252 double bbX = maxX - minX, bbY = maxY - minY;
00253 if(bbX < bbY) {
00254 uint tmp = sizeX;
00255 sizeX = sizeY;
00256 sizeY = tmp;
00257
00258 }
00259 double scaleX = double(sizeX - 2*border)/bbX;
00260 double scaleY = double(sizeY - 2*border)/bbY;
00261 scaleFactor = scaleX > scaleY ? scaleY : scaleX;
00262 offsetX = minX * scaleFactor - border, offsetY = minY * scaleFactor - border;
00263 m_sensorReference.seek(position);
00264 sizeX = bbX*scaleFactor + 2*border;
00265 sizeY = bbY*scaleFactor + 2*border;
00266 std::cout << " done." << std::endl;
00267 std::cout << "Bounding Box = " << bbX << "x" << bbY << std::endl;
00268 }
00269
00270 void computeMap(){
00271 unsigned int position = m_sensorReference.tell();
00272 m_sensorReference.seek(0,END);
00273 unsigned int last = m_sensorReference.tell();
00274 m_sensorReference.seek(0);
00275
00276 std::string bar(50, ' ');
00277 bar[0] = '#';
00278 unsigned int progress = 0;
00279
00280 cairo_translate(cairoMap, -offsetX, -offsetY);
00281 cairo_scale(cairoMap, scaleFactor, scaleFactor);
00282 cairo_set_line_cap(cairoMap, CAIRO_LINE_CAP_ROUND);
00283 cairo_set_line_width(cairoMap, 1./scaleFactor);
00284 while(!m_sensorReference.end()){
00285 unsigned int currentProgress = (m_sensorReference.tell()*100)/last;
00286 if (progress < currentProgress){
00287 progress = currentProgress;
00288 bar[progress/2] = '#';
00289 std::cout << "\rComputing Map [" << bar << "] " << (m_sensorReference.tell()*100)/last << "%" << std::flush;
00290 }
00291 const LaserReading* lreadReference = dynamic_cast<const LaserReading*>(m_sensorReference.next());
00292 if (lreadReference){
00293 const std::vector<Point2D>& points = lreadReference->getWorldCartesian();
00294 const std::vector<double>& rho = lreadReference->getRho();
00295 for(unsigned int i = 0; i < points.size(); i+=2){
00296 if(rho[i] >= lreadReference->getMaxRange()) continue;
00297
00298
00299 cairo_move_to(cairoMap, points[i].x, points[i].y);
00300 cairo_line_to(cairoMap, points[i].x, points[i].y);
00301 cairo_stroke(cairoMap);
00302 }
00303 }
00304 }
00305 m_sensorReference.seek(position);
00306 std::cout << " done." << std::endl;
00307 }
00308
00309 int main(int argc, char **argv){
00310
00311 std::string filename("");
00312 unsigned int scale = 5, dmst = 2, window = 3, detectorType = 0, descriptorType = 0, distanceType = 2, strategy = 0;
00313 double baseSigma = 0.2, sigmaStep = 1.4, minPeak = 0.34, minPeakDistance = 0.001, success = 0.95, inlier = 0.4, matchingThreshold = 0.4;
00314 bool useMaxRange = false;
00315
00316 int i = 1;
00317 while(i < argc){
00318 if(strncmp("-filename", argv[i], sizeof("-filename")) == 0 ){
00319 filename = argv[++i];
00320 i++;
00321 } else if(strncmp("-detector", argv[i], sizeof("-detector")) == 0 ){
00322 detectorType = atoi(argv[++i]);
00323 i++;
00324 } else if(strncmp("-descriptor", argv[i], sizeof("-descriptor")) == 0 ){
00325 descriptorType = atoi(argv[++i]);
00326 i++;
00327 } else if(strncmp("-corresp", argv[i], sizeof("-corresp")) == 0 ){
00328 corresp = atoi(argv[++i]);
00329 i++;
00330 } else if(strncmp("-distance", argv[i], sizeof("-distance")) == 0 ){
00331 distanceType = atoi(argv[++i]);
00332 i++;
00333 } else if(strncmp("-baseSigma", argv[i], sizeof("-baseSigma")) == 0 ){
00334 baseSigma = strtod(argv[++i], NULL);
00335 i++;
00336 } else if(strncmp("-sigmaStep", argv[i], sizeof("-sigmaStep")) == 0 ){
00337 sigmaStep = strtod(argv[++i], NULL);
00338 i++;
00339 } else if(strncmp("-minPeak", argv[i], sizeof("-minPeak")) == 0 ){
00340 minPeak = strtod(argv[++i], NULL);
00341 i++;
00342 } else if(strncmp("-minPeakDistance", argv[i], sizeof("-minPeakDistance")) == 0 ){
00343 minPeakDistance = strtod(argv[++i], NULL);
00344 i++;
00345 } else if(strncmp("-scale", argv[i], sizeof("-scale")) == 0 ){
00346 scale = atoi(argv[++i]);
00347 i++;
00348 } else if(strncmp("-dmst", argv[i], sizeof("-dmst")) == 0 ){
00349 dmst = atoi(argv[++i]);
00350 i++;
00351 } else if(strncmp("-window", argv[i], sizeof("-window")) == 0 ){
00352 scale = atoi(argv[++i]);
00353 i++;
00354 } else if(strncmp("-acceptanceSigma", argv[i], sizeof("-acceptanceSigma")) == 0 ){
00355 acceptanceSigma = strtod(argv[++i], NULL);
00356 i++;
00357 } else if(strncmp("-success", argv[i], sizeof("-success")) == 0 ){
00358 success = strtod(argv[++i], NULL);
00359 i++;
00360 } else if(strncmp("-inlier", argv[i], sizeof("-inlier")) == 0 ){
00361 inlier = strtod(argv[++i], NULL);
00362 i++;
00363 } else if(strncmp("-matchingThreshold", argv[i], sizeof("-matchingThreshold")) == 0 ){
00364 matchingThreshold = strtod(argv[++i], NULL);
00365 i++;
00366 } else {
00367 i++;
00368 }
00369 }
00370
00371 if(!filename.size()){
00372 help();
00373 exit(-1);
00374 }
00375
00376 CarmenLogWriter writer;
00377 CarmenLogReader reader;
00378
00379 m_sensorReference = LogSensorStream(&reader, &writer);
00380
00381 m_sensorReference.load(filename);
00382
00383 SimpleMinMaxPeakFinder *m_peakMinMax = new SimpleMinMaxPeakFinder(minPeak, minPeakDistance);
00384
00385
00386 std::string detector("");
00387 switch(detectorType){
00388 case 0:
00389 m_detectorCurvature = new CurvatureDetector(m_peakMinMax, scale, baseSigma, sigmaStep, dmst);
00390 m_detectorCurvature->setUseMaxRange(useMaxRange);
00391 m_detector = m_detectorCurvature;
00392 detector = "curvature";
00393 break;
00394 case 1:
00395 m_detectorNormalEdge = new NormalEdgeDetector(m_peakMinMax, scale, baseSigma, sigmaStep, window);
00396 m_detectorNormalEdge->setUseMaxRange(useMaxRange);
00397 m_detector = m_detectorNormalEdge;
00398 detector = "edge";
00399 break;
00400 case 2:
00401 m_detectorNormalBlob = new NormalBlobDetector(m_peakMinMax, scale, baseSigma, sigmaStep, window);
00402 m_detectorNormalBlob->setUseMaxRange(useMaxRange);
00403 m_detector = m_detectorNormalBlob;
00404 detector = "blob";
00405 break;
00406 case 3:
00407 m_detectorRange = new RangeDetector(m_peakMinMax, scale, baseSigma, sigmaStep);
00408 m_detectorRange->setUseMaxRange(useMaxRange);
00409 m_detector = m_detectorRange;
00410 detector = "range";
00411 break;
00412 default:
00413 std::cerr << "Wrong detector type" << std::endl;
00414 exit(-1);
00415 }
00416
00417 HistogramDistance<double> *dist = NULL;
00418
00419 std::string distance("");
00420 switch(distanceType){
00421 case 0:
00422 dist = new EuclideanDistance<double>();
00423 distance = "euclid";
00424 break;
00425 case 1:
00426 dist = new Chi2Distance<double>();
00427 distance = "chi2";
00428 break;
00429 case 2:
00430 dist = new SymmetricChi2Distance<double>();
00431 distance = "symchi2";
00432 break;
00433 case 3:
00434 dist = new BatthacharyyaDistance<double>();
00435 distance = "batt";
00436 break;
00437 case 4:
00438 dist = new KullbackLeiblerDistance<double>();
00439 distance = "kld";
00440 break;
00441 case 5:
00442 dist = new JensenShannonDistance<double>();
00443 distance = "jsd";
00444 break;
00445 default:
00446 std::cerr << "Wrong distance type" << std::endl;
00447 exit(-1);
00448 }
00449
00450 std::string descriptor("");
00451 switch(descriptorType){
00452 case 0:
00453 m_betaGenerator = new BetaGridGenerator(0.02, 0.5, 4, 12);
00454 m_betaGenerator->setDistanceFunction(dist);
00455 m_descriptor = m_betaGenerator;
00456 descriptor = "beta";
00457 break;
00458 case 1:
00459 m_shapeGenerator = new ShapeContextGenerator(0.02, 0.5, 4, 12);
00460 m_shapeGenerator->setDistanceFunction(dist);
00461 m_descriptor = m_shapeGenerator;
00462 descriptor = "shape";
00463 break;
00464 default:
00465 std::cerr << "Wrong descriptor type" << std::endl;
00466 exit(-1);
00467 }
00468
00469 std::cerr << "Processing file:\t" << filename << "\nDetector:\t\t" << detector << "\nDescriptor:\t\t" << descriptor << "\nDistance:\t\t" << distance << std::endl;
00470
00471 switch(strategy){
00472 case 0:
00473 m_ransac = new RansacFeatureSetMatcher(acceptanceSigma * acceptanceSigma * 5.99, success, inlier, matchingThreshold, acceptanceSigma * acceptanceSigma * 3.84, false);
00474 break;
00475 case 1:
00476 m_ransac = new RansacMultiFeatureSetMatcher(acceptanceSigma * acceptanceSigma * 5.99, success, inlier, matchingThreshold, acceptanceSigma * acceptanceSigma * 3.84, false);
00477 break;
00478 default:
00479 std::cerr << "Wrong strategy type" << std::endl;
00480 exit(-1);
00481 }
00482
00483 m_sensorReference.seek(0,END);
00484 unsigned int end = m_sensorReference.tell();
00485 m_sensorReference.seek(0,BEGIN);
00486
00487 m_pointsReference.resize(end + 1);
00488 m_posesReference.resize(end + 1);
00489
00490 computeBoundingBox();
00491
00492 detectLog();
00493 describeLog();
00494
00495 std::stringstream mapFile;
00496 mapFile << filename << "_map.png";
00497
00498
00499 cairo_surface_t * cairoMapSurface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, sizeX, sizeY);
00500
00501 cairoMap = cairo_create(cairoMapSurface);
00502
00503 std::stringstream outDir;
00504 outDir << filename << "_" << detector << "_" << descriptor << "_" << distance << "/";
00505 mkdir(outDir.str().c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
00506
00507 computeMap();
00508
00509 std::string bar(50,' ');
00510 bar[0] = '#';
00511 unsigned int progress = 0;
00512
00513
00514 for(unsigned int i =0; i < m_pointsReference.size(); i++){
00515 unsigned int currentProgress = (i*100)/(m_pointsReference.size() - 1);
00516 if (progress < currentProgress){
00517 progress = currentProgress;
00518 bar[progress/2] = '#';
00519 std::cout << "\rMatching [" << bar << "] " << progress << "%" << std::flush;
00520 }
00521 std::stringstream outFile;
00522 outFile << outDir.str() << "frame_";
00523 outFile.width(4);
00524 outFile.fill('0');
00525 outFile << std::right << i << ".png";
00526 cairo_surface_t * cairoOutSurface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, sizeX, sizeY);
00527
00528 cairoOut = cairo_create(cairoOutSurface);
00529 cairo_translate(cairoOut, -offsetX, -offsetY);
00530 cairo_scale(cairoOut, scaleFactor, scaleFactor);
00531 cairo_set_line_width(cairoOut, 1./scaleFactor);
00532 match(i);
00533 cairo_surface_write_to_png(cairoOutSurface, outFile.str().c_str());
00534 cairo_surface_destroy(cairoOutSurface);
00535 cairo_destroy(cairoOut);
00536 }
00537 std::cout << " done." << std::endl;
00538
00539 cairo_surface_write_to_png(cairoMapSurface, mapFile.str().c_str());
00540
00541 cairo_surface_destroy(cairoMapSurface);
00542 cairo_destroy(cairoMap);
00543
00544 }
00545