00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
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>
00041 #include <limits.h>
00042 #include <libgen.h>
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
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
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
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
00217
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 '?':
00272 return 1;
00273 break;
00274 default:
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
00293 if (!fileexists(model_file)){
00294
00295 #if _WINDOWS
00296 char buffer[MAX_PATH];
00297 GetModuleFileName(NULL,buffer,sizeof(buffer));
00298 string working_path=(buffer);
00299 string install_path_model="";
00300
00301 std::string::size_type pos=working_path.rfind("\\");
00302 if(pos!=std::string::npos)
00303 {
00304 working_path.erase(pos);
00305
00306 pos=working_path.rfind("\\");
00307 if(pos!=std::string::npos)
00308 {
00309 working_path.erase(pos);
00310
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
00344 if (output_pto == ("") && pto_file != ("")){
00345 output_pto = pto_file.substr(0,pto_file.length()-4).append("_celeste.pto");
00346 }
00347
00348
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
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
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
00390 transform(mask_format.begin(), mask_format.end(), mask_format.begin(),(int(*)(int)) tolower);
00391
00392
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
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
00413 vigra::UInt16RGBImage in=loadAndConvertImage(imagefile);
00414 if(in.width()==0 || in.height()==0)
00415 {
00416 continue;
00417 };
00418
00419
00420 string mask_name = generateMaskName(imagefile,mask_format);
00421
00422 cout << "Generating mask:\t" << mask_name << endl;
00423
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
00430 cout << "Unable to open file:\t" << imagefile << endl << endl;
00431 cout << e.what() << endl << endl;
00432 };
00433 };
00434 };
00435
00436
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
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
00494 cout << "Unable to open file:\t" << pano.getImage(i).getFilename() << endl << endl;
00495 cout << e.what() << endl << endl;
00496 };
00497 };
00498 };
00499
00500
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 }