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

Generated on 5 Feb 2016 for Hugintrunk by  doxygen 1.4.7