pto_gen.cpp

Go to the documentation of this file.
00001 // -*- c-basic-offset: 4 -*-
00002 
00011 /*  This program is free software; you can redistribute it and/or
00012  *  modify it under the terms of the GNU General Public
00013  *  License as published by the Free Software Foundation; either
00014  *  version 2 of the License, or (at your option) any later version.
00015  *
00016  *  This software is distributed in the hope that it will be useful,
00017  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00018  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00019  *  General Public License for more details.
00020  *
00021  *  You should have received a copy of the GNU General Public
00022  *  License along with this software. If not, see
00023  *  <http://www.gnu.org/licenses/>.
00024  *
00025  */
00026 
00027 #include <fstream>
00028 #include <getopt.h>
00029 #ifdef _WINDOWS
00030 #include <io.h>
00031 #endif
00032 #include <vigra/imageinfo.hxx>
00033 #include <panodata/Panorama.h>
00034 #include <panodata/StandardImageVariableGroups.h>
00035 #include <panodata/OptimizerSwitches.h>
00036 #include <algorithms/basic/CalculateMeanExposure.h>
00037 #include "hugin_utils/alphanum.h"
00038 #include <lensdb/LensDB.h>
00039 
00040 #ifdef __APPLE__
00041 #include <hugin_config.h>
00042 #include <mach-o/dyld.h>    /* _NSGetExecutablePath */
00043 #include <limits.h>         /* PATH_MAX */
00044 #include <libgen.h>         /* dirname */
00045 #endif
00046 
00047 using namespace std;
00048 using namespace HuginBase;
00049 
00050 static void usage(const char* name)
00051 {
00052     cout << name << ": generate project file from images" << endl
00053          << name << " version " << hugin_utils::GetHuginVersion() << endl
00054          << endl
00055          << "Usage:  " << name << " [options] image1 [...]" << endl
00056          << endl
00057          << "  Options:" << endl
00058          << "     -o, --output=file.pto  Output Hugin PTO file." << endl
00059          << "     -p, --projection=INT   Projection type (default: 0)" << endl
00060          << "     -f, --fov=FLOAT        Horizontal field of view of images (default: 50)" << endl
00061          << "     -c, --crop=left,right,top,bottom        Sets the crop of input" << endl
00062          << "                            images (especially for fisheye lenses)" << endl
00063          << "     -s, --stacklength=INT  Number of images in stack" << endl
00064          << "                            (default: automatic detection)" << endl
00065          << "     -l, --linkstacks       Link image positions in stacks" << endl
00066          << "     --distortion           Try to load distortion information from" << endl
00067          << "                            lens database" << endl
00068          << "     --vignetting           Try to load vignetting information from" << endl
00069          << "                            lens database" << endl
00070          << "     -h, --help             Shows this help" << endl
00071          << endl;
00072 }
00073 
00074 int main(int argc, char* argv[])
00075 {
00076     // parse arguments
00077     const char* optstring = "o:p:f:c:s:lh";
00078 
00079     static struct option longOptions[] =
00080     {
00081         {"output", required_argument, NULL, 'o' },
00082         {"projection", required_argument, NULL, 'p' },
00083         {"fov", required_argument, NULL, 'f' },
00084         {"crop", required_argument, NULL, 'c' },
00085         {"stacklength", required_argument, NULL, 's' },
00086         {"linkstacks", no_argument, NULL, 'l' },
00087         {"distortion", no_argument, NULL, 300 },
00088         {"vignetting", no_argument, NULL, 301 },
00089         {"help", no_argument, NULL, 'h' },
00090 
00091         0
00092     };
00093 
00094     int c;
00095     int optionIndex = 0;
00096     string output;
00097     int projection=-1;
00098     float fov=-1;
00099     int stackLength=0;
00100     bool linkStacks=false;
00101     vigra::Rect2D cropRect(0,0,0,0);
00102     bool loadDistortion=false;
00103     bool loadVignetting=false;
00104     while ((c = getopt_long (argc, argv, optstring, longOptions,&optionIndex)) != -1)
00105     {
00106         switch (c)
00107         {
00108             case 'o':
00109                 output = optarg;
00110                 break;
00111             case 'h':
00112                 usage(hugin_utils::stripPath(argv[0]).c_str());
00113                 return 0;
00114             case 'p':
00115                 {
00116                     projection=atoi(optarg);
00117                     if((projection==0) && (strcmp(optarg,"0")!=0))
00118                     {
00119                         cerr << "Could not parse image number.";
00120                         return 1;
00121                     };
00122                     if(projection<0)
00123                     {
00124                         cerr << "Invalid projection number." << endl;
00125                         return 1;
00126                     };
00127                 };
00128                 break;
00129             case 'f':
00130                 fov=atof(optarg);
00131                 if(fov<1 || fov>360)
00132                 {
00133                     cerr << "Invalid field of view";
00134                     return 1;
00135                 };
00136                 break;
00137             case 'c':
00138                 {
00139                     int left, right, top, bottom;
00140                     int n=sscanf(optarg, "%d,%d,%d,%d", &left, &right, &top, &bottom);
00141                     if (n==4)
00142                     {
00143                         if(right>left && bottom>top)
00144                         {
00145                             cropRect.setUpperLeft(vigra::Point2D(left,top));
00146                             cropRect.setLowerRight(vigra::Point2D(right,bottom));
00147                         }
00148                         else
00149                         {
00150                             cerr << "Invalid crop area" << endl;
00151                             return 1;
00152                         };
00153                     }
00154                     else
00155                     {
00156                         cerr << "Could not parse crop values" << endl;
00157                         return 1;
00158                     };
00159                 };
00160                 break;
00161             case 's':
00162                 stackLength=atoi(optarg);
00163                 if ((stackLength == 0) && (strcmp(optarg, "0") != 0))
00164                 {
00165                     cerr << "Could not parse stack length.";
00166                     return 1;
00167                 };
00168                 break;
00169             case 'l':
00170                 linkStacks=true;
00171                 break;
00172             case 300:
00173                 loadDistortion=true;
00174                 break;
00175             case 301:
00176                 loadVignetting=true;
00177                 break;
00178             case ':':
00179                 cerr <<"Option " << longOptions[optionIndex].name << " requires a number" << endl;
00180                 return 1;
00181                 break;
00182             case '?':
00183                 break;
00184             default:
00185                 abort ();
00186         }
00187     }
00188 
00189     if (argc - optind < 1)
00190     {
00191         usage(hugin_utils::stripPath(argv[0]).c_str());
00192         return 1;
00193     };
00194 
00195     cout << "Generating pto file..." << endl;
00196     cout.flush();
00197 
00198     std::vector<string> filelist;
00199     while(optind<argc)
00200     {
00201         string input;
00202 #ifdef _WINDOWS
00203         //do globbing
00204         input=GetAbsoluteFilename(argv[optind]);
00205         char drive[_MAX_DRIVE];
00206         char dir[_MAX_DIR];
00207         _splitpath(input.c_str(), drive, dir, NULL, NULL);
00208 
00209         struct _finddata_t finddata;
00210         intptr_t findhandle = _findfirst(input.c_str(), &finddata);
00211         if (findhandle != -1)
00212         {
00213             do
00214             {
00215                 //ignore folder, can be happen when using *.*
00216                 if((finddata.attrib & _A_SUBDIR)==0)
00217                 {
00218                     char fname[_MAX_FNAME];
00219                     char ext[_MAX_EXT];
00220                     char newFile[_MAX_PATH];
00221                     _splitpath(finddata.name, NULL, NULL, fname, ext);
00222                     _makepath(newFile, drive, dir, fname, ext);
00223                     //check if valid image file
00224                     if(vigra::isImage(newFile))
00225                     {
00226                         filelist.push_back(std::string(newFile));
00227                     };
00228                 };
00229             }
00230             while (_findnext(findhandle, &finddata) == 0);
00231             _findclose(findhandle);
00232         }
00233 #else
00234         input=argv[optind];
00235         if(hugin_utils::FileExists(input))
00236         {
00237             if(vigra::isImage(input.c_str()))
00238             {
00239                 filelist.push_back(GetAbsoluteFilename(input));
00240             };
00241         };
00242 #endif
00243         optind++;
00244     };
00245 
00246     if(filelist.size()==0)
00247     {
00248         cerr << "No valid image files given." << endl;
00249         return 1;
00250     };
00251 
00252     //sort filenames
00253     sort(filelist.begin(),filelist.end(),doj::alphanum_less());
00254 
00255     Panorama pano;
00256     for(size_t i=0; i<filelist.size(); i++)
00257     {
00258         SrcPanoImage srcImage;
00259         cout << "Reading " << filelist[i] << "..." << endl;
00260         srcImage.setFilename(filelist[i]);
00261         try
00262         {
00263             vigra::ImageImportInfo info(filelist[i].c_str());
00264             if(info.width()==0 || info.height()==0)
00265             {
00266                 cerr << "ERROR: Could not decode image " << filelist[i] << endl
00267                      << "Skipping this image." << endl << endl;
00268                 continue;
00269             }
00270             srcImage.setSize(info.size());
00271             // check for black/white images
00272             const std::string pixelType=info.getPixelType();
00273             if (pixelType == "BILEVEL")
00274             {
00275                 cerr << "ERROR: Image " << filelist[i] << " is a black/white images." << endl
00276                     << "       This is not supported. Convert to grayscale image and try again." << endl
00277                     << "       Skipping this image." << endl;
00278                 continue;
00279             }
00280             if((pixelType=="UINT8") || (pixelType=="UINT16") || (pixelType=="INT16"))
00281             {
00282                 srcImage.setResponseType(HuginBase::SrcPanoImage::RESPONSE_EMOR);
00283             }
00284             else
00285             {
00286                 srcImage.setResponseType(HuginBase::SrcPanoImage::RESPONSE_LINEAR);
00287             };
00288         }
00289         catch(std::exception& e)
00290         {
00291             cerr << "ERROR: caught exception: " << e.what() << endl;
00292             cerr << "Could not read image information for file " << filelist[i] << endl;
00293             cerr << "Skipping this image." << endl << endl;
00294             continue;
00295         };
00296 
00297         srcImage.readEXIF();
00298         bool fovOk=srcImage.applyEXIFValues();
00299         if(projection>=0)
00300         {
00301             srcImage.setProjection((HuginBase::BaseSrcPanoImage::Projection)projection);
00302         }
00303         else
00304         {
00305             srcImage.readProjectionFromDB();
00306         };
00307         if(fov>0)
00308         {
00309             srcImage.setHFOV(fov);
00310             if(srcImage.getCropFactor()==0)
00311             {
00312                 srcImage.setCropFactor(1.0);
00313             };
00314         }
00315         else
00316         {
00317             //set plausible default value if they could not read from exif
00318             if(!fovOk)
00319             {
00320                 cout << "\tNo value for field of view found in EXIF data. " << endl
00321                      << "\tAssuming a HFOV of 50 degrees. " << endl;
00322                 srcImage.setHFOV(50);
00323                 srcImage.setCropFactor(1.0);
00324             };
00325         };
00326         if(cropRect.width()>0 && cropRect.height()>0)
00327         {
00328             if(srcImage.isCircularCrop())
00329             {
00330                 srcImage.setCropMode(SrcPanoImage::CROP_CIRCLE);
00331             }
00332             else
00333             {
00334                 srcImage.setCropMode(SrcPanoImage::CROP_RECTANGLE);
00335             };
00336             srcImage.setAutoCenterCrop(false);
00337             srcImage.setCropRect(cropRect);
00338         };
00339         if(loadDistortion)
00340         {
00341             if(srcImage.readDistortionFromDB())
00342             {
00343                 cout << "\tRead distortion data from lens database." << endl;
00344             }
00345             else
00346             {
00347                 cout << "\tNo valid distortion data found in lens database." << endl;
00348             };
00349         };
00350         if(loadVignetting)
00351         {
00352             if(srcImage.readVignettingFromDB())
00353             {
00354                 cout << "\tRead vignetting data from lens database." << endl;
00355             }
00356             else
00357             {
00358                 cout << "\tNo valid vignetting data found in lens database." << endl;
00359             };
00360         };
00361 
00362         pano.addImage(srcImage);
00363     };
00364 
00365     if(pano.getNrOfImages()==0)
00366     {
00367         cerr << "Adding images to project files failed." << endl;
00368         HuginBase::LensDB::LensDB::Clean();
00369         return 1;
00370     };
00371 
00372     //link lenses
00373     if(pano.getNrOfImages()>1)
00374     {
00375         double redBalanceAnchor=pano.getImage(pano.getOptions().colorReferenceImage).getExifRedBalance();
00376         double blueBalanceAnchor=pano.getImage(pano.getOptions().colorReferenceImage).getExifBlueBalance();
00377         if(fabs(redBalanceAnchor)<1e-2)
00378         {
00379             redBalanceAnchor=1;
00380         };
00381         if(fabs(blueBalanceAnchor)<1e-2)
00382         {
00383             blueBalanceAnchor=1;
00384         };
00385         StandardImageVariableGroups variable_groups(pano);
00386         ImageVariableGroup& lenses = variable_groups.getLenses();
00387 
00388         for(size_t i=1; i<pano.getNrOfImages(); i++)
00389         {
00390             int image=-1;
00391             const SrcPanoImage& srcImg=pano.getImage(i);
00392             for(size_t j=0; j<i; j++)
00393             {
00394                 const SrcPanoImage& compareImg=pano.getImage(j);
00395                 if(srcImg.getHFOV()==compareImg.getHFOV() &&
00396                         srcImg.getProjection()==compareImg.getProjection() &&
00397                         srcImg.getExifModel()==compareImg.getExifModel() &&
00398                         srcImg.getExifMake()==compareImg.getExifMake() &&
00399                         srcImg.getSize()==compareImg.getSize())
00400                 {
00401                     image=j;
00402                     break;
00403                 };
00404             };
00405             if(image!=-1)
00406             {
00407                 SrcPanoImage img=pano.getSrcImage(i);
00408                 double ev=img.getExposureValue();
00409                 lenses.switchParts(i,lenses.getPartNumber(image));
00410                 lenses.unlinkVariableImage(HuginBase::ImageVariableGroup::IVE_ExposureValue, i);
00411                 img.setExposureValue(ev);
00412                 lenses.unlinkVariableImage(HuginBase::ImageVariableGroup::IVE_WhiteBalanceRed, i);
00413                 lenses.unlinkVariableImage(HuginBase::ImageVariableGroup::IVE_WhiteBalanceBlue, i);
00414                 img.setWhiteBalanceRed(img.getExifRedBalance()/redBalanceAnchor);
00415                 img.setWhiteBalanceBlue(img.getExifBlueBalance()/blueBalanceAnchor);
00416                 pano.setSrcImage(i, img);
00417             };
00418         };
00419         cout << endl << "Assigned " << lenses.getNumberOfParts() << " lenses." << endl;
00420         if(lenses.getNumberOfParts()>1 && stackLength!=1)
00421         {
00422             cout << "Project contains more than one lens, but you requested to assign" << endl
00423                  << "stacks. This is not supported. Therefore stacks will not be" << endl
00424                  << "assigned." << endl << endl;
00425             stackLength=1;
00426         };
00427 
00428         if (stackLength == 0)
00429         {
00430             // automatic detection
00431             if (pano.hasPossibleStacks())
00432             {
00433                 pano.linkPossibleStacks(linkStacks);
00434             };
00435         }
00436         else
00437         {
00438             if (stackLength > 1)
00439             {
00440                 stackLength = std::min<int>(stackLength, pano.getNrOfImages());
00441                 int stackCount = pano.getNrOfImages() / stackLength;
00442                 if (pano.getNrOfImages() % stackLength > 0)
00443                 {
00444                     stackCount++;
00445                 };
00446                 if (stackCount < pano.getNrOfImages())
00447                 {
00448                     for (size_t stackNr = 0; stackNr < stackCount; stackNr++)
00449                     {
00450                         size_t firstImgStack = stackNr*stackLength;
00451                         for (size_t i = 0; i < stackLength; i++)
00452                         {
00453                             if (firstImgStack + i < pano.getNrOfImages())
00454                             {
00455                                 pano.linkImageVariableStack(firstImgStack, firstImgStack + i);
00456                                 if (linkStacks)
00457                                 {
00458                                     pano.linkImageVariableYaw(firstImgStack, firstImgStack + i);
00459                                     pano.linkImageVariablePitch(firstImgStack, firstImgStack + i);
00460                                     pano.linkImageVariableRoll(firstImgStack, firstImgStack + i);
00461                                 };
00462                             };
00463                         };
00464                     };
00465                 };
00466             };
00467         };
00468 
00469         variable_groups.update();
00470         const size_t stackCount = variable_groups.getStacks().getNumberOfParts();
00471         if (stackCount != pano.getNrOfImages())
00472         {
00473             std::cout << "Assigned " << stackCount << " stacks: " << std::endl
00474                 << "\t" << (linkStacks ? "Linking position of images in stacks" : "Use individual positions of images in stacks") << std::endl;
00475         };
00476     };
00477 
00478     //set output exposure value
00479     PanoramaOptions opt = pano.getOptions();
00480     opt.outputExposureValue = CalculateMeanExposure::calcMeanExposure(pano);
00481     pano.setOptions(opt);
00482     // set optimizer switches
00483     pano.setOptimizerSwitch(HuginBase::OPT_PAIR);
00484     pano.setPhotometricOptimizerSwitch(HuginBase::OPT_EXPOSURE | HuginBase::OPT_VIGNETTING | HuginBase::OPT_RESPONSE);
00485 
00486     //output
00487     if(output=="")
00488     {
00489         output=hugin_utils::stripExtension(pano.getImage(0).getFilename());
00490         if(pano.getNrOfImages()>1)
00491         {
00492             output.append("-");
00493             output.append(hugin_utils::stripExtension(hugin_utils::stripPath(pano.getImage(pano.getNrOfImages()-1).getFilename())));
00494         };
00495         output=output.append(".pto");
00496     };
00497     output=GetAbsoluteFilename(output);
00498     //write output
00499     UIntSet imgs;
00500     fill_set(imgs,0, pano.getNrOfImages()-1);
00501     ofstream of(output.c_str());
00502     pano.printPanoramaScript(of, pano.getOptimizeVector(), pano.getOptions(), imgs, false, hugin_utils::getPathPrefix(output));
00503 
00504     cout << endl << "Written output to " << output << endl;
00505     HuginBase::LensDB::LensDB::Clean();
00506     return 0;
00507 }

Generated on 29 Jul 2015 for Hugintrunk by  doxygen 1.4.7