Hugin trunk 0.1
Loading...
Searching...
No Matches
pto_mask.cpp
Go to the documentation of this file.
1// -*- c-basic-offset: 4 -*-
2
11/* This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public
13 * License as published by the Free Software Foundation; either
14 * version 2 of the License, or (at your option) any later version.
15 *
16 * This software is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public
22 * License along with this software. If not, see
23 * <http://www.gnu.org/licenses/>.
24 *
25 */
26
27#include <fstream>
28#include <sstream>
29#include <getopt.h>
30#include <panodata/Panorama.h>
31
33{
34 size_t imageNr;
35 std::string maskFile;
36};
37
38static void usage(const char* name)
39{
40 std::cout << name << ": add mask to pto project" << std::endl
41 << name << " version " << hugin_utils::GetHuginVersion() << std::endl
42 << std::endl
43 << "Usage: " << name << " [options] input.pto" << std::endl
44 << std::endl
45 << " Options:" << std::endl
46 << " -o, --output=file.pto Output Hugin PTO file. Default: <filename>_mask.pto" << std::endl
47 << " --mask=filename@imgNr Read the mask from the file and" << std::endl
48 << " assign the mask to given image" << std::endl
49 << " --rotate=CLOCKWISE|90|COUNTERCLOCKWISE|-90" << std::endl
50 << " Rotates the mask clock- or counterclockwise" << std::endl
51 << " --process==CLIP|SCALE|PROP_SCALE Specify how the mask should be modified" << std::endl
52 << " if the mask is create for an image with" << std::endl
53 << " different size." << std::endl
54 << " * CLIP: clipping (Default)" << std::endl
55 << " * SCALE: Scaling width and height individually" << std::endl
56 << " * PROP_SCALE: Proportional scale" << std::endl
57 << " --delete-mask=maskNr@imgNr|ALL@imgNr|ALL" << std::endl
58 << " Removes the specified mask(s)" << std::endl
59 << " -h, --help Shows this help" << std::endl
60 << std::endl;
61}
62
63int main(int argc, char* argv[])
64{
65 // parse arguments
66 const char* optstring = "o:h";
67 enum
68 {
69 MASK_SWITCH=1000,
73 };
74 static struct option longOptions[] =
75 {
76 {"output", required_argument, NULL, 'o' },
79 {"process", required_argument, NULL, PROC_SWITCH},
80 {"delete-mask", required_argument, NULL, MASK_DELETE_SWITCH},
81 {"help", no_argument, NULL, 'h' },
82 0
83 };
84
85 int c;
86 std::vector<MaskFiles> maskFiles;
87 size_t rotate=0;
88 size_t process=0;
89 std::string output;
90 // boolean if all masks should be delete
91 bool deleteAllMasks = false;
92 // map, which contains all masks which should be deleted
93 // key is the image number, value is a set with the mask numbers
94 // UINT_MAX indicates all masks for given image
95 std::map<unsigned int, HuginBase::UIntSet> masksToDelete;
96 while ((c = getopt_long (argc, argv, optstring, longOptions,nullptr)) != -1)
97 {
98 switch (c)
99 {
100 case 'o':
101 output = optarg;
102 break;
103 case MASK_SWITCH:
104 {
105 std::string s=optarg;
106 size_t found=s.rfind('@');
108 if(found!=std::string::npos)
109 {
110 std::string s2=s.substr(found+1, std::string::npos);
111 mf.imageNr=atoi(s2.c_str());
112 if(mf.imageNr==0 && s2!="0")
113 {
114 std::cerr << hugin_utils::stripPath(argv[0]) << ": Could not parse image number: \"" << s2 << "\"." << std::endl;
115 return 1;
116 };
117 }
118 else
119 {
120 std::cerr << hugin_utils::stripPath(argv[0]) << ": No image number found in \"" << s << "\"." << std::endl;
121 return 1;
122 };
123 mf.maskFile=s.substr(0, found);
124 if(!hugin_utils::FileExists(mf.maskFile))
125 {
126 std::cerr << hugin_utils::stripPath(argv[0]) << ": File \"" << mf.maskFile << "\" does not exists." << std::endl;
127 return 1;
128 };
129 maskFiles.push_back(mf);
130 };
131 break;
132 case ROTATE_SWITCH:
133 {
134 std::string s=optarg;
136 if(s=="CLOCKWISE" || s=="90")
137 {
138 rotate=1;
139 }
140 else
141 {
142 if(s=="COUNTERCLOCKWISE" || s=="-90")
143 {
144 rotate=2;
145 }
146 else
147 {
148 std::cerr << hugin_utils::stripPath(argv[0]) << ": Unknown rotate command (" << optarg << ") found." << std::endl;
149 return 1;
150 };
151 };
152 };
153 break;
154 case PROC_SWITCH:
155 {
156 std::string s=optarg;
158 if(s=="CLIP")
159 {
160 process=0;
161 }
162 else
163 {
164 if(s=="SCALE")
165 {
166 process=1;
167 }
168 else
169 {
170 if(s=="PROP_SCALE")
171 {
172 process=2;
173 }
174 else
175 {
176 std::cerr << hugin_utils::stripPath(argv[0]) << ": Unknown process command (" << optarg << ") found." << std::endl;
177 return 1;
178 };
179 };
180 };
181 }
182 break;
184 {
185 std::string s = hugin_utils::tolower(optarg);
186 if (s == "all")
187 {
188 deleteAllMasks = true;
189 }
190 else
191 {
192 size_t found = s.rfind('@');
193 if (found != std::string::npos)
194 {
195 // parse mask number with special case for all
196 std::string s2 = s.substr(0, found);
197 unsigned int maskNr;
198 if (s2 == "all")
199 {
201 }
202 else
203 {
205 {
206 std::cerr << hugin_utils::stripPath(argv[0]) << ": Could not parse mask number: \"" << s2 << "\"." << std::endl;
207 return 1;
208 };
209 };
210 // now parse imgNr
211 s2 = s.substr(found + 1, std::string::npos);
212 unsigned int imgNr;
213 if (!hugin_utils::stringToUInt(s2, imgNr))
214 {
215 std::cerr << hugin_utils::stripPath(argv[0]) << ": Could not parse image number: \"" << s2 << "\"." << std::endl;
216 return 1;
217 };
218 // now remember parsed imgNr and maskNr
219 masksToDelete[imgNr].insert(maskNr);
220 }
221 else
222 {
223 std::cerr << hugin_utils::stripPath(argv[0]) << ": No image number found in \"" << s << "\"." << std::endl;
224 return 1;
225 };
226 };
227 };
228 break;
229 case 'h':
231 return 0;
232 case ':':
233 case '?':
234 // missing argument or invalid switch
235 return 1;
236 break;
237 default:
238 // this should not happen
239 abort();
240 }
241 }
242
243 if (argc - optind != 1)
244 {
245 if (argc - optind < 1)
246 {
247 std::cerr << hugin_utils::stripPath(argv[0]) << ": No project file given." << std::endl;
248 }
249 else
250 {
251 std::cerr << hugin_utils::stripPath(argv[0]) << ": Only one project file expected." << std::endl;
252 };
253 return 1;
254 };
255
256 if (maskFiles.empty() && !deleteAllMasks && masksToDelete.empty())
257 {
258 std::cerr << hugin_utils::stripPath(argv[0]) << ": No changes to mask given." << std::endl << std::endl;
259 return 1;
260 };
261
262 std::string input=argv[optind];
263 // read panorama
266 {
267 return 1;
268 };
269
270 //delete masks
271 if (deleteAllMasks)
272 {
273 // delete all masks
274 std::cout << "Deleting all masks in project file" << std::endl;
275 for (size_t i = 0; i < pano.getNrOfImages(); ++i)
276 {
278 };
279 }
280 else
281 {
282 if (!masksToDelete.empty())
283 {
284 for (auto it = masksToDelete.begin(); it != masksToDelete.end(); ++it)
285 {
286 // get image number and check that it is valid
287 const unsigned int imgNr = it->first;
288 if (imgNr < pano.getNrOfImages())
289 {
290 HuginBase::MaskPolygonVector masks = pano.getImage(imgNr).getMasks();
291 const HuginBase::UIntSet& masksSet = it->second;
292 if (*masksSet.rbegin() == UINT_MAX)
293 {
294 // delete all masks for given image
296 std::cout << "Deleting all masks for image " << imgNr << std::endl;
297 }
298 else
299 {
300 // iterate all masks
301 for (auto maskNr = masksSet.rbegin(); maskNr != masksSet.rend(); ++maskNr)
302 {
303 // range check
304 if (*maskNr < masks.size())
305 {
306 masks.erase(masks.begin() + (*maskNr));
307 std::cout << "Deleting mask " << *maskNr << " for image " << imgNr << std::endl;
308 }
309 else
310 {
311 std::cout << "Ignoring deleting non-existing mask number " << *maskNr << " for image " << imgNr << std::endl;
312 };
313 };
314 };
315 // finally update mask vector im panorama
316 pano.updateMasksForImage(imgNr, masks);
317 }
318 else
319 {
320 std::cout << "Ignoring deleting masks for invalid image nr " << imgNr << std::endl;
321 };
322 };
323 };
324 };
325 //read masks and apply
326 for(size_t i=0; i<maskFiles.size(); i++)
327 {
328 if(maskFiles[i].imageNr<pano.getNrOfImages())
329 {
330 std::ifstream in(maskFiles[i].maskFile.c_str());
331 vigra::Size2D maskImageSize;
333 LoadMaskFromStream(in, maskImageSize, loadedMasks, maskFiles[i].imageNr);
334 in.close();
335 if(maskImageSize.area()==0 || loadedMasks.empty())
336 {
337 std::cerr << "Error: Could not parse mask from file \"" << maskFiles[i].maskFile << "\"." << std::endl;
338 return 1;
339 };
340 double maskWidth;
341 double maskHeight;
342 if(rotate==0)
343 {
344 maskWidth=maskImageSize.width();
345 maskHeight=maskImageSize.height();
346 }
347 else
348 {
349 maskWidth=maskImageSize.height();
351 bool clockwise=(rotate==1);
352 for(unsigned int i=0; i<loadedMasks.size(); i++)
353 {
354 loadedMasks[i].rotate90(clockwise, maskImageSize.width(), maskImageSize.height());
355 }
356 };
357 // compare image size from file with that of current image alert user
358 // if different.
359 vigra::Size2D imageSize=pano.getImage(maskFiles[i].imageNr).getSize();
360 if (maskImageSize != imageSize)
361 {
362 switch(process)
363 {
364 case 0:
365 // clip mask
366 std::cout << "Clipping mask" << std::endl;
367 for(unsigned int i=0; i<loadedMasks.size(); i++)
368 loadedMasks[i].clipPolygon(vigra::Rect2D(-0.5*HuginBase::maskOffset, -0.5*HuginBase::maskOffset,
369 imageSize.width()+0.5*HuginBase::maskOffset, imageSize.height()+0.5*HuginBase::maskOffset));
370 break;
371 case 1:
372 // scale mask
373 std::cout << "Scaling mask" << std::endl;
374 for(unsigned int i=0; i<loadedMasks.size(); i++)
375 {
376 loadedMasks[i].scale((double)imageSize.width()/maskWidth,(double)imageSize.height()/maskHeight);
377 }
378 break;
379 case 2:
380 // proportional scale mask
381 std::cout << "Propotional scale mask" << std::endl;
382 {
383 double factor=std::min((double)imageSize.width()/maskWidth, (double)imageSize.height()/maskHeight);
384 for(unsigned int i=0; i<loadedMasks.size(); i++)
385 {
386 loadedMasks[i].scale(factor);
387 }
388 };
389 break;
390 };
391 };
392 HuginBase::MaskPolygonVector masks = pano.getImage(maskFiles[i].imageNr).getMasks();
393 for(size_t j=0; j<loadedMasks.size(); j++)
394 {
395 masks.push_back(loadedMasks[j]);
396 };
397 pano.updateMasksForImage(maskFiles[i].imageNr, masks);
398 }
399 else
400 {
401 std::cout << "Warning: Invalid image number \"" << maskFiles[i].imageNr << "\"." << std::endl
402 << " Project contains only " << pano.getNrOfImages()+1 << " images." << std::endl
403 << " Ignoring this mask." << std::endl;
404 };
405 };
406
407 //write output
408 // Set output .pto filename if not given
411 {
412 std::cout << std::endl << "Written project file " << output << std::endl;
413 };
414 return 0;
415}
static void usage()
Definition Main.cpp:32
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
void updateMasksForImage(unsigned int imgNr, MaskPolygonVector newMasks)
set complete mask list for image with number
Definition Panorama.cpp:882
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
std::size_t getNrOfImages() const
number of images.
Definition Panorama.h:205
std::vector< MaskPolygon > MaskPolygonVector
Definition Mask.h:150
const int maskOffset
polygon can exceed the image maximal maskOffset pixels in each direction bigger polygons will be clip...
Definition Mask.h:44
std::set< unsigned int > UIntSet
std::string toupper(const std::string &s)
Definition stl_utils.h:59
bool stringToUInt(const std::string &s, unsigned int &val)
convert string to unsigned integer value, returns true, if sucessful
Definition utils.cpp:280
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 GetOutputFilename(const std::string &out, const std::string &in, const std::string &suffix)
construct output filename, if ouput is known return this value otherwise use the input filename and a...
Definition utils.cpp:420
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
int main(int argc, char *argv[])
Definition pto_mask.cpp:63
size_t imageNr
Definition pto_mask.cpp:34
std::string maskFile
Definition pto_mask.cpp:35
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