OptimizePhotometricPanel.cpp

Go to the documentation of this file.
00001 // -*- c-basic-offset: 4 -*-
00002 
00011 /*  This program 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  *  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 <config.h>
00028 #include "panoinc_WX.h"
00029 
00030 #include "panoinc.h"
00031 
00032 #include <algorithms/point_sampler/PointSampler.h>
00033 #include <algorithms/optimizer/PhotometricOptimizer.h>
00034 #include <algorithms/basic/CalculateMeanExposure.h>
00035 
00036 #include <vigra_ext/Pyramid.h>
00037 #include <vigra_ext/openmp_vigra.h>
00038 #include "hugin/OptimizePhotometricPanel.h"
00039 #include "base_wx/CommandHistory.h"
00040 #include "base_wx/PanoCommand.h"
00041 #include "hugin/MainFrame.h"
00042 #include "base_wx/MyProgressDialog.h"
00043 #include "hugin/config_defaults.h"
00044 #include "base_wx/wxImageCache.h"
00045 #include "hugin/ImagesTree.h"
00046 #include "hugin_base/panodata/OptimizerSwitches.h"
00047 #include "hugin/PanoOperation.h"
00048 
00049 using namespace std;
00050 using namespace hugin_utils;
00051 using namespace vigra;
00052 using namespace vigra_ext;
00053 
00054 //============================================================================
00055 //============================================================================
00056 //============================================================================
00057 
00058 BEGIN_EVENT_TABLE(OptimizePhotometricPanel, wxPanel)
00059     EVT_CLOSE(OptimizePhotometricPanel::OnClose)
00060     EVT_BUTTON(XRCID("optimize_photo_panel_optimize"), OptimizePhotometricPanel::OnOptimizeButton)
00061     EVT_BUTTON(XRCID("optimize_photo_panel_reset"), OptimizePhotometricPanel::OnReset)
00062 END_EVENT_TABLE()
00063 
00064 OptimizePhotometricPanel::OptimizePhotometricPanel() : m_pano(0)
00065 {
00066 };
00067 
00068 bool OptimizePhotometricPanel::Create(wxWindow *parent, wxWindowID id, const wxPoint& pos, const wxSize& size,
00069                       long style, const wxString& name)
00070 {
00071     DEBUG_TRACE("");
00072     if (! wxPanel::Create(parent, id, pos, size, style, name))
00073     {
00074         return false;
00075     }
00076 
00077     wxXmlResource::Get()->LoadPanel(this, wxT("optimize_photo_panel"));
00078     wxPanel * panel = XRCCTRL(*this, "optimize_photo_panel", wxPanel);
00079 
00080     wxBoxSizer *topsizer = new wxBoxSizer( wxVERTICAL );
00081     topsizer->Add(panel, 1, wxEXPAND, 0);
00082     SetSizer(topsizer);
00083 
00084     m_only_active_images_cb = XRCCTRL(*this, "optimize_photo_panel_only_active_images", wxCheckBox);
00085     DEBUG_ASSERT(m_only_active_images_cb);
00086     m_only_active_images_cb->SetValue(wxConfigBase::Get()->Read(wxT("/OptimizeOptimizePhotometricPanelPanel/OnlyActiveImages"),1l) != 0);
00087 
00088     m_images_tree = XRCCTRL(*this, "optimize_photo_panel_images", ImagesTreeCtrl);
00089     DEBUG_ASSERT(m_images_tree);
00090     m_lens_tree = XRCCTRL(*this, "optimize_photo_panel_lens", ImagesTreeCtrl);
00091     DEBUG_ASSERT(m_lens_tree);
00092 
00093     XRCCTRL(*this, "optimize_photo_panel_splitter", wxSplitterWindow)->SetSashGravity(0.66);
00094 
00095     wxString text(_("Any variables below which are bold and underlined will be optimized."));
00096     text.Append(wxT(" "));
00097 #if defined __WXMAC__ || defined __WXOSX_COCOA__
00098     text.Append(_("Use command + left mouse click to toggle state of variables."));
00099 #else
00100     text.Append(_("Use control + left mouse click to toggle state of variables."));
00101 #endif
00102     text.Append(wxT(" "));
00103     text.Append(_("Variables which shown in normal font will act as references or anchors."));
00104     XRCCTRL(*this, "optimize_photo_panel_information_text", wxStaticText)->SetLabel(text);
00105 
00106     return true;
00107 }
00108 
00109 void OptimizePhotometricPanel::Init(Panorama * panorama)
00110 {
00111     m_pano = panorama;
00112     // observe the panorama
00113     m_pano->addObserver(this);
00114     m_images_tree->Init(m_pano);
00115     m_images_tree->SetOptimizerMode();
00116     m_images_tree->SetDisplayMode(ImagesTreeCtrl::DISPLAY_PHOTOMETRICS_IMAGES);
00117 
00118     m_lens_tree->Init(m_pano);
00119     m_lens_tree->SetOptimizerMode();
00120     m_lens_tree->SetGroupMode(ImagesTreeCtrl::GROUP_LENS);
00121     m_lens_tree->SetDisplayMode(ImagesTreeCtrl::DISPLAY_PHOTOMETRICS_LENSES);
00122     
00123 }
00124 
00125 void OptimizePhotometricPanel::SetGuiLevel(GuiLevel newGuiLevel)
00126 {
00127     m_images_tree->SetGuiLevel(newGuiLevel);
00128     m_lens_tree->SetGuiLevel(newGuiLevel);
00129 };
00130 
00131 OptimizePhotometricPanel::~OptimizePhotometricPanel()
00132 {
00133     DEBUG_TRACE("dtor, writing config");
00134     wxConfigBase::Get()->Write(wxT("/OptimizePhotometricPanel/OnlyActiveImages"),m_only_active_images_cb->IsChecked() ? 1l : 0l);
00135 
00136     m_pano->removeObserver(this);
00137     DEBUG_TRACE("dtor end");
00138 }
00139 
00140 void OptimizePhotometricPanel::OnOptimizeButton(wxCommandEvent & e)
00141 {
00142     DEBUG_TRACE("");
00143     // run optimizer
00144 
00145     UIntSet imgs;
00146     if (m_only_active_images_cb->IsChecked() || m_pano->getPhotometricOptimizerSwitch()!=0)
00147     {
00148         // use only selected images.
00149         imgs = m_pano->getActiveImages();
00150         if (imgs.size() < 2)
00151         {
00152             wxMessageBox(_("The project does not contain any active images.\nPlease activate at least 2 images in the (fast) preview window.\nOptimization canceled."),
00153 #ifdef _WINDOWS
00154                 _("Hugin"),
00155 #else
00156                 wxT(""),
00157 #endif
00158                 wxICON_ERROR | wxOK);
00159             return;
00160         } 
00161     }
00162     else
00163     {
00164         fill_set(imgs, 0, m_pano->getNrOfImages()-1);
00165     }
00166     runOptimizer(imgs);
00167 }
00168 
00169 void OptimizePhotometricPanel::panoramaChanged(HuginBase::Panorama & pano)
00170 {
00171     m_images_tree->Enable(m_pano->getPhotometricOptimizerSwitch()==0);
00172     m_lens_tree->Enable(m_pano->getPhotometricOptimizerSwitch()==0);
00173 }
00174 
00175 void OptimizePhotometricPanel::panoramaImagesChanged(HuginBase::Panorama &pano,
00176                                           const HuginBase::UIntSet & imgNr)
00177 {
00178     XRCCTRL(*this, "optimize_photo_panel_optimize", wxButton)->Enable(pano.getNrOfImages()>1);
00179     XRCCTRL(*this, "optimize_photo_panel_reset", wxButton)->Enable(pano.getNrOfImages()>0);    
00180 }
00181 
00182 void OptimizePhotometricPanel::runOptimizer(const UIntSet & imgs)
00183 {
00184     DEBUG_TRACE("");
00185     int mode = m_pano->getPhotometricOptimizerSwitch();
00186 
00187     // check if vignetting and response are linked, display a warning if they are not
00188     // The variables to check:
00189     const HuginBase::ImageVariableGroup::ImageVariableEnum vars[] = {
00190             HuginBase::ImageVariableGroup::IVE_EMoRParams,
00191             HuginBase::ImageVariableGroup::IVE_ResponseType,
00192             HuginBase::ImageVariableGroup::IVE_VigCorrMode,
00193             HuginBase::ImageVariableGroup::IVE_RadialVigCorrCoeff,
00194             HuginBase::ImageVariableGroup::IVE_RadialVigCorrCenterShift
00195         };
00196     // keep a list of commands needed to fix it:
00197     std::vector<PanoCommand::PanoCommand *> commands;
00198     HuginBase::ConstStandardImageVariableGroups variable_groups(*m_pano);
00199     HuginBase::ConstImageVariableGroup & lenses = variable_groups.getLenses();
00200     for (size_t i = 0; i < lenses.getNumberOfParts(); i++)
00201     {
00202         std::set<HuginBase::ImageVariableGroup::ImageVariableEnum> links_needed;
00203         links_needed.clear();
00204         for (int v = 0; v < 5; v++)
00205         {
00206             if (!lenses.getVarLinkedInPart(vars[v], i))
00207             {
00208                 links_needed.insert(vars[v]);
00209             }
00210         };
00211         if (!links_needed.empty())
00212         {
00213             commands.push_back(new PanoCommand::LinkLensVarsCmd(*m_pano, i, links_needed));
00214         }
00215     }
00216     // if the list of commands is empty, all is good and we don't need a warning.
00217     if (!commands.empty()) {
00218         int ok = wxMessageBox(_("The same vignetting and response parameters should\nbe applied for all images of a lens.\nCurrently each image can have different parameters.\nLink parameters?"), _("Link parameters"), wxYES_NO | wxICON_INFORMATION);
00219         if (ok == wxYES)
00220         {
00221             // perform all the commands we stocked up earilier.
00222             for (std::vector<PanoCommand::PanoCommand *>::iterator it = commands.begin(); it != commands.end(); ++it)
00223             {
00224                 PanoCommand::GlobalCmdHist::getInstance().addCommand(*it);
00225             }
00226         }
00227         else
00228         {
00229             // free all the commands, the user doesn't want them used.
00230             for (std::vector<PanoCommand::PanoCommand *>::iterator it = commands.begin(); it != commands.end(); ++it)
00231             {
00232                 delete *it;
00233             }
00234         }
00235     }
00236 
00237     Panorama optPano = m_pano->getSubset(imgs);
00238     HuginBase::PanoramaOptions opts = optPano.getOptions();
00239 
00240     HuginBase::OptimizeVector optvars;
00241     if(mode==0)
00242     {
00243         optvars = optPano.getOptimizeVector();
00244         bool valid=false;
00245         for(unsigned int i=0;i<optvars.size() && !valid;i++)
00246         {
00247             if(set_contains(optvars[i], "Eev") || set_contains(optvars[i], "Er") || set_contains(optvars[i], "Eb") ||
00248                 set_contains(optvars[i], "Vb") || set_contains(optvars[i], "Vx") || set_contains(optvars[i], "Ra"))
00249             {
00250                 valid=true;
00251             };
00252         };
00253         if(!valid)
00254         {
00255             wxMessageBox(_("You selected no parameters to optimize.\nTherefore optimization will be canceled."), _("Exposure optimization"), wxOK | wxICON_INFORMATION);
00256             return;
00257         };
00258     };
00259 
00260     double error = 0;
00261     {
00262         std::vector<vigra_ext::PointPairRGB> points;
00263         long nPoints = 200;
00264         wxConfigBase::Get()->Read(wxT("/OptimizePhotometric/nRandomPointsPerImage"), &nPoints , HUGIN_PHOTOMETRIC_OPTIMIZER_NRPOINTS);
00265 
00266         ProgressReporterDialog progress(optPano.getNrOfImages()+2, _("Photometric alignment"), _("Loading images"));
00267         progress.Show();
00268 
00269         nPoints = nPoints * optPano.getNrOfImages();
00270         // get the small images
00271         std::vector<vigra::FRGBImage *> srcImgs;
00272         for (size_t i=0; i < optPano.getNrOfImages(); i++)
00273         {
00274             ImageCache::EntryPtr e = ImageCache::getInstance().getSmallImage(optPano.getImage(i).getFilename());
00275             vigra::FRGBImage * img = new FRGBImage;
00276             if (!e)
00277             {
00278                 wxMessageBox(_("Error: could not load all images"), _("Error"));
00279                 return;
00280             }
00281             if (e->image8 && e->image8->width() > 0)
00282             {
00283                 reduceToNextLevel(*(e->image8), *img);
00284                 vigra::omp::transformImage(vigra::srcImageRange(*img), vigra::destImage(*img),
00285                                 vigra::functor::Arg1()/vigra::functor::Param(255.0));
00286             }
00287             else
00288             {
00289                 if (e->image16 && e->image16->width() > 0)
00290                 {
00291                     reduceToNextLevel(*(e->image16), *img);
00292                     vigra::omp::transformImage(vigra::srcImageRange(*img), vigra::destImage(*img),
00293                                    vigra::functor::Arg1()/vigra::functor::Param(65535.0));
00294                 }
00295                 else
00296                 {
00297                     reduceToNextLevel(*(e->imageFloat), *img);
00298                 }
00299             };
00300             srcImgs.push_back(img);
00301             if (!progress.updateDisplayValue())
00302             {
00303                 // check if user pressed cancel
00304                 return;
00305             };
00306         }   
00307         HuginBase::PointSampler::extractPoints(optPano, srcImgs, nPoints, true, &progress, points);
00308 
00309         if (!progress.updateDisplayValue())
00310         {
00311             return;
00312         };
00313         if (points.size() == 0)
00314         {
00315             wxMessageBox(_("Error: no overlapping points found, Photometric optimization aborted"), _("Error"));
00316             return;
00317         }
00318 
00319         progress.setMaximum(0);
00320         progress.updateDisplay(_("Optimize..."));
00321         try
00322         {
00323             if (mode != 0)
00324             {
00325                 // run automatic optimisation
00326                 // ensure that we have a valid anchor.
00327                 HuginBase::PanoramaOptions opts = optPano.getOptions();
00328                 if (opts.colorReferenceImage >= optPano.getNrOfImages())
00329                 {
00330                     opts.colorReferenceImage = 0;
00331                     optPano.setOptions(opts);
00332                 }
00333                 HuginBase::SmartPhotometricOptimizer::PhotometricOptimizeMode optMode;
00334                 switch(mode)
00335                 {
00336                     case (HuginBase::OPT_EXPOSURE | HuginBase::OPT_VIGNETTING | HuginBase::OPT_RESPONSE):
00337                         optMode = HuginBase::SmartPhotometricOptimizer::OPT_PHOTOMETRIC_LDR;
00338                         break;
00339                     case (HuginBase::OPT_EXPOSURE | HuginBase::OPT_VIGNETTING | HuginBase::OPT_RESPONSE | HuginBase::OPT_WHITEBALANCE):
00340                         optMode = HuginBase::SmartPhotometricOptimizer::OPT_PHOTOMETRIC_LDR_WB;
00341                         break;
00342                     case (HuginBase::OPT_VIGNETTING | HuginBase::OPT_RESPONSE):
00343                         optMode = HuginBase::SmartPhotometricOptimizer::OPT_PHOTOMETRIC_HDR;
00344                         break;
00345                     case (HuginBase::OPT_WHITEBALANCE | HuginBase::OPT_VIGNETTING | HuginBase::OPT_RESPONSE):
00346                         optMode = HuginBase::SmartPhotometricOptimizer::OPT_PHOTOMETRIC_HDR_WB;
00347                         break;
00348                     default:
00349                         //invalid combination
00350                         return;
00351                 };
00352                 HuginBase::SmartPhotometricOptimizer::smartOptimizePhotometric(optPano, optMode, points, &progress, error);
00353             }
00354             else
00355             {
00356                 // optimize selected parameters
00357                 HuginBase::PhotometricOptimizer::optimizePhotometric(optPano, optvars, points, &progress, error);
00358             }
00359             if (progress.wasCancelled())
00360             {
00361                 return;
00362             }
00363         }
00364         catch (std::exception & error)
00365         {
00366             wxMessageBox(_("Internal error during photometric optimization:\n") + wxString(error.what(), wxConvLocal), _("Internal error"));
00367             return;
00368         }
00369     }
00370     wxYield();
00371 
00372     // display information about the estimation process:
00373     int ret = wxMessageBox(wxString::Format(_("Photometric optimization results:\nAverage difference (RMSE) between overlapping pixels: %.2f gray values (0..255)\n\nApply results?"), error*255),
00374                            _("Photometric optimization finished"), wxYES_NO | wxICON_INFORMATION,this);
00375 
00376     if (ret == wxYES)
00377     {
00378         DEBUG_DEBUG("Applying vignetting corr");
00379         // TODO: merge into a single update command
00380         const HuginBase::VariableMapVector & vars = optPano.getVariables();
00381         PanoCommand::GlobalCmdHist::getInstance().addCommand(
00382                 new PanoCommand::UpdateImagesVariablesCmd(*m_pano, imgs, vars)
00383                                                );
00384         //now update panorama exposure value
00385         HuginBase::PanoramaOptions opts = m_pano->getOptions();
00386         opts.outputExposureValue = HuginBase::CalculateMeanExposure::calcMeanExposure(*m_pano);
00387         PanoCommand::GlobalCmdHist::getInstance().addCommand(
00388                 new PanoCommand::SetPanoOptionsCmd(*m_pano, opts)
00389                                                );
00390     }
00391 }
00392 
00393 void OptimizePhotometricPanel::OnClose(wxCloseEvent& event)
00394 {
00395     DEBUG_TRACE("OnClose");
00396     // do not close, just hide if we're not forced
00397     if (event.CanVeto())
00398     {
00399         event.Veto();
00400         Hide();
00401         DEBUG_DEBUG("Hiding");
00402     }
00403     else
00404     {
00405         DEBUG_DEBUG("Closing");
00406         Destroy();
00407     }
00408 }
00409 
00410 void OptimizePhotometricPanel::OnReset(wxCommandEvent& e)
00411 {
00412     PanoOperation::ResetOperation op(PanoOperation::ResetOperation::RESET_DIALOG_PHOTOMETRICS);
00413     PanoCommand::PanoCommand* cmd=op.GetCommand(this,*m_pano, m_images_tree->GetSelectedImages(), MainFrame::Get()->GetGuiLevel());
00414     if(cmd!=NULL)
00415     {
00416         PanoCommand::GlobalCmdHist::getInstance().addCommand(cmd);
00417     };
00418 };
00419 
00420 IMPLEMENT_DYNAMIC_CLASS(OptimizePhotometricPanel, wxPanel)
00421 
00422 OptimizePhotometricPanelXmlHandler::OptimizePhotometricPanelXmlHandler()
00423                 : wxXmlResourceHandler()
00424 {
00425     AddWindowStyles();
00426 }
00427 
00428 wxObject *OptimizePhotometricPanelXmlHandler::DoCreateResource()
00429 {
00430     XRC_MAKE_INSTANCE(cp, OptimizePhotometricPanel)
00431 
00432     cp->Create(m_parentAsWindow,
00433                    GetID(),
00434                    GetPosition(), GetSize(),
00435                    GetStyle(wxT("style")),
00436                    GetName());
00437 
00438     SetupWindow( cp);
00439 
00440     return cp;
00441 }
00442 
00443 bool OptimizePhotometricPanelXmlHandler::CanHandle(wxXmlNode *node)
00444 {
00445     return IsOfClass(node, wxT("OptimizePhotometricPanel"));
00446 }
00447 
00448 IMPLEMENT_DYNAMIC_CLASS(OptimizePhotometricPanelXmlHandler, wxXmlResourceHandler)

Generated on 28 Aug 2015 for Hugintrunk by  doxygen 1.4.7