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

Generated on 22 Apr 2017 for Hugintrunk by  doxygen 1.4.7