1 // -*- c-basic-offset: 4 -*-
2 
3 /** @file hugin_lensdb.cpp
4  *
5  *  @brief helper program for working with lens database
6  *
7  *
8  *  @author T. Modes
9  *
10  */
11 
12 /*  This program is free software; you can redistribute it and/or
13  *  modify it under the terms of the GNU General Public
14  *  License as published by the Free Software Foundation; either
15  *  version 2 of the License, or (at your option) any later version.
16  *
17  *  This software is distributed in the hope that it will be useful,
18  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
19  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20  *  General Public License for more details.
21  *
22  *  You should have received a copy of the GNU General Public
23  *  License along with this software. If not, see
24  *  <http://www.gnu.org/licenses/>.
25  *
26  */
27 
28 #include <iostream>
29 #include <string>
30 #include <fstream>
31 #include "hugin_config.h"
32 #include <hugin_utils/filesystem.h>
33 #include <getopt.h>
34 #include <panodata/Panorama.h>
35 #include <hugin_utils/stl_utils.h>
36 #include <lensdb/LensDB.h>
37 #include <panodata/StandardImageVariableGroups.h>
38 #include <hugin_base/panotools/PanoToolsUtils.h>
39 
40 typedef std::vector<fs::path> pathVec;
41 
42 
43 template <class iteratorType>
iterateFileSystem(std::string src,pathVec & projectFiles)44 bool iterateFileSystem(std::string src, pathVec& projectFiles)
45 {
46     try
47     {
48         for(iteratorType it(src); it != iteratorType(); it++)
49         {
50             std::string ext=hugin_utils::toupper(it->path().extension().string());
51             if(ext==".PTO")
52             {
53                 projectFiles.push_back(*it);
54             };
55         }
56     }
57     catch(fs::filesystem_error& e)
58     {
59         std::cout << e.what() << std::endl;
60         return false;
61     }
62     return true;
63 };
64 
FindPTOFiles(pathVec & projectFiles,std::string src,bool recursive)65 void FindPTOFiles(pathVec& projectFiles, std::string src, bool recursive)
66 {
67     if(recursive)
68     {
69         iterateFileSystem<fs::recursive_directory_iterator>(src, projectFiles);
70     }
71     else
72     {
73         iterateFileSystem<fs::directory_iterator>(src, projectFiles);
74     };
75 };
76 
CheckProjectFile(const fs::path filename)77 bool CheckProjectFile(const fs::path filename)
78 {
79     // open project file
80     HuginBase::Panorama pano;
81     std::string input = filename.string();
82     std::ifstream prjfile(input.c_str());
83     if (!prjfile.good())
84     {
85         std::cerr << "ERROR: Could not open script: " << filename.string() << endl;
86         return false;
87     }
88     std::string inputPathPrefix = hugin_utils::getPathPrefix(input);
89     pano.setFilePrefix(inputPathPrefix);
90     AppBase::DocumentData::ReadWriteError err = pano.readData(prjfile);
91     if (err != AppBase::DocumentData::SUCCESSFUL)
92     {
93         std::cerr << "ERROR: error while parsing panos tool script: " << input << std::endl
94                   << "DocumentData::ReadWriteError code: " << err << std::endl;
95         return false;
96     };
97     prjfile.close();
98     if (pano.getNrOfImages() == 0)
99     {
100         return false;
101     };
102     std::cout << "Checking " << filename.string() << "..." << std::endl;
103     HuginBase::StandardImageVariableGroups lenses(pano);
104     if (lenses.getLenses().getNumberOfParts()==1)
105     {
106         // read the EXIF data
107         for (size_t i = 0; i < pano.getNrOfImages(); ++i)
108         {
109             HuginBase::SrcPanoImage img = pano.getSrcImage(i);
110             if (!img.readEXIF())
111             {
112                 std::cout << " Ignored (File missing or missing metadata)" << std::endl;
113                 return false;
114             }
115             pano.setSrcImage(i, img);
116         };
117         // update cp errors
118         HuginBase::PTools::calcCtrlPointErrors(pano);
119         // now save in database
120         if (HuginBase::LensDB::SaveLensDataFromPano(pano))
121         {
122             std::cout << " Saved." << std::endl;
123             return true;
124         }
125         else
126         {
127             std::cout << " Ignored." << std::endl;
128             return false;
129         }
130     };
131     std::cout << " Ignored (More than one lens)." << std::endl;
132     return false;
133 };
134 
usage(const char * name)135 static void usage(const char* name)
136 {
137     std::cout << name << ": tool for lens database maintenance" << std::endl
138               << name << " version " << hugin_utils::GetHuginVersion() << std::endl
139               << std::endl
140               << "Usage:  hugin_lensdb [--recursive] --populate BASEPATH " << std::endl
141               << "             Populate database with information from all pto files" << std::endl
142               << "             in given BASEPATH" << std::endl
143               << "             With --recursive switch all subfolders will also be" << std::endl
144               << "             searched." << std::endl
145               << "        hugin_lensdb --compress" << std::endl
146               << "             Compresses the database by replacing single values" << std::endl
147               << "             with averaged values." << std::endl
148               << "        hugin_lensdb --remove-lens=LENS" << std::endl
149               << "             Removes given lens from the database." << std::endl
150               << "        hugin_lensdb --remove-camera=MAKER|MODEL" << std::endl
151               << "             Removes given camera from the database." << std::endl
152               << "        hugin_lensdb --export-database=FILENAME" << std::endl
153               << "             Export data from database to external file." << std::endl
154               << "        hugin_lensdb --import-from-file=FILENAME" << std::endl
155               << "             Import data from external file." << std::endl
156               << std::endl;
157 };
158 
main(int argc,char * argv[])159 int main(int argc, char* argv[])
160 {
161     // parse arguments
162     const char* optstring = "crph";
163     enum
164     {
165         REMOVE_LENS=1000,
166         REMOVE_CAM=1001,
167         EXPORT_DB=1002,
168         IMPORT_DB=1003,
169     };
170 
171     static struct option longOptions[] =
172     {
173         { "compress", no_argument, NULL, 'c' },
174         { "recursive", no_argument, NULL, 'r' },
175         { "populate", required_argument, NULL, 'p' },
176         { "remove-lens", required_argument, NULL, REMOVE_LENS },
177         { "remove-camera", required_argument, NULL, REMOVE_CAM },
178         { "export-database", required_argument, NULL, EXPORT_DB },
179         { "import-from-file", required_argument, NULL, IMPORT_DB },
180         { "help", no_argument, NULL, 'h' },
181         0
182     };
183 
184     bool recursive=false;
185     bool populate = false;
186     bool compress = false;
187     std::string basepath;
188     std::string lensToRemove;
189     std::string camToRemove;
190     std::string exportDatabase;
191     std::string importDatabase;
192     int c;
193     while ((c = getopt_long (argc, argv, optstring, longOptions,nullptr)) != -1)
194     {
195         switch (c)
196         {
197             case 'h':
198                 usage(hugin_utils::stripPath(argv[0]).c_str());
199                 return 0;
200             case 'c':
201                 compress=true;
202                 break;
203             case 'r':
204                 recursive=true;
205                 break;
206             case 'p':
207                 populate = true;
208                 basepath = hugin_utils::StrTrim(std::string(optarg));
209                 break;
210             case REMOVE_LENS:
211                 lensToRemove = hugin_utils::StrTrim(std::string(optarg));
212                 break;
213             case REMOVE_CAM:
214                 camToRemove = hugin_utils::StrTrim(std::string(optarg));
215                 break;
216             case EXPORT_DB:
217                 exportDatabase = hugin_utils::StrTrim(std::string(optarg));
218                 break;
219             case IMPORT_DB:
220                 importDatabase = hugin_utils::StrTrim(std::string(optarg));
221                 break;
222             case ':':
223             case '?':
224                 // missing argument or invalid switch
225                 return 1;
226                 break;
227             default:
228                 // this should not happen
229                 abort();
230         }
231     }
232 
233     if (!exportDatabase.empty() && !importDatabase.empty())
234     {
235         std::cerr << hugin_utils::stripPath(argv[0]) << ": Export and import can not be done at the same time. " << std::endl;
236         return -1;
237     };
238 
239     if (!populate && !compress && lensToRemove.empty() && camToRemove.empty() && exportDatabase.empty() && importDatabase.empty())
240     {
241         std::cout << "Lensdatabase file: " << HuginBase::LensDB::LensDB::GetSingleton().GetDBFilename() << std::endl;
242         std::cout << "Nothing to do." << std::endl;
243     };
244 
245     if (populate)
246     {
247         fs::path p(basepath);
248         if (fs::exists(p))
249         {
250             p = fs::absolute(p);
251             if (fs::is_directory(p))
252             {
253                 pathVec projectFiles;
254                 FindPTOFiles(projectFiles, p.string(), recursive);
255                 if (projectFiles.empty())
256                 {
257                     std::cerr << "ERROR: No project files found in given directory " << p.string() << std::endl;
258                     return 1;
259                 };
260                 for (pathVec::const_iterator it = projectFiles.begin(); it != projectFiles.end(); ++it)
261                 {
262                     CheckProjectFile(*it);
263                 };
264             }
265             else
266             {
267                 std::cerr << "ERROR: " << basepath << " is not a directory." << std::endl;
268                 return 1;
269             };
270         }
271         else
272         {
273             std::cerr << "ERROR: Path " << basepath << " does not exists." << std::endl;
274             return 1;
275         }
276     };
277 
278     if (compress)
279     {
280         std::cout << "Compressing database..." << std::endl;
281         if(HuginBase::LensDB::LensDB::GetSingleton().CleanUpDatabase())
282         {
283             std::cout << "Successful." << std::endl;
284         }
285         else
286         {
287             std::cout << "FAILED." << std::endl;
288         }
289     };
290     // remove lens
291     if (!lensToRemove.empty())
292     {
293         std::cout << "Removing lens \"" << lensToRemove << "\"..." << std::endl;
294         if (HuginBase::LensDB::LensDB::GetSingleton().RemoveLens(lensToRemove))
295         {
296             std::cout << "Successful." << std::endl;
297         }
298         else
299         {
300             std::cout << "FAILED." << std::endl;
301         }
302     };
303     // remove camera
304     if (!camToRemove.empty())
305     {
306         std::vector<std::string> input = hugin_utils::SplitString(camToRemove, "|");
307         if (input.size() == 2)
308         {
309             std::cout << "Removing camera \"" << input[1] << "\" (Maker: \"" << input[0] << "\")..." << std::endl;
310             if (HuginBase::LensDB::LensDB::GetSingleton().RemoveCamera(input[0], input[1]))
311             {
312                 std::cout << "Successful." << std::endl;
313             }
314             else
315             {
316                 std::cout << "FAILED." << std::endl;
317             }
318         }
319         else
320         {
321             std::cout << "\"" << camToRemove << "\" is not a valid string for the camera." << std::endl
322                 << "    Use syntax MAKER|MODEL (separate camera maker and model by |)" << std::endl;
323         };
324     };
325     // export database
326     if (!exportDatabase.empty())
327     {
328         std::cout << "Exporting database to \"" << exportDatabase << "\"..." << std::endl;
329         if (HuginBase::LensDB::LensDB::GetSingleton().ExportToFile(exportDatabase))
330         {
331             std::cout << "Successful." << std::endl;
332         }
333         else
334         {
335             std::cout << "FAILED." << std::endl;
336         };
337     };
338     // import database
339     if (!importDatabase.empty())
340     {
341         std::cout << "Importing data from \"" << importDatabase << "\"..." << std::endl;
342         if (HuginBase::LensDB::LensDB::GetSingleton().ImportFromFile(importDatabase))
343         {
344             std::cout << "Successful." << std::endl;
345         }
346         else
347         {
348             std::cout << "FAILED." << std::endl;
349         };
350     };
351     HuginBase::LensDB::LensDB::Clean();
352     return 0;
353 }
354