38 static void usage(
const char* name)
40 std::cout << name <<
": add mask to pto project" << std::endl
43 <<
"Usage: " << name <<
" [options] input.pto" << 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
63 int main(
int argc,
char* argv[])
66 const char* optstring =
"o:h";
74 static struct option longOptions[] =
76 {
"output", required_argument, NULL,
'o' },
77 {
"mask", required_argument, NULL, MASK_SWITCH },
78 {
"rotate", required_argument, NULL, ROTATE_SWITCH},
79 {
"process", required_argument, NULL, PROC_SWITCH},
80 {
"delete-mask", required_argument, NULL, MASK_DELETE_SWITCH},
81 {
"help", no_argument, NULL,
'h' },
86 std::vector<MaskFiles> maskFiles;
91 bool deleteAllMasks =
false;
95 std::map<unsigned int, HuginBase::UIntSet> masksToDelete;
96 while ((c = getopt_long (argc, argv, optstring, longOptions,
nullptr)) != -1)
105 std::string s=optarg;
106 size_t found=s.rfind(
'@');
108 if(found!=std::string::npos)
110 std::string s2=s.substr(found+1, std::string::npos);
114 std::cerr <<
hugin_utils::stripPath(argv[0]) <<
": Could not parse image number: \"" << s2 <<
"\"." << std::endl;
120 std::cerr <<
hugin_utils::stripPath(argv[0]) <<
": No image number found in \"" << s <<
"\"." << std::endl;
129 maskFiles.push_back(mf);
134 std::string s=optarg;
136 if(s==
"CLOCKWISE" || s==
"90")
142 if(s==
"COUNTERCLOCKWISE" || s==
"-90")
148 std::cerr <<
hugin_utils::stripPath(argv[0]) <<
": Unknown rotate command (" << optarg <<
") found." << std::endl;
156 std::string s=optarg;
176 std::cerr <<
hugin_utils::stripPath(argv[0]) <<
": Unknown process command (" << optarg <<
") found." << std::endl;
183 case MASK_DELETE_SWITCH:
188 deleteAllMasks =
true;
192 size_t found = s.rfind(
'@');
193 if (found != std::string::npos)
196 std::string s2 = s.substr(0, found);
206 std::cerr <<
hugin_utils::stripPath(argv[0]) <<
": Could not parse mask number: \"" << s2 <<
"\"." << std::endl;
211 s2 = s.substr(found + 1, std::string::npos);
215 std::cerr <<
hugin_utils::stripPath(argv[0]) <<
": Could not parse image number: \"" << s2 <<
"\"." << std::endl;
219 masksToDelete[imgNr].insert(maskNr);
223 std::cerr <<
hugin_utils::stripPath(argv[0]) <<
": No image number found in \"" << s <<
"\"." << std::endl;
243 if (argc - optind != 1)
245 if (argc - optind < 1)
256 if (maskFiles.empty() && !deleteAllMasks && masksToDelete.empty())
262 std::string input=argv[optind];
274 std::cout <<
"Deleting all masks in project file" << std::endl;
282 if (!masksToDelete.empty())
284 for (
auto it = masksToDelete.begin(); it != masksToDelete.end(); ++it)
287 const unsigned int imgNr = it->first;
292 if (*masksSet.rbegin() == UINT_MAX)
296 std::cout <<
"Deleting all masks for image " << imgNr << std::endl;
301 for (
auto maskNr = masksSet.rbegin(); maskNr != masksSet.rend(); ++maskNr)
304 if (*maskNr < masks.size())
306 masks.erase(masks.begin() + (*maskNr));
307 std::cout <<
"Deleting mask " << *maskNr <<
" for image " << imgNr << std::endl;
311 std::cout <<
"Ignoring deleting non-existing mask number " << *maskNr <<
" for image " << imgNr << std::endl;
320 std::cout <<
"Ignoring deleting masks for invalid image nr " << imgNr << std::endl;
326 for(
size_t i=0; i<maskFiles.size(); i++)
330 std::ifstream in(maskFiles[i].maskFile.c_str());
331 vigra::Size2D maskImageSize;
335 if(maskImageSize.area()==0 || loadedMasks.empty())
337 std::cerr <<
"Error: Could not parse mask from file \"" << maskFiles[i].maskFile <<
"\"." << std::endl;
344 maskWidth=maskImageSize.width();
345 maskHeight=maskImageSize.height();
349 maskWidth=maskImageSize.height();
350 maskHeight=maskImageSize.width();
351 bool clockwise=(rotate==1);
352 for(
unsigned int i=0; i<loadedMasks.size(); i++)
354 loadedMasks[i].rotate90(clockwise, maskImageSize.width(), maskImageSize.height());
359 vigra::Size2D imageSize=pano.
getImage(maskFiles[i].imageNr).getSize();
360 if (maskImageSize != imageSize)
366 std::cout <<
"Clipping mask" << std::endl;
367 for(
unsigned int i=0; i<loadedMasks.size(); i++)
373 std::cout <<
"Scaling mask" << std::endl;
374 for(
unsigned int i=0; i<loadedMasks.size(); i++)
376 loadedMasks[i].scale((
double)imageSize.width()/maskWidth,(double)imageSize.height()/maskHeight);
381 std::cout <<
"Propotional scale mask" << std::endl;
383 double factor=
std::min((
double)imageSize.width()/maskWidth, (double)imageSize.height()/maskHeight);
384 for(
unsigned int i=0; i<loadedMasks.size(); i++)
386 loadedMasks[i].scale(factor);
393 for(
size_t j=0; j<loadedMasks.size(); j++)
395 masks.push_back(loadedMasks[j]);
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;
412 std::cout << std::endl <<
"Written project file " << output << std::endl;
bool FileExists(const std::string &filename)
checks if file exists
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...
const int maskOffset
polygon can exceed the image maximal maskOffset pixels in each direction bigger polygons will be clip...
void LoadMaskFromStream(std::istream &stream, vigra::Size2D &imageSize, MaskPolygonVector &newMasks, size_t imgNr)
load the mask from stream
std::string toupper(const std::string &s)
bool stringToUInt(const std::string &s, unsigned int &val)
convert string to unsigned integer value, returns true, if sucessful
std::set< unsigned int > UIntSet
std::string getPathPrefix(const std::string &filename)
Get the path to a filename.
std::size_t getNrOfImages() const
number of images.
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...
std::vector< MaskPolygon > MaskPolygonVector
std::string GetHuginVersion()
return a string with version numbers
bool WritePTOFile(const std::string &filename, const std::string &prefix="")
write data to given pto file
const SrcPanoImage & getImage(std::size_t nr) const
get a panorama image, counting starts with 0
void updateMasksForImage(unsigned int imgNr, MaskPolygonVector newMasks)
set complete mask list for image with number
std::string tolower(const std::string &s)
convert a string to lowercase
std::string stripPath(const std::string &filename)
remove the path of a filename (mainly useful for gui display of filenames)
int main(int argc, char *argv[])