Hugintrunk  0.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
Main.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  * Copyright (C) 2008 by Tim Nugent *
3  * timnugent@gmail.com *
4  * *
5  * This program is free software; you can redistribute it and/or modify *
6  * it under the terms of the GNU General Public License as published by *
7  * the Free Software Foundation; either version 2 of the License, or *
8  * (at your option) any later version. *
9  * *
10  * This program is distributed in the hope that it will be useful, *
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13  * GNU General Public License for more details. *
14  * *
15  * You should have received a copy of the GNU General Public License *
16  * along with this program. If not, see <http://www.gnu.org/licenses/>. *
17  ***************************************************************************/
18 
19 #include <fstream>
20 #include <sstream>
21 #include <string>
22 #include "Celeste.h"
23 #include <sys/stat.h>
24 #include "hugin_config.h"
25 #include <vigra_ext/impexalpha.hxx>
26 #include <vigra_ext/cms.h>
27 #include <panodata/Panorama.h>
28 #include <hugin_utils/utils.h>
29 
30 #include <getopt.h>
31 
32 static void usage(){
33 
34  // Print usage and exit
35  std::cout << std::endl << "Celeste: Removes cloud-like control points from Hugin project files and creates image masks" << std::endl;
36  std::cout << "using Support Vector Machines." << std::endl;
37  std::cout << std::endl << "Version " << hugin_utils::GetHuginVersion() << std::endl;
38  std::cout << std::endl << "Usage: celeste_standalone [options] image1 image2 [..]" << std::endl << std::endl;
39  std::cout << "Options:" << std::endl << std::endl;
40  std::cout << " -i <filename> Input Hugin PTO file. Control points over SVM threshold will" << std::endl;
41  std::cout << " be removed before being written to the output file. If -m is" << std::endl;
42  std::cout << " set to 1, images in the file will be also be masked." << std::endl;
43  std::cout << " -o <filename> Output Hugin PTO file. Default: '<filename>_celeste.pto'" << std::endl;
44  std::cout << " -d <filename> SVM model file. Default: 'data/celeste.model'" << std::endl;
45  std::cout << " -s <int> Maximum dimension for re-sized image prior to processing. A" << std::endl;
46  std::cout << " higher value will increase the resolution of the mask but is" << std::endl;
47  std::cout << " significantly slower. Default: 800" << std::endl;
48  std::cout << " -t <float> SVM threshold. Raise this value to remove fewer control points," << std::endl;
49  std::cout << " lower it to remove more. Range 0 to 1. Default: 0.5" << std::endl;
50  std::cout << " -m <1|0> Create masks when processing Hugin PTO file. Default: 0" << std::endl;
51  std::cout << " -f <std::string> Mask file format. Options are PNG, JPEG, BMP, GIF and TIFF." << std::endl;
52  std::cout << " Default: PNG" << std::endl;
53  std::cout << " -r <1|0> Filter radius. 0 = large (more accurate), 1 = small (higher" << std::endl;
54  std::cout << " resolution mask, slower, less accurate). Default: 0" << std::endl;
55  std::cout << " -h|--help Print usage." << std::endl;
56  std::cout << " image1 image2.. Image files to be masked." << std::endl << std::endl;
57  exit(1);
58 
59 }
60 
61 vigra::UInt16RGBImage loadAndConvertImage(std::string imagefile)
62 {
63  vigra::ImageImportInfo info(imagefile.c_str());
64  std::string pixelType=info.getPixelType();
65  vigra::UInt16RGBImage image;
66  if(!info.isColor())
67  {
68  std::cerr << "Celeste works only on colour images, " << std::endl
69  << "but image " << imagefile << " has " << info.numBands() << " channels." << std::endl
70  << "Skipping this image." << std::endl;
71  return image;
72  };
73  if(pixelType=="UINT8" || pixelType=="INT16")
74  {
75  vigra::UInt16RGBImage imageIn(info.width(),info.height());
76  if(info.numExtraBands()==1)
77  {
78  vigra::BImage mask(info.size());
80  mask.resize(0,0);
81  }
82  else
83  {
84  importImage(info,destImage(imageIn));
85  };
86  celeste::convertToUInt16(imageIn,pixelType,image);
87  imageIn.resize(0,0);
88  }
89  else
90  if(pixelType=="UINT16")
91  {
92  image.resize(info.width(),info.height());
93  if(info.numExtraBands()==1)
94  {
95  vigra::BImage mask(info.size());
97  mask.resize(0,0);
98  }
99  else
100  {
101  importImage(info,destImage(image));
102  };
103  }
104  else
105  if(pixelType=="INT32" || pixelType=="UINT32")
106  {
107  vigra::UInt32RGBImage imageIn(info.width(),info.height());
108  if(info.numExtraBands()==1)
109  {
110  vigra::BImage mask(info.size());
112  mask.resize(0,0);
113  }
114  else
115  {
116  importImage(info,destImage(imageIn));
117  };
118  celeste::convertToUInt16(imageIn,pixelType,image);
119  imageIn.resize(0,0);
120  }
121  else
122  if(pixelType=="FLOAT" || pixelType=="DOUBLE")
123  {
124  vigra::FRGBImage imagefloat(info.width(),info.height());
125  if(info.numExtraBands()==1)
126  {
127  vigra::BImage mask(info.size());
128  vigra::importImageAlpha(info,destImage(imagefloat),destImage(mask));
129  mask.resize(0,0);
130  }
131  else
132  {
133  importImage(info,destImage(imagefloat));
134  };
135  celeste::convertToUInt16(imagefloat,pixelType,image);
136  imagefloat.resize(0,0);
137  }
138  else
139  {
140  std::cerr << "Unsupported pixel type" << std::endl;
141  };
142  // convert to sRGB colorspace if images contains icc profile
143  if (!info.getICCProfile().empty())
144  {
145  HuginBase::Color::ApplyICCProfile(image, info.getICCProfile(), TYPE_RGB_16);
146  }
147  return image;
148 };
149 
150 std::string generateMaskName(std::string imagefile,std::string mask_format)
151 {
152  std::string mask_name = ("");
153  if (imagefile.substr(imagefile.length()-4,1)==("."))
154  {
155  mask_name.append(imagefile.substr(0,imagefile.length()-4));
156  }
157  else
158  {
159  mask_name.append(imagefile.substr(0,imagefile.length()-4));
160  }
161  mask_name.append("_mask.");
162  mask_name.append(mask_format);
163  return mask_name;
164 };
165 
166 
167 int main(int argc, char* argv[])
168 {
169  int mask = 0;
170  double threshold = 0.5;
171  std::vector<std::string> images_to_mask;
172  std::string pto_file = (""),output_pto = ("");
173  std::string mask_format = ("PNG");
174  std::string model_file = ("celeste.model");
175  int course_fine = 0;
176  int resize_dimension=800;
177 
178  // Deal with arguments
179  // parse arguments
180  int c;
181  const char * optstring = "i:o:d:s:t:m:f:r:h";
182  static struct option longOptions[] =
183  {
184  { "output", required_argument, NULL, 'o' },
185  { "help", no_argument, NULL, 'h' },
186  0
187  };
188 
189  while ((c = getopt_long(argc, argv, optstring, longOptions, nullptr)) != -1)
190  {
191  switch(c)
192  {
193  case 'h':
194  usage();
195  break;
196  case 'i':
197  pto_file=optarg;
198  break;
199  case 'o':
200  output_pto=optarg;
201  break;
202  case 't':
203  threshold = atof(optarg);
204  if(threshold<=0 || threshold>1)
205  {
206  std::cerr << hugin_utils::stripPath(argv[0]) << ": Invalid parameter: threshold (-t) should be between 0 and 1" << std::endl;
207  return 1;
208  };
209  break;
210  case 'm':
211  mask = atoi(optarg);
212  if(mask<0 || mask>1)
213  {
214  std::cerr << hugin_utils::stripPath(argv[0]) << ": Invalid parameter: mask parameter (-m) can only be 0 or 1" << std::endl;
215  return 1;
216  };
217  break;
218  case 'f':
219  mask_format = optarg;
220  break;
221  case 'd':
222  model_file = optarg;
223  break;
224  case 'r':
225  course_fine = atoi(optarg);
226  break;
227  case 's':
228  resize_dimension = atoi(optarg);
229  if(resize_dimension<100)
230  {
231  std::cerr << hugin_utils::stripPath(argv[0]) << ": Invalid parameter: maximum dimension (-s) should be bigger than 100" << std::endl;
232  return 1;
233  };
234  break;
235  case ':':
236  case '?':
237  // missing argument or invalid switch
238  return 1;
239  break;
240  default:
241  // this should not happen
242  abort();
243  };
244  };
245 
246  while(optind<argc)
247  {
248  images_to_mask.push_back(argv[optind]);
249  optind++;
250  };
251 
252  if(images_to_mask.empty() && pto_file.empty())
253  {
254  std::cerr << hugin_utils::stripPath(argv[0]) << ": No project file or image files given." << std::endl;
255  return 1;
256  };
257 
258 
259  // Check model file
260  if (!hugin_utils::FileExists(model_file))
261  {
262  std::string install_path_model=hugin_utils::GetDataDir();
263  install_path_model.append(model_file);
264 
265  if (!hugin_utils::FileExists(install_path_model))
266  {
267  std::cerr << std::endl << "Couldn't open SVM model file " << model_file << std::endl
268  << "Also tried " << install_path_model << std::endl << std::endl;
269  exit(1);
270  }
271  else
272  {
273  model_file = install_path_model;
274  }
275  }
276 
277  // Set output .pto filename if not given
278  if (output_pto == ("") && pto_file != ("")){
279  output_pto = pto_file.substr(0,pto_file.length()-4).append("_celeste.pto");
280  }
281 
282  // Convert mask format to upper case
283  mask_format=hugin_utils::toupper(mask_format);
284  if (mask_format == ("JPG")){
285  mask_format = ("JPEG");
286  }
287  if (mask_format != ("PNG") &&mask_format != ("BMP") && mask_format != ("GIF") && mask_format != ("JPEG") && mask_format != ("TIFF")){
288  mask_format = ("TIFF");
289  }
290 
291  // Print some stuff out
292  std::cout << std::endl << "Celeste: Removes cloud-like control points from Hugin project files and creates image masks" << std::endl;
293  std::cout << "using Support Vector Machines." << std::endl;
294  std::cout << std::endl << "Version " << hugin_utils::GetHuginVersion() << std::endl << std::endl;
295  std::cout << "Arguments:" << std::endl;
296  std::cout << "Input Hugin file:\t" << pto_file << std::endl;
297  std::cout << "Output Hugin file:\t" << output_pto << std::endl;
298  std::cout << "SVM model file:\t\t" << model_file << std::endl;
299  std::cout << "Max dimension:\t\t" << resize_dimension << std::endl;
300  std::cout << "SVM threshold:\t\t" << threshold << std::endl;
301  std::cout << "Create PTO masks:\t";
302  if (mask){
303  std::cout << "Yes" << std::endl;
304  }else{
305  std::cout << "No" << std::endl;
306  }
307  std::cout << "Mask format:\t\t" << mask_format << std::endl;
308  std::cout << "Filter radius:\t\t";
309 
310  // Mask resolution
311  int radius;
312  if (course_fine)
313  {
314  radius = 10;
315  std::cout << "Small" << std::endl << std::endl;
316  }
317  else
318  {
319  radius=20;
320  std::cout << "Large" << std::endl << std::endl;
321  }
322 
323  // Convert mask format to lower case
324  mask_format=hugin_utils::tolower(mask_format);
325 
326  struct celeste::svm_model* model;
327  if(!celeste::loadSVMmodel(model,model_file))
328  {
329  return 1;
330  };
331 
332  // Mask images
333  if (images_to_mask.size())
334  {
335  std::cout << "Masking images..." << std::endl << std::endl;
336  for (unsigned int l = 0; l < images_to_mask.size(); l++)
337  {
338  std::string imagefile=images_to_mask[l];
339  try
340  {
341  std::cout << "Opening image file:\t" << imagefile << std::endl;
342  // Read image given and convert to UInt16
343  vigra::UInt16RGBImage in=loadAndConvertImage(imagefile);
344  if(in.width()==0 || in.height()==0)
345  {
346  continue;
347  };
348 
349  // Create mask file name
350  std::string mask_name = generateMaskName(imagefile,mask_format);
351 
352  std::cout << "Generating mask:\t" << mask_name << std::endl;
353  // Create mask
354  vigra::BImage* mask=celeste::getCelesteMask(model,in,radius,threshold,resize_dimension);
355  exportImage(srcImageRange(*mask), vigra::ImageExportInfo(mask_name.c_str()).setPixelType("UINT8"));
356  delete mask;
357  }
358  catch (vigra::StdException & e)
359  {
360  // catch any errors that might have occurred and print their reason
361  std::cout << "Unable to open file:\t" << imagefile << std::endl << std::endl;
362  std::cout << e.what() << std::endl << std::endl;
363  };
364  };
365  };
366 
367  // Process PTO file
368  if (pto_file != (""))
369  {
370  std::cout << "Parsing Hugin project file " << pto_file << std::endl << std::endl;
371 
372  HuginBase::Panorama pano;
373  if (!pano.ReadPTOFile(pto_file, hugin_utils::getPathPrefix(pto_file)))
374  {
376  return 1;
377  };
378 
379  for(unsigned int i=0;i<pano.getNrOfImages();i++)
380  {
382  if(!cps.empty() || mask)
383  {
384  try
385  {
386  std::string imagefile=pano.getImage(i).getFilename();
387  vigra::UInt16RGBImage in=loadAndConvertImage(imagefile);
388  if(in.width()==0 || in.height()==0)
389  {
390  continue;
391  };
392  if(!cps.empty())
393  {
394  HuginBase::UIntSet cloudCP = celeste::getCelesteControlPoints(model, in, cps, radius, threshold, resize_dimension);
395  if(!cloudCP.empty())
396  {
397  for (HuginBase::UIntSet::reverse_iterator it = cloudCP.rbegin(); it != cloudCP.rend(); ++it)
398  {
399  pano.removeCtrlPoint(*it);
400  };
401  };
402  };
403  if(mask)
404  {
405  std::string mask_name = generateMaskName(imagefile,mask_format);
406  // Create mask
407  vigra::BImage* mask=celeste::getCelesteMask(model,in,radius,threshold,resize_dimension);
408  exportImage(srcImageRange(*mask), vigra::ImageExportInfo(mask_name.c_str()).setPixelType("UINT8"));
409  delete mask;
410  };
411  }
412  catch (vigra::StdException & e)
413  {
414  // catch any errors that might have occurred and print their reason
415  std::cout << "Unable to open file:\t" << pano.getImage(i).getFilename() << std::endl << std::endl;
416  std::cout << e.what() << std::endl << std::endl;
417  };
418  };
419  };
420 
421  // write new pto file
422  if (pano.WritePTOFile(output_pto, hugin_utils::getPathPrefix(output_pto)))
423  {
424  std::cout << std::endl << "Written output to " << output_pto << std::endl;
425  };
426  }
428  return(0);
429 
430 }
void destroySVMmodel(struct svm_model *&model)
frees the resource of model
Definition: Celeste.cpp:60
bool FileExists(const std::string &filename)
checks if file exists
Definition: utils.cpp:362
vigra::BImage * getCelesteMask(struct svm_model *model, vigra::UInt16RGBImage &input, int radius, float threshold, int resize_dimension, bool adaptThreshold, bool verbose)
calculates the mask using SVM
Definition: Celeste.cpp:313
void convertToUInt16(SrcIMG &src, const std::string &origType, vigra::UInt16RGBImage &dest)
converts the given image to UInt16RGBImage only this image is correctly processed by celeste ...
Definition: Celeste.h:70
std::string toupper(const std::string &s)
Definition: stl_utils.h:59
std::set< unsigned int > UIntSet
Definition: PanoramaData.h:51
vigra::UInt16RGBImage loadAndConvertImage(std::string imagefile)
Definition: Main.cpp:61
Model for a panorama.
Definition: Panorama.h:152
std::string GetDataDir()
returns the full path to the data directory
Definition: utils.cpp:441
bool loadSVMmodel(struct svm_model *&model, std::string &model_file)
loads the SVM model from file
Definition: Celeste.cpp:45
std::string getPathPrefix(const std::string &filename)
Get the path to a filename.
Definition: utils.cpp:184
std::size_t getNrOfImages() const
number of images.
Definition: Panorama.h:205
bool ReadPTOFile(const std::string &filename, const std::string &prefix="")
read pto file from the given filename into Panorama object it does some checks on the file and issues...
Definition: Panorama.cpp:2023
vigra::pair< typename ROIImage< Image, Alpha >::image_traverser, typename ROIImage< Image, Alpha >::ImageAccessor > destImage(ROIImage< Image, Alpha > &img)
Definition: ROIImage.h:324
CPointVector getCtrlPointsVectorForImage(unsigned int imgNr) const
return a vector of std::pairs with global ctrl point nr and ControlPoint In the class ControlPoint th...
Definition: Panorama.cpp:96
vigra::triple< typename ROIImage< Image, Mask >::image_const_traverser, typename ROIImage< Image, Mask >::image_const_traverser, typename ROIImage< Image, Mask >::ImageConstAccessor > srcImageRange(const ROIImage< Image, Mask > &img)
helper function for ROIImages
Definition: ROIImage.h:287
std::string generateMaskName(std::string imagefile, std::string mask_format)
Definition: Main.cpp:150
std::vector< CPoint > CPointVector
Definition: ControlPoint.h:102
std::vector< deghosting::BImagePtr > threshold(const std::vector< deghosting::FImagePtr > &inputImages, const double threshold, const uint16_t flags)
Threshold function used for creating alpha masks for images.
Definition: threshold.h:41
void importImageAlpha(const ImageImportInfo &import_info, ImageIterator image_iterator, ImageAccessor image_accessor, AlphaIterator alpha_iterator, AlphaAccessor alpha_accessor)
Read the image specified by the given vigra::ImageImportInfo object including its alpha channel...
Definition: impexalpha.hxx:479
HuginBase::UIntSet getCelesteControlPoints(struct svm_model *model, vigra::UInt16RGBImage &input, HuginBase::CPointVector cps, int radius, float threshold, int resize_dimension, bool verbose)
Definition: Celeste.cpp:363
static void usage()
Definition: Main.cpp:32
std::string GetHuginVersion()
return a string with version numbers
Definition: utils.cpp:907
void removeCtrlPoint(unsigned int pNr)
remove a control point.
Definition: Panorama.cpp:391
bool WritePTOFile(const std::string &filename, const std::string &prefix="")
write data to given pto file
Definition: Panorama.cpp:2059
static void info(const char *fmt,...)
Definition: svm.cpp:95
const SrcPanoImage & getImage(std::size_t nr) const
get a panorama image, counting starts with 0
Definition: Panorama.h:211
functions to handle icc profiles in images
std::string tolower(const std::string &s)
convert a string to lowercase
Definition: stl_utils.h:49
void ApplyICCProfile(ImageType &image, const vigra::ImageImportInfo::ICCProfile &iccProfile, const cmsUInt32Number imageFormat)
converts given image with iccProfile to sRGB/gray space, need to give pixel type in lcms2 format work...
Definition: cms.h:37
std::string stripPath(const std::string &filename)
remove the path of a filename (mainly useful for gui display of filenames)
Definition: utils.cpp:160
int main(int argc, char *argv[])
Definition: Main.cpp:167