Hugin trunk 0.1
Loading...
Searching...
No Matches
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"
26#include <vigra_ext/cms.h>
27#include <panodata/Panorama.h>
28#include <hugin_utils/utils.h>
29
30#include <getopt.h>
31
32static 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
61vigra::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());
79 vigra::importImageAlpha(info,destImage(imageIn),destImage(mask));
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());
96 vigra::importImageAlpha(info,destImage(image),destImage(mask));
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());
111 vigra::importImageAlpha(info,destImage(imageIn),destImage(mask));
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
150std::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
167int 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);
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':
220 break;
221 case 'd':
223 break;
224 case 'r':
226 break;
227 case 's':
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
261 {
264
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 {
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
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
325
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
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
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 {
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 {
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
423 {
424 std::cout << std::endl << "Written output to " << output_pto << std::endl;
425 };
426 }
428 return(0);
429
430}
std::string generateMaskName(std::string imagefile, std::string mask_format)
Definition Main.cpp:150
int main(int argc, char *argv[])
Definition Main.cpp:167
static void usage()
Definition Main.cpp:32
vigra::UInt16RGBImage loadAndConvertImage(std::string imagefile)
Definition Main.cpp:61
Model for a panorama.
Definition Panorama.h:153
const SrcPanoImage & getImage(std::size_t nr) const
get a panorama image, counting starts with 0
Definition Panorama.h:211
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
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...
bool WritePTOFile(const std::string &filename, const std::string &prefix="")
write data to given pto file
void removeCtrlPoint(unsigned int pNr)
remove a control point.
Definition Panorama.cpp:391
std::size_t getNrOfImages() const
number of images.
Definition Panorama.h:205
functions to handle icc profiles in images
class AlphaIterator class AlphaAccessor inline void importImageAlpha(const ImageImportInfo &import_info, ImageIterator image_iterator, ImageAccessor image_accessor, AlphaIterator alpha_iterator, AlphaAccessor alpha_accessor)
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::vector< CPoint > CPointVector
std::set< unsigned int > UIntSet
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
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
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
void destroySVMmodel(struct svm_model *&model)
frees the resource of model
Definition Celeste.cpp:60
bool loadSVMmodel(struct svm_model *&model, std::string &model_file)
loads the SVM model from file
Definition Celeste.cpp:45
std::string toupper(const std::string &s)
Definition stl_utils.h:59
std::string GetDataDir()
returns the full path to the data directory
Definition utils.cpp:441
std::string tolower(const std::string &s)
convert a string to lowercase
Definition stl_utils.h:49
std::string GetHuginVersion()
return a string with version numbers
Definition utils.cpp:907
bool FileExists(const std::string &filename)
checks if file exists
Definition utils.cpp:362
std::string stripPath(const std::string &filename)
remove the path of a filename (mainly useful for gui display of filenames)
Definition utils.cpp:160
std::string getPathPrefix(const std::string &filename)
Get the path to a filename.
Definition utils.cpp:184
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