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, see <http://www.gnu.org/licenses/>. *
00017  ***************************************************************************/
00018 
00019 #include <fstream>
00020 #include <sstream>
00021 #include <string>
00022 #include "Celeste.h"
00023 #include <sys/stat.h> 
00024 #include "hugin_config.h" 
00025 #include <vigra_ext/impexalpha.hxx>
00026 #include <panodata/Panorama.h>
00027 #include <hugin_utils/utils.h>
00028 
00029 #ifdef _WINDOWS
00030 #include <getopt.h>
00031 #else
00032  #include <unistd.h>
00033 #endif
00034 
00035 using namespace std;
00036 using namespace HuginBase;
00037 using namespace AppBase;
00038 
00039 static void usage(){
00040 
00041         // Print usage and exit
00042         cout << endl << "Celeste: Removes cloud-like control points from Hugin project files and creates image masks" << endl;
00043         cout << "using Support Vector Machines." << endl;
00044     cout << endl << "Version " << hugin_utils::GetHuginVersion() << endl;
00045         cout << endl << "Usage: celeste_standalone [options] image1 image2 [..]" << endl << endl;
00046         cout << "Options:" << endl << endl;
00047         cout << "  -i <filename>   Input Hugin PTO file. Control points over SVM threshold will" << endl;
00048         cout << "                  be removed before being written to the output file. If -m is" << endl;
00049         cout << "                  set to 1, images in the file will be also be masked." << endl;
00050         cout << "  -o <filename>   Output Hugin PTO file. Default: '<filename>_celeste.pto'" << endl;
00051         cout << "  -d <filename>   SVM model file. Default: 'data/celeste.model'" << endl;
00052         cout << "  -s <int>        Maximum dimension for re-sized image prior to processing. A" << endl;
00053         cout << "                  higher value will increase the resolution of the mask but is" << endl;
00054         cout << "                  significantly slower. Default: 800" << endl;
00055         cout << "  -t <float>      SVM threshold. Raise this value to remove fewer control points," << endl;
00056         cout << "                  lower it to remove more. Range 0 to 1. Default: 0.5" << endl;
00057         cout << "  -m <1|0>        Create masks when processing Hugin PTO file. Default: 0" << endl;
00058         cout << "  -f <string>     Mask file format. Options are PNG, JPEG, BMP, GIF and TIFF." << endl;
00059         cout << "                  Default: PNG" << endl;
00060         cout << "  -r <1|0>        Filter radius. 0 = large (more accurate), 1 = small (higher" << endl;
00061         cout << "                  resolution mask, slower, less accurate). Default: 0" << endl;
00062         cout << "  -h              Print usage." << endl; 
00063         cout << "  image1 image2.. Image files to be masked." << endl << endl;
00064         exit(1);
00065 
00066 }
00067 
00068 vigra::UInt16RGBImage loadAndConvertImage(string imagefile)
00069 {
00070     vigra::ImageImportInfo info(imagefile.c_str());
00071     std::string pixelType=info.getPixelType();
00072     vigra::UInt16RGBImage image;
00073     if(!info.isColor())
00074     {
00075         std::cerr << "Celeste works only on colour images, " << std::endl 
00076             << "but image " << imagefile << " has  " << info.numBands() << " channels." << std::endl 
00077             << "Skipping this image." << std::endl;
00078         return image;
00079     };
00080     if(pixelType=="UINT8" || pixelType=="INT16")
00081     {
00082         vigra::UInt16RGBImage imageIn(info.width(),info.height());
00083         if(info.numExtraBands()==1)
00084         {
00085             vigra::BImage mask(info.size());
00086             vigra::importImageAlpha(info,destImage(imageIn),destImage(mask));
00087             mask.resize(0,0);
00088         }
00089         else
00090         {
00091             importImage(info,destImage(imageIn));
00092         };
00093         celeste::convertToUInt16(imageIn,pixelType,image);
00094         imageIn.resize(0,0);
00095     }
00096     else
00097         if(pixelType=="UINT16")
00098         {
00099             image.resize(info.width(),info.height());
00100             if(info.numExtraBands()==1)
00101             {
00102                 vigra::BImage mask(info.size());
00103                 vigra::importImageAlpha(info,destImage(image),destImage(mask));
00104                 mask.resize(0,0);
00105             }
00106             else
00107             {
00108                 importImage(info,destImage(image));
00109             };
00110         }
00111         else
00112             if(pixelType=="INT32" || pixelType=="UINT32")
00113             {
00114                 vigra::UInt32RGBImage imageIn(info.width(),info.height());
00115                 if(info.numExtraBands()==1)
00116                 {
00117                     vigra::BImage mask(info.size());
00118                     vigra::importImageAlpha(info,destImage(imageIn),destImage(mask));
00119                     mask.resize(0,0);
00120                 }
00121                 else
00122                 {
00123                     importImage(info,destImage(imageIn));
00124                 };
00125                 celeste::convertToUInt16(imageIn,pixelType,image);
00126                 imageIn.resize(0,0);
00127             }
00128             else
00129                 if(pixelType=="FLOAT" || pixelType=="DOUBLE")
00130                 {
00131                     vigra::FRGBImage imagefloat(info.width(),info.height());
00132                     if(info.numExtraBands()==1)
00133                     {
00134                         vigra::BImage mask(info.size());
00135                         vigra::importImageAlpha(info,destImage(imagefloat),destImage(mask));
00136                         mask.resize(0,0);
00137                     }
00138                     else
00139                     {
00140                         importImage(info,destImage(imagefloat));
00141                     };
00142                     celeste::convertToUInt16(imagefloat,pixelType,image);
00143                     imagefloat.resize(0,0);
00144                 }
00145                 else
00146                 {
00147                     std::cerr << "Unsupported pixel type" << std::endl;
00148                 };
00149     return image;
00150 };
00151 
00152 std::string generateMaskName(string imagefile,string mask_format)
00153 {
00154     string mask_name = ("");
00155     if (imagefile.substr(imagefile.length()-4,1)==("."))
00156     {
00157         mask_name.append(imagefile.substr(0,imagefile.length()-4));
00158     }
00159     else
00160     {
00161         mask_name.append(imagefile.substr(0,imagefile.length()-4));
00162     }
00163     mask_name.append("_mask.");
00164     mask_name.append(mask_format);
00165     return mask_name;
00166 };
00167 
00168 
00169 int main(int argc, char* argv[])
00170 {
00171 
00172     // Exit with usage unless filename given as argument
00173     if (argc < 2)
00174     {
00175             usage();
00176     }
00177 
00178     int mask = 0;
00179     double threshold = 0.5;
00180     vector<string> images_to_mask;
00181     string pto_file = (""),output_pto = ("");
00182     string mask_format = ("PNG");
00183     string model_file = ("celeste.model");
00184     int course_fine = 0;
00185     int resize_dimension=800;
00186 
00187     // Deal with arguments
00188     // parse arguments
00189     int c;
00190     const char * optstring = "i:o:d:s:t:m:f:r:h";
00191 
00192     while ((c = getopt (argc, argv, optstring)) != -1)
00193     {
00194         switch(c)
00195         {
00196             case 'h': 
00197                 usage();
00198                 break;
00199             case 'i':
00200                 pto_file=optarg;
00201                 break;
00202             case 'o':
00203                 output_pto=optarg;
00204                 break;
00205             case 't':
00206                 threshold = atof(optarg);
00207                 if(threshold<=0 || threshold>1)
00208                 {
00209                     cerr << "Invalid parameter: threshold (-t) should be between 0 and 1" << std::endl;
00210                     return 1;
00211                 };
00212                 break;
00213             case 'm': 
00214                 mask = atoi(optarg);
00215                 if(mask<0 || mask>1)
00216                 {
00217                     cerr << "Invalid parameter: mask parameter (-m) can only be 0 or 1" << std::endl;
00218                     return 1;
00219                 };
00220                 break;
00221             case 'f': 
00222                 mask_format = optarg; 
00223                 break;
00224             case 'd':
00225                 model_file = optarg;
00226                 break;
00227             case 'r': 
00228                 course_fine = atoi(optarg);
00229                 break;
00230             case 's': 
00231                 resize_dimension = atoi(optarg);
00232                 if(resize_dimension<100)
00233                 {
00234                     cerr << "Invalid parameter: maximum dimension (-s) should be bigger than 100" << std::endl;
00235                     return 1;
00236                 };
00237                 break;
00238             case ':':
00239                 cerr <<"Missing parameter for parameter " << argv[optind] << endl;
00240                 return 1;
00241                 break;
00242             case '?': /* invalid parameter */
00243                 return 1;
00244                 break;
00245             default: /* unknown */
00246                 usage();
00247         };
00248     };
00249 
00250     while(optind<argc)
00251     {
00252         images_to_mask.push_back(argv[optind]);
00253         optind++;
00254     };
00255     
00256     if(images_to_mask.size()==0 && pto_file.empty())
00257     {
00258         cout << "No project file or image files given."<< endl;
00259         return 1;
00260     };
00261 
00262     
00263         // Check model file
00264     if (!hugin_utils::FileExists(model_file))
00265     {
00266         string install_path_model=hugin_utils::GetDataDir();
00267                 install_path_model.append(model_file);
00268                 
00269                 if (!hugin_utils::FileExists(install_path_model)){
00270                 
00271                         cout << endl << "Couldn't open SVM model file " << model_file << endl;
00272                         cout << "Also tried " << install_path_model << endl << endl; 
00273                         exit(1);
00274 
00275                 }else{
00276                 
00277                         model_file = install_path_model;
00278                 
00279                 }
00280         }
00281 
00282         // Set output .pto filename if not given
00283         if (output_pto == ("") && pto_file != ("")){
00284                 output_pto = pto_file.substr(0,pto_file.length()-4).append("_celeste.pto");
00285         }
00286 
00287         // Convert mask format to upper case
00288     mask_format=hugin_utils::toupper(mask_format);
00289         if (mask_format == ("JPG")){
00290                 mask_format = ("JPEG");
00291         }
00292         if (mask_format != ("PNG") &&mask_format != ("BMP") && mask_format != ("GIF") && mask_format != ("JPEG") && mask_format != ("TIFF")){
00293                 mask_format = ("TIFF");
00294         }
00295 
00296         // Print some stuff out
00297         cout << endl << "Celeste: Removes cloud-like control points from Hugin project files and creates image masks" << endl;
00298         cout << "using Support Vector Machines." << endl;
00299     cout << endl << "Version " << hugin_utils::GetHuginVersion() << endl << endl;
00300         cout << "Arguments:" << endl;
00301         cout << "Input Hugin file:\t" << pto_file << endl;
00302         cout << "Output Hugin file:\t" << output_pto << endl;
00303         cout << "SVM model file:\t\t" << model_file << endl;
00304         cout << "Max dimension:\t\t" << resize_dimension << endl;
00305         cout << "SVM threshold:\t\t" << threshold << endl;
00306         cout << "Create PTO masks:\t";
00307         if (mask){
00308                 cout << "Yes" << endl;
00309         }else{
00310                 cout << "No" << endl;
00311         } 
00312         cout << "Mask format:\t\t" << mask_format << endl;
00313         cout << "Filter radius:\t\t";
00314 
00315         // Mask resolution
00316     int radius;
00317         if (course_fine)
00318     {
00319         radius = 10;
00320         cout << "Small" << endl << endl;
00321         }
00322     else
00323     {
00324         radius=20;
00325         cout << "Large" << endl << endl;
00326         } 
00327         
00328         // Convert mask format to lower case
00329     mask_format=hugin_utils::tolower(mask_format);
00330 
00331     struct celeste::svm_model* model;
00332     if(!celeste::loadSVMmodel(model,model_file))
00333     {
00334         return 1;
00335     };
00336 
00337     // Mask images
00338         if (images_to_mask.size())
00339     {
00340         cout << "Masking images..." << endl << endl;
00341         for (unsigned int l = 0; l < images_to_mask.size(); l++)
00342         {
00343             std::string imagefile=images_to_mask[l];
00344             try
00345             {
00346                 cout << "Opening image file:\t" << imagefile << endl;
00347                 // Read image given and convert to UInt16
00348                 vigra::UInt16RGBImage in=loadAndConvertImage(imagefile);
00349                 if(in.width()==0 || in.height()==0)
00350                 {
00351                     continue;
00352                 };
00353 
00354                 // Create mask file name
00355                 string mask_name = generateMaskName(imagefile,mask_format);
00356 
00357                 cout << "Generating mask:\t" << mask_name << endl;                              
00358                 // Create mask
00359                 vigra::BImage mask=celeste::getCelesteMask(model,in,radius,threshold,resize_dimension);
00360                 exportImage(srcImageRange(mask), vigra::ImageExportInfo(mask_name.c_str()).setPixelType("UINT8"));
00361             }
00362             catch (vigra::StdException & e)
00363             {
00364                 // catch any errors that might have occured and print their reason
00365                 cout << "Unable to open file:\t" << imagefile << endl << endl;
00366                 cout << e.what() << endl << endl;
00367                 };
00368         };
00369         };
00370 
00371     // Process PTO file
00372     if (pto_file != (""))
00373     {
00374                 cout << "Parsing Hugin project file " << pto_file << endl << endl;
00375 
00376         Panorama pano;
00377         ifstream prjfile(pto_file.c_str());
00378         if (!prjfile.good())
00379         {
00380             cerr << "could not open script : " << pto_file << endl;
00381             celeste::destroySVMmodel(model);
00382             return 1;
00383         }
00384         pano.setFilePrefix(hugin_utils::getPathPrefix(pto_file));
00385         DocumentData::ReadWriteError err = pano.readData(prjfile);
00386         if (err != DocumentData::SUCCESSFUL)
00387         {
00388             cerr << "error while parsing panos tool script: " << pto_file << endl;
00389             cerr << "DocumentData::ReadWriteError code: " << err << endl;
00390             celeste::destroySVMmodel(model);
00391             return 1;
00392         }
00393 
00394         for(unsigned int i=0;i<pano.getNrOfImages();i++)
00395         {
00396             CPointVector cps=pano.getCtrlPointsVectorForImage(i);
00397             if(cps.size()>0 || mask)
00398             {
00399                 try
00400                 {
00401                     string imagefile=pano.getImage(i).getFilename();
00402                     vigra::UInt16RGBImage in=loadAndConvertImage(imagefile);
00403                     if(in.width()==0 || in.height()==0)
00404                     {
00405                         continue;
00406                     };
00407                     if(cps.size()>0)
00408                     {
00409                         UIntSet cloudCP=celeste::getCelesteControlPoints(model,in,cps,radius,threshold,resize_dimension);
00410                         if(cloudCP.size()>0)
00411                         {
00412                             for(UIntSet::reverse_iterator it=cloudCP.rbegin();it!=cloudCP.rend();++it)
00413                             {
00414                                 pano.removeCtrlPoint(*it);
00415                             };
00416                         };
00417                     };
00418                     if(mask)
00419                     {
00420                         string mask_name = generateMaskName(imagefile,mask_format);
00421                         // Create mask
00422                         vigra::BImage mask=celeste::getCelesteMask(model,in,radius,threshold,resize_dimension);
00423                         exportImage(srcImageRange(mask), vigra::ImageExportInfo(mask_name.c_str()).setPixelType("UINT8"));
00424                     };
00425                 }
00426                 catch (vigra::StdException & e)
00427                 {
00428                     // catch any errors that might have occured and print their reason
00429                     cout << "Unable to open file:\t" << pano.getImage(i).getFilename() << endl << endl;
00430                     cout << e.what() << endl << endl;
00431                     };
00432             };
00433         };
00434 
00435                 // write new pto file
00436         ofstream of(output_pto.c_str());
00437         UIntSet imgs;
00438         fill_set(imgs,0, pano.getNrOfImages()-1);
00439         pano.printPanoramaScript(of, pano.getOptimizeVector(), pano.getOptions(), imgs, false, hugin_utils::getPathPrefix(pto_file));
00440     
00441         cout << endl << "Written file " << output_pto << endl << endl;
00442         }
00443     celeste::destroySVMmodel(model);
00444         return(0);
00445         
00446 }       

Generated on 3 Aug 2015 for Hugintrunk by  doxygen 1.4.7