Hugintrunk  0.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
PapywizardImport.cpp
Go to the documentation of this file.
1 
9 /* This is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public
11 * License as published by the Free Software Foundation; either
12 * version 2 of the License, or (at your option) any later version.
13 *
14 * This software is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public
20 * License along with this software. If not, see
21 * <http://www.gnu.org/licenses/>.
22 *
23 */
24 
25 #include "panoinc_WX.h"
26 #include "PapywizardImport.h"
27 
28 #include "hugin_utils/utils.h"
29 #include <wx/xml/xml.h>
30 #include <wx/msgdlg.h>
31 #include <wx/stdpaths.h>
32 #include "hugin/huginApp.h"
33 #include "base_wx/CommandHistory.h"
34 #include "base_wx/PanoCommand.h"
35 #include "base_wx/platform.h"
36 #include "base_wx/wxutils.h"
38 #include "base_wx/wxPanoCommand.h"
40 #include "hugin/config_defaults.h"
41 
42 namespace Papywizard
43 {
44 
47 {
48 public:
49  // read from header
50  double focallength;
51  double cropfactor;
53  // read from shoot section
55  {
56  size_t id;
57  size_t bracket;
58  double yaw, pitch, roll;
59  };
60 
61  std::vector<PapywizardImage> images;
64  {
65  focallength = 0;
66  cropfactor = 1;
68  };
69 
70  bool HasBracketImages() const
71  {
72  for (size_t i = 0; i < images.size(); ++i)
73  {
74  if (images[i].bracket > 1)
75  {
76  return true;
77  };
78  };
79  return false;
80  };
81 };
82 
84 bool ParseHeader(wxXmlNode* root, PapywizardSettings& images)
85 {
86  wxXmlNode* child = root->GetChildren();
87  while (child)
88  {
89  // section <camera>
90  if (child->GetName().CmpNoCase(wxT("camera")) == 0)
91  {
92  wxXmlNode* camChild = child->GetChildren();
93  while (camChild)
94  {
95  // crop factor is saved as attribute in <sensor>
96  if (camChild->GetName().CmpNoCase(wxT("sensor")) == 0)
97  {
98  wxString number;
99  if (camChild->GetAttribute(wxT("coef"), &number))
100  {
101  if (!hugin_utils::stringToDouble(std::string(number.mb_str(wxConvLocal)), images.cropfactor))
102  {
103  return false;
104  };
105  };
106  }
107  camChild = camChild->GetNext();
108  }
109  }
110  // section <lens>
111  if (child->GetName().CmpNoCase(wxT("lens")) == 0)
112  {
113  // projection as type attribute
114  wxString projection = child->GetAttribute(wxT("type"), wxEmptyString).Trim().Trim(false);
115  if (!projection.empty())
116  {
117  if (projection.CmpNoCase(wxT("rectilinear")) == 0)
118  {
120  }
121  else
122  {
123  if (projection.CmpNoCase(wxT("fisheye")) == 0)
124  {
126  }
127  else
128  {
129  return false;
130  };
131  };
132  };
133  // focal length as own element
134  wxXmlNode* lensChild = child->GetChildren();
135  while (lensChild)
136  {
137  if (lensChild->GetName().CmpNoCase(wxT("focal")) == 0)
138  {
139  wxString focallength = lensChild->GetNodeContent().Trim().Trim(false);
140  if(!hugin_utils::stringToDouble(std::string(focallength.mb_str(wxConvLocal)), images.focallength))
141  {
142  return false;
143  };
144  }
145  lensChild = lensChild->GetNext();
146  }
147  };
148  child = child->GetNext();
149  };
150  return true;
151 };
152 
154 bool ParseShoot(wxXmlNode* root, PapywizardSettings& images)
155 {
156  wxXmlNode* child = root->GetChildren();
157  size_t id = 1;
158  while (child)
159  {
160  if (child->GetName().CmpNoCase(wxT("pict")) == 0)
161  {
163  long longVal;
164  wxString s;
165 #ifdef PAPYWIZARD_USE_ID_CHECK
166  // according to the spec the id should be unique
167  // but not all programs write the id in this way
168  // e.g. PCPano is using the same id for each image in a bracket
169  // see https://bugs.launchpad.net/hugin/+bug/1840110
170  // so the check id code is disabled for now
171  // so activate the check again define PAPYWIZARD_USE_ID_CHECK
172  if (!child->GetAttribute(wxT("id"), &s))
173  {
174  return false;
175  };
176  if (!s.ToLong(&longVal))
177  {
178  return false;
179  };
180  if (longVal < id)
181  {
182  return false;
183  };
184  image.id = longVal;
185 #else
186  // instead simply store a continous id
187  image.id = id;
188 #endif
189  ++id;
190  // read bracket attribute
191  if(!child->GetAttribute(wxT("bracket"), &s))
192  {
193  return false;
194  };
195  if (!s.ToLong(&longVal))
196  {
197  return false;
198  };
199  image.bracket = longVal;
200  // now parse all position entries
201  wxXmlNode* posChild = child->GetChildren();
202  while (posChild)
203  {
204  if (posChild->GetName().CmpNoCase(wxT("position")) == 0)
205  {
206  if (!posChild->GetAttribute(wxT("yaw"), &s))
207  {
208  return false;
209  };
210  if (!hugin_utils::stringToDouble(std::string(s.mb_str(wxConvLocal)), image.yaw))
211  {
212  return false;
213  };
214  if (!posChild->GetAttribute(wxT("pitch"), &s))
215  {
216  return false;
217  }
218  if (!hugin_utils::stringToDouble(std::string(s.mb_str(wxConvLocal)), image.pitch))
219  {
220  return false;
221  };
222  if (!posChild->GetAttribute(wxT("roll"), &s))
223  {
224  return false;
225  };
226  if (!hugin_utils::stringToDouble(std::string(s.mb_str(wxConvLocal)), image.roll))
227  {
228  return false;
229  };
230  images.images.push_back(image);
231  // we are ignoring all further entries
232  break;
233  };
234  posChild = posChild->GetNext();
235  };
236  };
237  child = child->GetNext();
238  };
239  return true;
240 };
241 
242 bool ParsePapywizardFile(const wxString& filename, PapywizardSettings& images)
243 {
244  wxXmlDocument xmlFile;
245  if (!xmlFile.Load(filename))
246  {
247  return false;
248  }
249  if (xmlFile.GetRoot()->GetName().CmpNoCase(wxT("papywizard")) != 0)
250  {
251  // not a papywizard file
252  return false;
253  };
254  // iterate all children
255  wxXmlNode* child = xmlFile.GetRoot()->GetChildren();
256  while (child)
257  {
258  if (child->GetName().CmpNoCase(wxT("header")) == 0)
259  {
260  if (!ParseHeader(child, images))
261  {
262  return false;
263  };
264  };
265  if (child->GetName().CmpNoCase(wxT("shoot")) == 0)
266  {
267  if (!ParseShoot(child, images))
268  {
269  return false;
270  };
271  }
272  child = child->GetNext();
273  }
274  return true;
275 };
276 
277 class PapywizardImportDialog: public wxDialog
278 {
279 public:
281  PapywizardImportDialog(wxWindow *parent)
282  {
283  wxXmlResource::Get()->LoadDialog(this, parent, wxT("papywizard_import_dialog"));
284 
285  m_linkPos = XRCCTRL(*this, "papywizard_link_positions", wxCheckBox);
286  m_cpfind = XRCCTRL(*this, "papywizard_cpfind", wxCheckBox);
287  m_cpfind->Bind(wxEVT_CHECKBOX, &PapywizardImportDialog::OnCpfindCheck, this);
288  m_cpfindParams = XRCCTRL(*this, "papywizard_cpfind_parameters", wxTextCtrl);
289  m_geocpset = XRCCTRL(*this, "papywizard_geocpset", wxCheckBox);
290  const wxString cpfindParams = wxConfig::Get()->Read(wxT("/PapywizardImportCpfind"), wxEmptyString);
291  m_cpfindParams->SetValue(cpfindParams);
292  RestoreFramePosition(this, wxT("PapywizardImportDialog"));
293  };
296  {
297  StoreFramePosition(this, wxT("PapywizardImportDialog"));
298  if (m_cpfind->IsChecked())
299  {
300  wxConfig::Get()->Write(wxT("/PapywizardImportCpfind"), m_cpfindParams->GetValue());
301  };
302  };
303  void EnableStack(const bool hasStacks)
304  {
305  m_linkPos->Enable(hasStacks);
306  m_linkPos->SetValue(hasStacks);
307  };
308  const bool LinkStacks() const
309  {
310  return m_linkPos->IsEnabled() && m_linkPos->IsChecked();
311  };
312  const bool RunCpfind() const
313  {
314  return m_cpfind->IsChecked();
315  };
316  const wxString GetCPFindParam() const
317  {
318  return m_cpfindParams->GetValue();
319  };
320  const bool RunGeocpset() const
321  {
322  return m_geocpset->IsEnabled() && m_geocpset->IsChecked();
323  };
324 protected:
325  void OnCpfindCheck(wxCommandEvent& e)
326  {
327  const bool cpfindActive = m_cpfind->IsChecked();
328  m_cpfindParams->Enable(cpfindActive);
329  m_geocpset->Enable(cpfindActive);
330  };
331 private:
332  wxCheckBox* m_linkPos;
333  wxCheckBox* m_cpfind;
334  wxTextCtrl* m_cpfindParams;
335  wxCheckBox* m_geocpset;
336 };
337 
338 bool ImportPapywizardFile(const wxString& filename, HuginBase::Panorama& pano)
339 {
340  PapywizardSettings papyImages;
341  if (!ParsePapywizardFile(filename, papyImages))
342  {
343  wxMessageBox(wxString::Format(_("Could not parse file %s as Papywizard XML file."), filename.c_str()),
344 #ifdef __WXMSW__
345  _("Hugin"),
346 #else
347  wxT(""),
348 #endif
349  wxOK);
350  return false;
351  };
352  // check if number of images matches
353  if(papyImages.images.size()!=pano.getNrOfImages())
354  {
355  wxMessageBox(wxString::Format(_("The current project does not match with the Papywizard xml file.\nThe Papywizard file \"%s\" contains %lu images, but the Hugin project contains %lu images."), filename.c_str(), static_cast<unsigned long>(papyImages.images.size()), static_cast<unsigned long>(pano.getNrOfImages())),
356 #ifdef __WXMSW__
357  _("Hugin"),
358 #else
359  wxT(""),
360 #endif
361  wxOK);
362  return false;
363  };
364  PapywizardImportDialog dialog(wxGetActiveWindow());
365  dialog.EnableStack(papyImages.HasBracketImages());
366  if(dialog.ShowModal()!=wxID_OK)
367  {
368  return false;
369  };
370  // now we can build all commands
371  std::vector<PanoCommand::PanoCommand *> commands;
372  HuginBase::UIntSet images;
373  fill_set(images, 0, pano.getNrOfImages() - 1);
374  // all images share the same lens
376  commands.push_back(new PanoCommand::ChangeImageProjectionCmd(pano, images, papyImages.projection));
377  if (papyImages.focallength > 0)
378  {
379  commands.push_back(new PanoCommand::UpdateCropFactorCmd(pano, images, papyImages.cropfactor));
380  commands.push_back(new PanoCommand::UpdateFocalLengthCmd(pano, images, papyImages.focallength));
381  };
382  // remove all existing stacks
383  for (size_t i = 1; i < pano.getNrOfImages(); i++)
384  {
385  HuginBase::UIntSet imgs;
386  imgs.insert(i);
388  };
389  // create stacks
390  if (papyImages.HasBracketImages())
391  {
392  size_t stackNr = 0;
393  size_t imgNr = 0;
394  while (imgNr < pano.getNrOfImages())
395  {
396  HuginBase::UIntSet imgs;
397  do
398  {
399  imgs.insert(imgNr);
400  imgNr++;
401  } while (imgNr < pano.getNrOfImages() && papyImages.images[imgNr].bracket != 1);
403  stackNr++;
404  };
405  // unlink position if wished by user
406  if (!dialog.LinkStacks())
407  {
408  std::set<HuginBase::ImageVariableGroup::ImageVariableEnum> variables;
409  variables.insert(HuginBase::ImageVariableGroup::IVE_Yaw);
410  variables.insert(HuginBase::ImageVariableGroup::IVE_Pitch);
411  variables.insert(HuginBase::ImageVariableGroup::IVE_Roll);
412  variables.insert(HuginBase::ImageVariableGroup::IVE_X);
413  variables.insert(HuginBase::ImageVariableGroup::IVE_Y);
414  variables.insert(HuginBase::ImageVariableGroup::IVE_Z);
415  variables.insert(HuginBase::ImageVariableGroup::IVE_TranslationPlaneYaw);
416  variables.insert(HuginBase::ImageVariableGroup::IVE_TranslationPlanePitch);
417  commands.push_back(new PanoCommand::ChangePartImagesLinkingCmd(pano, images, variables, false, HuginBase::StandardImageVariableGroups::getStackVariables()));
418  }
419  };
420  // now set the positions
421  HuginBase::VariableMapVector variables = pano.getVariables();
422  for (size_t i = 0; i < papyImages.images.size(); ++i)
423  {
424  map_get(variables[i], "y").setValue(papyImages.images[i].yaw);
425  map_get(variables[i], "p").setValue(papyImages.images[i].pitch);
426  map_get(variables[i], "r").setValue(papyImages.images[i].roll);
427  map_get(variables[i], "TrX").setValue(0);
428  map_get(variables[i], "TrY").setValue(0);
429  map_get(variables[i], "TrZ").setValue(0);
430  map_get(variables[i], "Tpy").setValue(0);
431  map_get(variables[i], "Tpp").setValue(0);
432  // fov gets updated when focal length is set, this has not yet been happen
433  // so remove fov from list to prevent overwritting with old value, because
434  // variables have not yet been updated yet
435  variables[i].erase("v");
436  };
437  commands.push_back(new PanoCommand::UpdateVariablesCmd(pano, variables));
439 
440  // now create the cp
441  if (dialog.RunCpfind())
442  {
443  //save project into temp directory
444  wxString tempDir = wxConfig::Get()->Read(wxT("tempDir"), wxT(""));
445  if (!tempDir.IsEmpty())
446  {
447  if (tempDir.Last() != wxFileName::GetPathSeparator())
448  {
449  tempDir.Append(wxFileName::GetPathSeparator());
450  }
451  };
452  wxFileName scriptFileName(wxFileName::CreateTempFileName(tempDir + wxT("hp")));
453  const std::string scriptString(scriptFileName.GetFullPath().mb_str(HUGIN_CONV_FILENAME));
454  pano.WritePTOFile(scriptString, hugin_utils::getPathPrefix(scriptString));
455  // build command queue
456  const wxFileName exePath(wxStandardPaths::Get().GetExecutablePath());
458  const wxString quotedProject(HuginQueue::wxEscapeFilename(scriptFileName.GetFullPath()));
459  commands->push_back(new HuginQueue::NormalCommand(HuginQueue::GetInternalProgram(exePath.GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR), wxT("cpfind")),
460  dialog.GetCPFindParam() + wxT(" --prealigned -o ") + quotedProject + wxT(" ") + quotedProject, _("Searching for control points...")));
461  if (dialog.RunGeocpset())
462  {
463  commands->push_back(new HuginQueue::NormalCommand(HuginQueue::GetInternalProgram(exePath.GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR), wxT("geocpset")),
464  wxT("-o ") + quotedProject + wxT(" ") + quotedProject, _("Connecting overlapping images")));
465  };
466  //execute queue
467  MyExecuteCommandQueue(commands, wxGetActiveWindow(), _("Searching control points"));
468  //read back panofile
470  (const char *)scriptFileName.GetFullPath().mb_str(HUGIN_CONV_FILENAME),
471  (const char *)scriptFileName.GetPath(wxPATH_NATIVE | wxPATH_GET_SEPARATOR).mb_str(HUGIN_CONV_FILENAME),
472  false, false));
473  //delete temporary files
474  wxRemoveFile(scriptFileName.GetFullPath());
475  };
476  return true;
477 }
478 
479 } // namespace Papywizard
480 
int MyExecuteCommandQueue(HuginQueue::CommandQueue *queue, wxWindow *parent, const wxString &title, const wxString &comment)
execute all commands in queue with redirection of output to frame and allow canceling the queue will ...
bool ImportPapywizardFile(const wxString &filename, HuginBase::Panorama &pano)
import the settings from given filename into pano
normal command for queue, processing is stopped if an error occurred in program
Definition: Executor.h:37
static const std::set< ConstImageVariableGroup::ImageVariableEnum > & getLensVariables()
Get the set of lens image variables.
PapywizardImportDialog(wxWindow *parent)
Constructor, read from xrc ressource.
implementation of huginApp Class
Change the linking of some variables across parts of an ImageVariableGroup containing some specified ...
Definition: PanoCommand.h:524
read settings from papywizard xml file
#define HUGIN_CONV_FILENAME
Definition: platform.h:40
wxString GetInternalProgram(const wxString &bindir, const wxString &name)
return path and name of external program, which comes bundled with Hugin
Definition: Executor.cpp:129
PapywizardSettings()
constructor, initialize some values
std::vector< PapywizardImage > images
PanoCommand to combine other PanoCommands.
Definition: PanoCommand.h:39
bool ParseShoot(wxXmlNode *root, PapywizardSettings &images)
parse shoot section of papywizard file
std::set< unsigned int > UIntSet
Definition: PanoramaData.h:51
std::vector< VariableMap > VariableMapVector
Model for a panorama.
Definition: Panorama.h:152
std::string getPathPrefix(const std::string &filename)
Get the path to a filename.
Definition: utils.cpp:184
HuginBase::SrcPanoImage::Projection projection
VariableMapVector getVariables() const
get variables of this panorama
Definition: Panorama.cpp:118
Make a new part in a ImageVariableGroup for a set of images, given the variables that make up the gro...
Definition: PanoCommand.h:591
bool ParsePapywizardFile(const wxString &filename, PapywizardSettings &images)
void OnCpfindCheck(wxCommandEvent &e)
std::size_t getNrOfImages() const
number of images.
Definition: Panorama.h:205
Map::mapped_type & map_get(Map &m, const typename Map::key_type &key)
get a map element.
Definition: stl_utils.h:98
Update the focal length.
Definition: PanoCommand.h:476
Switch the part number of an image.
Definition: PanoCommand.h:506
wxwindows specific panorama commands
~PapywizardImportDialog()
destructor, save settings
static GlobalCmdHist & getInstance()
void StoreFramePosition(wxTopLevelWindow *frame, const wxString &basename)
Store window size and position in configfile/registry.
Definition: wxutils.cpp:133
void addCommand(PanoCommand *command, bool execute=true)
Adds a command to the history.
bool stringToDouble(const STR &str_, double &dest)
convert a string to a double, ignore localisation.
Definition: utils.h:114
include file for the hugin project
str wxEscapeFilename(const str &arg)
special escaping routine for CommandQueues
Definition: Executor.h:79
bool WritePTOFile(const std::string &filename, const std::string &prefix="")
write data to given pto file
Definition: Panorama.cpp:2059
Update the crop factor.
Definition: PanoCommand.h:491
update all variables
Definition: PanoCommand.h:105
void fill_set(_Container &c, typename _Container::key_type begin, typename _Container::key_type end)
Definition: stl_utils.h:81
bool ParseHeader(wxXmlNode *root, PapywizardSettings &images)
parse header of papywizard file
dump the current project and load a new one.
Definition: wxPanoCommand.h:66
class which holds all read settings from a Papywizard xml file
static const std::set< ConstImageVariableGroup::ImageVariableEnum > & getStackVariables()
Get the set of stack image variables.
std::vector< NormalCommand * > CommandQueue
Definition: Executor.h:61
const wxString GetCPFindParam() const
void EnableStack(const bool hasStacks)
void RestoreFramePosition(wxTopLevelWindow *frame, const wxString &basename)
Restore window size and position from configfile/registry.
Definition: wxutils.cpp:65