FindPanoDialog.cpp

Go to the documentation of this file.
00001 // -*- c-basic-offset: 4 -*-
00002 
00011 /*  This is free software; you can redistribute it and/or
00012  *  modify it under the terms of the GNU General Public
00013  *  License as published by the Free Software Foundation; either
00014  *  version 2 of the License, or (at your option) any later version.
00015  *
00016  *  This software is distributed in the hope that it will be useful,
00017  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00018  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00019  *  Lesser General Public License for more details.
00020  *
00021  *  You should have received a copy of the GNU General Public
00022  *  License along with this software. If not, see
00023  *  <http://www.gnu.org/licenses/>.
00024  *
00025  */
00026 
00027 #include "FindPanoDialog.h"
00028 #include "base_wx/wxPlatform.h"
00029 #include "panoinc.h"
00030 #include "panodata/OptimizerSwitches.h"
00031 #include "PTBatcherGUI.h"
00032 #include "hugin_utils/alphanum.h"
00033 #include "hugin/config_defaults.h"
00034 #include "wx/mstream.h"
00035 #include "exiv2/exiv2.hpp"
00036 #include "exiv2/preview.hpp"
00037 #ifdef _WIN32
00038 #include <CommCtrl.h>
00039 #endif
00040 #include "base_wx/LensTools.h"
00041 #include "panodata/StandardImageVariableGroups.h"
00042 
00043 enum
00044 {
00045     ID_REMOVE_IMAGE = wxID_HIGHEST + 300,
00046     ID_SPLIT_PANOS = wxID_HIGHEST + 301
00047 };
00048 
00049 BEGIN_EVENT_TABLE(FindPanoDialog,wxDialog)
00050     EVT_BUTTON(XRCID("find_pano_close"), FindPanoDialog::OnButtonClose)
00051     EVT_BUTTON(XRCID("find_pano_select_dir"), FindPanoDialog::OnButtonChoose)
00052     EVT_BUTTON(XRCID("find_pano_start_stop"), FindPanoDialog::OnButtonStart)
00053     EVT_BUTTON(XRCID("find_pano_add_queue"), FindPanoDialog::OnButtonSend)
00054     EVT_LISTBOX(XRCID("find_pano_list"), FindPanoDialog::OnSelectPossiblePano)
00055     EVT_LIST_ITEM_RIGHT_CLICK(XRCID("find_pano_selected_thumbslist"), FindPanoDialog::OnListItemRightClick)
00056     EVT_MENU(ID_REMOVE_IMAGE, FindPanoDialog::OnRemoveImage)
00057     EVT_MENU(ID_SPLIT_PANOS, FindPanoDialog::OnSplitPanos)
00058     EVT_CLOSE(FindPanoDialog::OnClose)
00059 END_EVENT_TABLE()
00060 
00061 bool SortFilename::operator()(const HuginBase::SrcPanoImage* img1, const HuginBase::SrcPanoImage* img2)
00062 {
00063     return doj::alphanum_comp(img1->getFilename(),img2->getFilename())<0;
00064 };
00065 
00066 // thumbnail size currently set to 80x80
00067 #define THUMBSIZE 80
00068 
00069 FindPanoDialog::FindPanoDialog(BatchFrame* batchframe, wxString xrcPrefix)
00070 {
00071     // load our children. some children might need special
00072     // initialization. this will be done later.
00073     wxXmlResource::Get()->LoadDialog(this,batchframe,wxT("find_pano_dialog"));
00074 
00075 #ifdef __WXMSW__
00076     wxIcon myIcon(xrcPrefix+ wxT("data/ptbatcher.ico"),wxBITMAP_TYPE_ICO);
00077 #else
00078     wxIcon myIcon(xrcPrefix + wxT("data/ptbatcher.png"),wxBITMAP_TYPE_PNG);
00079 #endif
00080     SetIcon(myIcon);
00081     m_batchframe=batchframe;
00082     m_isRunning=false;
00083     m_stopped=false;
00084 
00085     m_button_start=XRCCTRL(*this,"find_pano_start_stop",wxButton);
00086     m_button_choose=XRCCTRL(*this,"find_pano_select_dir",wxButton);
00087     m_button_send=XRCCTRL(*this,"find_pano_add_queue",wxButton);
00088     m_button_close=XRCCTRL(*this,"find_pano_close",wxButton);
00089     m_textctrl_dir=XRCCTRL(*this,"find_pano_dir",wxTextCtrl);
00090     m_textctrl_dir->AutoCompleteDirectories();
00091     m_cb_subdir=XRCCTRL(*this,"find_pano_subdir",wxCheckBox);
00092     m_statustext=XRCCTRL(*this,"find_pano_label",wxStaticText);
00093     m_list_pano=XRCCTRL(*this,"find_pano_list",wxCheckListBox);
00094     m_ch_naming=XRCCTRL(*this,"find_pano_naming",wxChoice);
00095     m_cb_createLinks=XRCCTRL(*this,"find_pano_create_links",wxCheckBox);
00096     m_cb_loadDistortion=XRCCTRL(*this,"find_pano_load_distortion",wxCheckBox);
00097     m_cb_loadVignetting=XRCCTRL(*this,"find_pano_load_vignetting",wxCheckBox);
00098     m_sc_minNumberImages=XRCCTRL(*this, "find_pano_min_number_images", wxSpinCtrl);
00099     m_sc_maxTimeDiff=XRCCTRL(*this, "find_pano_max_time_diff", wxSpinCtrl);
00100     m_ch_blender = XRCCTRL(*this, "find_pano_default_blender", wxChoice);
00101     FillBlenderList(m_ch_blender);
00102 
00103     //set parameters
00104     wxConfigBase* config = wxConfigBase::Get();
00105     // restore position and size
00106     int dx,dy;
00107     wxDisplaySize(&dx,&dy);
00108     bool maximized = config->Read(wxT("/FindPanoDialog/maximized"), 0l) != 0;
00109     if (maximized)
00110     {
00111         this->Maximize();
00112     }
00113     else
00114     {
00115         //size
00116         int w = config->Read(wxT("/FindPanoDialog/width"),-1l);
00117         int h = config->Read(wxT("/FindPanoDialog/height"),-1l);
00118         if (w > 0 && w <= dx)
00119         {
00120             this->SetClientSize(w,h);
00121         }
00122         else
00123         {
00124             this->Fit();
00125         }
00126         //position
00127         int x = config->Read(wxT("/FindPanoDialog/positionX"),-1l);
00128         int y = config->Read(wxT("/FindPanoDialog/positionY"),-1l);
00129         if ( y >= 0 && x >= 0 && x < dx && y < dy)
00130         {
00131             this->Move(x, y);
00132         }
00133         else
00134         {
00135             this->Move(0, 44);
00136         }
00137     }
00138     long splitterPos = config->Read(wxT("/FindPanoDialog/splitterPos"), -1l);
00139     if (splitterPos != -1)
00140     {
00141         XRCCTRL(*this, "find_pano_splitter", wxSplitterWindow)->SetSashPosition(splitterPos);
00142     };
00143     wxString path=config->Read(wxT("/FindPanoDialog/actualPath"),wxEmptyString);
00144     if(!path.IsEmpty())
00145     {
00146         m_textctrl_dir->SetValue(path);
00147     }
00148     bool val;
00149     config->Read(wxT("/FindPanoDialog/includeSubDirs"),&val,false);
00150     m_cb_subdir->SetValue(val);
00151     long i=config->Read(wxT("/FindPanoDialog/Naming"),0l);
00152     m_ch_naming->SetSelection(i);
00153     config->Read(wxT("/FindPanoDialog/linkStacks"),&val,true);
00154     m_cb_createLinks->SetValue(val);
00155     config->Read(wxT("/FindPanoDialog/loadDistortion"),&val,false);
00156     m_cb_loadDistortion->SetValue(val);
00157     config->Read(wxT("/FindPanoDialog/loadVignetting"),&val,false);
00158     m_cb_loadVignetting->SetValue(val);
00159     i=config->Read(wxT("/FindPanoDialog/MinNumberImages"), 2l);
00160     m_sc_minNumberImages->SetValue(i);
00161     i=config->Read(wxT("/FindPanoDialog/MaxTimeDiff"), 30l);
00162     m_sc_maxTimeDiff->SetValue(i);
00163     i = config->Read(wxT("/FindPanoDialog/DefaultBlender"), static_cast<long>(HuginBase::PanoramaOptions::ENBLEND_BLEND));
00164     SelectListValue(m_ch_blender, i);
00165     m_button_send->Disable();
00166     m_thumbs = new wxImageList(THUMBSIZE, THUMBSIZE, true, 0);
00167     m_thumbsList = XRCCTRL(*this, "find_pano_selected_thumbslist", wxListCtrl);
00168     m_thumbsList->SetImageList(m_thumbs, wxIMAGE_LIST_NORMAL);
00169 #ifdef _WIN32
00170     // default image spacing is too big, wxWidgets does not provide direct 
00171     // access to the spacing, so using the direct API function
00172     ListView_SetIconSpacing(m_thumbsList->GetHandle(), THUMBSIZE + 20, THUMBSIZE + 20);
00173 #endif
00174 };
00175 
00176 FindPanoDialog::~FindPanoDialog()
00177 {
00178     wxConfigBase* config=wxConfigBase::Get();
00179     if(!this->IsMaximized())
00180     {
00181         wxSize sz = this->GetClientSize();
00182         config->Write(wxT("/FindPanoDialog/width"), sz.GetWidth());
00183         config->Write(wxT("/FindPanoDialog/height"), sz.GetHeight());
00184         wxPoint ps = this->GetPosition();
00185         config->Write(wxT("/FindPanoDialog/positionX"), ps.x);
00186         config->Write(wxT("/FindPanoDialog/positionY"), ps.y);
00187         config->Write(wxT("/FindPanoDialog/maximized"), 0);
00188     }
00189     else
00190     {
00191         config->Write(wxT("/FindPanoDialog/maximized"), 1l);
00192     };
00193     config->Write(wxT("/FindPanoDialog/splitterPos"), XRCCTRL(*this, "find_pano_splitter", wxSplitterWindow)->GetSashPosition());
00194     config->Write(wxT("/FindPanoDialog/actualPath"),m_textctrl_dir->GetValue());
00195     config->Write(wxT("/FindPanoDialog/includeSubDirs"),m_cb_subdir->GetValue());
00196     config->Write(wxT("/FindPanoDialog/Naming"),m_ch_naming->GetSelection());
00197     config->Write(wxT("/FindPanoDialog/linkStacks"),m_cb_createLinks->GetValue());
00198     config->Write(wxT("/FindPanoDialog/loadDistortion"),m_cb_loadDistortion->GetValue());
00199     config->Write(wxT("/FindPanoDialog/loadVignetting"),m_cb_loadDistortion->GetValue());
00200     config->Write(wxT("/FindPanoDialog/MinNumberImages"), m_sc_minNumberImages->GetValue());
00201     config->Write(wxT("/FindPanoDialog/MaxTimeDiff"), m_sc_maxTimeDiff->GetValue());
00202     config->Write(wxT("/FindPanoDialog/DefaultBlender"), static_cast<long>(GetSelectedValue(m_ch_blender)));
00203     CleanUpPanolist();
00204     delete m_thumbs;
00205 };
00206 
00207 void FindPanoDialog::CleanUpPanolist()
00208 {
00209     if(m_panos.size()>0)
00210     {
00211         while(!m_panos.empty())
00212         {
00213             delete m_panos.back();
00214             m_panos.pop_back();
00215         };
00216     };
00217 };
00218 
00219 //prevent closing window when running detection
00220 void FindPanoDialog::OnClose(wxCloseEvent& e)
00221 {
00222     if(e.CanVeto() && m_isRunning)
00223     {
00224         wxBell();
00225         e.Veto();
00226     }
00227     else
00228     {
00229         e.Skip();
00230     };
00231 };
00232 
00233 void FindPanoDialog::OnButtonClose(wxCommandEvent& e)
00234 {
00235     if(m_panos.size()>0)
00236     {
00237         if(wxMessageBox(_("The list contains possibly unprocessed panoramas.\nIf you close the dialog, you will lose them.\nContinue anyway?"),
00238                         _("Question"),wxYES_NO|wxICON_QUESTION,this)==wxNO)
00239         {
00240             return;
00241         };
00242     };
00243     this->Close();
00244 };
00245 
00246 void FindPanoDialog::OnButtonChoose(wxCommandEvent& e)
00247 {
00248     wxDirDialog dlg(this, _("Specify a directory to search for projects in"),
00249                     m_textctrl_dir->GetValue(), wxDD_DEFAULT_STYLE | wxDD_DIR_MUST_EXIST);
00250     if (dlg.ShowModal()==wxID_OK)
00251     {
00252         m_textctrl_dir->SetValue(dlg.GetPath());
00253     };
00254 };
00255 
00256 void FindPanoDialog::OnButtonStart(wxCommandEvent& e)
00257 {
00258     if(m_isRunning)
00259     {
00260         //stop detection
00261         m_stopped=true;
00262         m_button_start->SetLabel(_("Accepted"));
00263     }
00264     else
00265     {
00266         //start detection
00267         m_start_dir=m_textctrl_dir->GetValue();
00268         if(wxDir::Exists(m_start_dir))
00269         {
00270             if(m_panos.size()>0)
00271             {
00272                 if(wxMessageBox(_("The list contains still not yet processed panoramas.\nIf you continue, they will be disregarded.\nDo you still want to continue?"),
00273                                 _("Question"),wxYES_NO|wxICON_QUESTION,this)==wxNO)
00274                 {
00275                     return;
00276                 };
00277             };
00278             m_isRunning=true;
00279             m_stopped=false;
00280             //deactivate TIFF warning message boxes
00281             m_oldtiffwarning=TIFFSetWarningHandler(NULL);
00282             m_button_start->SetLabel(_("Stop"));
00283             CleanUpPanolist();
00284             m_list_pano->Clear();
00285             wxCommandEvent dummy;
00286             OnSelectPossiblePano(dummy);
00287             EnableButtons(false);
00288             SearchInDir(m_start_dir,m_cb_subdir->GetValue(), m_cb_loadDistortion->GetValue(), m_cb_loadVignetting->GetValue(), 
00289                 m_sc_minNumberImages->GetValue(), m_sc_maxTimeDiff->GetValue());
00290         }
00291         else
00292         {
00293             wxMessageBox(wxString::Format(_("Directory %s does not exist.\nPlease give an existing directory."),m_start_dir.c_str()),
00294                          _("Warning"),wxOK | wxICON_EXCLAMATION,this);
00295         };
00296     };
00297 }
00298 
00299 void FindPanoDialog::OnButtonSend(wxCommandEvent& e)
00300 {
00301     if(m_panos.size()==0)
00302     {
00303         return;
00304     }
00305     unsigned int nr=0;
00306     for(unsigned int i=0; i<m_list_pano->GetCount(); i++)
00307     {
00308         if(m_list_pano->IsChecked(i))
00309         {
00310             nr++;
00311         };
00312     };
00313     if(nr==0)
00314     {
00315         wxMessageBox(_("You have selected no possible panorama.\nPlease select at least one panorama and try again."),_("Warning"),wxOK|wxICON_EXCLAMATION,this);
00316         return;
00317     }
00318     bool failed=false;
00319     bool createLinks=m_cb_createLinks->GetValue();
00320     for(unsigned int i=0; i<m_list_pano->GetCount(); i++)
00321     {
00322         if(m_list_pano->IsChecked(i))
00323         {
00324             wxString filename=m_panos[i]->GeneratePanorama((PossiblePano::NamingConvention)(m_ch_naming->GetSelection()),createLinks, 
00325                 static_cast<HuginBase::PanoramaOptions::BlendingMechanism>(GetSelectedValue(m_ch_blender)));
00326             if(!filename.IsEmpty())
00327             {
00328                 m_batchframe->AddToList(filename,Project::DETECTING);
00329             }
00330             else
00331             {
00332                 failed=true;
00333             };
00334         };
00335     };
00336     if(failed)
00337     {
00338         wxMessageBox(_("Not all project files could be written successfully.\nMaybe you have no write permission for these directories or your disc is full."),_("Error"),wxOK,this);
00339     };
00340     this->Close();
00341 };
00342 
00343 void FindPanoDialog::EnableButtons(const bool state)
00344 {
00345     m_textctrl_dir->Enable(state);
00346     m_button_choose->Enable(state);
00347     m_cb_subdir->Enable(state);
00348     m_ch_naming->Enable(state);
00349     m_cb_createLinks->Enable(state);
00350     m_button_close->Enable(state);
00351     m_button_send->Enable(state);
00352 };
00353 
00354 void FindPanoDialog::OnSelectPossiblePano(wxCommandEvent &e)
00355 {
00356     int selected = m_list_pano->GetSelection();
00357     if (selected != wxNOT_FOUND)
00358     {
00359         XRCCTRL(*this, "find_pano_selected_cam", wxStaticText)->SetLabel(m_panos[selected]->GetCameraName());
00360         XRCCTRL(*this, "find_pano_selected_lens", wxStaticText)->SetLabel(m_panos[selected]->GetLensName());
00361         XRCCTRL(*this, "find_pano_selected_focallength", wxStaticText)->SetLabel(m_panos[selected]->GetFocalLength());
00362         XRCCTRL(*this, "find_pano_selected_date_time", wxStaticText)->SetLabel(m_panos[selected]->GetStartString() + wxT(" (")+ m_panos[selected]->GetDuration() + wxT(")"));
00363         m_panos[selected]->PopulateListCtrl(m_thumbsList, m_thumbs);
00364     }
00365     else
00366     {
00367         XRCCTRL(*this, "find_pano_selected_cam", wxStaticText)->SetLabel(wxEmptyString);
00368         XRCCTRL(*this, "find_pano_selected_lens", wxStaticText)->SetLabel(wxEmptyString);
00369         XRCCTRL(*this, "find_pano_selected_focallength", wxStaticText)->SetLabel(wxEmptyString);
00370         XRCCTRL(*this, "find_pano_selected_date_time", wxStaticText)->SetLabel(wxEmptyString);
00371         m_thumbsList->DeleteAllItems();
00372         m_thumbs->RemoveAll();
00373     };
00374 };
00375 
00376 void FindPanoDialog::OnListItemRightClick(wxListEvent &e)
00377 {
00378     // build menu
00379     wxMenu contextMenu;
00380     const int selectedPano = m_list_pano->GetSelection();
00381     if (m_panos[selectedPano]->GetImageCount() > 2)
00382     {
00383         contextMenu.Append(ID_REMOVE_IMAGE, _("Remove image from project"));
00384     };
00385     long imageIndex = -1;
00386     imageIndex = m_thumbsList->GetNextItem(imageIndex, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
00387     if(imageIndex > 1 && imageIndex <= static_cast<long>(m_panos[selectedPano]->GetImageCount()) - 2)
00388     {
00389         contextMenu.Append(ID_SPLIT_PANOS, _("Split here into two panoramas"));
00390     }
00391     // show popup menu
00392     if (contextMenu.GetMenuItemCount() > 0)
00393     {
00394         PopupMenu(&contextMenu);
00395     };
00396 };
00397 
00398 void FindPanoDialog::OnRemoveImage(wxCommandEvent &e)
00399 {
00400     const int selectedPano = m_list_pano->GetSelection();
00401     if (selectedPano != wxNOT_FOUND)
00402     {
00403         if (m_panos[selectedPano]->GetImageCount() > 2)
00404         {
00405             long imageIndex = -1;
00406             imageIndex = m_thumbsList->GetNextItem(imageIndex, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
00407             if (imageIndex != wxNOT_FOUND)
00408             {
00409                 // remove image from possible pano
00410                 m_panos[selectedPano]->RemoveImage(imageIndex);
00411                 // now remove from the wxListCtrl
00412                 m_thumbsList->DeleteItem(imageIndex);
00413                 // update the pano list
00414                 m_list_pano->SetString(selectedPano, m_panos[selectedPano]->GetItemString(m_start_dir));
00415                 // update the labels above
00416                 wxCommandEvent dummy;
00417                 OnSelectPossiblePano(dummy);
00418             };
00419         };
00420     };
00421 };
00422 
00423 void FindPanoDialog::OnSplitPanos(wxCommandEvent &e)
00424 {
00425     const int selectedPano = m_list_pano->GetSelection();
00426     if (selectedPano != wxNOT_FOUND)
00427     {
00428         long imageIndex = -1;
00429         imageIndex = m_thumbsList->GetNextItem(imageIndex, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
00430         if (imageIndex != wxNOT_FOUND)
00431         {
00432             if (imageIndex > 1 && imageIndex <= static_cast<long>(m_panos[selectedPano]->GetImageCount()) - 2)
00433             {
00434                 // do split
00435                 PossiblePano* newSubPano = m_panos[selectedPano]->SplitPano(imageIndex);
00436                 if (newSubPano->GetImageCount() > 0)
00437                 {
00438                     // insert new pano into internal list
00439                     m_panos.insert(m_panos.begin() + selectedPano + 1, newSubPano);
00440                     // update pano list
00441                     m_list_pano->SetString(selectedPano, m_panos[selectedPano]->GetItemString(m_start_dir));
00442                     int newItem = m_list_pano->Insert(m_panos[selectedPano + 1]->GetItemString(m_start_dir), selectedPano + 1);
00443                     m_list_pano->Check(newItem, true);
00444                     // update display
00445                     wxCommandEvent dummy;
00446                     OnSelectPossiblePano(dummy);
00447                 }
00448                 else
00449                 {
00450                     wxBell();
00451                     delete newSubPano;
00452                 };
00453             };
00454         };
00455     };
00456 }
00457 
00458 int SortWxFilenames(const wxString& s1,const wxString& s2)
00459 {
00460     return doj::alphanum_comp(std::string(s1.mb_str(wxConvLocal)),std::string(s2.mb_str(wxConvLocal)));
00461 };
00462 
00463 void FindPanoDialog::SearchInDir(wxString dirstring, const bool includeSubdir, const bool loadDistortion, const bool loadVignetting, const size_t minNumberImages, const size_t maxTimeDiff)
00464 {
00465     std::vector<PossiblePano*> newPanos;
00466     wxTimeSpan max_diff(0, 0, maxTimeDiff, 0); 
00467     wxString filename;
00468     wxArrayString fileList;
00469     wxDir::GetAllFiles(dirstring,&fileList,wxEmptyString,wxDIR_FILES|wxDIR_HIDDEN);
00470     fileList.Sort(SortWxFilenames);
00471     //map for caching projection information to prevent reading from database for each image
00472     for(size_t j=0; j<fileList.size() && !m_stopped; j++)
00473     {
00474         m_statustext->SetLabel(wxString::Format(_("Reading file %s"),fileList[j].c_str()));
00475         wxFileName file(fileList[j]);
00476         file.MakeAbsolute();
00477         wxString ext=file.GetExt();
00478         if(ext.CmpNoCase(wxT("jpg"))==0 || ext.CmpNoCase(wxT("jpeg"))==0 ||
00479                 ext.CmpNoCase(wxT("tif"))==0 || ext.CmpNoCase(wxT("tiff"))==0)
00480         {
00481             std::string filenamestr(file.GetFullPath().mb_str(HUGIN_CONV_FILENAME));
00482             HuginBase::SrcPanoImage* img = new HuginBase::SrcPanoImage;
00483             img->setFilename(filenamestr);
00484             img->readEXIF();
00485             // check for black/white images, if so skip
00486             const HuginBase::FileMetaData& metadata = img->getFileMetadata();
00487             HuginBase::FileMetaData::const_iterator it = metadata.find("pixeltype");
00488             if (it != metadata.end())
00489             {
00490                 if (it->second == "BILEVEL")
00491                 { 
00492                     wxGetApp().Yield(true);
00493                     continue;
00494                 };
00495             };
00496             img->applyEXIFValues();
00497             if(!img->getExifMake().empty() && !img->getExifModel().empty() && 
00498                 img->getExifFocalLength()!=0 && img->getCropFactor()!=0)
00499             {
00500                 img->readProjectionFromDB();
00501                 if(loadDistortion)
00502                 {
00503                     img->readDistortionFromDB();
00504                 };
00505                 if(loadVignetting)
00506                 {
00507                     img->readVignettingFromDB();
00508                 };
00509                 bool found=false;
00510                 for(unsigned int i=0; i<newPanos.size() && !m_stopped && !found; i++)
00511                 {
00512                     //compare with all other image groups
00513                     if(newPanos[i]->BelongsTo(img,max_diff))
00514                     {
00515                         newPanos[i]->AddSrcPanoImage(img);
00516                         found=true;
00517                     };
00518                     if(i%10==0)
00519                     {
00520                         wxGetApp().Yield(true);
00521                     };
00522                 };
00523                 if(!found)
00524                 {
00525                     PossiblePano* newPano=new PossiblePano();
00526                     newPano->AddSrcPanoImage(img);
00527                     newPanos.push_back(newPano);
00528                 };
00529             }
00530             else
00531             {
00532                 //could not read exif infos, disregard this image
00533                 delete img;
00534             };
00535         };
00536         //allow processing events
00537         wxGetApp().Yield(true);
00538     };
00539     if(!m_stopped && newPanos.size()>0)
00540     {
00541         for(size_t i=0; i<newPanos.size(); i++)
00542         {
00543             if(newPanos[i]->GetImageCount()>=minNumberImages)
00544             {
00545                 m_panos.push_back(newPanos[i]);
00546                 int newItem=m_list_pano->Append(m_panos[m_panos.size()-1]->GetItemString(m_start_dir));
00547                 m_list_pano->Check(newItem,true);
00548             }
00549             else
00550             {
00551                 delete newPanos[i];
00552             };
00553         };
00554     };
00555 
00556     if(includeSubdir && !m_stopped)
00557     {
00558         //now we go into all directories
00559         wxDir dir(dirstring);
00560         bool cont=dir.GetFirst(&filename,wxEmptyString,wxDIR_DIRS);
00561         while(cont && !m_stopped)
00562         {
00563             SearchInDir(dir.GetName()+wxFileName::GetPathSeparator()+filename,includeSubdir, loadDistortion, loadVignetting, minNumberImages, maxTimeDiff);
00564             cont=dir.GetNext(&filename);
00565         }
00566     };
00567     if(m_start_dir.Cmp(dirstring)==0)
00568     {
00569         m_stopped=false;
00570         m_isRunning=false;
00571         m_button_start->SetLabel(_("Start"));
00572         EnableButtons(true);
00573         //enable send button if at least one panorama found
00574         m_button_send->Enable(m_panos.size()>0);
00575         if(m_panos.size()>0)
00576         {
00577             m_statustext->SetLabel(wxString::Format(_("Found %d possible panoramas."), static_cast<int>(m_panos.size())));
00578         }
00579         else
00580         {
00581             m_statustext->SetLabel(_("No possible panoramas found."));
00582         };
00583         TIFFSetWarningHandler(m_oldtiffwarning);
00584     };
00585 };
00586 
00587 PossiblePano::~PossiblePano()
00588 {
00589     if(!m_images.empty())
00590     {
00591         for(ImageSet::reverse_iterator it=m_images.rbegin(); it!=m_images.rend(); ++it)
00592         {
00593             delete (*it);
00594         }
00595     };
00596 };
00597 
00598 bool PossiblePano::BelongsTo(HuginBase::SrcPanoImage* img, const wxTimeSpan max_time_diff)
00599 {
00600     if(m_make.compare(img->getExifMake())!=0)
00601     {
00602         return false;
00603     }
00604     if(m_camera.compare(img->getExifModel())!=0)
00605     {
00606         return false;
00607     }
00608     if(m_lens.compare(img->getExifLens())!=0)
00609     {
00610         return false;
00611     }
00612     if(fabs(m_focallength-img->getExifFocalLength())>0.01)
00613     {
00614         return false;
00615     }
00616     if(m_size!=img->getSize())
00617     {
00618         return false;
00619     }
00620     if(!GetDateTime(img).IsBetween(m_dt_start-max_time_diff,m_dt_end+max_time_diff))
00621     {
00622         return false;
00623     };
00624     return true;
00625 };
00626 
00627 const wxDateTime PossiblePano::GetDateTime(const HuginBase::SrcPanoImage* img)
00628 {
00629     struct tm exifdatetime;
00630     if(img->getExifDateTime(&exifdatetime)==0)
00631     {
00632         return wxDateTime(exifdatetime);
00633     }
00634     else
00635     {
00636         wxFileName file(wxString(img->getFilename().c_str(),HUGIN_CONV_FILENAME));
00637         return file.GetModificationTime();
00638     };
00639 };
00640 
00641 void PossiblePano::AddSrcPanoImage(HuginBase::SrcPanoImage* img)
00642 {
00643     if(m_images.empty())
00644     {
00645         //fill all values from first image
00646         m_make=img->getExifMake();
00647         m_camera=img->getExifModel();
00648         m_lens=img->getExifLens();
00649         m_focallength=img->getExifFocalLength();
00650         m_size=img->getSize();
00651         m_dt_start=GetDateTime(img);
00652         m_dt_end=m_dt_start;
00653     }
00654     else
00655     {
00656         wxDateTime dt=GetDateTime(img);
00657         if(dt.IsEarlierThan(m_dt_start))
00658         {
00659             m_dt_start=dt;
00660         }
00661         if(dt.IsLaterThan(m_dt_end))
00662         {
00663             m_dt_end=dt;
00664         };
00665     };
00666     m_images.insert(img);
00667 };
00668 
00669 const wxString PossiblePano::GetFilestring(const wxString BasePath, const bool stripExtension) const
00670 {
00671     ImageSet::const_iterator it=m_images.begin();
00672     wxFileName f1(wxString((*it)->getFilename().c_str(),HUGIN_CONV_FILENAME));
00673     f1.MakeRelativeTo(BasePath);
00674     ImageSet::const_reverse_iterator rit=m_images.rbegin();
00675     wxFileName f2(wxString((*rit)->getFilename().c_str(),HUGIN_CONV_FILENAME));
00676     if(stripExtension)
00677     {
00678         return f1.GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR)+f1.GetName()+wxT("-")+f2.GetName();
00679     }
00680     else
00681     {
00682         return f1.GetFullPath()+wxT(" - ")+f2.GetFullName();
00683     };
00684 };
00685 
00686 const wxString PossiblePano::GetItemString(const wxString BasePath) const
00687 {
00688     return wxString::Format(_("%d images: %s"), static_cast<int>(m_images.size()), GetFilestring(BasePath).c_str());
00689 };
00690 
00691 bool PossiblePano::GetNewProjectFilename(NamingConvention nc,const wxString basePath, wxFileName& projectFile)
00692 {
00693     wxString mask;
00694     unsigned int i=1;
00695     projectFile.SetPath(basePath);
00696     projectFile.SetName(wxT("pano"));
00697     projectFile.SetExt(wxT("pto"));
00698     if(!projectFile.IsDirWritable())
00699     {
00700         return false;
00701     };
00702     switch(nc)
00703     {
00704         case NAMING_PANO:
00705             mask=wxT("panorama%d");
00706             break;
00707         case NAMING_FIRST_LAST:
00708             mask=GetFilestring(basePath,true);
00709             projectFile.SetName(mask);
00710             if(!projectFile.FileExists())
00711             {
00712                 return true;
00713             };
00714             mask=mask+wxT("_%d");
00715             break;
00716         case NAMING_FOLDER:
00717             {
00718                 wxArrayString folders=projectFile.GetDirs();
00719                 if(folders.GetCount()==0)
00720                 {
00721                     return false;
00722                 }
00723                 mask=folders.Last();
00724                 projectFile.SetName(mask);
00725                 if(!projectFile.FileExists())
00726                 {
00727                     return true;
00728                 }
00729                 mask=mask+wxT("_%d");
00730             }
00731             break;
00732         case NAMING_TEMPLATE:
00733             {
00734                 HuginBase::Panorama tempPano;
00735                 tempPano.addImage(**m_images.begin());
00736                 tempPano.addImage(**m_images.rbegin());
00737                 wxFileName newProject(getDefaultProjectName(tempPano));
00738                 mask=newProject.GetName();
00739                 projectFile.SetName(mask);
00740                 if(!projectFile.FileExists())
00741                 {
00742                     return true;
00743                 }
00744                 mask=mask+wxT("_%d");
00745             };
00746             break;
00747         default:
00748             mask=wxT("panorama%d");
00749     };
00750 
00751     projectFile.SetName(wxString::Format(mask,i));
00752     while(projectFile.FileExists())
00753     {
00754         i++;
00755         projectFile.SetName(wxString::Format(mask,i));
00756         //security fall through
00757         if(i>1000)
00758         {
00759             return false;
00760         };
00761     }
00762     return true;
00763 };
00764 
00765 wxString PossiblePano::GeneratePanorama(NamingConvention nc, bool createLinks, HuginBase::PanoramaOptions::BlendingMechanism defaultBlender)
00766 {
00767     if(m_images.empty())
00768     {
00769         return wxEmptyString;
00770     };
00771     ImageSet::const_iterator it=m_images.begin();
00772     wxFileName firstFile(wxString((*it)->getFilename().c_str(),HUGIN_CONV_FILENAME));
00773     firstFile.MakeAbsolute();
00774     wxFileName projectFile;
00775     if(!GetNewProjectFilename(nc,firstFile.GetPath(),projectFile))
00776     {
00777         return wxEmptyString;
00778     };
00779     //generate panorama
00780     HuginBase::Panorama pano;
00781     for(ImageSet::iterator it=m_images.begin(); it!=m_images.end(); ++it)
00782     {
00783         pano.addImage(*(*it));
00784     };
00785     //assign all images the same lens number
00786     HuginBase::StandardImageVariableGroups variable_groups(pano);
00787     HuginBase::ImageVariableGroup& lenses = variable_groups.getLenses();
00788     if(pano.getNrOfImages()>1)
00789     {
00790         double redBalanceAnchor=pano.getImage(pano.getOptions().colorReferenceImage).getExifRedBalance();
00791         double blueBalanceAnchor=pano.getImage(pano.getOptions().colorReferenceImage).getExifBlueBalance();
00792         if(fabs(redBalanceAnchor)<1e-2)
00793         {
00794             redBalanceAnchor=1;
00795         };
00796         if(fabs(blueBalanceAnchor)<1e-2)
00797         {
00798             blueBalanceAnchor=1;
00799         };
00800         for(unsigned int i=1; i<pano.getNrOfImages(); i++)
00801         {
00802             HuginBase::SrcPanoImage img = pano.getSrcImage(i);
00803             double ev=img.getExposureValue();
00804             lenses.switchParts(i,lenses.getPartNumber(0));
00805             lenses.unlinkVariableImage(HuginBase::ImageVariableGroup::IVE_ExposureValue, i);
00806             img.setExposureValue(ev);
00807             lenses.unlinkVariableImage(HuginBase::ImageVariableGroup::IVE_WhiteBalanceRed, i);
00808             lenses.unlinkVariableImage(HuginBase::ImageVariableGroup::IVE_WhiteBalanceBlue, i);
00809             img.setWhiteBalanceRed(img.getExifRedBalance()/redBalanceAnchor);
00810             img.setWhiteBalanceBlue(img.getExifBlueBalance()/blueBalanceAnchor);
00811             pano.setSrcImage(i, img);
00812         };
00813     };
00814     if (pano.hasPossibleStacks())
00815     {
00816         pano.linkPossibleStacks(createLinks);
00817     };
00818     // Setup pano with options from preferences
00819     HuginBase::PanoramaOptions opts = pano.getOptions();
00820     //set default exposure value
00821     opts.outputExposureValue = pano.getImage(0).getExposureValue();
00822     wxConfigBase* config = wxConfigBase::Get();
00823     opts.quality = config->Read(wxT("/output/jpeg_quality"),HUGIN_JPEG_QUALITY);
00824     switch(config->Read(wxT("/output/tiff_compression"), HUGIN_TIFF_COMPRESSION))
00825     {
00826         case 0:
00827         default:
00828             opts.outputImageTypeCompression = "NONE";
00829             opts.tiffCompression = "NONE";
00830             break;
00831         case 1:
00832             opts.outputImageTypeCompression = "PACKBITS";
00833             opts.tiffCompression = "PACKBITS";
00834             break;
00835         case 2:
00836             opts.outputImageTypeCompression = "LZW";
00837             opts.tiffCompression = "LZW";
00838             break;
00839         case 3:
00840             opts.outputImageTypeCompression = "DEFLATE";
00841             opts.tiffCompression = "DEFLATE";
00842             break;
00843     }
00844     switch (config->Read(wxT("/output/ldr_format"), HUGIN_LDR_OUTPUT_FORMAT))
00845     {
00846         case 1:
00847             opts.outputImageType ="jpg";
00848             break;
00849         case 2:
00850             opts.outputImageType ="png";
00851             break;
00852         case 3:
00853             opts.outputImageType ="exr";
00854             break;
00855         default:
00856         case 0:
00857             opts.outputImageType ="tif";
00858             break;
00859     }
00860     opts.outputFormat = HuginBase::PanoramaOptions::TIFF_m;
00861     opts.blendMode = defaultBlender;
00862     opts.enblendOptions = config->Read(wxT("Enblend/Args"),wxT(HUGIN_ENBLEND_ARGS)).mb_str(wxConvLocal);
00863     opts.enfuseOptions = config->Read(wxT("Enfuse/Args"),wxT(HUGIN_ENFUSE_ARGS)).mb_str(wxConvLocal);
00864     opts.interpolator = (vigra_ext::Interpolator)config->Read(wxT("Nona/Interpolator"),HUGIN_NONA_INTERPOLATOR);
00865     opts.tiff_saveROI = config->Read(wxT("Nona/CroppedImages"),HUGIN_NONA_CROPPEDIMAGES)!=0;
00866     opts.hdrMergeMode = HuginBase::PanoramaOptions::HDRMERGE_AVERAGE;
00867     opts.hdrmergeOptions = HUGIN_HDRMERGE_ARGS;
00868     opts.verdandiOptions = config->Read(wxT("/VerdandiDefaultArgs"), wxEmptyString).mb_str(wxConvLocal);
00869     pano.setOptions(opts);
00870     // set optimizer switches
00871     pano.setOptimizerSwitch(HuginBase::OPT_POSITION);
00872     pano.setPhotometricOptimizerSwitch(HuginBase::OPT_EXPOSURE | HuginBase::OPT_VIGNETTING | HuginBase::OPT_RESPONSE);
00873 
00874     std::ofstream script(projectFile.GetFullPath().mb_str(HUGIN_CONV_FILENAME));
00875     script.exceptions ( std::ofstream::eofbit | std::ofstream::failbit | std::ofstream::badbit );
00876     if(!script.good())
00877     {
00878         return wxEmptyString;
00879     };
00880     HuginBase::UIntSet all;
00881     fill_set(all, 0, pano.getNrOfImages()-1);
00882     try
00883     {
00884         std::string Pathprefix(projectFile.GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR).mb_str(HUGIN_CONV_FILENAME));
00885         pano.printPanoramaScript(script, pano.getOptimizeVector(), pano.getOptions(), all, false, Pathprefix);
00886     }
00887     catch (...)
00888     {
00889         return wxEmptyString;
00890     };
00891     script.close();
00892     return projectFile.GetFullPath();
00893 };
00894 
00895 wxString PossiblePano::GetCameraName()
00896 {
00897     return wxString(m_camera.c_str(), wxConvLocal);
00898 }
00899 
00900 wxString PossiblePano::GetLensName()
00901 {
00902     return wxString(m_lens.c_str(), wxConvLocal);
00903 };
00904 
00905 wxString PossiblePano::GetFocalLength()
00906 {
00907     return wxString::Format(wxT("%0.1f mm"), m_focallength);
00908 };
00909 
00910 wxString PossiblePano::GetStartString()
00911 {
00912     return m_dt_start.Format();
00913 };
00914 
00915 wxString PossiblePano::GetDuration()
00916 {
00917     wxTimeSpan diff = m_dt_end.Subtract(m_dt_start);
00918     if (diff.GetSeconds() > 60)
00919     {
00920         return diff.Format(_("%M:%S min"));
00921     }
00922     else
00923     {
00924         return diff.Format(_("%S s"));
00925     };
00926 };
00927 
00928 void PossiblePano::PopulateListCtrl(wxListCtrl* list, wxImageList* thumbs)
00929 {
00930     list->DeleteAllItems();
00931     thumbs->RemoveAll();
00932     wxBusyCursor cursor;
00933     for (ImageSet::iterator it = m_images.begin(); it != m_images.end(); ++it)
00934     {
00935         Exiv2::Image::AutoPtr image;
00936         bool opened = false;
00937         try
00938         {
00939             image = Exiv2::ImageFactory::open((*it)->getFilename().c_str());
00940             opened = true;
00941         }
00942         catch (...)
00943         {
00944             std::cerr << __FILE__ << " " << __LINE__ << " Error opening file" << std::endl;
00945         }
00946         int index = -1;
00947         if (opened)
00948         {
00949             image->readMetadata();
00950             // read all thumbnails
00951             Exiv2::PreviewManager previews(*image);
00952             Exiv2::PreviewPropertiesList lists = previews.getPreviewProperties();
00953             if (!lists.empty())
00954             {
00955                 // select a preview with matching size
00956                 int previewIndex = 0;
00957                 while (previewIndex < lists.size() - 1 && lists[previewIndex].width_ < THUMBSIZE && lists[previewIndex].height_ < THUMBSIZE)
00958                 {
00959                     ++previewIndex;
00960                 };
00961                 // load preview image to wxImage
00962                 wxImage rawImage;
00963                 Exiv2::PreviewImage previewImage = previews.getPreviewImage(lists[previewIndex]);
00964                 wxMemoryInputStream stream(previewImage.pData(), previewImage.size());
00965                 rawImage.LoadFile(stream, wxString(previewImage.mimeType().c_str(), wxConvLocal), -1);
00966                 int x = 0;
00967                 int y = 0;
00968                 if (previewImage.width() > previewImage.height())
00969                 {
00970                     //landscape format
00971                     int newHeight = THUMBSIZE*previewImage.height() / previewImage.width();
00972                     rawImage.Rescale(THUMBSIZE, newHeight);
00973                     x = 0;
00974                     y = (THUMBSIZE - newHeight) / 2;
00975                 }
00976                 else
00977                 {
00978                     //portrait format
00979                     int newWidth = THUMBSIZE*previewImage.width() / previewImage.height();
00980                     rawImage.Rescale(newWidth, THUMBSIZE);
00981                     x = (THUMBSIZE - newWidth) / 2;
00982                     y = 0;
00983                 }
00984                 // create final bitmap with centered thumbnail
00985                 wxBitmap bitmap(THUMBSIZE, THUMBSIZE);
00986                 wxMemoryDC dc(bitmap);
00987                 dc.SetBackground(list->GetBackgroundColour());
00988                 dc.Clear();
00989                 dc.DrawBitmap(rawImage, x, y);
00990                 dc.SelectObject(wxNullBitmap);
00991                 // create mask bitmap
00992                 wxImage mask(THUMBSIZE, THUMBSIZE);
00993                 mask.SetRGB(wxRect(0, 0, THUMBSIZE, THUMBSIZE), 0, 0, 0);
00994                 mask.SetRGB(wxRect(x, y, THUMBSIZE - 2 * x, THUMBSIZE - 2 * y), 255, 255, 255);
00995                 // add to wxImageList
00996                 index = thumbs->Add(bitmap, wxBitmap(mask, 1));
00997             };
00998         };
00999         // create item in thumb list
01000         wxFileName fn(wxString((*it)->getFilename().c_str(), HUGIN_CONV_FILENAME));
01001         list->InsertItem(list->GetItemCount(), fn.GetFullName(), index);
01002     };
01003 };
01004 
01005 void PossiblePano::RemoveImage(const unsigned int index)
01006 {
01007     // remove image with given index
01008     if (index < m_images.size())
01009     {
01010         ImageSet::iterator item = m_images.begin();
01011         std::advance(item, index);
01012         delete *item;
01013         m_images.erase(item);
01014         //update the internal times
01015         UpdateDateTimes();
01016     };
01017 }
01018 
01019 PossiblePano* PossiblePano::SplitPano(const unsigned int index)
01020 {
01021     PossiblePano* newPano = new PossiblePano();
01022     if (index < m_images.size())
01023     {
01024         // now move all images to right pano
01025         ImageSet allImages = m_images;
01026         m_images.clear();
01027         ImageSet::iterator img = allImages.begin();
01028         while (m_images.size() < index && img != allImages.end())
01029         {
01030             m_images.insert(*img);
01031             ++img;
01032         };
01033         while (img != allImages.end())
01034         {
01035             newPano->AddSrcPanoImage(*img);
01036             ++img;
01037         }
01038         UpdateDateTimes();
01039     };
01040     return newPano;
01041 }
01042 
01043 void PossiblePano::UpdateDateTimes()
01044 {
01045     // update internal stored start and end time
01046     m_dt_start = GetDateTime(*m_images.begin());
01047     m_dt_end = m_dt_start;
01048     for (auto& img : m_images)
01049     {
01050         wxDateTime dt = GetDateTime(img);
01051         if (dt.IsEarlierThan(m_dt_start))
01052         {
01053             m_dt_start = dt;
01054         }
01055         if (dt.IsLaterThan(m_dt_end))
01056         {
01057             m_dt_end = dt;
01058         };
01059     };
01060 }

Generated on 9 Dec 2016 for Hugintrunk by  doxygen 1.4.7