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

Generated on 10 Dec 2016 for Hugintrunk by  doxygen 1.4.7