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

Generated on 7 Dec 2016 for Hugintrunk by  doxygen 1.4.7