Main.cpp

Go to the documentation of this file.
00001 /***************************************************************************
00002  *   Copyright (C) 2008 by Tim Nugent                                      *
00003  *   timnugent@gmail.com                                                   *
00004  *                                                                         *
00005  *   This program is free software; you can redistribute it and/or modify  *
00006  *   it under the terms of the GNU General Public License as published by  *
00007  *   the Free Software Foundation; either version 2 of the License, or     *
00008  *   (at your option) any later version.                                   *
00009  *                                                                         *
00010  *   This program is distributed in the hope that it will be useful,       *
00011  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
00012  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
00013  *   GNU General Public License for more details.                          *
00014  *                                                                         *
00015  *   You should have received a copy of the GNU General Public License     *
00016  *   along with this program; if not, write to the                         *
00017  *   Free Software Foundation, Inc.,                                       *
00018  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
00019  ***************************************************************************/
00020 
00021 #include <fstream>
00022 #include <sstream>
00023 #include <string>
00024 #include "Celeste.h"
00025 #include <sys/stat.h> 
00026 #include "hugin_config.h" 
00027 #include <hugin_version.h>
00028 #include <vigra_ext/impexalpha.hxx>
00029 #include <panodata/Panorama.h>
00030 
00031 #ifdef _WINDOWS
00032 #include <windows.h>
00033 #include <getopt.h>
00034 #else
00035  #include <unistd.h>
00036 #endif
00037 
00038 #ifdef __APPLE__
00039 #include <hugin_config.h>
00040 #include <mach-o/dyld.h>        /* _NSGetExecutablePath */
00041 #include <limits.h>             /* PATH_MAX */
00042 #include <libgen.h>             /* dirname */
00043 #endif
00044 
00045 using namespace std;
00046 using namespace HuginBase;
00047 using namespace AppBase;
00048 
00049 bool fileexists(string strFilename) {
00050 
00051         struct stat stFileInfo;
00052         bool blnReturn;
00053         int intStat;
00054 
00055         // Attempt to get the file attributes
00056         intStat = stat(strFilename.c_str(),&stFileInfo);
00057         if(intStat == 0) {
00058 
00059                 blnReturn = true;
00060         }else{
00061 
00062                 blnReturn = false;
00063         }
00064   
00065         return(blnReturn);
00066 }
00067 
00068 static void usage(){
00069 
00070         // Print usage and exit
00071         cout << endl << "Celeste: Removes cloud-like control points from Hugin project files and creates image masks" << endl;
00072         cout << "using Support Vector Machines." << endl;
00073         cout << endl << "Version " << DISPLAY_VERSION << endl;
00074         cout << endl << "Usage: celeste_standalone [options] image1 image2 [..]" << endl << endl;
00075         cout << "Options:" << endl << endl;
00076         cout << "  -i <filename>   Input Hugin PTO file. Control points over SVM threshold will" << endl;
00077         cout << "                  be removed before being written to the output file. If -m is" << endl;
00078         cout << "                  set to 1, images in the file will be also be masked." << endl;
00079         cout << "  -o <filename>   Output Hugin PTO file. Default: '<filename>_celeste.pto'" << endl;
00080         cout << "  -d <filename>   SVM model file. Default: 'data/celeste.model'" << endl;
00081         cout << "  -s <int>        Maximum dimension for re-sized image prior to processing. A" << endl;
00082         cout << "                  higher value will increase the resolution of the mask but is" << endl;
00083         cout << "                  significantly slower. Default: 800" << endl;
00084         cout << "  -t <float>      SVM threshold. Raise this value to remove fewer control points," << endl;
00085         cout << "                  lower it to remove more. Range 0 to 1. Default: 0.5" << endl;
00086         cout << "  -m <1|0>        Create masks when processing Hugin PTO file. Default: 0" << endl;
00087         cout << "  -f <string>     Mask file format. Options are PNG, JPEG, BMP, GIF and TIFF." << endl;
00088         cout << "                  Default: PNG" << endl;
00089         cout << "  -r <1|0>        Filter radius. 0 = large (more accurate), 1 = small (higher" << endl;
00090         cout << "                  resolution mask, slower, less accurate). Default: 0" << endl;
00091         cout << "  -h              Print usage." << endl; 
00092         cout << "  image1 image2.. Image files to be masked." << endl << endl;
00093         exit(1);
00094 
00095 }
00096 
00097 vigra::UInt16RGBImage loadAndConvertImage(string imagefile)
00098 {
00099     vigra::ImageImportInfo info(imagefile.c_str());
00100     std::string pixelType=info.getPixelType();
00101     vigra::UInt16RGBImage image;
00102     if(!info.isColor())
00103     {
00104         std::cerr << "Celeste works only on colour images, " << std::endl 
00105             << "but image " << imagefile << " has  " << info.numBands() << " channels." << std::endl 
00106             << "Skipping this image." << std::endl;
00107         return image;
00108     };
00109     if(pixelType=="UINT8" || pixelType=="INT16")
00110     {
00111         vigra::UInt16RGBImage imageIn(info.width(),info.height());
00112         if(info.numExtraBands()==1)
00113         {
00114             vigra::BImage mask(info.size());
00115             vigra::importImageAlpha(info,destImage(imageIn),destImage(mask));
00116             mask.resize(0,0);
00117         }
00118         else
00119         {
00120             importImage(info,destImage(imageIn));
00121         };
00122         celeste::convertToUInt16(imageIn,pixelType,image);
00123         imageIn.resize(0,0);
00124     }
00125     else
00126         if(pixelType=="UINT16")
00127         {
00128             image.resize(info.width(),info.height());
00129             if(info.numExtraBands()==1)
00130             {
00131                 vigra::BImage mask(info.size());
00132                 vigra::importImageAlpha(info,destImage(image),destImage(mask));
00133                 mask.resize(0,0);
00134             }
00135             else
00136             {
00137                 importImage(info,destImage(image));
00138             };
00139         }
00140         else
00141             if(pixelType=="INT32" || pixelType=="UINT32")
00142             {
00143                 vigra::UInt32RGBImage imageIn(info.width(),info.height());
00144                 if(info.numExtraBands()==1)
00145                 {
00146                     vigra::BImage mask(info.size());
00147                     vigra::importImageAlpha(info,destImage(imageIn),destImage(mask));
00148                     mask.resize(0,0);
00149                 }
00150                 else
00151                 {
00152                     importImage(info,destImage(imageIn));
00153                 };
00154                 celeste::convertToUInt16(imageIn,pixelType,image);
00155                 imageIn.resize(0,0);
00156             }
00157             else
00158                 if(pixelType=="FLOAT" || pixelType=="DOUBLE")
00159                 {
00160                     vigra::FRGBImage imagefloat(info.width(),info.height());
00161                     if(info.numExtraBands()==1)
00162                     {
00163                         vigra::BImage mask(info.size());
00164                         vigra::importImageAlpha(info,destImage(imagefloat),destImage(mask));
00165                         mask.resize(0,0);
00166                     }
00167                     else
00168                     {
00169                         importImage(info,destImage(imagefloat));
00170                     };
00171                     celeste::convertToUInt16(imagefloat,pixelType,image);
00172                     imagefloat.resize(0,0);
00173                 }
00174                 else
00175                 {
00176                     std::cerr << "Unsupported pixel type" << std::endl;
00177                 };
00178     return image;
00179 };
00180 
00181 std::string generateMaskName(string imagefile,string mask_format)
00182 {
00183     string mask_name = ("");
00184     if (imagefile.substr(imagefile.length()-4,1)==("."))
00185     {
00186         mask_name.append(imagefile.substr(0,imagefile.length()-4));
00187     }
00188     else
00189     {
00190         mask_name.append(imagefile.substr(0,imagefile.length()-4));
00191     }
00192     mask_name.append("_mask.");
00193     mask_name.append(mask_format);
00194     return mask_name;
00195 };
00196 
00197 
00198 int main(int argc, char* argv[])
00199 {
00200 
00201     // Exit with usage unless filename given as argument
00202     if (argc < 2)
00203     {
00204             usage();
00205     }
00206 
00207     unsigned int i = 1, mask = 0;
00208     double threshold = 0.5;
00209     vector<string> images_to_mask;
00210     string pto_file = (""),output_pto = ("");
00211     string mask_format = ("PNG");
00212     string model_file = ("celeste.model");
00213     int course_fine = 0;
00214     int resize_dimension=800;
00215 
00216     // Deal with arguments
00217     // parse arguments
00218     int c;
00219     const char * optstring = "i:o:d:s:t:m:f:r:h";
00220 
00221     while ((c = getopt (argc, argv, optstring)) != -1)
00222     {
00223         switch(c)
00224         {
00225             case 'h': 
00226                 usage();
00227                 break;
00228             case 'i':
00229                 pto_file=optarg;
00230                 break;
00231             case 'o':
00232                 output_pto=optarg;
00233                 break;
00234             case 't':
00235                 threshold = atof(optarg);
00236                 if(threshold<=0 || threshold>1)
00237                 {
00238                     cerr << "Invalid parameter: threshold (-t) should be between 0 and 1" << std::endl;
00239                     return 1;
00240                 };
00241                 break;
00242             case 'm': 
00243                 mask = atoi(optarg);
00244                 if(mask<0 || mask>1)
00245                 {
00246                     cerr << "Invalid parameter: mask parameter (-m) can only be 0 or 1" << std::endl;
00247                     return 1;
00248                 };
00249                 break;
00250             case 'f': 
00251                 mask_format = optarg; 
00252                 break;
00253             case 'd':
00254                 model_file = optarg;
00255                 break;
00256             case 'r': 
00257                 course_fine = atoi(optarg);
00258                 break;
00259             case 's': 
00260                 resize_dimension = atoi(optarg);
00261                 if(resize_dimension<100)
00262                 {
00263                     cerr << "Invalid parameter: maximum dimension (-s) should be bigger than 100" << std::endl;
00264                     return 1;
00265                 };
00266                 break;
00267             case ':':
00268                 cerr <<"Missing parameter for parameter " << argv[optind] << endl;
00269                 return 1;
00270                 break;
00271             case '?': /* invalid parameter */
00272                 return 1;
00273                 break;
00274             default: /* unknown */
00275                 usage();
00276         };
00277     };
00278 
00279     while(optind<argc)
00280     {
00281         images_to_mask.push_back(argv[optind]);
00282         optind++;
00283     };
00284     
00285     if(images_to_mask.size()==0 && pto_file.empty())
00286     {
00287         cout << "No project file or image files given."<< endl;
00288         return 1;
00289     };
00290 
00291     
00292         // Check model file
00293         if (!fileexists(model_file)){
00294         
00295 #if _WINDOWS
00296         char buffer[MAX_PATH];//always use MAX_PATH for filepaths
00297         GetModuleFileName(NULL,buffer,sizeof(buffer));
00298         string working_path=(buffer);
00299         string install_path_model="";
00300         //remove filename
00301         std::string::size_type pos=working_path.rfind("\\");
00302         if(pos!=std::string::npos)
00303         {
00304             working_path.erase(pos);
00305             //remove last dir: should be bin
00306             pos=working_path.rfind("\\");
00307             if(pos!=std::string::npos)
00308             {
00309                 working_path.erase(pos);
00310                 //append path delimiter and path
00311                 working_path.append("\\share\\hugin\\data\\");
00312                 install_path_model=working_path;
00313             }
00314         }
00315 #elif defined MAC_SELF_CONTAINED_BUNDLE
00316                 char path[PATH_MAX + 1];
00317                 uint32_t size = sizeof(path);
00318                 string install_path_model("");
00319                 if (_NSGetExecutablePath(path, &size) == 0)
00320                 {
00321                         install_path_model=dirname(path);
00322                         install_path_model.append("/../Resources/xrc/");
00323                 }
00324 #else
00325         string install_path_model = (INSTALL_DATA_DIR);
00326 #endif
00327 
00328                 install_path_model.append(model_file);
00329                 
00330                 if (!fileexists(install_path_model)){
00331                 
00332                         cout << endl << "Couldn't open SVM model file " << model_file << endl;
00333                         cout << "Also tried " << install_path_model << endl << endl; 
00334                         exit(1);
00335 
00336                 }else{
00337                 
00338                         model_file = install_path_model;
00339                 
00340                 }
00341         }
00342 
00343         // Set output .pto filename if not given
00344         if (output_pto == ("") && pto_file != ("")){
00345                 output_pto = pto_file.substr(0,pto_file.length()-4).append("_celeste.pto");
00346         }
00347 
00348         // Convert mask format to upper case
00349         transform(mask_format.begin(), mask_format.end(), mask_format.begin(),(int(*)(int)) toupper);
00350         if (mask_format == ("JPG")){
00351                 mask_format = ("JPEG");
00352         }
00353         if (mask_format != ("PNG") &&mask_format != ("BMP") && mask_format != ("GIF") && mask_format != ("JPEG") && mask_format != ("TIFF")){
00354                 mask_format = ("TIFF");
00355         }
00356 
00357         // Print some stuff out
00358         cout << endl << "Celeste: Removes cloud-like control points from Hugin project files and creates image masks" << endl;
00359         cout << "using Support Vector Machines." << endl;
00360         cout << endl << "Version " << DISPLAY_VERSION << endl << endl;
00361         cout << "Arguments:" << endl;
00362         cout << "Input Hugin file:\t" << pto_file << endl;
00363         cout << "Output Hugin file:\t" << output_pto << endl;
00364         cout << "SVM model file:\t\t" << model_file << endl;
00365         cout << "Max dimension:\t\t" << resize_dimension << endl;
00366         cout << "SVM threshold:\t\t" << threshold << endl;
00367         cout << "Create PTO masks:\t";
00368         if (mask){
00369                 cout << "Yes" << endl;
00370         }else{
00371                 cout << "No" << endl;
00372         } 
00373         cout << "Mask format:\t\t" << mask_format << endl;
00374         cout << "Filter radius:\t\t";
00375 
00376         // Mask resolution
00377     int radius;
00378         if (course_fine)
00379     {
00380         radius = 10;
00381         cout << "Small" << endl << endl;
00382         }
00383     else
00384     {
00385         radius=20;
00386         cout << "Large" << endl << endl;
00387         } 
00388         
00389         // Convert mask format to lower case
00390         transform(mask_format.begin(), mask_format.end(), mask_format.begin(),(int(*)(int)) tolower);
00391 
00392         // Vectors to store SVM responses and PTO file info etc
00393         vector<string> images,pto_file_top,pto_file_cps,pto_file_end;
00394         vector<double> svm_responses;
00395 
00396     struct celeste::svm_model* model;
00397     if(!celeste::loadSVMmodel(model,model_file))
00398     {
00399         return 1;
00400     };
00401 
00402     // Mask images
00403         if (images_to_mask.size())
00404     {
00405         cout << "Masking images..." << endl << endl;
00406         for (unsigned int l = 0; l < images_to_mask.size(); l++)
00407         {
00408             std::string imagefile=images_to_mask[l];
00409             try
00410             {
00411                 cout << "Opening image file:\t" << imagefile << endl;
00412                 // Read image given and convert to UInt16
00413                 vigra::UInt16RGBImage in=loadAndConvertImage(imagefile);
00414                 if(in.width()==0 || in.height()==0)
00415                 {
00416                     continue;
00417                 };
00418 
00419                 // Create mask file name
00420                 string mask_name = generateMaskName(imagefile,mask_format);
00421 
00422                 cout << "Generating mask:\t" << mask_name << endl;                              
00423                 // Create mask
00424                 vigra::BImage mask=celeste::getCelesteMask(model,in,radius,threshold,resize_dimension);
00425                 exportImage(srcImageRange(mask), vigra::ImageExportInfo(mask_name.c_str()).setPixelType("UINT8"));
00426             }
00427             catch (vigra::StdException & e)
00428             {
00429                 // catch any errors that might have occured and print their reason
00430                 cout << "Unable to open file:\t" << imagefile << endl << endl;
00431                 cout << e.what() << endl << endl;
00432                 };
00433         };
00434         };
00435 
00436     // Process PTO file
00437     if (pto_file != (""))
00438     {
00439                 cout << "Parsing Hugin project file " << pto_file << endl << endl;
00440 
00441         Panorama pano;
00442         ifstream prjfile(pto_file.c_str());
00443         if (!prjfile.good())
00444         {
00445             cerr << "could not open script : " << pto_file << endl;
00446             celeste::destroySVMmodel(model);
00447             return 1;
00448         }
00449         pano.setFilePrefix(hugin_utils::getPathPrefix(pto_file));
00450         DocumentData::ReadWriteError err = pano.readData(prjfile);
00451         if (err != DocumentData::SUCCESSFUL)
00452         {
00453             cerr << "error while parsing panos tool script: " << pto_file << endl;
00454             cerr << "DocumentData::ReadWriteError code: " << err << endl;
00455             celeste::destroySVMmodel(model);
00456             return 1;
00457         }
00458 
00459         for(unsigned int i=0;i<pano.getNrOfImages();i++)
00460         {
00461             CPointVector cps=pano.getCtrlPointsVectorForImage(i);
00462             if(cps.size()>0 || mask)
00463             {
00464                 try
00465                 {
00466                     string imagefile=pano.getImage(i).getFilename();
00467                     vigra::UInt16RGBImage in=loadAndConvertImage(imagefile);
00468                     if(in.width()==0 || in.height()==0)
00469                     {
00470                         continue;
00471                     };
00472                     if(cps.size()>0)
00473                     {
00474                         UIntSet cloudCP=celeste::getCelesteControlPoints(model,in,cps,radius,threshold,resize_dimension);
00475                         if(cloudCP.size()>0)
00476                         {
00477                             for(UIntSet::reverse_iterator it=cloudCP.rbegin();it!=cloudCP.rend();++it)
00478                             {
00479                                 pano.removeCtrlPoint(*it);
00480                             };
00481                         };
00482                     };
00483                     if(mask)
00484                     {
00485                         string mask_name = generateMaskName(imagefile,mask_format);
00486                         // Create mask
00487                         vigra::BImage mask=celeste::getCelesteMask(model,in,radius,threshold,resize_dimension);
00488                         exportImage(srcImageRange(mask), vigra::ImageExportInfo(mask_name.c_str()).setPixelType("UINT8"));
00489                     };
00490                 }
00491                 catch (vigra::StdException & e)
00492                 {
00493                     // catch any errors that might have occured and print their reason
00494                     cout << "Unable to open file:\t" << pano.getImage(i).getFilename() << endl << endl;
00495                     cout << e.what() << endl << endl;
00496                     };
00497             };
00498         };
00499 
00500                 // write new pto file
00501         ofstream of(output_pto.c_str());
00502         UIntSet imgs;
00503         fill_set(imgs,0, pano.getNrOfImages()-1);
00504         pano.printPanoramaScript(of, pano.getOptimizeVector(), pano.getOptions(), imgs, false, hugin_utils::getPathPrefix(pto_file));
00505     
00506         cout << endl << "Written file " << output_pto << endl << endl;
00507         }
00508     celeste::destroySVMmodel(model);
00509         return(0);
00510         
00511 }       

Generated on 24 Nov 2014 for Hugintrunk by  doxygen 1.4.7