00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include <iostream>
00023 #include <vector>
00024 #include <string>
00025 #include <boost/foreach.hpp>
00026 #include "Utils.h"
00027 #include <getopt.h>
00028
00029 using namespace std;
00030
00031 #include "PanoDetector.h"
00032
00033 void printVersion()
00034 {
00035 std::cout << "Hugin's cpfind " << DISPLAY_VERSION << endl;
00036 std::cout << "based on Pan-o-matic by Anael Orlinski" << endl;
00037 };
00038
00039 void printUsage()
00040 {
00041 printVersion();
00042 cout << endl
00043 << "Basic usage: " << endl
00044 << " cpfind -o output_project project.pto" << endl
00045 << " cpfind -k i0 -k i1 ... -k in project.pto" << endl
00046 << " cpfind --kall project.pto" << endl
00047 << endl << "The input project file is required." << endl
00048 << endl << "General options" << endl
00049 << " -q|--quiet Do not output progress" << endl
00050 << " -v|--verbose Verbose output" << endl
00051 << " -h|--help Shows this help screen" << endl
00052 << " --version Prints the version number and exits then" << endl
00053 << " -o|--output=<string> Sets the filename of the output file" << endl
00054 << " (default: default.pto)" << endl
00055 << endl << "Matching strategy (these options are mutually exclusive)" << endl
00056 << " --linearmatch Enable linear images matching" << endl
00057 << " Can be fine tuned with" << endl
00058 << " --linearmatchlen=<int> Number of images to match (default: 1)" << endl
00059 << " --multirow Enable heuristic multi row matching" << endl
00060 << " --prealigned Match only overlapping images," << endl
00061 << " requires a rough aligned panorama" << endl
00062 << endl << "Feature description options" << endl
00063 << " --sieve1width=<int> Sieve 1: Number of buckets on width (default: 10)" << endl
00064 << " --sieve1height=<int> Sieve 1: Number of buckets on height (default: 10)" << endl
00065 << " --sieve1size=<int> Sieve 1: Max points per bucket (default: 100)" << endl
00066 << " --kdtreesteps=<int> KDTree: search steps (default: 200)" << endl
00067 << " --kdtreeseconddist=<double> KDTree: distance of 2nd match (default: 0.25)" << endl
00068 << endl << "Feature matching options" << endl
00069 << " --ransaciter=<int> Ransac: iterations (default: 1000)" << endl
00070 << " --ransacdist=<int> Ransac: homography estimation distance threshold" << endl
00071 << " (in pixels) (default: 25)" << endl
00072 << " --ransacmode=<string> Ransac: Select the mode used in the ransac step." << endl
00073 << " Possible values: auto, hom, rpy, rpyv, rpyb" << endl
00074 << " (default: auto)" << endl
00075 << " --minmatches=<int> Minimum matches (default: 6)" << endl
00076 << " --sieve2width=<int> Sieve 2: Number of buckets on width (default: 5)" << endl
00077 << " --sieve2height=<int> Sieve 2: Number of buckets on height (default: 5)" << endl
00078 << " --sieve2size=<int> Sieve 2: Max points per bucket (default: 1)" << endl
00079 << endl << "Caching options" << endl
00080 << " -c|--cache Caches automaticall keypoints to external file" << endl
00081 << " --clean Clean up cached keyfiles" << endl
00082 << " -p|--keypath=<string> Store keyfiles in given path" << endl
00083 << " -k|--writekeyfile=<int> Write a keyfile for this image number" << endl
00084 << " --kall Write keyfiles for all images in the project" << endl
00085 << endl << "Advanced options" << endl
00086 << " --celeste Masks area with clouds before running feature descriptor" << endl
00087 << " Celeste can be fine tuned with the following parameters" << endl
00088 << " --celestethreshold=<int> Threshold for celeste (default 0.5)" << endl
00089 << " --celesteradius=<int> Radius for celeste (in pixels, default 20)" << endl
00090 << " --ncores=<int> Number of threads to use (default: autodetect number of cores)" << endl;
00091 };
00092
00093 bool parseOptions(int argc, char** argv, PanoDetector& ioPanoDetector)
00094 {
00095 enum
00096 {
00097 SIEVE1WIDTH=256,
00098 SIEVE1HEIGHT,
00099 SIEVE1SIZE,
00100 LINEARMATCH,
00101 LINEARMATCHLEN,
00102 MULTIROW,
00103 PREALIGNED,
00104 KDTREESTEPS,
00105 KDTREESECONDDIST,
00106 MINMATCHES,
00107 RANSACMODE,
00108 RANSACITER,
00109 RANSACDIST,
00110 SIEVE2WIDTH,
00111 SIEVE2HEIGHT,
00112 SIEVE2SIZE,
00113 KALL,
00114 CLEAN,
00115 CELESTE,
00116 CELESTETHRESHOLD,
00117 CELESTERADIUS,
00118 CPFINDVERSION
00119 };
00120 const char* optstring = "qvftn:o:k:cp:h";
00121 static struct option longOptions[] =
00122 {
00123 {"quiet", no_argument, NULL, 'q' },
00124 {"verbose", no_argument, NULL, 'v'},
00125 {"fullscale", no_argument, NULL, 'f'},
00126 {"sieve1width", required_argument, NULL, SIEVE1WIDTH},
00127 {"sieve1height", required_argument, NULL, SIEVE1HEIGHT},
00128 {"sieve1size", required_argument, NULL, SIEVE1SIZE},
00129 {"linearmatch", no_argument, NULL, LINEARMATCH},
00130 {"linearmatchlen", required_argument, NULL, LINEARMATCHLEN},
00131 {"multirow", no_argument, NULL, MULTIROW},
00132 {"prealigned", no_argument, NULL, PREALIGNED},
00133 {"kdtreesteps", required_argument, NULL, KDTREESTEPS},
00134 {"kdtreeseconddist", required_argument, NULL, KDTREESECONDDIST},
00135 {"minmatches", required_argument, NULL, MINMATCHES},
00136 {"ransacmode", required_argument, NULL, RANSACMODE},
00137 {"ransaciter", required_argument, NULL, RANSACITER},
00138 {"ransacdist", required_argument, NULL, RANSACDIST},
00139 {"sieve2width", required_argument, NULL, SIEVE2WIDTH},
00140 {"sieve2height", required_argument, NULL, SIEVE2HEIGHT},
00141 {"sieve2size", required_argument, NULL, SIEVE2SIZE},
00142 {"test", no_argument, NULL, 't'},
00143 {"ncores", required_argument, NULL, 'n'},
00144 {"output", required_argument, NULL, 'o'},
00145 {"writekeyfile", required_argument, NULL, 'k'},
00146 {"kall", no_argument, NULL, KALL},
00147 {"cache", no_argument, NULL, 'c'},
00148 {"clean", no_argument, NULL, CLEAN},
00149 {"keypath", required_argument, NULL, 'p'},
00150 {"celeste", no_argument, NULL, CELESTE},
00151 {"celestethreshold", required_argument, NULL, CELESTETHRESHOLD},
00152 {"celesteradius", required_argument, NULL, CELESTERADIUS},
00153 {"version", no_argument, NULL, CPFINDVERSION},
00154 {"help", no_argument, NULL, 'h'},
00155 0
00156 };
00157
00158 int c;
00159 int optionIndex = 0;
00160 int number;
00161 double floatNumber;
00162 string ransacMode;
00163 vector<int> keyfilesIndex;
00164 int doLinearMatch=0;
00165 int doMultirow=0;
00166 int doPrealign=0;
00167 while ((c = getopt_long (argc, argv, optstring, longOptions,&optionIndex)) != -1)
00168 {
00169 switch (c)
00170 {
00171 case 'q':
00172 ioPanoDetector.setVerbose(0);
00173 break;
00174 case 'v':
00175 ioPanoDetector.setVerbose(2);
00176 break;
00177 case 'f':
00178 ioPanoDetector.setDownscale(false);
00179 break;
00180 case SIEVE1WIDTH:
00181 number=atoi(optarg);
00182 if(number>0)
00183 {
00184 ioPanoDetector.setSieve1Width(number);
00185 };
00186 break;
00187 case SIEVE1HEIGHT:
00188 number=atoi(optarg);
00189 if(number>0)
00190 {
00191 ioPanoDetector.setSieve1Height(number);
00192 };
00193 break;
00194 case SIEVE1SIZE:
00195 number=atoi(optarg);
00196 if(number>0)
00197 {
00198 ioPanoDetector.setSieve1Size(number);
00199 };
00200 break;
00201 case LINEARMATCH:
00202 doLinearMatch=1;
00203 break;
00204 case LINEARMATCHLEN:
00205 number=atoi(optarg);
00206 if(number>0)
00207 {
00208 ioPanoDetector.setLinearMatchLen(number);
00209 };
00210 break;
00211 case MULTIROW:
00212 doMultirow=1;
00213 break;
00214 case PREALIGNED:
00215 doPrealign=1;
00216 break;
00217 case KDTREESTEPS:
00218 number=atoi(optarg);
00219 if(number>0)
00220 {
00221 ioPanoDetector.setKDTreeSearchSteps(number);
00222 };
00223 break;
00224 case KDTREESECONDDIST:
00225 floatNumber=atof(optarg);
00226 if(floatNumber>0)
00227 {
00228 ioPanoDetector.setKDTreeSecondDistance(floatNumber);
00229 };
00230 break;
00231 case MINMATCHES:
00232 number=atoi(optarg);
00233 if(number>0)
00234 {
00235 ioPanoDetector.setMinimumMatches(number);
00236 };
00237 break;
00238 case RANSACMODE:
00239 ransacMode=optarg;
00240 cout << "Ransac: " << ransacMode << endl;
00241 transform(ransacMode.begin(), ransacMode.end(), ransacMode.begin(),(int(*)(int)) tolower);
00242 cout << "Ransac: " << ransacMode << endl;
00243 if(ransacMode=="auto")
00244 {
00245 ioPanoDetector.setRansacMode(RANSACOptimizer::AUTO);
00246 }
00247 else
00248 {
00249 if(ransacMode=="hom")
00250 {
00251 ioPanoDetector.setRansacMode(RANSACOptimizer::HOMOGRAPHY);
00252 }
00253 else
00254 {
00255 if(ransacMode=="rpy")
00256 {
00257 ioPanoDetector.setRansacMode(RANSACOptimizer::RPY);
00258 }
00259 else
00260 {
00261 if(ransacMode=="rpyv")
00262 {
00263 ioPanoDetector.setRansacMode(RANSACOptimizer::RPYV);
00264 }
00265 else
00266 {
00267 if(ransacMode=="rpyvb")
00268 {
00269 ioPanoDetector.setRansacMode(RANSACOptimizer::RPYVB);
00270 }
00271 else
00272 {
00273 cout << "Warning: Invalid parameter in --ransacmode." << endl;
00274 };
00275 };
00276 };
00277 };
00278 };
00279 break;
00280 case RANSACITER:
00281 number=atoi(optarg);
00282 if(number>0)
00283 {
00284 ioPanoDetector.setRansacIterations(number);
00285 };
00286 break;
00287 case RANSACDIST:
00288 number=atoi(optarg);
00289 if(number>0)
00290 {
00291 ioPanoDetector.setRansacDistanceThreshold(number);
00292 };
00293 break;
00294 case SIEVE2WIDTH:
00295 number=atoi(optarg);
00296 if(number>0)
00297 {
00298 ioPanoDetector.setSieve2Width(number);
00299 };
00300 break;
00301 case SIEVE2HEIGHT:
00302 number=atoi(optarg);
00303 if(number>0)
00304 {
00305 ioPanoDetector.setSieve2Height(number);
00306 };
00307 break;
00308 case SIEVE2SIZE:
00309 number=atoi(optarg);
00310 if(number>0)
00311 {
00312 ioPanoDetector.setSieve2Size(number);
00313 };
00314 break;
00315 case 't':
00316 ioPanoDetector.setTest(true);
00317 break;
00318 case 'n':
00319 number=atoi(optarg);
00320 if(number>0)
00321 {
00322 ioPanoDetector.setCores(number);
00323 };
00324 break;
00325 case 'o':
00326 ioPanoDetector.setOutputFile(optarg);
00327 break;
00328 case 'k':
00329 number=atoi(optarg);
00330 if((number==0) && (strcmp(optarg,"0")!=0))
00331 {
00332 cout << "Warning: " << optarg << " is not a valid image number of writekeyfile." << endl;
00333 }
00334 else
00335 {
00336 keyfilesIndex.push_back(number);
00337 };
00338 break;
00339 case KALL:
00340 ioPanoDetector.setWriteAllKeyPoints();
00341 break;
00342 case 'c':
00343 ioPanoDetector.setCached(true);
00344 break;
00345 case CLEAN:
00346 ioPanoDetector.setCleanup(true);
00347 break;
00348 case 'p':
00349 ioPanoDetector.setKeyfilesPath(optarg);
00350 break;
00351 case CELESTE:
00352 ioPanoDetector.setCeleste(true);
00353 break;
00354 case CELESTETHRESHOLD:
00355 floatNumber=atof(optarg);
00356 if(floatNumber>0.0)
00357 {
00358 ioPanoDetector.setCelesteThreshold(floatNumber);
00359 };
00360 break;
00361 case CELESTERADIUS:
00362 number=atoi(optarg);
00363 if(number>0)
00364 {
00365 ioPanoDetector.setCelesteRadius(number);
00366 };
00367 break;
00368 case CPFINDVERSION:
00369 printVersion();
00370 return false;
00371 break;
00372 case 'h':
00373 printUsage();
00374 return false;
00375 break;
00376 case ':':
00377 cerr <<"Option " << longOptions[optionIndex].name << " requires an argument" << endl;
00378 return false;
00379 break;
00380 case '?':
00381 default:
00382 break;
00383 };
00384 };
00385
00386 if (argc - optind != 1)
00387 {
00388 cout << "Error: cpfind requires at least an input project file." << endl;
00389 return false;
00390 };
00391 ioPanoDetector.setInputFile(argv[optind]);
00392 if(doLinearMatch + doMultirow + doPrealign>1)
00393 {
00394 cout << "Error: The arguments --linearmatch, --multirow and --prealigned are" << endl
00395 << " mutually exclusive. Use only one of them." << endl;
00396 return false;
00397 };
00398 if(doLinearMatch)
00399 {
00400 ioPanoDetector.setMatchingStrategy(PanoDetector::LINEAR);
00401 };
00402 if(doMultirow)
00403 {
00404 ioPanoDetector.setMatchingStrategy(PanoDetector::MULTIROW);
00405 };
00406 if(doPrealign)
00407 {
00408 ioPanoDetector.setMatchingStrategy(PanoDetector::PREALIGNED);
00409 };
00410 if(keyfilesIndex.size()>0)
00411 {
00412 ioPanoDetector.setKeyPointsIdx(keyfilesIndex);
00413 };
00414 return true;
00415 };
00416
00417 int main(int argc, char** argv)
00418 {
00419
00420 PanoDetector aPanoDetector;
00421 if(!parseOptions(argc, argv, aPanoDetector))
00422 {
00423 return 0;
00424 }
00425
00426 if (!aPanoDetector.checkData())
00427 {
00428 return 0;
00429 }
00430
00431 printVersion();
00432 if (aPanoDetector.getVerbose() > 1)
00433 {
00434 aPanoDetector.printDetails();
00435 }
00436
00437 TIMETRACE("Detection",aPanoDetector.run());
00438
00439 return 0;
00440
00441 }