StitchingExecutor.cpp

Go to the documentation of this file.
00001 
00008 /*  This is free software; you can redistribute it and/or
00009 *  modify it under the terms of the GNU General Public
00010 *  License as published by the Free Software Foundation; either
00011 *  version 2 of the License, or (at your option) any later version.
00012 *
00013 *  This software is distributed in the hope that it will be useful,
00014 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00015 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00016 *  Lesser General Public License for more details.
00017 *
00018 *  You should have received a copy of the GNU General Public
00019 *  License along with this software. If not, see
00020 *  <http://www.gnu.org/licenses/>.
00021 *
00022 */
00023 
00024 #include "StitchingExecutor.h"
00025 
00026 #include <list>
00027 #include <wx/config.h>
00028 #include <wx/translation.h>
00029 #include <wx/arrstr.h>
00030 #include <wx/filefn.h>
00031 #include <wx/txtstrm.h>
00032 #include <wx/wfstream.h>
00033 #include "hugin_utils/utils.h"
00034 #include "hugin_base/panotools/PanoToolsUtils.h"
00035 #include "hugin_base/panodata/PanoramaOptions.h"
00036 #include "hugin_base/algorithms/basic/LayerStacks.h"
00037 #include "base_wx/platform.h"
00038 #include "base_wx/wxPlatform.h"
00039 #include "base_wx/LensTools.h"
00040 #include "hugin/config_defaults.h"
00041 
00042 namespace HuginQueue
00043 {
00044     namespace detail
00045     {
00046         // contains some helper functions
00048         wxArrayString GetNumberedFilename(const wxString& prefix, const wxString& postfix, const HuginBase::UIntSet& img)
00049         {
00050             wxArrayString filenames;
00051             for (HuginBase::UIntSet::const_iterator it = img.begin(); it != img.end(); ++it)
00052             {
00053                 filenames.Add(wxString::Format(wxT("%s%04u%s"), prefix.c_str(), *it, postfix.c_str()));
00054             };
00055             return filenames;
00056         };
00057 
00059         void AddToArray(const wxArrayString& input, wxArrayString& output)
00060         {
00061             for (size_t i = 0; i < input.size(); ++i)
00062             {
00063                 output.Add(input[i]);
00064             };
00065         };
00066 
00068         const wxString GetConfigTempDir(const wxConfigBase* config)
00069         {
00070             wxString tempDir = config->Read(wxT("tempDir"), wxT(""));
00071             if (!tempDir.IsEmpty())
00072             {
00073                 if (tempDir.Last() != wxFileName::GetPathSeparator())
00074                 {
00075                     tempDir.Append(wxFileName::GetPathSeparator());
00076                 }
00077             };
00078             return tempDir;
00079         };
00080 
00084         wxString GenerateFinalArgfile(const HuginBase::Panorama & pano, const wxString& projectName, const wxConfigBase* config, const HuginBase::UIntSet& images, const double exifToolVersion)
00085         {
00086             wxString argfileInput = config->Read(wxT("/output/FinalArgfile"), wxEmptyString);
00087             const bool generateGPanoTags = (config->Read(wxT("/output/writeGPano"), HUGIN_EXIFTOOL_CREATE_GPANO) == 1l) && (exifToolVersion >= 9.09);
00088             pano_projection_features proj;
00089             const HuginBase::PanoramaOptions &opts = pano.getOptions();
00090             const bool readProjectionName = panoProjectionFeaturesQuery(opts.getProjection(), &proj) != 0;
00091             // build placeholder map
00092             struct Placeholder
00093             {
00094                 wxString placeholder;
00095                 wxString value;
00096                 Placeholder(const wxString& holder, const wxString& newValue)
00097                 {
00098                     placeholder = holder;
00099                     value = newValue;
00100                 };
00101             };
00102             std::list<Placeholder> placeholders;
00103 #ifdef _WIN32
00104             const wxString linebreak(wxT("&#xd;&#xa;"));
00105 #else
00106             const wxString linebreak(wxT("&#xa;"));
00107 #endif
00108             if (readProjectionName)
00109             {
00110                 // %projectionNumber have to be processed before %projection, otherwise it does not work
00111                 placeholders.push_back(Placeholder(wxT("%projectionNumber"), wxString::Format(wxT("%d"), opts.getProjection())));
00112                 placeholders.push_back(Placeholder(wxT("%projection"), wxString(proj.name, wxConvLocal)));
00113             };
00114             // fill in some placeholders
00115             placeholders.push_back(Placeholder(wxT("%hfov"), wxString::Format(wxT("%.0f"), opts.getHFOV())));
00116             placeholders.push_back(Placeholder(wxT("%vfov"), wxString::Format(wxT("%.0f"), opts.getVFOV())));
00117             placeholders.push_back(Placeholder(wxT("%ev"), wxString::Format(wxT("%.2f"), opts.outputExposureValue)));
00118             placeholders.push_back(Placeholder(wxT("%nrImages"), wxString::Format(wxT("%lu"), (unsigned long)images.size())));
00119             placeholders.push_back(Placeholder(wxT("%nrAllImages"), wxString::Format(wxT("%lu"), (unsigned long)pano.getNrOfImages())));
00120             placeholders.push_back(Placeholder(wxT("%fullwidth"), wxString::Format(wxT("%u"), opts.getWidth())));
00121             placeholders.push_back(Placeholder(wxT("%fullheight"), wxString::Format(wxT("%u"), opts.getHeight())));
00122             placeholders.push_back(Placeholder(wxT("%width"), wxString::Format(wxT("%d"), opts.getROI().width())));
00123             placeholders.push_back(Placeholder(wxT("%height"), wxString::Format(wxT("%d"), opts.getROI().height())));
00124             wxFileName projectFilename(projectName);
00125             placeholders.push_back(Placeholder(wxT("%projectname"), projectFilename.GetFullName()));
00126             // now open the final argfile
00127             wxFileName tempArgfileFinal(wxFileName::CreateTempFileName(GetConfigTempDir(config) + wxT("he")));
00128             wxFFileOutputStream outputStream(tempArgfileFinal.GetFullPath());
00129             wxTextOutputStream outputFile(outputStream);
00130             // write argfile
00131             outputFile << wxT("-Software=Hugin ") << wxString(hugin_utils::GetHuginVersion().c_str(), wxConvLocal) << endl;
00132             outputFile << wxT("-E") << endl;
00133             outputFile << wxT("-UserComment<${UserComment}") << linebreak;
00134             if (readProjectionName)
00135             {
00136                 outputFile << wxT("Projection: ") << wxString(proj.name, wxConvLocal) << wxT(" (") << opts.getProjection() << wxT(")") << linebreak;
00137             };
00138             outputFile << wxT("FOV: ") << wxString::Format(wxT("%.0f"), opts.getHFOV()) << wxT(" x ") << wxString::Format(wxT("%.0f"), opts.getVFOV()) << linebreak;
00139             outputFile << wxT("Ev: ") << wxString::Format(wxT("%.2f"), opts.outputExposureValue) << endl;
00140             outputFile << wxT("-f") << endl;
00141             if (generateGPanoTags)
00142             {
00143                 //GPano tags are only indented for equirectangular images
00144                 if (opts.getProjection() == HuginBase::PanoramaOptions::EQUIRECTANGULAR)
00145                 {
00146                     const vigra::Rect2D roi = opts.getROI();
00147                     int left = roi.left();
00148                     int top = roi.top();
00149                     int width = roi.width();
00150                     int height = roi.height();
00151 
00152                     int fullWidth = opts.getWidth();
00153                     if (opts.getHFOV()<360)
00154                     {
00155                         fullWidth = static_cast<int>(opts.getWidth() * 360.0 / opts.getHFOV());
00156                         left += (fullWidth - opts.getWidth()) / 2;
00157                     };
00158                     int fullHeight = opts.getHeight();
00159                     if (opts.getVFOV()<180)
00160                     {
00161                         fullHeight = static_cast<int>(opts.getHeight() * 180.0 / opts.getVFOV());
00162                         top += (fullHeight - opts.getHeight()) / 2;
00163                     };
00164                     outputFile << wxT("-UsePanoramaViewer=True") << endl;
00165                     outputFile << wxT("-StitchingSoftware=Hugin") << endl;
00166                     outputFile << wxT("-ProjectionType=equirectangular") << endl;
00167                     outputFile << wxT("-CroppedAreaLeftPixels=") << left << endl;
00168                     outputFile << wxT("-CroppedAreaTopPixels=") << top << endl;
00169                     outputFile << wxT("-CroppedAreaImageWidthPixels=") << width << endl;
00170                     outputFile << wxT("-CroppedAreaImageHeightPixels=") << height << endl;
00171                     outputFile << wxT("-FullPanoWidthPixels=") << fullWidth << endl;
00172                     outputFile << wxT("-FullPanoHeightPixels=") << fullHeight << endl;
00173                     outputFile << wxT("-SourcePhotosCount=") << static_cast<wxUint32>(pano.getNrOfImages()) << endl;
00174                 };
00175             };
00176             // now open the input file and append it
00177             if (!argfileInput.IsEmpty())
00178             {
00179                 if (wxFileExists(argfileInput))
00180                 {
00181                     wxFileInputStream inputFileStream(argfileInput);
00182                     wxTextInputStream input(inputFileStream);
00183                     while (inputFileStream.IsOk() && !inputFileStream.Eof())
00184                     {
00185                         wxString line = input.ReadLine();
00186                         // replace all placeholders
00187                         for (auto variable : placeholders)
00188                         {
00189                             line.Replace(variable.placeholder, variable.value, true);
00190                         };
00191                         // now append to existing argfile
00192                         outputFile << line << endl;
00193                     };
00194                 };
00195             };
00196             return tempArgfileFinal.GetFullPath();
00197         };
00198 
00199         wxString PrintDetailInfo(const HuginBase::Panorama& pano, const HuginBase::PanoramaOptions& opts, const HuginBase::UIntSet& allActiveImages, const wxString& prefix, const wxString& bindir, wxConfigBase* config, double& exiftoolVersion)
00200         {
00201             wxString output;
00202             const wxString wxEndl(wxT("\n"));
00203             output
00204                 << wxT("============================================") << wxEndl
00205                 << _("Stitching panorama...") << wxEndl
00206                 << wxT("============================================") << wxEndl
00207                 << wxEndl
00208                 << _("Platform:") << wxT(" ") << wxGetOsDescription() << wxEndl
00209                 << _("Version:") << wxT(" ") << wxString(hugin_utils::GetHuginVersion().c_str(), wxConvLocal) << wxEndl
00210                 << _("Working directory:") << wxT(" ") << wxFileName::GetCwd() << wxEndl
00211                 << _("Output prefix:") << wxT(" ") << prefix << wxEndl
00212                 << wxEndl;
00213             if (opts.outputLDRBlended || opts.outputLDRExposureBlended || opts.outputLDRExposureLayersFused || 
00214                 opts.outputHDRBlended || opts.outputLDRExposureLayers)
00215             {
00216                 switch (opts.blendMode)
00217                 {
00218                     case HuginBase::PanoramaOptions::ENBLEND_BLEND:
00219                         {
00220                             wxArrayString version;
00221                             if (wxExecute(wxEscapeFilename(GetExternalProgram(config, bindir, wxT("enblend"))) + wxT(" --version"), version, wxEXEC_SYNC) == 0l)
00222                             {
00223                                 output << _("Blender:") << wxT(" ") << version[0] << wxEndl;
00224                             }
00225                             else
00226                             {
00227                                 output << _("Blender:") << wxT(" ") << _("Unknown blender (enblend --version failed)") << wxEndl;
00228                             };
00229                         };
00230                         break;
00231                     case HuginBase::PanoramaOptions::INTERNAL_BLEND:
00232                     default:  // switch to internal blender for all other cases, not exposed in GUI
00233                         output << _("Blender:") << wxT(" ") << _("internal") << wxEndl;
00234                         break;
00235                 };
00236             };
00237             if (opts.outputLDRExposureBlended || opts.outputLDRExposureLayersFused || opts.outputLDRStacks )
00238             {
00239                 wxArrayString version;
00240                 if (wxExecute(wxEscapeFilename(GetExternalProgram(config, bindir, wxT("enfuse"))) + wxT(" --version"), version, wxEXEC_SYNC) == 0l)
00241                 {
00242                     output << _("Exposure fusion:") << wxT(" ") << version[0] << wxEndl;
00243                 }
00244                 else
00245                 {
00246                     output << _("Exposure fusion:") << wxT(" ") << _("Unknown exposure fusion (enfuse --version failed)") << wxEndl;
00247                 };
00248             };
00249             if (config->Read(wxT("/output/useExiftool"), HUGIN_USE_EXIFTOOL) == 1l)
00250             {
00251                 wxArrayString version;
00252                 if (wxExecute(wxEscapeFilename(GetExternalProgram(config, bindir, wxT("exiftool"))) + wxT(" -ver"), version, wxEXEC_SYNC) == 0l)
00253                 {
00254                     output << _("ExifTool version:") << wxT(" ") << version[0] << wxEndl;
00255                     version[0].ToCDouble(&exiftoolVersion);
00256                 }
00257                 else
00258                 {
00259                     output << _("ExifTool:") << wxT(" ") << _("FAILED") << wxEndl;
00260                     exiftoolVersion = 1;
00261                 };
00262             };
00263             output
00264                 << wxEndl
00265                 << _("Number of active images:") << wxT(" ") << allActiveImages.size() << wxEndl
00266                 << wxString::Format(_("Output exposure value: %.1f"), opts.outputExposureValue) << wxEndl
00267                 << wxString::Format(_("Canvas size: %dx%d"), opts.getSize().width(), opts.getSize().height()) << wxEndl
00268                 << wxString::Format(_("ROI: (%d, %d) - (%d, %d)"), opts.getROI().left(), opts.getROI().top(), opts.getROI().right(), opts.getROI().bottom()) << wxT(" ") << wxEndl
00269                 << wxString::Format(_("FOV: %.0fx%.0f"), opts.getHFOV(), opts.getVFOV()) << wxEndl;
00270             pano_projection_features proj;
00271             const bool readProjectionName = panoProjectionFeaturesQuery(opts.getProjection(), &proj) != 0;
00272             if (readProjectionName)
00273             {
00274                 output
00275                     << _("Projection:") << wxT(" ") << wxGetTranslation(wxString(proj.name, wxConvLocal))
00276                     << wxT("(") << opts.getProjection() << wxT(")") << wxEndl;
00277             }
00278             else
00279             {
00280                 output
00281                     << _("Projection:") << wxT(" ") << opts.getProjection() << wxEndl;
00282             };
00283             output
00284                 << _("Using GPU for remapping:") << wxT(" ") << (opts.remapUsingGPU ? _("true") : _("false")) << wxEndl
00285                 << wxEndl;
00286             if (opts.outputLDRBlended || opts.outputLDRExposureBlended || opts.outputLDRExposureLayersFused || opts.outputHDRBlended)
00287             {
00288                 output << _("Panorama Outputs:") << wxEndl;
00289                 if (opts.outputLDRBlended)
00290                 {
00291                     output << wxT("* ") << _("Exposure corrected, low dynamic range") << wxEndl;
00292                 };
00293                 if (opts.outputLDRExposureBlended)
00294                 {
00295                     output << wxT("* ") << _("Exposure fused from stacks") << wxEndl;
00296                 };
00297                 if (opts.outputLDRExposureLayersFused)
00298                 {
00299                     output << wxT("* ") << _("Exposure fused from any arrangement") << wxEndl;
00300                 };
00301                 if (opts.outputHDRBlended)
00302                 {
00303                     output << wxT("* ") << _("High dynamic range") << wxEndl;
00304                 };
00305                 output << wxEndl;
00306             };
00307             if (opts.outputLDRLayers || opts.outputLDRExposureRemapped || opts.outputHDRLayers)
00308             {
00309                 output << _("Remapped Images:") << wxEndl;
00310                 if (opts.outputLDRBlended)
00311                 {
00312                     output << wxT("* ") << _("Exposure corrected, low dynamic range") << wxEndl;
00313                 };
00314                 if (opts.outputLDRExposureRemapped)
00315                 {
00316                     output << wxT("* ") << _("No exposure correction, low dynamic range") << wxEndl;
00317                 };
00318                 if (opts.outputHDRLayers)
00319                 {
00320                     output << wxT("* ") << _("High dynamic range") << wxEndl;
00321                 };
00322                 output << wxEndl;
00323             };
00324             if (opts.outputLDRStacks || opts.outputHDRStacks)
00325             {
00326                 output << _("Combined stacks:") << wxEndl;
00327                 if (opts.outputLDRStacks)
00328                 {
00329                     output << wxT("* ") << _("Exposure fused stacks") << wxEndl;
00330                 };
00331                 if (opts.outputHDRStacks)
00332                 {
00333                     output << wxT("* ") << _("High dynamic range") << wxEndl;
00334                 };
00335                 output << wxEndl;
00336             };
00337             if (opts.outputLDRExposureLayers)
00338             {
00339                 output << _("Layers:") << wxEndl
00340                     << wxT("* ") << _("Blended layers of similar exposure, without exposure correction") << wxEndl
00341                     << wxEndl;
00342             };
00343             const HuginBase::SrcPanoImage img = pano.getImage(*allActiveImages.begin());
00344             output << _("First input image") << wxEndl
00345                 << _("Number:") << wxT(" ") << *allActiveImages.begin() << wxEndl
00346                 << _("Filename:") << wxT(" ") << img.getFilename() << wxEndl
00347                 << wxString::Format(_("Size: %dx%d"), img.getWidth(), img.getHeight()) << wxEndl
00348                 << _("Projection:") << wxT(" ") << getProjectionString(img) << wxEndl
00349                 << _("Response type:") << wxT(" ") << getResponseString(img) << wxEndl
00350                 << wxString::Format(_("HFOV: %.0f"), img.getHFOV()) << wxEndl
00351                 << wxString::Format(_("Exposure value: %.1f"), img.getExposureValue()) << wxEndl
00352                 << wxEndl;
00353             return output;
00354         };
00355 
00357         wxString GetQuotedFilenamesStringForVerdandi(const wxArrayString& files, const HuginBase::Panorama& pano, const HuginBase::UIntSetVector& stacks, const int referenceImage, const bool hardSeam)
00358         {
00359             // if output is hard seam we keep the order
00360             if (hardSeam)
00361             {
00362                 return GetQuotedFilenamesString(files);
00363             };
00364             // user wants a blended seam, we need to figure out the correct order
00365             int refImage = 0;
00366             // first build a subpano which contains only one image per stack of the original pano
00367             HuginBase::UIntSet stackImgs;
00368             for (size_t i = 0; i < stacks.size(); ++i)
00369             {
00370                 if (set_contains(stacks[i], referenceImage))
00371                 {
00372                     refImage = i;
00373                 };
00374                 stackImgs.insert(*(stacks[i].begin()));
00375             };
00376             // now create the subpano, don't forget to delete at end
00377             HuginBase::PanoramaData* subpano = pano.getNewSubset(stackImgs);
00378             HuginBase::UIntSet subpanoImgs;
00379             fill_set(subpanoImgs, 0, stackImgs.size() - 1);
00380             // find the blend order
00381             HuginBase::UIntVector blendOrder = HuginBase::getEstimatedBlendingOrder(*subpano, subpanoImgs, refImage);
00382             delete subpano;
00383             // now build the string in the correct order
00384             wxString s;
00385             for (size_t i = 0; i < blendOrder.size();++i)
00386             {
00387                 s.Append(wxEscapeFilename(files[blendOrder[i]]) + wxT(" "));
00388             };
00389             return s;
00390         };
00391     } // namespace detail
00392 
00393     CommandQueue* GetStitchingCommandQueue(const HuginBase::Panorama & pano, const wxString& ExePath, const wxString& project, const wxString& prefix, wxString& statusText, wxArrayString& outputFiles, wxArrayString& tempFilesDelete)
00394     {
00395         CommandQueue* commands = new CommandQueue;
00396         const HuginBase::UIntSet allActiveImages = getImagesinROI(pano, pano.getActiveImages());
00397         if (allActiveImages.empty())
00398         {
00399             std::cerr << "ERROR: No active images in ROI. Nothing to do." << std::endl;
00400             return commands;
00401         }
00402         std::vector<HuginBase::UIntSet> stacks;
00403 
00404         // check options, not all are currently supported
00405         HuginBase::PanoramaOptions opts = pano.getOptions();
00406         wxConfigBase* config = wxConfigBase::Get();
00407         opts.remapUsingGPU = config->Read(wxT("/Nona/UseGPU"), HUGIN_NONA_USEGPU) == 1;
00408         if (opts.remapper != HuginBase::PanoramaOptions::NONA)
00409         {
00410             std::cerr << "ERROR: Only nona remappper is supported by hugin_executor." << std::endl;
00411             return commands;
00412         };
00413         if (opts.blendMode != HuginBase::PanoramaOptions::ENBLEND_BLEND && opts.blendMode != HuginBase::PanoramaOptions::INTERNAL_BLEND)
00414         {
00415             std::cerr << "ERROR: Only enblend and internal remappper are currently supported by hugin_executor." << std::endl;
00416             return commands;
00417         };
00418         if (opts.hdrMergeMode != HuginBase::PanoramaOptions::HDRMERGE_AVERAGE)
00419         {
00420             std::cerr << "ERROR: Only hdr merger HDRMERGE_AVERAGE is currently supported by hugin_executor." << std::endl;
00421             return commands;
00422         };
00423         double exiftoolVersion;
00424         statusText=detail::PrintDetailInfo(pano, opts, allActiveImages, prefix, ExePath, config, exiftoolVersion);
00425         // prepare some often needed variables
00426         const wxString quotedProject(wxEscapeFilename(project));
00427         // prepare nona arguments
00428         wxString nonaArgs(wxT("-v "));
00429         wxString enLayersCompressionArgs;
00430         if (!opts.outputLayersCompression.empty())
00431         {
00432             nonaArgs.Append(wxT("-z ") + opts.outputLayersCompression + wxT(" "));
00433             enLayersCompressionArgs.Append(wxT(" --compression=") + opts.outputLayersCompression + wxT(" "));
00434         }
00435         else
00436         {
00437             if (opts.outputImageType == "jpg")
00438             {
00439                 nonaArgs.Append(wxT("-z LZW "));
00440             }
00441         };
00442         if (opts.remapUsingGPU)
00443         {
00444             nonaArgs.Append(wxT("-g "));
00445         };
00446         // prepare enblend arguments
00447         wxString enblendArgs;
00448         if (opts.blendMode == HuginBase::PanoramaOptions::ENBLEND_BLEND)
00449         {
00450             enblendArgs.Append(opts.enblendOptions);
00451             if ((opts.getHFOV() == 360.0) && (opts.getWidth()==opts.getROI().width()))
00452             {
00453                 enblendArgs.Append(wxT(" -w"));
00454             };
00455             const vigra::Rect2D roi (opts.getROI());
00456             if (roi.top() != 0 || roi.left() != 0)
00457             {
00458                 enblendArgs << wxT(" -f") << roi.width() << wxT("x") << roi.height() << wxT("+") << roi.left() << wxT("+") << roi.top();
00459             }
00460             else
00461             {
00462                 enblendArgs << wxT(" -f") << roi.width() << wxT("x") << roi.height();
00463             };
00464             enblendArgs.Append(wxT(" "));
00465         };
00466         // prepare internal blending arguments
00467         wxString verdandiArgs;
00468         if (opts.blendMode == HuginBase::PanoramaOptions::INTERNAL_BLEND)
00469         {
00470             verdandiArgs.Append(opts.verdandiOptions);
00471             if ((opts.getHFOV() == 360.0) && (opts.getWidth() == opts.getROI().width()))
00472             {
00473                 verdandiArgs.Append(wxT(" -w"));
00474             };
00475         };
00476         // prepare the compression switches
00477         wxString finalCompressionArgs;
00478         if (opts.outputImageType == "tif" && !opts.outputImageTypeCompression.empty())
00479         {
00480             finalCompressionArgs << wxT(" --compression=") << opts.outputImageTypeCompression;
00481         }
00482         else
00483         {
00484             if (opts.outputImageType == "jpg")
00485             {
00486                 finalCompressionArgs << wxT(" --compression=") << opts.quality;
00487             };
00488         };
00489         finalCompressionArgs.Append(wxT(" "));
00490         // prepare enfuse arguments
00491         wxString enfuseArgs(opts.enfuseOptions + wxT(" "));
00492         if ((opts.getHFOV() == 360.0) && (opts.getWidth() == opts.getROI().width()))
00493         {
00494             enfuseArgs.Append(wxT(" -w"));
00495         };
00496         const vigra::Rect2D roi (opts.getROI());
00497         if (roi.top() != 0 || roi.left() != 0)
00498         {
00499             enfuseArgs << wxT(" -f") << roi.width() << wxT("x") << roi.height() << wxT("+") << roi.left() << wxT("+") << roi.top();
00500         }
00501         else
00502         {
00503             enfuseArgs << wxT(" -f") << roi.width() << wxT("x") << roi.height();
00504         };
00505         enfuseArgs.Append(wxT(" "));
00506 
00507         // prepare exiftool args
00508         const bool copyMetadata = config->Read(wxT("/output/useExiftool"), HUGIN_USE_EXIFTOOL) == 1l;
00509         wxString exiftoolArgs;
00510         wxString exiftoolArgsFinal;
00511         if (copyMetadata)
00512         {
00513             exiftoolArgs = wxT("-overwrite_original -TagsFromFile ");
00514             exiftoolArgs.Append(wxEscapeFilename(wxString(pano.getImage(0).getFilename().c_str(), HUGIN_CONV_FILENAME)));
00515             // required tags, can not be overwritten
00516             exiftoolArgs.Append(wxT(" -WhitePoint -ColorSpace"));
00517             wxString exiftoolArgfile = config->Read(wxT("/output/CopyArgfile"), wxEmptyString);
00518             if (exiftoolArgfile.IsEmpty())
00519             {
00520                 exiftoolArgfile = wxString(std::string(hugin_utils::GetDataDir() + "hugin_exiftool_copy.arg").c_str(), HUGIN_CONV_FILENAME);
00521             };
00522             wxFileName argfile(exiftoolArgfile);
00523             argfile.Normalize();
00524             exiftoolArgs.Append(wxT(" -@ ") + wxEscapeFilename(argfile.GetFullPath()) + wxT(" "));
00525             wxString finalArgfile = detail::GenerateFinalArgfile(pano, project, config, allActiveImages, exiftoolVersion);
00526             if (!finalArgfile.IsEmpty())
00527             {
00528                 exiftoolArgsFinal.Append(wxT(" -@ ") + wxEscapeFilename(finalArgfile) + wxT(" "));
00529                 tempFilesDelete.Add(finalArgfile);
00530             };
00531         };
00532         wxArrayString filesForFullExiftool;
00533         wxArrayString filesForCopyTagsExiftool;
00534 
00535         // normal output
00536         if (opts.outputLDRBlended || opts.outputLDRLayers)
00537         {
00538             const wxArrayString remappedImages(detail::GetNumberedFilename(prefix, wxT(".tif"), allActiveImages));
00539             const wxString finalFilename(prefix + wxT(".") + opts.outputImageType);
00540             if (opts.blendMode == HuginBase::PanoramaOptions::INTERNAL_BLEND && opts.outputLDRBlended)
00541             {
00542                 wxString finalNonaArgs(wxT("-v -r ldr "));
00543                 if (opts.remapUsingGPU)
00544                 {
00545                     finalNonaArgs.Append(wxT("-g "));
00546                 }
00547                 if (!opts.verdandiOptions.empty())
00548                 {
00549                     finalNonaArgs.Append(opts.verdandiOptions);
00550                     finalNonaArgs.Append(wxT(" "));
00551                 };
00552                 if (opts.outputImageType == "tif")
00553                 {
00554                     finalNonaArgs.Append(wxT("-m TIFF "));
00555                     if (!opts.outputImageTypeCompression.empty())
00556                     {
00557                         finalNonaArgs.Append(wxT("-z ") + opts.outputImageTypeCompression + wxT(" "));
00558                     };
00559                 }
00560                 else
00561                 {
00562                     if (opts.outputImageType == "jpg")
00563                     {
00564                         finalNonaArgs.Append(wxT("-m JPEG -z "));
00565                         finalNonaArgs << opts.quality << wxT(" ");
00566                     }
00567                     else
00568                     {
00569                         if (opts.outputImageType == "png")
00570                         {
00571                             finalNonaArgs.Append(wxT("-m PNG "));
00572                         }
00573                         else
00574                         {
00575                             std::cerr << "ERROR: Invalid output image type found." << std::endl;
00576                             return commands;
00577                         };
00578                     };
00579                 };
00580                 if (opts.outputLDRLayers)
00581                 {
00582                     finalNonaArgs.Append(wxT("--save-intermediate-images "));
00583                     detail::AddToArray(remappedImages, outputFiles);
00584                 }
00585                 finalNonaArgs.Append(wxT("-o ") + wxEscapeFilename(prefix) + wxT(" ") + quotedProject);
00586                 commands->push_back(new NormalCommand(GetInternalProgram(ExePath, wxT("nona")),
00587                     finalNonaArgs, _("Remapping and blending LDR images...")));
00588                 outputFiles.Add(finalFilename);
00589                 if (copyMetadata)
00590                 {
00591                     filesForFullExiftool.Add(finalFilename);
00592                 };
00593             }
00594             else
00595             {
00596                 commands->push_back(new NormalCommand(GetInternalProgram(ExePath, wxT("nona")),
00597                     nonaArgs + wxT("-r ldr -m TIFF_m -o ") + wxEscapeFilename(prefix) + wxT(" ") + quotedProject,
00598                     _("Remapping LDR images...")));
00599                 detail::AddToArray(remappedImages, outputFiles);
00600                 if (opts.outputLDRBlended)
00601                 {
00602                     if (opts.blendMode == HuginBase::PanoramaOptions::ENBLEND_BLEND)
00603                     {
00604                         wxString finalEnblendArgs(enblendArgs + finalCompressionArgs);
00605                         finalEnblendArgs.Append(wxT(" -o ") + wxEscapeFilename(finalFilename) + wxT(" -- "));
00606                         commands->push_back(new NormalCommand(
00607                             GetExternalProgram(config, ExePath, wxT("enblend")), 
00608                             finalEnblendArgs + wxT(" ") + GetQuotedFilenamesString(remappedImages), 
00609                             _("Blending images..."))
00610                         );
00611                         outputFiles.Add(finalFilename);
00612                         if (copyMetadata)
00613                         {
00614                             filesForFullExiftool.Add(finalFilename);
00615                         };
00616                     };
00617                     if (!opts.outputLDRLayers)
00618                     {
00619                         detail::AddToArray(remappedImages, tempFilesDelete);
00620                     };
00621                 };
00622             };
00623         };
00624         // exposure fusion output
00625         if (opts.outputLDRExposureRemapped || opts.outputLDRStacks || opts.outputLDRExposureLayers ||
00626             opts.outputLDRExposureBlended || opts.outputLDRExposureLayersFused)
00627         {
00628             const wxArrayString remappedImages = detail::GetNumberedFilename(prefix + wxT("_exposure_layers_"), wxT(".tif"), allActiveImages);
00629             std::vector<HuginBase::UIntSet> exposureLayers;
00630             wxArrayString exposureLayersFiles;
00631             if (opts.outputLDRExposureLayers || opts.outputLDRExposureLayersFused)
00632             {
00633                 exposureLayers = getExposureLayers(pano, allActiveImages, opts);
00634             };
00635             if (opts.blendMode == HuginBase::PanoramaOptions::INTERNAL_BLEND &&
00636                 (opts.outputLDRExposureLayers || opts.outputLDRExposureLayersFused))
00637             {
00638                 // directly export exposure layers by nona
00639                 wxString finalNonaArgs(nonaArgs);
00640                 if (!opts.verdandiOptions.empty())
00641                 {
00642                     finalNonaArgs.Append(opts.verdandiOptions);
00643                     finalNonaArgs.Append(wxT(" "));
00644                 };
00645                 finalNonaArgs.append(wxT("-r ldr --create-exposure-layers --ignore-exposure -o ") + wxEscapeFilename(prefix + wxT("_exposure_")));
00646                 if (opts.outputLDRExposureRemapped || opts.outputLDRStacks || opts.outputLDRExposureBlended)
00647                 {
00648                     finalNonaArgs.append(wxT(" --save-intermediate-images --intermediate-suffix=layers_"));
00649                     detail::AddToArray(remappedImages, outputFiles);
00650                     if (!opts.outputLDRExposureRemapped)
00651                     {
00652                         detail::AddToArray(remappedImages, tempFilesDelete);
00653                     }
00654                 };
00655                 finalNonaArgs.append(wxT(" "));
00656                 finalNonaArgs.append(quotedProject);
00657                 commands->push_back(new NormalCommand(GetInternalProgram(ExePath, wxT("nona")),
00658                     finalNonaArgs, _("Remapping LDR images and blending exposure layers...")));
00659                 HuginBase::UIntSet exposureLayersNumber;
00660                 fill_set(exposureLayersNumber, 0, exposureLayers.size() - 1);
00661                 exposureLayersFiles = detail::GetNumberedFilename(prefix + wxT("_exposure_"), wxT(".tif"), exposureLayersNumber);
00662                 detail::AddToArray(exposureLayersFiles, outputFiles);
00663                 if (!opts.outputLDRExposureLayers)
00664                 {
00665                     detail::AddToArray(exposureLayersFiles, tempFilesDelete);
00666                 };
00667                 if (copyMetadata && opts.outputLDRExposureLayers)
00668                 {
00669                     detail::AddToArray(exposureLayersFiles, filesForCopyTagsExiftool);
00670                 };
00671             }
00672             else
00673             {
00674                 commands->push_back(new NormalCommand(GetInternalProgram(ExePath, wxT("nona")),
00675                     nonaArgs + wxT("-r ldr -m TIFF_m --ignore-exposure -o ") + wxEscapeFilename(prefix + wxT("_exposure_layers_")) + wxT(" ") + quotedProject,
00676                     _("Remapping LDR images without exposure correction...")));
00677                 detail::AddToArray(remappedImages, outputFiles);
00678                 if (!opts.outputLDRExposureRemapped)
00679                 {
00680                     detail::AddToArray(remappedImages, tempFilesDelete);
00681                 };
00682                 if (opts.outputLDRExposureLayers || opts.outputLDRExposureLayersFused)
00683                 {
00684                     // blending exposure layers, then fusing
00685                     // fuse all exposure layers
00686                     for (unsigned exposureLayer = 0; exposureLayer < exposureLayers.size(); ++exposureLayer)
00687                     {
00688                         const wxArrayString exposureLayersImgs = detail::GetNumberedFilename(prefix + wxT("_exposure_layers_"), wxT(".tif"), exposureLayers[exposureLayer]);
00689                         const wxString exposureLayerImgName = wxString::Format(wxT("%s_exposure_%04u%s"), prefix.c_str(), exposureLayer, wxT(".tif"));
00690                         exposureLayersFiles.Add(exposureLayerImgName);
00691                         outputFiles.Add(exposureLayerImgName);
00692                         // variant with internal blender is handled before, so we need only enblend
00693                         commands->push_back(new NormalCommand(
00694                             GetExternalProgram(config, ExePath, wxT("enblend")),
00695                             enblendArgs + enLayersCompressionArgs + wxT(" -o ") + wxEscapeFilename(exposureLayerImgName) + wxT(" -- ") + GetQuotedFilenamesString(exposureLayersImgs),
00696                             wxString::Format(_("Blending exposure layer %u..."), exposureLayer))
00697                         );
00698                         if (copyMetadata && opts.outputLDRExposureLayers)
00699                         {
00700                             filesForCopyTagsExiftool.Add(exposureLayerImgName);
00701                         };
00702                         if (!opts.outputLDRExposureLayers)
00703                         {
00704                             tempFilesDelete.Add(exposureLayerImgName);
00705                         };
00706                     };
00707                 };
00708             };
00709             if (opts.outputLDRExposureLayersFused)
00710             {
00711                 wxString finalEnfuseArgs(enfuseArgs + finalCompressionArgs);
00712                 const wxString fusedExposureLayersFilename(prefix + wxT("_blended_fused.") + opts.outputImageType);
00713                 finalEnfuseArgs.Append(wxT(" -o ") + wxEscapeFilename(fusedExposureLayersFilename) + wxT(" -- "));
00714                 commands->push_back(new NormalCommand(
00715                     GetExternalProgram(config, ExePath, wxT("enfuse")),
00716                     finalEnfuseArgs + wxT(" ")+GetQuotedFilenamesString(exposureLayersFiles), 
00717                     _("Fusing all exposure layers..."))
00718                 );
00719                 outputFiles.Add(fusedExposureLayersFilename);
00720                 if (copyMetadata)
00721                 {
00722                     filesForFullExiftool.Add(fusedExposureLayersFilename);
00723                 };
00724             };
00725             if (opts.outputLDRStacks || opts.outputLDRExposureBlended)
00726             {
00727                 // fusing stacks, then blending
00728                 stacks = getHDRStacks(pano, allActiveImages, opts);
00729                 wxArrayString stackedImages;
00730                 // fuse all stacks
00731                 for (unsigned stackNr = 0; stackNr < stacks.size(); ++stackNr)
00732                 {
00733                     const wxArrayString stackImgs = detail::GetNumberedFilename(prefix + wxT("_exposure_layers_"), wxT(".tif"), stacks[stackNr]);
00734                     const wxString stackImgName = wxString::Format(wxT("%s_stack_ldr_%04u%s"), prefix.c_str(), stackNr, wxT(".tif"));
00735                     outputFiles.Add(stackImgName);
00736                     stackedImages.Add(stackImgName);
00737                     commands->push_back(new NormalCommand(
00738                         GetExternalProgram(config, ExePath, wxT("enfuse")),
00739                         enfuseArgs + enLayersCompressionArgs + wxT(" -o ") + wxEscapeFilename(stackImgName) + wxT(" -- ") + GetQuotedFilenamesString(stackImgs),
00740                         wxString::Format(_("Fusing stack number %u..."), stackNr))
00741                     );
00742                     if (copyMetadata && opts.outputLDRStacks)
00743                     {
00744                         filesForCopyTagsExiftool.Add(stackImgName);
00745                     };
00746                     if (!opts.outputLDRStacks)
00747                     {
00748                         tempFilesDelete.Add(stackImgName);
00749                     };
00750                 };
00751                 if (opts.outputLDRExposureBlended)
00752                 {
00753                     const wxString fusedStacksFilename(prefix + wxT("_fused.") + opts.outputImageType);
00754                     switch (opts.blendMode)
00755                     {
00756                     case HuginBase::PanoramaOptions::ENBLEND_BLEND:
00757                         {
00758                             wxString finalEnblendArgs(enblendArgs + finalCompressionArgs);
00759                             finalEnblendArgs.Append(wxT(" -o ") + wxEscapeFilename(fusedStacksFilename) + wxT(" -- "));
00760                             commands->push_back(new NormalCommand(
00761                                 GetExternalProgram(config, ExePath, wxT("enblend")),
00762                                 finalEnblendArgs+wxT(" ")+GetQuotedFilenamesString(stackedImages), 
00763                                 _("Blending all stacks..."))
00764                             );
00765                         };
00766                         break;
00767                     case HuginBase::PanoramaOptions::INTERNAL_BLEND:
00768                     default:  // switch to internal blender for all other cases, not exposed in GUI
00769                         {
00770                             wxString finalVerdandiArgs(verdandiArgs + finalCompressionArgs);
00771                             finalVerdandiArgs.Append(wxT(" -o ") + wxEscapeFilename(fusedStacksFilename));
00772                             finalVerdandiArgs.Append(wxT(" -- ") + detail::GetQuotedFilenamesStringForVerdandi(stackedImages, pano, stacks, opts.colorReferenceImage, opts.verdandiOptions.find("--seam=blend") == std::string::npos));
00773                             commands->push_back(new NormalCommand(GetInternalProgram(ExePath, wxT("verdandi")),
00774                                 finalVerdandiArgs, _("Blending all stacks...")));
00775                         };
00776                         break;
00777                     };
00778                     outputFiles.Add(fusedStacksFilename);
00779                     if (copyMetadata)
00780                     {
00781                         filesForFullExiftool.Add(fusedStacksFilename);
00782                     };
00783                 };
00784             };
00785         };
00786         // hdr output
00787         if (opts.outputHDRLayers || opts.outputHDRStacks || opts.outputHDRBlended)
00788         {
00789             commands->push_back(new NormalCommand(GetInternalProgram(ExePath, wxT("nona")),
00790                 nonaArgs + wxT("-r hdr -m EXR_m  -o ") + wxEscapeFilename(prefix + wxT("_hdr_")) + wxT(" ") + quotedProject,
00791                 _("Remapping HDR images...")));
00792             const wxArrayString remappedHDR = detail::GetNumberedFilename(prefix + wxT("_hdr_"), wxT(".exr"), allActiveImages);
00793             const wxArrayString remappedHDRComp = detail::GetNumberedFilename(prefix + wxT("_hdr_"), wxT("_gray.pgm"), allActiveImages);
00794             detail::AddToArray(remappedHDR, outputFiles);
00795             detail::AddToArray(remappedHDRComp, outputFiles);
00796             if (opts.outputHDRStacks || opts.outputHDRBlended)
00797             {
00798                 // merging stacks, then blending
00799                 if (stacks.empty())
00800                 {
00801                     stacks = getHDRStacks(pano, allActiveImages, opts);
00802                 };
00803                 wxArrayString stackedImages;
00804                 // merge all stacks
00805                 for (unsigned stackNr = 0; stackNr < stacks.size(); ++stackNr)
00806                 {
00807                     const wxArrayString stackImgs = detail::GetNumberedFilename(prefix + wxT("_hdr_"), wxT(".exr"), stacks[stackNr]);
00808                     const wxString stackImgName = wxString::Format(wxT("%s_stack_hdr_%04u%s"), prefix.c_str(), stackNr, wxT(".exr"));
00809                     stackedImages.Add(stackImgName);
00810                     outputFiles.Add(stackImgName);
00811                     commands->push_back(new NormalCommand(GetInternalProgram(ExePath, wxT("hugin_hdrmerge")),
00812                         opts.hdrmergeOptions + wxT(" -o ") + wxEscapeFilename(stackImgName) + wxT(" -- ") + GetQuotedFilenamesString(stackImgs),
00813                         wxString::Format(_("Merging HDR stack number %u..."), stackNr)));
00814                     if (!opts.outputHDRStacks)
00815                     {
00816                         tempFilesDelete.Add(stackImgName);
00817                     };
00818                 };
00819                 if (opts.outputHDRBlended)
00820                 {
00821                     const wxString mergedStacksFilename(prefix + wxT("_hdr.") + opts.outputImageTypeHDR);
00822                     wxString finalBlendArgs(wxT(" -o ") + wxEscapeFilename(mergedStacksFilename) + wxT(" -- "));
00823                     switch (opts.blendMode)
00824                     {
00825                         case HuginBase::PanoramaOptions::ENBLEND_BLEND:
00826                             commands->push_back(new NormalCommand(
00827                                 GetExternalProgram(config, ExePath, wxT("enblend")),
00828                                 enblendArgs + finalBlendArgs + wxT(" ") + GetQuotedFilenamesString(stackedImages),
00829                                     _("Blending HDR stacks..."))
00830                             );
00831                             break;
00832                         case HuginBase::PanoramaOptions::INTERNAL_BLEND:
00833                         default:  // switch to internal blender for all other cases, not exposed in GUI
00834                             commands->push_back(new NormalCommand(GetInternalProgram(ExePath, wxT("verdandi")),
00835                                 verdandiArgs + finalBlendArgs + detail::GetQuotedFilenamesStringForVerdandi(stackedImages, pano, stacks, opts.colorReferenceImage, opts.verdandiOptions.find("--seam=blend") == std::string::npos),
00836                                 _("Blending HDR stacks...")));
00837                             break;
00838                     };
00839                     outputFiles.Add(mergedStacksFilename);
00840                     if (copyMetadata)
00841                     {
00842                         filesForFullExiftool.Add(mergedStacksFilename);
00843                     };
00844                 };
00845             };
00846             if (!opts.outputHDRLayers)
00847             {
00848                 detail::AddToArray(remappedHDR, tempFilesDelete);
00849                 detail::AddToArray(remappedHDRComp, tempFilesDelete);
00850             };
00851         };
00852         // update metadata
00853         if (!filesForCopyTagsExiftool.IsEmpty())
00854         {
00855             commands->push_back(new OptionalCommand(GetExternalProgram(config, ExePath, wxT("exiftool")),
00856                 exiftoolArgs + GetQuotedFilenamesString(filesForCopyTagsExiftool),
00857                 _("Updating metadata...")));
00858         };
00859         if (!filesForFullExiftool.IsEmpty())
00860         {
00861             commands->push_back(new OptionalCommand(GetExternalProgram(config, ExePath, wxT("exiftool")),
00862                 exiftoolArgs + exiftoolArgsFinal + GetQuotedFilenamesString(filesForFullExiftool),
00863                 _("Updating metadata...")));
00864         };
00865         return commands;
00866     };
00867 
00868     // now the user defined stitching engine
00869     // we read the settings from an ini file and construct our CommandQueue
00870     namespace detail
00871     {
00872         // add program with argment to queue
00873         // do some checks on the way
00874         // if an error occurs or the input is not valid, the queue is cleared and the function returns false
00875         bool AddBlenderCommand(CommandQueue* queue, const wxString& ExePath, const wxString& prog, 
00876             const int& stepNr, const wxString& arguments, const wxString& description)
00877         {
00878             if (prog.IsEmpty())
00879             {
00880                 std::cerr << "ERROR: Step " << stepNr << " has no program name specified." << std::endl;
00881                 CleanQueue(queue);
00882                 return false;
00883             }
00884             // check program name
00885             // get full path for some internal commands
00886             wxString program;
00887             if (prog.CmpNoCase(wxT("verdandi")) == 0)
00888             { 
00889                 program = GetInternalProgram(ExePath, wxT("verdandi"));
00890             }
00891             else
00892             {
00893                 if (prog.CmpNoCase(wxT("hugin_hdrmerge")) == 0)
00894                 {
00895                     program = GetInternalProgram(ExePath, wxT("hugin_hdrmerge"));
00896                 }
00897                 else
00898                 {
00899                     program = prog;
00900                 }
00901             };
00902             // now add to queue
00903             queue->push_back(new NormalCommand(program, arguments, description));
00904             return true;
00905         };
00906 
00907         // replace the %prefix% placeholder, with optional postfix
00908         // the string is modified in place
00909         // return true on success, false if there are errors
00910         bool ReplacePrefixPlaceholder(wxString& args, const wxString prefix)
00911         {
00912             int prefixPos = args.Find("%prefix");
00913             while (prefixPos != wxNOT_FOUND)
00914             {
00915                 const wxString nextChar = args.Mid(prefixPos + 7, 1);
00916                 if (nextChar == "%")
00917                 {
00918                     args.Replace("%prefix%", wxEscapeFilename(prefix), true);
00919                 }
00920                 else
00921                 {
00922                     if (nextChar == ",")
00923                     {
00924                         const int closingPercent = args.Mid(prefixPos + 8).Find("%");
00925                         if (closingPercent < 2)
00926                         {
00927                             return false;
00928                         };
00929                         wxString postfix = args.Mid(prefixPos + 8, closingPercent);
00930                         args.Replace("%prefix," + postfix + "%", wxEscapeFilename(prefix + postfix), true);
00931                     }
00932                     else
00933                     {
00934                         return false;
00935                     };
00936                 };
00937                 prefixPos = args.Find("%prefix");
00938             };
00939             return true;
00940         };
00941 
00942         // replace the %with% or %height% placeholder
00943         bool ReplaceWidthHeightPlaceHolder(wxString& args, const wxString name, int value)
00944         {
00945             int pos = args.Find("%" + name);
00946             while (pos != wxNOT_FOUND)
00947             {
00948                 const wxString nextChar = args.Mid(pos + 1 + name.Len(), 1);
00949                 if (nextChar == "%")
00950                 {
00951                     args.Replace("%" + name + "%", wxString::Format("%d", value), true);
00952                 }
00953                 else
00954                 {
00955                     if (nextChar == "*")
00956                     {
00957                         const int closingPercent = args.Mid(pos + 2 + name.Len()).Find("%");
00958                         if (closingPercent < 2)
00959                         {
00960                             return false;
00961                         };
00962                         wxString factorString = args.Mid(pos + 2 + name.Len(), closingPercent);
00963                         double factor;
00964                         if (!factorString.ToCDouble(&factor))
00965                         {
00966                             return false;
00967                         };
00968                         args.Replace("%" + name + "*" + factorString + "%", wxString::Format("%d", hugin_utils::roundi(factor*value)), true);
00969                     }
00970                     else
00971                     {
00972                         return false;
00973                     };
00974                 };
00975                 pos = args.Find("%" + name);
00976             };
00977             return true;
00978         };
00979 
00980     }
00981 
00982     CommandQueue* GetStitchingCommandQueueUserOutput(const HuginBase::Panorama & pano, const wxString& ExePath, const wxString& project, const wxString& prefix, const wxString& outputSettings, wxString& statusText, wxArrayString& outputFiles, wxArrayString& tempFilesDelete)
00983     {
00984         CommandQueue* commands = new CommandQueue;
00985         const HuginBase::UIntSet allActiveImages = getImagesinROI(pano, pano.getActiveImages());
00986         if (allActiveImages.empty())
00987         {
00988             std::cerr << "ERROR: No active images in ROI. Nothing to do." << std::endl;
00989             return commands;
00990         }
00991         wxFileInputStream input(outputSettings);
00992         if (!input.IsOk())
00993         {
00994             std::cerr << "ERROR: Can not open file \"" << outputSettings.mb_str(wxConvLocal) << "\"." << std::endl;
00995             return commands;
00996         }
00997         wxFileConfig settings(input);
00998         long stepCount;
00999         settings.Read(wxT("/General/StepCount"), &stepCount, 0);
01000         if (stepCount == 0)
01001         {
01002             std::cerr << "ERROR: User-setting does not define any output steps." << std::endl;
01003             return commands;
01004         }
01005         const wxString desc = GetSettingString(&settings, wxT("/General/Description"), wxEmptyString);
01006         if (desc.IsEmpty())
01007         {
01008             statusText = wxString::Format(_("Stitching using \"%s\""), outputSettings.c_str());
01009         }
01010         else
01011         {
01012             statusText = wxString::Format(_("Stitching using \"%s\""), desc.c_str());
01013         };
01014         wxString intermediateImageType = GetSettingString(&settings, wxT("/General/IntermediateImageType"), wxT(".tif"));
01015         // add point if missing
01016         if (intermediateImageType.Left(1).Cmp(wxT("."))!=0)
01017         {
01018             intermediateImageType.Prepend(wxT("."));
01019         }
01020         // prepare some often needed variables/strings
01021         const HuginBase::PanoramaOptions opts = pano.getOptions();
01022         const bool needsWrapSwitch = (opts.getHFOV() == 360.0) && (opts.getWidth() == opts.getROI().width());
01023         const vigra::Rect2D roi(opts.getROI());
01024         wxString sizeString;
01025         if (roi.top() != 0 || roi.left() != 0)
01026         {
01027             sizeString << roi.width() << wxT("x") << roi.height() << wxT("+") << roi.left() << wxT("+") << roi.top();
01028         }
01029         else
01030         {
01031             sizeString << roi.width() << wxT("x") << roi.height();
01032         };
01033         const wxArrayString remappedImages(detail::GetNumberedFilename(prefix, intermediateImageType, allActiveImages));
01034         wxArrayString inputImages;
01035         for (auto& i : allActiveImages)
01036         {
01037             inputImages.Add(wxString(pano.getImage(i).getFilename().c_str(), HUGIN_CONV_FILENAME));
01038         };
01039 
01040         std::vector<HuginBase::UIntSet> exposureLayers;
01041         wxArrayString exposureLayersFiles;
01042 
01043         std::vector<HuginBase::UIntSet> stacks;
01044         wxArrayString stacksFiles;
01045 
01046         // now iterate all steps
01047         for (size_t i = 0; i < stepCount; ++i)
01048         {
01049             wxString stepString(wxT("/Step"));
01050             stepString << i;
01051             if (!settings.HasGroup(stepString))
01052             {
01053                 std::cerr << "ERROR: Output specifies " << stepCount << " steps, but step " << i << " is missing in configuration." << std::endl;
01054                 CleanQueue(commands);
01055                 return commands;
01056             }
01057             settings.SetPath(stepString);
01058             const wxString stepType=GetSettingString(&settings, wxT("Type"));
01059             if (stepType.IsEmpty())
01060             {
01061                 std::cerr << "ERROR: \"" << stepString.mb_str(wxConvLocal) << "\" has no type defined." << std::endl;
01062                 CleanQueue(commands);
01063                 return commands;
01064             };
01065             wxString args = GetSettingString(&settings, wxT("Arguments"));
01066             if (args.IsEmpty())
01067             {
01068                 std::cerr << "ERROR: Step " << i << " has no arguments given." << std::endl;
01069                 CleanQueue(commands);
01070                 return commands;
01071             }
01072             const wxString description = GetSettingString(&settings, wxT("Description"));
01073             if (stepType.CmpNoCase(wxT("remap")) == 0)
01074             {
01075                 // build nona command
01076                 const bool outputLayers = (settings.Read(wxT("OutputExposureLayers"), 0l) == 1l);
01077                 if (outputLayers)
01078                 {
01079                     args.Append(wxT(" --create-exposure-layers -o ") + wxEscapeFilename(prefix + wxT("_layer")));
01080                 }
01081                 else
01082                 {
01083                     args.Append(wxT(" -o ") + wxEscapeFilename(prefix));
01084                 };
01085                 args.Append(wxT(" ") + wxEscapeFilename(project));
01086                 commands->push_back(new NormalCommand(GetInternalProgram(ExePath, wxT("nona")),
01087                     args, description));
01088                 if (outputLayers)
01089                 {
01090                     if (exposureLayers.empty())
01091                     {
01092                         exposureLayers = getExposureLayers(pano, allActiveImages, opts);
01093                         HuginBase::UIntSet exposureLayersNumber;
01094                         fill_set(exposureLayersNumber, 0, exposureLayers.size() - 1);
01095                         exposureLayersFiles = detail::GetNumberedFilename(prefix + wxT("_layer"), intermediateImageType, exposureLayersNumber);
01096                     };
01097                     detail::AddToArray(exposureLayersFiles, outputFiles);
01098                     if (settings.Read(wxT("Keep"), 0l) == 0l)
01099                     {
01100                         detail::AddToArray(exposureLayersFiles, tempFilesDelete);
01101                     };
01102                 }
01103                 else
01104                 {
01105                     const wxArrayString remappedHDRComp = detail::GetNumberedFilename(prefix, wxT("_gray.pgm"), allActiveImages);
01106                     const bool hdrOutput = args.MakeLower().Find(wxT("-r hdr")) != wxNOT_FOUND;
01107                     detail::AddToArray(remappedImages, outputFiles);
01108                     if (hdrOutput)
01109                     {
01110                         detail::AddToArray(remappedHDRComp, outputFiles);
01111                     };
01112                     if (settings.Read(wxT("Keep"), 0l) == 0l)
01113                     {
01114                         detail::AddToArray(remappedImages, tempFilesDelete);
01115                         if (hdrOutput)
01116                         {
01117                             detail::AddToArray(remappedHDRComp, tempFilesDelete);
01118                         };
01119                     };
01120                 };
01121             }
01122             else
01123             {
01124                 if (stepType.CmpNoCase(wxT("merge")) == 0)
01125                 {
01126                     // build a merge command
01127                     wxString resultFile = GetSettingString(&settings, wxT("Result"));
01128                     if (resultFile.IsEmpty())
01129                     {
01130                         std::cerr << "ERROR: Step " << i << " has no result file specified." << std::endl;
01131                         CleanQueue(commands);
01132                         return commands;
01133                     };
01134                     resultFile.Replace(wxT("%prefix%"), prefix, true);
01135                     if (args.Replace(wxT("%result%"), wxEscapeFilename(resultFile), true) == 0)
01136                     {
01137                         std::cerr << "ERROR: Step " << i << " has missing %result% placeholder in arguments." << std::endl;
01138                         CleanQueue(commands);
01139                         return commands;
01140                     };
01141                     const wxString BlenderInput = GetSettingString(&settings, wxT("Input"), wxT("all"));
01142                     // set the input images depending on the input
01143                     if (BlenderInput.CmpNoCase(wxT("all")) == 0)
01144                     {
01145                         if (args.Replace(wxT("%input%"), GetQuotedFilenamesString(remappedImages), true) == 0)
01146                         {
01147                             std::cerr << "ERROR: Step " << i << " has missing %input% placeholder in arguments." << std::endl;
01148                             CleanQueue(commands);
01149                             return commands;
01150                         };
01151                     }
01152                     else
01153                     {
01154                         if (BlenderInput.CmpNoCase(wxT("stacks")) == 0)
01155                         {
01156                             if (stacks.empty())
01157                             {
01158                                 stacks= HuginBase::getHDRStacks(pano, allActiveImages, opts);
01159                                 HuginBase::UIntSet stackNumbers;
01160                                 fill_set(stackNumbers, 0, stacks.size() - 1);
01161                                 stacksFiles = detail::GetNumberedFilename(prefix + wxT("_stack"), intermediateImageType, stackNumbers);
01162                             };
01163                             if (args.Replace(wxT("%input%"), GetQuotedFilenamesString(stacksFiles), true) == 0)
01164                             {
01165                                 std::cerr << "ERROR: Step " << i << " has missing %input% placeholder in arguments." << std::endl;
01166                                 CleanQueue(commands);
01167                                 return commands;
01168                             };
01169                         }
01170                         else
01171                         {
01172                             if (BlenderInput.CmpNoCase(wxT("layers")) == 0)
01173                             {
01174                                 if (exposureLayers.empty())
01175                                 { 
01176                                     exposureLayers = getExposureLayers(pano, allActiveImages, opts);
01177                                     HuginBase::UIntSet exposureLayersNumber;
01178                                     fill_set(exposureLayersNumber, 0, exposureLayers.size() - 1);
01179                                     exposureLayersFiles = detail::GetNumberedFilename(prefix + wxT("_layer"), intermediateImageType, exposureLayersNumber);
01180                                 };
01181                                 if (args.Replace(wxT("%input%"), GetQuotedFilenamesString(exposureLayersFiles), true) == 0)
01182                                 {
01183                                     std::cerr << "ERROR: Step " << i << " has missing %input% placeholder in arguments." << std::endl;
01184                                     CleanQueue(commands);
01185                                     return commands;
01186                                 };
01187                             }
01188                             else
01189                             {
01190                                 std::cerr << "ERROR: Step " << i << " has invalid input type: \"" << BlenderInput.mb_str(wxConvLocal) << "\"." << std::endl;
01191                                 CleanQueue(commands);
01192                                 return commands;
01193                             };
01194                         };
01195                     };
01196                     args.Replace(wxT("%size%"), sizeString, true);
01197                     wxString wrapSwitch = GetSettingString(&settings, wxT("WrapArgument"));
01198                     if (needsWrapSwitch && !wrapSwitch.IsEmpty())
01199                     {
01200                         args.Prepend(wrapSwitch + wxT(" "));
01201                     }
01202                     if (!detail::AddBlenderCommand(commands, ExePath, GetSettingString(&settings, wxT("Program")), i,
01203                         args, description))
01204                     {
01205                         return commands;
01206                     };
01207                     outputFiles.Add(resultFile);
01208                     if (settings.Read(wxT("Keep"), 1l) == 0l)
01209                     {
01210                         tempFilesDelete.Add(resultFile);
01211                     };
01212                 }
01213                 else
01214                 {
01215                     if (stepType.CmpNoCase(wxT("stack")) == 0)
01216                     {
01217                         // build command for each stack
01218                         if (stacks.empty())
01219                         {
01220                             stacks = HuginBase::getHDRStacks(pano, allActiveImages, opts);
01221                             HuginBase::UIntSet stackNumbers;
01222                             fill_set(stackNumbers, 0, stacks.size() - 1);
01223                             stacksFiles = detail::GetNumberedFilename(prefix + wxT("_stack"), intermediateImageType, stackNumbers);
01224                         };
01225                         const bool clean = (settings.Read(wxT("Keep"), 0l) == 0l);
01226                         args.Replace(wxT("%size%"), sizeString, true);
01227                         // now iterate each stack
01228                         for (size_t stackNr = 0; stackNr < stacks.size(); ++stackNr)
01229                         {
01230                             wxString finalArgs(args);
01231                             wxArrayString remappedStackImages = detail::GetNumberedFilename(prefix, intermediateImageType, stacks[stackNr]);
01232                             if (finalArgs.Replace(wxT("%input%"), GetQuotedFilenamesString(remappedStackImages), true) == 0)
01233                             {
01234                                 std::cerr << "ERROR: Step " << i << " has missing %input% placeholder in arguments." << std::endl;
01235                                 CleanQueue(commands);
01236                                 return commands;
01237                             };
01238                             if (finalArgs.Replace(wxT("%output%"), wxEscapeFilename(stacksFiles[stackNr]), true) == 0)
01239                             {
01240                                 std::cerr << "ERROR: Step " << i << " has missing %output% placeholder in arguments." << std::endl;
01241                                 CleanQueue(commands);
01242                                 return commands;
01243                             };
01244                             if (!detail::AddBlenderCommand(commands, ExePath, GetSettingString(&settings, wxT("Program")), i,
01245                                 finalArgs, description))
01246                             {
01247                                 return commands;
01248                             };
01249                             outputFiles.Add(stacksFiles[stackNr]);
01250                             if (clean)
01251                             {
01252                                 tempFilesDelete.Add(stacksFiles[stackNr]);
01253                             };
01254                         }
01255                     }
01256                     else
01257                     {
01258                         if (stepType.CmpNoCase(wxT("layer")) == 0)
01259                         {
01260                             // build command for each exposure layer
01261                             if (exposureLayers.empty())
01262                             {
01263                                 exposureLayers = HuginBase::getExposureLayers(pano, allActiveImages, opts);
01264                                 HuginBase::UIntSet exposureLayersNumber;
01265                                 fill_set(exposureLayersNumber, 0, exposureLayers.size() - 1);
01266                                 exposureLayersFiles = detail::GetNumberedFilename(prefix + wxT("_layer"), intermediateImageType, exposureLayersNumber);
01267                             };
01268                             const bool clean = (settings.Read(wxT("Keep"), 0l) == 0l);
01269                             args.Replace(wxT("%size%"), sizeString, true);
01270                             // iterate all exposure layers
01271                             for (size_t exposureLayerNr = 0; exposureLayerNr < exposureLayers.size(); ++exposureLayerNr)
01272                             {
01273                                 wxString finalArgs(args);
01274                                 wxArrayString remappedLayerImages = detail::GetNumberedFilename(prefix, intermediateImageType, exposureLayers[exposureLayerNr]);
01275                                 if (finalArgs.Replace(wxT("%input%"), GetQuotedFilenamesString(remappedLayerImages), true) == 0)
01276                                 {
01277                                     std::cerr << "ERROR: Step " << i << " has missing %input% placeholder in arguments." << std::endl;
01278                                     CleanQueue(commands);
01279                                     return commands;
01280                                 };
01281                                 if (finalArgs.Replace(wxT("%output%"), wxEscapeFilename(exposureLayersFiles[exposureLayerNr]), true) == 0)
01282                                 {
01283                                     std::cerr << "ERROR: Step " << i << " has missing %output% placeholder in arguments." << std::endl;
01284                                     CleanQueue(commands);
01285                                     return commands;
01286                                 };
01287                                 if (!detail::AddBlenderCommand(commands, ExePath, GetSettingString(&settings, wxT("Program")), i,
01288                                     finalArgs, description))
01289                                 {
01290                                     return commands;
01291                                 };
01292                                 outputFiles.Add(exposureLayersFiles[exposureLayerNr]);
01293                                 if (clean)
01294                                 {
01295                                     tempFilesDelete.Add(exposureLayersFiles[exposureLayerNr]);
01296                                 };
01297                             }
01298                         }
01299                         else
01300                         {
01301                             if (stepType.CmpNoCase(wxT("modify")) == 0)
01302                             {
01303                                 // build a modify command
01304                                 wxString inputFiles = GetSettingString(&settings, wxT("File"));
01305                                 if (inputFiles.IsEmpty())
01306                                 {
01307                                     std::cerr << "ERROR: Step " << i << " has no input/output file specified." << std::endl;
01308                                     CleanQueue(commands);
01309                                     return commands;
01310                                 };
01311                                 if (args.Find(wxT("%file%")) == wxNOT_FOUND)
01312                                 {
01313                                     std::cerr << "ERROR: Step " << i << " has missing %file% placeholder in arguments." << std::endl;
01314                                     CleanQueue(commands);
01315                                     return commands;
01316                                 };
01317                                 args.Replace(wxT("%project%"), wxEscapeFilename(project), true);
01318                                 if (!detail::ReplacePrefixPlaceholder(args, prefix))
01319                                 {
01320                                     std::cerr << "ERROR: Step " << i << " has invalid %prefix% placeholder in arguments." << std::endl;
01321                                     CleanQueue(commands);
01322                                     return commands;
01323                                 };
01324                                 if (!detail::ReplaceWidthHeightPlaceHolder(args, "width", opts.getWidth()))
01325                                 {
01326                                     std::cerr << "ERROR: Step " << i << " has invalid %width% placeholder in arguments." << std::endl;
01327                                     CleanQueue(commands);
01328                                     return commands;
01329                                 }
01330                                 if (!detail::ReplaceWidthHeightPlaceHolder(args, "height", opts.getHeight()))
01331                                 {
01332                                     std::cerr << "ERROR: Step " << i << " has invalid %height% placeholder in arguments." << std::endl;
01333                                     CleanQueue(commands);
01334                                     return commands;
01335                                 }
01336                                 const wxString progName = GetSettingString(&settings, wxT("Program"));
01337                                 if (progName.IsEmpty())
01338                                 {
01339                                     std::cerr << "ERROR: Step " << i << " has no program name specified." << std::endl;
01340                                     CleanQueue(commands);
01341                                     return commands;
01342                                 };
01343 #ifdef __WXMAC__
01344                                 // check if program can be found in bundle
01345                                 const wxString prog = GetExternalProgram(wxConfig::Get(), ExePath, progName);
01346 #elif defined __WXMSW__
01347 
01348                                 const wxString prog = MSWGetProgname(ExePath, progName);
01349 #else
01350                                 const wxString prog = progName;
01351 #endif
01352                                 if (inputFiles.CmpNoCase(wxT("all")) == 0)
01353                                 {
01354                                     for (size_t imgNr = 0; imgNr < remappedImages.size(); ++imgNr)
01355                                     {
01356                                         wxString finalArgs(args);
01357                                         finalArgs.Replace(wxT("%file%"), wxEscapeFilename(remappedImages[imgNr]), true);
01358                                         finalArgs.Replace(wxT("%sourceimage%"), wxEscapeFilename(inputImages[imgNr]), true);
01359                                         commands->push_back(new NormalCommand(prog, finalArgs, description));
01360                                     };
01361                                 }
01362                                 else
01363                                 {
01364                                     if (inputFiles.CmpNoCase(wxT("stacks")) == 0)
01365                                     {
01366                                         if (stacks.empty())
01367                                         {
01368                                             std::cerr << "ERROR: Step " << i << " requests to modify stacks, but no stack was created before." << std::endl;
01369                                             CleanQueue(commands);
01370                                             return commands;
01371                                         };
01372                                         for (size_t stackNr = 0; stackNr < stacksFiles.size(); ++stackNr)
01373                                         {
01374                                             wxString finalArgs(args);
01375                                             finalArgs.Replace(wxT("%file%"), wxEscapeFilename(stacksFiles[stackNr]), true);
01376                                             commands->push_back(new NormalCommand(prog, finalArgs, description));
01377                                         };
01378                                     }
01379                                     else
01380                                     {
01381                                         if (inputFiles.CmpNoCase(wxT("layers")) == 0)
01382                                         {
01383                                             if (exposureLayers.empty())
01384                                             {
01385                                                 std::cerr << "ERROR: Step " << i << " requests to modify exposure layers, but no exposure layer was created before." << std::endl;
01386                                                 CleanQueue(commands);
01387                                                 return commands;
01388                                             };
01389                                             for (size_t layerNr = 0; layerNr < exposureLayersFiles.size(); ++layerNr)
01390                                             {
01391                                                 wxString finalArgs(args);
01392                                                 finalArgs.Replace(wxT("%file%"), wxEscapeFilename(exposureLayersFiles[layerNr]), true);
01393                                                 commands->push_back(new NormalCommand(prog, finalArgs, description));
01394                                             };
01395                                         }
01396                                         else
01397                                         {
01398                                             inputFiles.Replace(wxT("%prefix%"), prefix, true);
01399                                             args.Replace(wxT("%file%"), wxEscapeFilename(inputFiles), true);
01400                                             commands->push_back(new NormalCommand(prog , args, description));
01401                                         };
01402                                     };
01403                                 };
01404                             }
01405                             else
01406                             {
01407                                 if (stepType.CmpNoCase(wxT("exiftool")) == 0)
01408                                 {
01409                                     wxString resultFile = GetSettingString(&settings, wxT("Result"));
01410                                     if (resultFile.IsEmpty())
01411                                     {
01412                                         std::cerr << "ERROR: Step " << i << " has no result file specified." << std::endl;
01413                                         CleanQueue(commands);
01414                                         return commands;
01415                                     };
01416                                     resultFile.Replace(wxT("%prefix%"), prefix, true);
01417                                     if (args.Replace(wxT("%result%"), wxEscapeFilename(resultFile), true) == 0)
01418                                     {
01419                                         std::cerr << "ERROR: Step " << i << " has missing %result% placeholder in arguments." << std::endl;
01420                                         CleanQueue(commands);
01421                                         return commands;
01422                                     };
01423                                     args.Replace(wxT("%image0%"), wxEscapeFilename(wxString(pano.getImage(0).getFilename().c_str(), HUGIN_CONV_FILENAME)), true);
01424                                     commands->push_back(new OptionalCommand(GetExternalProgram(wxConfigBase::Get(), ExePath, wxT("exiftool")),
01425                                         args, description));
01426                                 }
01427                                 else
01428                                 {
01429                                     std::cerr << "ERROR: Step " << i << " has unknown Type \"" << stepType.mb_str(wxConvLocal) << "\"." << std::endl;
01430                                     CleanQueue(commands);
01431                                     return commands;
01432                                 };
01433                             };
01434                         };
01435                     };
01436                 };
01437             };
01438         };
01439         return commands;
01440     }
01441 
01443     wxString GetQuotedFilenamesString(const wxArrayString& files)
01444     {
01445         wxString s;
01446         for (size_t i = 0; i < files.size(); ++i)
01447         {
01448             s.Append(wxEscapeFilename(files[i]) + wxT(" "));
01449         };
01450         return s;
01451     };
01452 
01453 }; // namespace

Generated on 26 Apr 2018 for Hugintrunk by  doxygen 1.4.7