From: Pablo d'A. <da...@us...> - 2006-12-17 10:49:58
|
Update of /cvsroot/hugin/hugin/src/hugin In directory sc8-pr-cvs5.sourceforge.net:/tmp/cvs-serv27675/hugin Modified Files: AutoCtrlPointCreator.cpp ImagesPanel.cpp LensPanel.cpp MainFrame.cpp Makefile.am OptimizePanel.cpp PanoDruid.cpp PanoPanel.cpp huginApp.cpp Added Files: AssistantPanel.cpp Log Message: added new "Assistant" panel, offering an "Align images" button, that creates control points and optimises and centers the panorama. Index: AutoCtrlPointCreator.cpp =================================================================== RCS file: /cvsroot/hugin/hugin/src/hugin/AutoCtrlPointCreator.cpp,v retrieving revision 1.46 retrieving revision 1.47 diff -u -d -r1.46 -r1.47 --- AutoCtrlPointCreator.cpp 12 Aug 2006 19:18:19 -0000 1.46 +++ AutoCtrlPointCreator.cpp 17 Dec 2006 10:49:50 -0000 1.47 @@ -49,13 +49,13 @@ using namespace PT; using namespace utils; -void AutoCtrlPointCreator::readUpdatedControlPoints(const std::string & file, +CPVector AutoCtrlPointCreator::readUpdatedControlPoints(const std::string & file, PT::Panorama & pano) { ifstream stream(file.c_str()); if (! stream.is_open()) { DEBUG_ERROR("Could not open autopano output: " << file); - return; + return CPVector(); } Panorama tmpp; @@ -77,7 +77,7 @@ } if (! set_contains(imgMapping, ni)) { DEBUG_ERROR("Could not find image " << ni << ", name: " << tmpp.getImage(ni).getFilename() << " in autopano output"); - return; + return CPVector(); } } @@ -90,18 +90,15 @@ (*it).image2Nr = imgMapping[(*it).image2Nr]; } - wxString msg; - wxMessageBox(wxString::Format(_("Added %d control points"), ctrlPoints.size()), _("Autopano result")); - GlobalCmdHist::getInstance().addCommand( - new PT::AddCtrlPointsCmd(pano, ctrlPoints) - ); + return ctrlPoints; } -void AutoCtrlPointCreator::automatch(Panorama & pano, +CPVector AutoCtrlPointCreator::automatch(Panorama & pano, const UIntSet & imgs, int nFeatures) { + CPVector cps; int t = wxConfigBase::Get()->Read(wxT("/AutoPano/Type"),HUGIN_AP_TYPE); if (t < 0) { @@ -115,7 +112,7 @@ if (d.ShowModal() == wxID_OK) { t = d.GetSelection(); } else { - return; + return cps; } } @@ -126,35 +123,35 @@ _("Would you like to use Autopano-Sift instead?"), wxOK|wxCANCEL|wxICON_EXCLAMATION) == wxOK) t=1; - else return; + else return cps; } #endif - switch (t) { case 0: { // autopano@kolor AutoPanoKolor matcher; - matcher.automatch(pano, imgs, nFeatures); + cps = matcher.automatch(pano, imgs, nFeatures); break; } case 1: { // autopano-sift AutoPanoSift matcher; - matcher.automatch(pano, imgs, nFeatures); + cps = matcher.automatch(pano, imgs, nFeatures); break; } default: DEBUG_ERROR("Invalid autopano type"); } wxConfigBase::Get()->Write(wxT("/AutoPano/Type"),t); - + return cps; } -void AutoPanoSift::automatch(Panorama & pano, const UIntSet & imgs, +CPVector AutoPanoSift::automatch(Panorama & pano, const UIntSet & imgs, int nFeatures) { + CPVector cps; // create suitable command line.. #ifdef __WXMSW__ @@ -169,7 +166,7 @@ wxConfigBase::Get()->Write(wxT("/AutopanoSift/AutopanoExe"),autopanoExe); } else { wxLogError(_("No autopano selected")); - return; + return cps; } } #elif (defined __WXMAC__) @@ -192,7 +189,7 @@ || !wxFileExists(autopanoExeDir+wxT("/libsift.dll")) ) { wxMessageBox(wxT(""), _("Autopano-SIFT is not installed.")); - return; + return cps; } } } else if(!wxFileExists(autopanoExe)) { @@ -205,7 +202,7 @@ wxConfigBase::Get()->Write(wxT("/AutopanoSift/AutopanoExe"), autopanoExe); } else { wxLogError(_("No autopano selected")); - return; + return cps; } } #else @@ -244,12 +241,12 @@ if (use_namefile && use_params) { wxMessageBox(_("Please use either %namefile or %i in the autopano-sift command line."), _("Error in Autopano command"), wxOK | wxICON_ERROR); - return; + return cps; } if ((! use_namefile) && (! use_params)) { wxMessageBox(_("Please use %namefile or %i to specify the input files for autopano-sift"), _("Error in Autopano command"), wxOK | wxICON_ERROR); - return; + return cps; } wxFile namefile; @@ -285,7 +282,7 @@ wxMessageBox(_("autopano command line too long.\nThis is a windows limitation\nPlease select less images, or place the images in a folder with\na shorter pathname"), _("Too many images selected"), wxCANCEL | wxICON_ERROR ); - return; + return cps; } #endif @@ -343,22 +340,23 @@ if (ret == -1) { wxMessageBox( _("Could not execute command: " + cmd), _("wxExecute Error"), wxOK | wxICON_ERROR); - return; + return cps; } else if (ret > 0) { wxMessageBox(_("command: ") + cmd + _("\nfailed with error code: ") + wxString::Format(wxT("%d"),ret), _("wxExecute Error"), wxOK | wxICON_ERROR); - return; + return cps; } if (! wxFileExists(ptofile.c_str())) { wxMessageBox(wxString(_("Could not open ")) + ptofile + _(" for reading\nThis is an indicator that the autopano call failed,\nor wrong command line parameters have been used.\n\nAutopano command: ") + cmd, _("autopano failure"), wxOK | wxICON_ERROR ); - return; + return cps; } + // read and update control points - readUpdatedControlPoints((const char *)ptofile.mb_str(), pano); + cps = readUpdatedControlPoints((const char *)ptofile.mb_str(), pano); #ifdef __WXMSW__ // set old cwd. @@ -373,12 +371,15 @@ if (!wxRemoveFile(ptofile)) { DEBUG_DEBUG("could not remove temporary file: " << ptofile.c_str()); } + + return cps; } -void AutoPanoKolor::automatch(Panorama & pano, const UIntSet & imgs, +CPVector AutoPanoKolor::automatch(Panorama & pano, const UIntSet & imgs, int nFeatures) { + CPVector cps; #ifdef __WXMSW__ wxString autopanoExe = wxConfigBase::Get()->Read(wxT("/AutoPanoKolor/AutopanoExe"), wxT(HUGIN_APKOLOR_EXE)); if (!wxFile::Exists(autopanoExe)){ @@ -391,7 +392,7 @@ wxConfigBase::Get()->Write(wxT("/AutoPanoKolor/AutopanoExe"),autopanoExe); } else { wxLogError(_("No autopano selected")); - return; + return cps; } } #else @@ -431,7 +432,7 @@ wxMessageBox(_("autopano command line too long.\nThis is a windows limitation\nPlease select less images, or place the images in a folder with\na shorter pathname"), _("Too many images selected"), wxCANCEL ); - return; + return cps; } #endif DEBUG_DEBUG("Executing: " << cmd.c_str()); @@ -453,13 +454,13 @@ if (ret == -1) { wxMessageBox( _("Could not execute command: " + cmd), _("wxExecute Error"), wxOK | wxICON_ERROR); - return; + return cps; } else if (ret > 0) { wxMessageBox(_("command: ") + cmd + _("\nfailed with error code: ") + wxString::Format(wxT("%d"),ret), _("wxExecute Error"), wxOK | wxICON_ERROR); - return; + return cps; } ptofile.append(wxT("0.oto")); @@ -468,14 +469,15 @@ + cmd + _("\n current directory:") + wxGetCwd(), _("autopano failure"), wxCANCEL ); - return; + return cps; } // read and update control points - readUpdatedControlPoints((const char *)ptofile.mb_str(), pano); + cps = readUpdatedControlPoints((const char *)ptofile.mb_str(), pano); if (!wxRemoveFile(ptofile)) { DEBUG_DEBUG("could not remove temporary file: " << ptofile.c_str()); } + return cps; } Index: ImagesPanel.cpp =================================================================== RCS file: /cvsroot/hugin/hugin/src/hugin/ImagesPanel.cpp,v retrieving revision 1.97 retrieving revision 1.98 diff -u -d -r1.97 -r1.98 --- ImagesPanel.cpp 9 Aug 2006 20:55:39 -0000 1.97 +++ ImagesPanel.cpp 17 Dec 2006 10:49:50 -0000 1.98 @@ -290,7 +290,13 @@ , wxSpinCtrl)->GetValue(); AutoCtrlPointCreator matcher; - matcher.automatch(pano, selImg, nFeatures); + CPVector cps = matcher.automatch(pano, selImg, nFeatures); + wxString msg; + wxMessageBox(wxString::Format(_("Added %d control points"), cps.size()), _("Autopano result")); + GlobalCmdHist::getInstance().addCommand( + new PT::AddCtrlPointsCmd(pano, cps) + ); + }; // Yaw by text -> double --- NEW FILE: AssistantPanel.cpp --- // -*- c-basic-offset: 4 -*- /** @file AssistantPanel.cpp * * @brief implementation of AssistantPanel Class * * @author Pablo d'Angelo <pab...@we...> * * $Id: AssistantPanel.cpp,v 1.1 2006/12/17 10:49:50 dangelo Exp $ * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this software; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include <config.h> #include "panoinc_WX.h" #include "panoinc.h" #include "common/stl_utils.h" #include <map> //#include <vigra_ext/PointMatching.h> //#include <vigra_ext/LoweSIFT.h> #include "PT/PTOptimise.h" #include "common/wxPlatform.h" #include "hugin/AssistantPanel.h" #include "hugin/CommandHistory.h" #include "hugin/ImageCache.h" #include "hugin/ImagesList.h" #include "hugin/LensPanel.h" #include "hugin/MainFrame.h" #include "hugin/huginApp.h" #include "hugin/AutoCtrlPointCreator.h" #include "hugin/PTWXDlg.h" #include "hugin/TextKillFocusHandler.h" #include "hugin/PanoDruid.h" #include "hugin/config_defaults.h" using namespace PT; using namespace PTools; using namespace utils; using namespace vigra; using namespace vigra_ext; using namespace std; //------------------------------------------------------------------------------ #define GET_VAR(val) m_pano.getVariable(orientationEdit_RefImg).val.getValue() BEGIN_EVENT_TABLE(AssistantPanel, wxWindow) EVT_SIZE ( AssistantPanel::OnSize ) // EVT_MOUSE_EVENTS ( AssistantPanel::OnMouse ) // EVT_MOTION ( AssistantPanel::ChangePreview ) EVT_CHECKBOX ( XRCID("ass_exif_cb"), AssistantPanel::OnExifToggle) EVT_CHOICE ( XRCID("ass_lens_type_choice"), AssistantPanel::OnLensTypeChanged) EVT_TEXT_ENTER ( XRCID("ass_focallength_text"), AssistantPanel::OnFocalLengthChanged) EVT_TEXT_ENTER ( XRCID("ass_cropfactor_text"), AssistantPanel::OnCropFactorChanged) EVT_BUTTON ( XRCID("ass_load_lens_button"), AssistantPanel::OnLoadLens) EVT_BUTTON ( XRCID("ass_align_button"), AssistantPanel::OnAlign) EVT_BUTTON ( XRCID("ass_create_button"), AssistantPanel::OnCreate) END_EVENT_TABLE() // Define a constructor for the Assistant Panel AssistantPanel::AssistantPanel(wxWindow *parent, const wxPoint& pos, const wxSize& size, Panorama* pano) : wxPanel (parent, -1, wxDefaultPosition, wxDefaultSize, wxEXPAND|wxGROW), m_pano(*pano), m_restoreLayoutOnResize(false), m_noImage(true) { DEBUG_TRACE(""); wxXmlResource::Get()->LoadPanel (this, wxT("assistant_panel")); m_imagesText = XRCCTRL(*this, "ass_load_images_text", wxStaticText); DEBUG_ASSERT(m_imagesText); m_exifToggle = XRCCTRL(*this, "ass_exif_cb", wxCheckBox); DEBUG_ASSERT(m_exifToggle); m_focalLengthText = XRCCTRL(*this, "ass_focallength_text", wxTextCtrl); DEBUG_ASSERT(m_focalLengthText); m_focalLengthText->PushEventHandler(new TextKillFocusHandler(this)); m_cropFactorText = XRCCTRL(*this, "ass_cropfactor_text", wxTextCtrl); DEBUG_ASSERT(m_cropFactorText); m_cropFactorText->PushEventHandler(new TextKillFocusHandler(this)); m_alignButton = XRCCTRL(*this, "ass_align_button", wxButton); DEBUG_ASSERT(m_alignButton); m_alignButton->Disable(); m_alignText = XRCCTRL(*this, "ass_align_text", wxStaticText); DEBUG_ASSERT(m_alignText); m_createButton = XRCCTRL(*this, "ass_create_button", wxButton); DEBUG_ASSERT(m_createButton); m_createButton->Disable(); // druid is currently disabled m_druid = 0; /* m_druid = new PanoDruid(this); wxXmlResource::Get()->AttachUnknownControl (wxT("ass_druid"), m_druid ); m_druid->Update(m_pano); */ #ifdef USE_WX253 m_panel = XRCCTRL(*this, "ass_control_panel", wxScrolledWindow); DEBUG_ASSERT(m_panel); m_panel->FitInside(); m_panel->SetScrollRate(10, 10); #endif #ifdef USE_WX253 SetAutoLayout(false); #endif m_degDigits = 1; // observe the panorama m_pano.addObserver(this); } AssistantPanel::~AssistantPanel(void) { DEBUG_TRACE("dtor"); m_focalLengthText->PopEventHandler(true); m_cropFactorText->PopEventHandler(true); m_pano.removeObserver(this); DEBUG_TRACE("dtor end"); } void AssistantPanel::RestoreLayout() { DEBUG_TRACE(""); #ifdef USE_WX253 int winWidth, winHeight; GetClientSize(&winWidth, &winHeight); DEBUG_INFO( "window size: " << winWidth <<"x"<< winHeight); #endif } // We need to override the default handling of size events because the // sizers set the virtual size but not the actual size. We reverse // the standard handling and fit the child to the parent rather than // fitting the parent around the child void AssistantPanel::OnSize( wxSizeEvent & e ) { #ifdef USE_WX253 int winWidth, winHeight; GetClientSize(&winWidth, &winHeight); XRCCTRL(*this, "assistant_panel", wxPanel)->SetSize (winWidth, winHeight); DEBUG_INFO( "assistant panel: " << winWidth <<"x"<< winHeight ); DEBUG_INFO( "assistant controls: " << winWidth <<"x"<< winHeight ); m_panel->SetSize(winWidth, winHeight); #else wxSize new_size = GetSize(); XRCCTRL(*this, "assistant_panel", wxPanel)->SetSize ( new_size ); DEBUG_INFO( "assistant panel: " << new_size.GetWidth() <<"x"<< new_size.GetHeight() ); #endif if (m_restoreLayoutOnResize) { m_restoreLayoutOnResize = false; RestoreLayout(); } e.Skip(); } void AssistantPanel::panoramaImagesChanged(PT::Panorama &pano, const PT::UIntSet & _imgNr) { } void AssistantPanel::panoramaChanged(PT::Panorama &pano) { DEBUG_TRACE(""); if (m_druid) m_druid->Update(m_pano); m_alignButton->Enable(pano.getNrOfImages() > 1); if (pano.getNrOfImages() == 0) { m_createButton->Disable(); m_imagesText->SetLabel(_("Please load images by pressing on the Load images button.")); m_exifToggle->Disable(); XRCCTRL(*this, "ass_lens_group", wxPanel)->Disable(); m_noImage = true; } else { // TODO: check if the positions are not all zero, if more than 1 image has been loaded m_createButton->Enable(); m_imagesText->SetLabel(wxString::Format(_("%d images loaded."), pano.getNrOfImages())); const Lens & lens = pano.getLens(0); if (m_noImage) { // straight after loading the first image, set exif checkbox, if available if (lens.m_hasExif) { m_exifToggle->Enable(); m_exifToggle->SetValue(true); } else { m_exifToggle->Disable(); m_exifToggle->SetValue(false); XRCCTRL(*this, "ass_lens_group", wxPanel)->Enable(); } } m_noImage = false; // update focal length double focal_length = lens.getFocalLength(); m_focalLengthText->SetValue(doubleTowxString(focal_length,m_degDigits)); // update focal length factor double focal_length_factor = lens.getCropFactor(); m_cropFactorText->SetValue(doubleTowxString(focal_length_factor,m_degDigits)); } if (pano.getNrOfImages() > 1) { wxString alignMsg = wxString::Format(_("%d control points.\n"), pano.getCtrlPoints().size()); double min; double max; double mean; double var; m_pano.calcCtrlPntsErrorStats( min, max, mean, var); wxString distStr; if (mean < 1) distStr = _("Very good fit"); else if (mean < 3) distStr = _("Good fit"); else if (mean < 7) distStr = _("Bad fit, some control points might be bad, or there are parallax and movement errors"); else distStr = _("Very bad fit. Check for bad control points, or images with parallax or movement. The optimizer might have failed. Manual intervention required."); alignMsg = alignMsg + wxString::Format(_("Mean error after optimization: %.1f pixel\n"), mean) + distStr; // TODO: check if all images are connected, and notify user, if they are not. // need to resize the text widget somehow! m_alignText->SetLabel(alignMsg); } // TODO: update meaningful help text and dynamic links to relevant tabs } // ##### Here start the eventhandlers ##### // Yaw by text -> double void AssistantPanel::OnAlign( wxCommandEvent & e ) { // create control points // all images.. UIntSet imgs; if (m_pano.getNrOfImages() < 2) { wxMessageBox(_("At least two images are required."),_("Error")); return; } fill_set(imgs, 0, m_pano.getNrOfImages()-1); // TODO: make configurable long nFeatures = 15; bool createCtrlP = true; // TODO: handle existing control points properly instead of adding them twice. if (m_pano.getNrOfCtrlPoints() > 0) { int a = wxMessageBox(wxString::Format(_("The panorama already has %d control points.\n\nSkip control points creation?"), m_pano.getNrOfCtrlPoints()), _("Skip control point creation?"), wxICON_QUESTION | wxYES_NO); createCtrlP = a != wxYES; } wxString alignMsg; if (createCtrlP) { AutoCtrlPointCreator matcher; CPVector cps = matcher.automatch(m_pano, imgs, nFeatures); int nCP = cps.size(); GlobalCmdHist::getInstance().addCommand( new PT::AddCtrlPointsCmd(m_pano, cps) ); } // TODO: check if enough control points have been created. and that there are // no missing links // optimize panorama Panorama optPano = m_pano.getSubset(imgs); // TODO: set proper optimisation settings. // set TIFF_m with enblend PanoramaOptions opts = m_pano.getOptions(); opts.outputFormat = PanoramaOptions::TIFF; opts.blendMode = PanoramaOptions::ENBLEND_BLEND; opts.remapper = PanoramaOptions::NONA; opts.tiff_saveROI = true; opts.tiffCompression = "Deflate"; opts.setProjection(PanoramaOptions::EQUIRECTANGULAR); // calculate proper scaling, 1:1 resolution. // Otherwise optimizer distances are meaningless. opts.setWidth(30000, false); opts.setHeight(15000); optPano.setOptions(opts); int w = optPano.calcOptimalWidth(); opts.setWidth(w); opts.setHeight(w/2); optPano.setOptions(opts); { wxBusyCursor bc; // temporarily disable PT progress dialog.. deregisterPTWXDlgFcn(); smartOptimize(optPano); registerPTWXDlgFcn(); } // center and resize frame optPano.centerHorizontically(); opts = optPano.getOptions(); double hfov, vfov, height; optPano.fitPano(hfov, height); opts.setHFOV(hfov); opts.setHeight(roundi(height)); vfov = opts.getVFOV(); // choose proper projection type if (vfov < 100) { // cylindrical or rectilinear if (hfov < 100) { opts.setProjection(PanoramaOptions::RECTILINEAR); } else { opts.setProjection(PanoramaOptions::CYLINDRICAL); } } // calc optimal size using output projection optPano.setOptions(opts); w = optPano.calcOptimalWidth(); opts.setWidth(w, true); optPano.setOptions(opts); // TODO: merge the following commands. // copy information into the main panorama GlobalCmdHist::getInstance().addCommand( new PT::UpdateVariablesCPSetCmd(m_pano, imgs, optPano.getVariables(), optPano.getCtrlPoints()) ); // copy information into our panorama GlobalCmdHist::getInstance().addCommand( new PT::SetPanoOptionsCmd(m_pano, opts) ); // show preview frame wxCommandEvent dummy; MainFrame::Get()->OnTogglePreviewFrame(dummy); // enable stitch button m_createButton->Enable(); } void AssistantPanel::OnCreate( wxCommandEvent & e ) { // just run the stitcher // this is kind of a bad hack, since several settings are determined // based on the current state of PanoPanel, and not the Panorama object itself wxCommandEvent dummy; MainFrame::Get()->OnDoStitch(dummy); } void AssistantPanel::OnLoadLens(wxCommandEvent & e) { unsigned int imgNr = 0; unsigned int lensNr = 0; Lens lens = m_pano.getLens(lensNr); VariableMap vars = m_pano.getImageVariables(imgNr); ImageOptions imgopts = m_pano.getImage(imgNr).getOptions(); wxString fname; wxFileDialog dlg(this, _("Load lens parameters"), wxConfigBase::Get()->Read(wxT("lensPath"),wxT("")), wxT(""), _("Lens Project Files (*.ini)|*.ini|All files (*.*)|*.*"), wxOPEN, wxDefaultPosition); if (dlg.ShowModal() == wxID_OK) { fname = dlg.GetPath(); wxConfig::Get()->Write(wxT("lensPath"), dlg.GetDirectory()); // remember for later // read with with standart C numeric format char * old_locale = setlocale(LC_NUMERIC,NULL); setlocale(LC_NUMERIC,"C"); { wxFileConfig cfg(wxT("hugin lens file"),wxT(""),fname); int integer=0; double d; cfg.Read(wxT("Lens/type"), &integer); lens.setProjection ((Lens::LensProjectionFormat) integer); cfg.Read(wxT("Lens/crop"), &d); lens.setCropFactor(d); cfg.Read(wxT("Lens/hfov"), &d); map_get(vars,"v").setValue(d); DEBUG_DEBUG("read lens hfov: " << d); // loop to load lens variables char ** varname = Lens::variableNames; while (*varname) { wxString key(wxT("Lens/")); key.append(wxString(*varname, *wxConvCurrent)); d = 0; if (cfg.Read(key,&d)) { // only set value if variabe was found in the script map_get(vars,*varname).setValue(d); integer = 1; key.append(wxT("_link")); cfg.Read(key, &integer); map_get(lens.variables, *varname).setLinked(integer != 0); } varname++; } long vigCorrMode=0; cfg.Read(wxT("Lens/vigCorrMode"), &vigCorrMode); imgopts.m_vigCorrMode = vigCorrMode; wxString flatfield; bool readok = cfg.Read(wxT("Lens/flatfield"), &flatfield); imgopts.m_flatfield = std::string((const char *)flatfield.mb_str()); // TODO: crop parameters } // reset locale setlocale(LC_NUMERIC,old_locale); GlobalCmdHist::getInstance().addCommand( new PT::ChangeLensCmd(m_pano, lensNr, lens) ); GlobalCmdHist::getInstance().addCommand( new PT::UpdateImageVariablesCmd(m_pano, imgNr, vars) ); // get all images with the current lens. UIntSet imgs; for (unsigned int i = 0; i < m_pano.getNrOfImages(); i++) { if (m_pano.getImage(i).getLensNr() == lensNr) { imgs.insert(i); } } // set image options. GlobalCmdHist::getInstance().addCommand( new PT::SetImageOptionsCmd(m_pano, imgopts, imgs) ); } } void AssistantPanel::OnExifToggle (wxCommandEvent & e) { if (m_exifToggle->GetValue()) { // if activated, load exif info Lens lens = m_pano.getLens(0); // check file extension wxFileName file(wxString(m_pano.getImage(0).getFilename().c_str(), *wxConvCurrent)); if (file.GetExt().CmpNoCase(wxT("jpg")) == 0 || file.GetExt().CmpNoCase(wxT("jpeg")) == 0 ) { double c=0; double roll = 0; initLensFromFile(m_pano.getImage(0).getFilename().c_str(), c, lens, roll); GlobalCmdHist::getInstance().addCommand( new PT::ChangeLensCmd( m_pano, 0, lens ) ); } else { wxLogError(_("Not a jpeg file:") + file.GetName()); } XRCCTRL(*this, "ass_lens_group", wxPanel)->Disable(); } else { // exif disabled XRCCTRL(*this, "ass_lens_group", wxPanel)->Enable(); } } void AssistantPanel::OnLensTypeChanged (wxCommandEvent & e) { // uses enum Lens::LensProjectionFormat from PanoramaMemento.h int var = m_lensTypeChoice->GetSelection(); Lens lens = m_pano.getLens(0); if (lens.getProjection() != (Lens::LensProjectionFormat) var) { lens.setProjection((Lens::LensProjectionFormat) (var)); GlobalCmdHist::getInstance().addCommand( new PT::ChangeLensCmd( m_pano, 0, lens ) ); } } void AssistantPanel::OnFocalLengthChanged(wxCommandEvent & e) { // always change first lens wxString text = m_focalLengthText->GetValue(); DEBUG_INFO("focal length: " << text.mb_str()); double val; if (!str2double(text, val)) { return; } // always change first lens... Lens lens = m_pano.getLens(0); lens.setFocalLength(val); GlobalCmdHist::getInstance().addCommand( new PT::ChangeLensCmd( m_pano, 0, lens) ); } void AssistantPanel::OnCropFactorChanged(wxCommandEvent & e) { wxString text = m_cropFactorText->GetValue(); DEBUG_INFO("crop factor: " << text.mb_str()); double val; if (!str2double(text, val)) { return; } // always change first lens... Lens lens = m_pano.getLens(0); double fl = lens.getFocalLength(); lens.setCropFactor(val); lens.setFocalLength(fl); GlobalCmdHist::getInstance().addCommand( new PT::ChangeLensCmd( m_pano, 0, lens) ); } Index: huginApp.cpp =================================================================== RCS file: /cvsroot/hugin/hugin/src/hugin/huginApp.cpp,v retrieving revision 1.98 retrieving revision 1.99 diff -u -d -r1.98 -r1.99 --- huginApp.cpp 11 Nov 2006 21:29:25 -0000 1.98 +++ huginApp.cpp 17 Dec 2006 10:49:50 -0000 1.99 @@ -286,6 +286,7 @@ wxXmlResource::Get()->Load(m_xrcPrefix + wxT("vig_corr_dlg.xrc")); #ifdef USE_WX253 wxXmlResource::Get()->Load(m_xrcPrefix + wxT("cp_editor_panel-2.5.xrc")); + wxXmlResource::Get()->Load(m_xrcPrefix + wxT("assistant_panel.xrc")); wxXmlResource::Get()->Load(m_xrcPrefix + wxT("images_panel-2.5.xrc")); wxXmlResource::Get()->Load(m_xrcPrefix + wxT("lens_panel-2.5.xrc")); wxXmlResource::Get()->Load(m_xrcPrefix + wxT("main_frame-2.5.xrc")); Index: Makefile.am =================================================================== RCS file: /cvsroot/hugin/hugin/src/hugin/Makefile.am,v retrieving revision 1.26 retrieving revision 1.27 diff -u -d -r1.26 -r1.27 --- Makefile.am 19 Oct 2006 16:30:47 -0000 1.26 +++ Makefile.am 17 Dec 2006 10:49:50 -0000 1.27 @@ -18,7 +18,8 @@ TextKillFocusHandler.cpp ImageOrientationPanel.cpp ImageOrientationFrame.cpp PanoDruid.cpp \ CPZoomDisplayPanel.cpp CPFineTuneFrame.cpp \ PreferencesDialog.cpp LocalizedFileTipProvider.cpp VigCorrDialog.cpp \ - wxPanoCommand.cpp MyExternalCmdExecDialog.cpp UniversalCursor.cpp + wxPanoCommand.cpp MyExternalCmdExecDialog.cpp UniversalCursor.cpp \ + AssistantPanel.cpp if HAVE_MINGW WIN_SRC = hugin_rc.rc Index: MainFrame.cpp =================================================================== RCS file: /cvsroot/hugin/hugin/src/hugin/MainFrame.cpp,v retrieving revision 1.182 retrieving revision 1.183 diff -u -d -r1.182 -r1.183 --- MainFrame.cpp 30 Nov 2006 07:36:48 -0000 1.182 +++ MainFrame.cpp 17 Dec 2006 10:49:50 -0000 1.183 @@ -39,6 +39,7 @@ #include "hugin/wxPanoCommand.h" #include "hugin/CommandHistory.h" #include "hugin/PanoPanel.h" +#include "hugin/AssistantPanel.h" #include "hugin/ImagesPanel.h" #include "hugin/LensPanel.h" #include "hugin/OptimizePanel.h" @@ -208,11 +209,19 @@ // Disable tools by default enableTools(false); - // image_panel // put an "unknown" object in an xrc file and // take as wxObject (second argument) the return value of wxXmlResource::Get // finish the images_panel DEBUG_TRACE(""); + + assistant_panel = new AssistantPanel( this, wxDefaultPosition, wxDefaultSize, + &pano); + + wxXmlResource::Get()->AttachUnknownControl ( + wxT("assistant_panel_unknown"), + assistant_panel ); + + // image_panel images_panel = new ImagesPanel( this, wxDefaultPosition, wxDefaultSize, &pano); @@ -1020,6 +1029,14 @@ opt_panel->OnOptimizeButton(dummy); } +void MainFrame::OnDoStitch(wxCommandEvent & e) +{ + DEBUG_TRACE(""); + wxCommandEvent dummy; + pano_panel->DoStitch(dummy); +} + + void MainFrame::OnFineTuneAll(wxCommandEvent & e) { DEBUG_TRACE(""); @@ -1171,7 +1188,7 @@ void MainFrame::ShowCtrlPoint(unsigned int cpNr) { DEBUG_DEBUG("Showing control point " << cpNr); - m_notebook->SetSelection(2); + m_notebook->SetSelection(3); cpe->ShowControlPoint(cpNr); } Index: OptimizePanel.cpp =================================================================== RCS file: /cvsroot/hugin/hugin/src/hugin/OptimizePanel.cpp,v retrieving revision 1.39 retrieving revision 1.40 diff -u -d -r1.39 -r1.40 --- OptimizePanel.cpp 14 Aug 2006 21:32:48 -0000 1.39 +++ OptimizePanel.cpp 17 Dec 2006 10:49:50 -0000 1.40 @@ -527,22 +527,11 @@ bool OptimizePanel::AskApplyResult(const Panorama & pano) { - // calculate average cp distance - double sum_error = 0; - double sum_squared_error = 0; - double max_error = 0; - const CPVector & cps = pano.getCtrlPoints(); - CPVector::const_iterator it; - for (it = cps.begin() ; it != cps.end(); it++) { - sum_error += (*it).error; - sum_squared_error += (*it).error * (*it).error; - if ((*it).error > max_error) { - max_error = (*it).error; - } - } - double std_dev = sqrt((cps.size()*sum_squared_error - sum_error*sum_error) / (cps.size()*(cps.size()-1))); - double mean_error = sum_error / cps.size(); - + double min; + double max; + double mean; + double var; + pano.calcCtrlPntsErrorStats( min, max, mean, var); // check for HFOV lines. if smaller than 1 report a warning; // also check for high distortion coefficients. bool smallHFOV=false; @@ -563,11 +552,11 @@ style = wxYES_NO; } else if (highDist) { msg.Printf(_("Optimizer run finished.\nResults:\n average control point distance: %f\n standard deviation: %f\n maximum: %f\n\n*WARNING*: very high distortion coefficients (a,b,c) have been estimated.\nThe results are probably invalid.\nOnly optimize all distortion parameters when many, well spread control points are used.\nPlease reset the a,b and c parameters to zero and add more control points\n\nApply the changes anyway?"), - mean_error, std_dev, max_error); + mean, var, max); style = wxYES_NO | wxICON_EXCLAMATION; } else { msg.Printf(_("Optimizer run finished.\nResults:\n average control point distance: %f\n standard deviation: %f\n maximum: %f\n\nApply the changes?"), - mean_error, std_dev, max_error); + mean, var, max); style = wxYES_NO | wxICON_EXCLAMATION; } Index: LensPanel.cpp =================================================================== RCS file: /cvsroot/hugin/hugin/src/hugin/LensPanel.cpp,v retrieving revision 1.98 retrieving revision 1.99 diff -u -d -r1.98 -r1.99 --- LensPanel.cpp 11 Nov 2006 21:29:25 -0000 1.98 +++ LensPanel.cpp 17 Dec 2006 10:49:50 -0000 1.99 @@ -995,11 +995,15 @@ _("Adding Image"), tval); t.ToDouble(&cropFactor); wxConfigBase::Get()->Write(wxT("/LensDefaults/CropFactor"), cropFactor); - + l.m_hasExif = true; return l.initFromFile(filename, cropFactor, roll); + } else { + // no exif info found.. ask user for all details + l.m_hasExif = false; } return false; } + l.m_hasExif = true; return true; } Index: PanoDruid.cpp =================================================================== RCS file: /cvsroot/hugin/hugin/src/hugin/PanoDruid.cpp,v retrieving revision 1.21 retrieving revision 1.22 diff -u -d -r1.21 -r1.22 --- PanoDruid.cpp 17 Mar 2006 04:53:09 -0000 1.21 +++ PanoDruid.cpp 17 Dec 2006 10:49:50 -0000 1.22 @@ -398,3 +398,50 @@ } return NULL; } + + + +////////////////////////////////////////////////////////////////////////////// + +// The Panorama Druid is a set of tiered heuristics and advice on how to +// improve the current panorama. + +struct advocation +{ + const wxChar* name; + const wxChar* graphic; + const wxChar* brief; + const wxChar* text; +}; + +static struct advocation _advice[] = +{ + { wxT("ERROR"), wxT("druid.images.128.png"), // "ERROR" must be at index 0 + _("The druid has no advice at this time."), wxT("") }, + + { wxT("READY"), wxT("druid.stitch.128.png"), + _("The druid finds no problems with your panorama."), + _("Stitch your final image now, and then use an image editor\nsuch as the GNU Image Manipulation Program (the GIMP)\nto add any finishing touches.") }, + + { wxT("NO IMAGES"), wxT("druid.images.128.png"), + _("To get started, add some image files."), + _("You can add any number of images using the Images tab.") }, + + { wxT("ONE IMAGE"), wxT("druid.images.128.png"), + _("Add at least one more image."), + _("You should have at least two files listed in the Images tab.") }, + + { wxT("LOW HFOV"), wxT("druid.lenses.128.png"), + _("The Horizontal Field of View (HFOV) may be too low."), + _("Check that the focal lengths and/or hfov figures\nfor each image are correct for the camera settings.\nThen calculate the visible field of view again.\nHFOV is measured in degrees of arc, usually between\n5 and 120 degrees per image unless using specialized\nlenses.") }, + + { wxT("HUGE FINAL"), wxT("druid.stitch.128.png"), + _("Warning: current stitch has huge dimensions."), + _("Very large pixel dimensions are currently entered.\nSome computers may take an excessively long time\nto render such a large final image.\nFor best results, use the automatic Calc button on\nthe Panorama Options tab to determine the\npixel dimensions which will give the best quality.") }, + + { wxT("UNSAVED"), wxT("druid.stitch.128.png"), + _("Warning: you haven't saved the current project."), + _("While everything else seems to be ready to stitch,\ndon't forget to save your project file so you can\nexperiment or adjust the settings later.") }, + + { NULL, NULL, wxT("") } +}; Index: PanoPanel.cpp =================================================================== RCS file: /cvsroot/hugin/hugin/src/hugin/PanoPanel.cpp,v retrieving revision 1.102 retrieving revision 1.103 diff -u -d -r1.102 -r1.103 --- PanoPanel.cpp 15 Dec 2006 23:10:12 -0000 1.102 +++ PanoPanel.cpp 17 Dec 2006 10:49:50 -0000 1.103 @@ -431,7 +431,7 @@ if (preset == 3) { opts.setWidth(1024); } else { - opts.setWidth(CalcOptimalWidth()); + opts.setWidth(pano.calcOptimalWidth()); } switch (preset) { @@ -609,28 +609,12 @@ } -unsigned PanoPanel::CalcOptimalWidth() -{ - PanoramaOptions opt = pano.getOptions(); - double scale = 0; - for (unsigned i = 0; i < pano.getNrOfImages(); i++) { - PT::PanoImage img = pano.getImage(i); - PT::ImageOptions iopt = img.getOptions(); - // get hfov - //double v = const_map_get(pano.getImageVariables(i),"v").getValue(); - double s = calcOptimalPanoScale(pano.getSrcImage(i), opt); - if (scale < s) { - scale = s; - } - } - return roundi(scale * opt.getWidth()); -} void PanoPanel::DoCalcOptimalWidth(wxCommandEvent & e) { PanoramaOptions opt = pano.getOptions(); - unsigned width = CalcOptimalWidth(); + unsigned width = pano.calcOptimalWidth(); if (width > 0) { opt.setWidth( width ); GlobalCmdHist::getInstance().addCommand( |