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

Generated on 10 Dec 2016 for Hugintrunk by  doxygen 1.4.7