27 #include <wx/config.h>
28 #include <wx/translation.h>
29 #include <wx/arrstr.h>
30 #include <wx/filefn.h>
31 #include <wx/txtstrm.h>
32 #include <wx/wfstream.h>
49 wxArrayString filenames;
50 for (HuginBase::UIntSet::const_iterator it = img.begin(); it != img.end(); ++it)
52 filenames.Add(wxString::Format(wxT(
"%s%04u%s"), prefix.c_str(), *it, postfix.c_str()));
58 void AddToArray(
const wxArrayString& input, wxArrayString& output)
60 for (
size_t i = 0; i < input.size(); ++i)
71 wxString argfileInput = config->Read(wxT(
"/output/FinalArgfile"), wxEmptyString);
72 const bool generateGPanoTags = (config->Read(wxT(
"/output/writeGPano"),
HUGIN_EXIFTOOL_CREATE_GPANO) == 1l) && (exifToolVersion >= 9.09);
73 pano_projection_features proj;
75 const bool readProjectionName = panoProjectionFeaturesQuery(opts.
getProjection(), &proj) != 0;
81 Placeholder(
const wxString& holder,
const wxString& newValue)
87 std::list<Placeholder> placeholders;
89 const wxString linebreak(wxT(
"
"));
91 const wxString linebreak(wxT(
"
"));
93 if (readProjectionName)
96 placeholders.push_back(Placeholder(wxT(
"%projectionNumber"), wxString::Format(wxT(
"%d"), opts.
getProjection())));
97 placeholders.push_back(Placeholder(wxT(
"%projection"), wxString(proj.name, wxConvLocal)));
100 placeholders.push_back(Placeholder(wxT(
"%hfov"), wxString::Format(wxT(
"%.0f"), opts.
getHFOV())));
101 placeholders.push_back(Placeholder(wxT(
"%vfov"), wxString::Format(wxT(
"%.0f"), opts.
getVFOV())));
102 placeholders.push_back(Placeholder(wxT(
"%ev"), wxString::Format(wxT(
"%.2f"), opts.
outputExposureValue)));
103 placeholders.push_back(Placeholder(wxT(
"%nrImages"), wxString::Format(wxT(
"%lu"), (
unsigned long)images.size())));
104 placeholders.push_back(Placeholder(wxT(
"%nrAllImages"), wxString::Format(wxT(
"%lu"), (
unsigned long)pano.
getNrOfImages())));
105 placeholders.push_back(Placeholder(wxT(
"%fullwidth"), wxString::Format(wxT(
"%u"), opts.
getWidth())));
106 placeholders.push_back(Placeholder(wxT(
"%fullheight"), wxString::Format(wxT(
"%u"), opts.
getHeight())));
107 placeholders.push_back(Placeholder(wxT(
"%width"), wxString::Format(wxT(
"%d"), opts.
getROI().width())));
108 placeholders.push_back(Placeholder(wxT(
"%height"), wxString::Format(wxT(
"%d"), opts.
getROI().height())));
109 wxFileName projectFilename(projectName);
110 placeholders.push_back(Placeholder(wxT(
"%projectname"), projectFilename.GetFullName()));
112 wxFileName tempArgfileFinal(wxFileName::CreateTempFileName(
GetConfigTempDir(config) + wxT(
"he")));
113 wxFFileOutputStream outputStream(tempArgfileFinal.GetFullPath());
114 wxTextOutputStream outputFile(outputStream);
117 outputFile << wxT(
"-E") << endl;
118 outputFile << wxT(
"-UserComment<${UserComment}") << linebreak;
119 if (readProjectionName)
121 outputFile << wxT(
"Projection: ") << wxString(proj.name, wxConvLocal) << wxT(
" (") << opts.
getProjection() << wxT(
")") << linebreak;
123 outputFile << wxT(
"FOV: ") << wxString::Format(wxT(
"%.0f"), opts.
getHFOV()) << wxT(
" x ") << wxString::Format(wxT(
"%.0f"), opts.
getVFOV()) << linebreak;
124 outputFile << wxT(
"Ev: ") << wxString::Format(wxT(
"%.2f"), opts.
outputExposureValue) << endl;
125 outputFile << wxT(
"-f") << endl;
126 if (exifToolVersion >= 11.53)
130 outputFile << wxT(
"-CompositeImage=General Composite Image") << endl;
131 outputFile << wxString::Format(wxT(
"-CompositeImageCount=%lu %lu"), (
unsigned long)pano.
getNrOfImages(), (
unsigned long)pano.
getActiveImages().size()) << endl;
133 if (generateGPanoTags)
139 const vigra::Rect2D roi = opts.
getROI();
140 int left = roi.left();
142 int width = roi.width();
143 int height = roi.height();
148 fullWidth =
static_cast<int>(opts.
getWidth() * 360.0 / opts.
getHFOV());
149 left += (fullWidth - opts.
getWidth()) / 2;
152 if (!isCylindrical && opts.
getVFOV()<180)
155 top += (fullHeight - opts.
getHeight()) / 2;
160 top = height/2 - top;
162 outputFile << wxT(
"-UsePanoramaViewer=True") << endl;
163 outputFile << wxT(
"-StitchingSoftware=Hugin") << endl;
166 outputFile << wxT(
"-ProjectionType=cylindrical") << endl;
170 outputFile << wxT(
"-ProjectionType=equirectangular") << endl;
172 outputFile << wxT(
"-CroppedAreaLeftPixels=") << left << endl;
173 outputFile << wxT(
"-CroppedAreaTopPixels=") << top << endl;
174 outputFile << wxT(
"-CroppedAreaImageWidthPixels=") << width << endl;
175 outputFile << wxT(
"-CroppedAreaImageHeightPixels=") << height << endl;
176 outputFile << wxT(
"-FullPanoWidthPixels=") << fullWidth << endl;
180 outputFile << wxT(
"-FullPanoHeightPixels=") << fullHeight << endl;
182 outputFile << wxT(
"-SourcePhotosCount=") <<
static_cast<wxUint32
>(pano.
getNrOfImages()) << endl;
186 if (!argfileInput.IsEmpty())
188 if (wxFileExists(argfileInput))
190 wxFileInputStream inputFileStream(argfileInput);
191 wxTextInputStream input(inputFileStream);
192 while (inputFileStream.IsOk() && !inputFileStream.Eof())
194 wxString
line = input.ReadLine();
196 for (
auto variable : placeholders)
198 line.Replace(variable.placeholder, variable.value,
true);
201 outputFile << line << endl;
205 return tempArgfileFinal.GetFullPath();
211 const wxString wxEndl(wxT(
"\n"));
213 << wxT(
"============================================") << wxEndl
214 << _(
"Stitching panorama...") << wxEndl
215 << wxT(
"============================================") << wxEndl
217 << _(
"Platform:") << wxT(
" ") << wxGetOsDescription() << wxEndl
219 << _(
"Working directory:") << wxT(
" ") << wxFileName::GetCwd() << wxEndl
220 << _(
"Output prefix:") << wxT(
" ") << prefix << wxEndl
229 wxArrayString version;
232 output << _(
"Blender:") << wxT(
" ") << version[0] << wxEndl;
236 output << _(
"Blender:") << wxT(
" ") << _(
"Unknown blender (enblend --version failed)") << wxEndl;
242 output << _(
"Blender:") << wxT(
" ") << _(
"internal") << wxEndl;
248 wxArrayString version;
251 output << _(
"Exposure fusion:") << wxT(
" ") << version[0] << wxEndl;
255 output << _(
"Exposure fusion:") << wxT(
" ") << _(
"Unknown exposure fusion (enfuse --version failed)") << wxEndl;
260 wxArrayString version;
263 output << _(
"ExifTool version:") << wxT(
" ") << version[0] << wxEndl;
264 version[0].ToCDouble(&exiftoolVersion);
268 output << _(
"ExifTool:") << wxT(
" ") << _(
"FAILED") << wxEndl;
274 << _(
"Number of active images:") << wxT(
" ") << allActiveImages.size() << wxEndl
276 << wxString::Format(_(
"Canvas size: %dx%d"), opts.
getSize().width(), opts.
getSize().height()) << wxEndl
277 << wxString::Format(_(
"ROI: (%d, %d) - (%d, %d)"), opts.
getROI().left(), opts.
getROI().top(), opts.
getROI().right(), opts.
getROI().bottom()) << wxT(
" ") << wxEndl
278 << wxString::Format(_(
"FOV: %.0fx%.0f"), opts.
getHFOV(), opts.
getVFOV()) << wxEndl;
279 pano_projection_features proj;
280 const bool readProjectionName = panoProjectionFeaturesQuery(opts.
getProjection(), &proj) != 0;
281 if (readProjectionName)
284 << _(
"Projection:") << wxT(
" ") << wxGetTranslation(wxString(proj.name, wxConvLocal))
290 << _(
"Projection:") << wxT(
" ") << opts.
getProjection() << wxEndl;
293 << _(
"Using GPU for remapping:") << wxT(
" ") << (opts.
remapUsingGPU ? _(
"true") : _(
"false")) << wxEndl
297 output << _(
"Panorama Outputs:") << wxEndl;
300 output << wxT(
"* ") << _(
"Exposure corrected, low dynamic range") << wxEndl;
304 output << wxT(
"* ") << _(
"Exposure fused from stacks") << wxEndl;
308 output << wxT(
"* ") << _(
"Exposure fused from any arrangement") << wxEndl;
312 output << wxT(
"* ") << _(
"High dynamic range") << wxEndl;
318 output << _(
"Remapped Images:") << wxEndl;
321 output << wxT(
"* ") << _(
"Exposure corrected, low dynamic range") << wxEndl;
325 output << wxT(
"* ") << _(
"No exposure correction, low dynamic range") << wxEndl;
329 output << wxT(
"* ") << _(
"High dynamic range") << wxEndl;
335 output << _(
"Combined stacks:") << wxEndl;
338 output << wxT(
"* ") << _(
"Exposure fused stacks") << wxEndl;
342 output << wxT(
"* ") << _(
"High dynamic range") << wxEndl;
348 output << _(
"Layers:") << wxEndl
349 << wxT(
"* ") << _(
"Blended layers of similar exposure, without exposure correction") << wxEndl
353 output << _(
"First input image") << wxEndl
354 << _(
"Number:") << wxT(
" ") << *allActiveImages.begin() << wxEndl
355 << _(
"Filename:") << wxT(
" ") << img.getFilename() << wxEndl
359 << wxString::Format(_(
"HFOV: %.0f"), img.getHFOV()) << wxEndl
360 << wxString::Format(_(
"Exposure value: %.1f"), img.getExposureValue()) << wxEndl
377 for (
size_t i = 0; i < stacks.size(); ++i)
383 stackImgs.insert(*(stacks[i].begin()));
388 fill_set(subpanoImgs, 0, stackImgs.size() - 1);
394 for (
size_t i = 0; i < blendOrder.size();++i)
409 _(
"Filling edges...")));
419 if (allActiveImages.empty())
421 errStream <<
"ERROR: No active images in ROI. Nothing to do." << std::endl;
424 std::vector<HuginBase::UIntSet> stacks;
428 wxConfigBase* config = wxConfigBase::Get();
432 errStream <<
"ERROR: Only nona remappper is supported by hugin_executor." << std::endl;
437 errStream <<
"ERROR: Only enblend and internal remappper are currently supported by hugin_executor." << std::endl;
442 errStream <<
"ERROR: Only hdr merger HDRMERGE_AVERAGE is currently supported by hugin_executor." << std::endl;
445 double exiftoolVersion;
450 wxString nonaArgs(wxT(
"-v "));
451 wxString enLayersCompressionArgs;
461 nonaArgs.Append(wxT(
"-z LZW "));
466 nonaArgs.Append(wxT(
"-g "));
469 wxString enblendArgs;
475 enblendArgs.Append(wxT(
" -w"));
477 const vigra::Rect2D roi (opts.
getROI());
478 if (roi.top() != 0 || roi.left() != 0)
480 enblendArgs << wxT(
" -f") << roi.width() << wxT(
"x") << roi.height() << wxT(
"+") << roi.left() << wxT(
"+") << roi.top();
484 enblendArgs << wxT(
" -f") << roi.width() << wxT(
"x") << roi.height();
486 enblendArgs.Append(wxT(
" "));
489 wxString verdandiArgs;
495 verdandiArgs.Append(wxT(
" -w"));
499 wxString finalCompressionArgs;
508 finalCompressionArgs << wxT(
" --compression=") << opts.
quality;
511 finalCompressionArgs.Append(wxT(
" "));
516 enfuseArgs.Append(wxT(
" -w"));
518 const vigra::Rect2D roi (opts.
getROI());
519 if (roi.top() != 0 || roi.left() != 0)
521 enfuseArgs << wxT(
" -f") << roi.width() << wxT(
"x") << roi.height() << wxT(
"+") << roi.left() << wxT(
"+") << roi.top();
525 enfuseArgs << wxT(
" -f") << roi.width() << wxT(
"x") << roi.height();
527 enfuseArgs.Append(wxT(
" "));
530 const bool copyMetadata = config->Read(wxT(
"/output/useExiftool"),
HUGIN_USE_EXIFTOOL) == 1l;
531 wxString exiftoolArgs;
532 wxString exiftoolArgsFinal;
535 exiftoolArgs = wxT(
"-overwrite_original -TagsFromFile ");
538 exiftoolArgs.Append(wxT(
" -WhitePoint -ColorSpace"));
539 wxString exiftoolArgfile = config->Read(wxT(
"/output/CopyArgfile"), wxEmptyString);
540 if (exiftoolArgfile.IsEmpty())
544 wxFileName argfile(exiftoolArgfile);
545 argfile.Normalize(wxPATH_NORM_ABSOLUTE | wxPATH_NORM_DOTS | wxPATH_NORM_TILDE | wxPATH_NORM_SHORTCUT);
546 exiftoolArgs.Append(wxT(
" -@ ") +
wxEscapeFilename(argfile.GetFullPath()) + wxT(
" "));
548 if (!finalArgfile.IsEmpty())
550 exiftoolArgsFinal.Append(wxT(
" -@ ") +
wxEscapeFilename(finalArgfile) + wxT(
" "));
551 tempFilesDelete.Add(finalArgfile);
554 wxArrayString filesForFullExiftool;
555 wxArrayString filesForCopyTagsExiftool;
563 const wxString finalFilename(prefix + wxT(
".") + opts.
outputImageType);
566 wxString finalNonaArgs(wxT(
"-v -r ldr "));
569 finalNonaArgs.Append(wxT(
"-g "));
574 finalNonaArgs.Append(wxT(
" "));
576 wxString edgeInputFile;
579 finalNonaArgs.Append(
"-m TIFF -z " + opts.
tiffCompression +
" --final-suffix=_nofill ");
580 edgeInputFile = prefix +
"_nofill.tif";
581 outputFiles.Add(edgeInputFile);
586 filesForFullExiftool.Add(edgeInputFile);
591 tempFilesDelete.Add(edgeInputFile);
598 finalNonaArgs.Append(wxT(
"-m TIFF "));
608 finalNonaArgs.Append(wxT(
"-m JPEG -z "));
609 finalNonaArgs << opts.
quality << wxT(
" ");
615 finalNonaArgs.Append(wxT(
"-m PNG "));
619 errStream <<
"ERROR: Invalid output image type found." << std::endl;
627 finalNonaArgs.Append(wxT(
"--save-intermediate-images "));
630 finalNonaArgs.Append(wxT(
"-o ") +
wxEscapeFilename(prefix) + wxT(
" ") + quotedProject);
632 finalNonaArgs, _(
"Remapping and blending LDR images...")));
633 outputFiles.Add(finalFilename);
640 filesForFullExiftool.Add(finalFilename);
646 nonaArgs + wxT(
"-r ldr -m TIFF_m -o ") +
wxEscapeFilename(prefix) + wxT(
" ") + quotedProject,
647 _(
"Remapping LDR images...")));
653 wxString finalEnblendArgs(enblendArgs);
654 wxString edgeFillInput;
657 edgeFillInput = prefix +
"_nofill.tif";
662 finalEnblendArgs.Append(finalCompressionArgs + wxT(
" -o ") +
wxEscapeFilename(finalFilename) + wxT(
" -- "));
667 _(
"Blending images..."))
672 outputFiles.Add(edgeFillInput);
675 tempFilesDelete.Add(edgeFillInput);
678 outputFiles.Add(finalFilename);
683 filesForFullExiftool.Add(edgeFillInput);
685 filesForFullExiftool.Add(finalFilename);
700 std::vector<HuginBase::UIntSet> exposureLayers;
701 wxArrayString exposureLayersFiles;
710 wxString finalNonaArgs(nonaArgs);
714 finalNonaArgs.Append(wxT(
" "));
716 finalNonaArgs.append(wxT(
"-r ldr --create-exposure-layers --ignore-exposure -o ") +
wxEscapeFilename(prefix + wxT(
"_exposure_")));
719 finalNonaArgs.append(wxT(
" --save-intermediate-images --intermediate-suffix=layers_"));
726 finalNonaArgs.append(wxT(
" "));
727 finalNonaArgs.append(quotedProject);
729 finalNonaArgs, _(
"Remapping LDR images and blending exposure layers...")));
731 fill_set(exposureLayersNumber, 0, exposureLayers.size() - 1);
746 nonaArgs + wxT(
"-r ldr -m TIFF_m --ignore-exposure -o ") +
wxEscapeFilename(prefix + wxT(
"_exposure_layers_")) + wxT(
" ") + quotedProject,
747 _(
"Remapping LDR images without exposure correction...")));
757 for (
unsigned exposureLayer = 0; exposureLayer < exposureLayers.size(); ++exposureLayer)
759 const wxArrayString exposureLayersImgs =
detail::GetNumberedFilename(prefix + wxT(
"_exposure_layers_"), wxT(
".tif"), exposureLayers[exposureLayer]);
760 const wxString exposureLayerImgName = wxString::Format(wxT(
"%s_exposure_%04u%s"), prefix.c_str(), exposureLayer, wxT(
".tif"));
761 exposureLayersFiles.Add(exposureLayerImgName);
762 outputFiles.Add(exposureLayerImgName);
767 wxString::Format(_(
"Blending exposure layer %u..."), exposureLayer))
771 filesForCopyTagsExiftool.Add(exposureLayerImgName);
775 tempFilesDelete.Add(exposureLayerImgName);
782 const wxString fusedExposureLayersFilename(prefix + wxT(
"_blended_fused.") + opts.
outputImageType);
783 wxString finalEnfuseArgs(enfuseArgs);
784 wxString edgeFillInput;
787 edgeFillInput = prefix +
"_blended_fused_nofill.tif";
792 finalEnfuseArgs.Append(finalCompressionArgs + wxT(
" -o ") +
wxEscapeFilename(fusedExposureLayersFilename) + wxT(
" -- "));
797 _(
"Fusing all exposure layers..."))
799 outputFiles.Add(fusedExposureLayersFilename);
803 outputFiles.Add(edgeFillInput);
806 tempFilesDelete.Add(edgeFillInput);
813 filesForFullExiftool.Add(edgeFillInput);
815 filesForFullExiftool.Add(fusedExposureLayersFilename);
822 wxArrayString stackedImages;
824 for (
unsigned stackNr = 0; stackNr < stacks.size(); ++stackNr)
827 const wxString stackImgName = wxString::Format(wxT(
"%s_stack_ldr_%04u%s"), prefix.c_str(), stackNr, wxT(
".tif"));
828 outputFiles.Add(stackImgName);
829 stackedImages.Add(stackImgName);
833 wxString::Format(_(
"Fusing stack number %u..."), stackNr))
837 filesForCopyTagsExiftool.Add(stackImgName);
841 tempFilesDelete.Add(stackImgName);
846 const wxString fusedStacksFilename(prefix + wxT(
"_fused.") + opts.
outputImageType);
847 wxString edgeFillInput;
850 edgeFillInput = prefix +
"_fused_nofill.tif";
856 wxString finalEnblendArgs(enblendArgs);
863 finalEnblendArgs.Append(finalCompressionArgs + wxT(
" -o ") +
wxEscapeFilename(fusedStacksFilename) + wxT(
" -- "));
868 _(
"Blending all stacks..."))
875 wxString finalVerdandiArgs(verdandiArgs);
882 finalVerdandiArgs.Append(finalCompressionArgs + wxT(
" -o ") +
wxEscapeFilename(fusedStacksFilename));
886 finalVerdandiArgs, _(
"Blending all stacks...")));
890 outputFiles.Add(fusedStacksFilename);
894 outputFiles.Add(edgeFillInput);
897 tempFilesDelete.Add(edgeFillInput);
904 filesForFullExiftool.Add(edgeFillInput);
906 filesForFullExiftool.Add(fusedStacksFilename);
915 nonaArgs + wxT(
"-r hdr -m EXR_m -o ") +
wxEscapeFilename(prefix + wxT(
"_hdr_")) + wxT(
" ") + quotedProject,
916 _(
"Remapping HDR images...")));
928 wxArrayString stackedImages;
930 for (
unsigned stackNr = 0; stackNr < stacks.size(); ++stackNr)
933 const wxString stackImgName = wxString::Format(wxT(
"%s_stack_hdr_%04u%s"), prefix.c_str(), stackNr, wxT(
".exr"));
934 stackedImages.Add(stackImgName);
935 outputFiles.Add(stackImgName);
938 wxString::Format(_(
"Merging HDR stack number %u..."), stackNr)));
941 tempFilesDelete.Add(stackImgName);
947 wxString finalBlendArgs;
948 wxString edgeFillInput;
951 edgeFillInput = prefix +
"_hdr_nofill.exr";
952 finalBlendArgs.Append(wxT(
" -o ") +
wxEscapeFilename(edgeFillInput) + wxT(
" -- "));
956 finalBlendArgs.Append(wxT(
" -o ") +
wxEscapeFilename(mergedStacksFilename) + wxT(
" -- "));
964 _(
"Blending HDR stacks..."))
971 _(
"Blending HDR stacks...")));
974 outputFiles.Add(mergedStacksFilename);
978 outputFiles.Add(edgeFillInput);
981 tempFilesDelete.Add(edgeFillInput);
988 filesForFullExiftool.Add(edgeFillInput);
990 filesForFullExiftool.Add(mergedStacksFilename);
1001 if (!filesForCopyTagsExiftool.IsEmpty())
1005 _(
"Updating metadata...")));
1007 if (!filesForFullExiftool.IsEmpty())
1011 _(
"Updating metadata...")));
1024 const int& stepNr,
const wxString& arguments,
const wxString& description, std::ostream& errStream)
1028 errStream <<
"ERROR: Step " << stepNr <<
" has no program name specified." << std::endl;
1035 if (prog.CmpNoCase(wxT(
"verdandi")) == 0)
1041 if (prog.CmpNoCase(wxT(
"hugin_hdrmerge")) == 0)
1051 queue->push_back(
new NormalCommand(program, arguments, description));
1060 int prefixPos = args.Find(
"%prefix");
1061 while (prefixPos != wxNOT_FOUND)
1063 const wxString nextChar = args.Mid(prefixPos + 7, 1);
1064 if (nextChar ==
"%")
1070 if (nextChar ==
",")
1072 const int closingPercent = args.Mid(prefixPos + 8).Find(
"%");
1073 if (closingPercent < 2)
1077 wxString postfix = args.Mid(prefixPos + 8, closingPercent);
1078 args.Replace(
"%prefix," + postfix +
"%",
wxEscapeFilename(prefix + postfix),
true);
1085 prefixPos = args.Find(
"%prefix");
1093 int pos = args.Find(
"%" + name);
1094 while (pos != wxNOT_FOUND)
1096 const wxString nextChar = args.Mid(pos + 1 + name.Len(), 1);
1097 if (nextChar ==
"%")
1099 args.Replace(
"%" + name +
"%", wxString::Format(
"%d", value),
true);
1103 if (nextChar ==
"*")
1105 const int closingPercent = args.Mid(pos + 2 + name.Len()).Find(
"%");
1106 if (closingPercent < 2)
1110 wxString factorString = args.Mid(pos + 2 + name.Len(), closingPercent);
1112 if (!factorString.ToCDouble(&factor))
1116 args.Replace(
"%" + name +
"*" + factorString +
"%", wxString::Format(
"%d",
hugin_utils::roundi(factor*value)),
true);
1123 pos = args.Find(
"%" + name);
1134 if (allActiveImages.empty())
1136 errStream <<
"ERROR: No active images in ROI. Nothing to do." << std::endl;
1139 wxFileInputStream input(outputSettings);
1142 errStream <<
"ERROR: Can not open file \"" << outputSettings.mb_str(wxConvLocal) <<
"\"." << std::endl;
1145 wxFileConfig settings(input);
1147 settings.Read(wxT(
"/General/StepCount"), &stepCount, 0);
1150 errStream <<
"ERROR: User-setting does not define any output steps." << std::endl;
1156 statusText = wxString::Format(_(
"Stitching using \"%s\""), outputSettings.c_str());
1160 statusText = wxString::Format(_(
"Stitching using \"%s\""), desc.c_str());
1162 wxString intermediateImageType =
GetSettingString(&settings, wxT(
"/General/IntermediateImageType"), wxT(
".tif"));
1164 if (intermediateImageType.Left(1).Cmp(wxT(
"."))!=0)
1166 intermediateImageType.Prepend(wxT(
"."));
1170 const bool needsWrapSwitch = (opts.
getHFOV() == 360.0) && (opts.
getWidth() == opts.
getROI().width());
1171 const vigra::Rect2D roi(opts.
getROI());
1172 wxString sizeString;
1173 if (roi.top() != 0 || roi.left() != 0)
1175 sizeString << roi.width() << wxT(
"x") << roi.height() << wxT(
"+") << roi.left() << wxT(
"+") << roi.top();
1179 sizeString << roi.width() << wxT(
"x") << roi.height();
1182 wxArrayString inputImages;
1183 for (
auto& i : allActiveImages)
1188 std::vector<HuginBase::UIntSet> exposureLayers;
1189 wxArrayString exposureLayersFiles;
1191 std::vector<HuginBase::UIntSet> stacks;
1192 wxArrayString stacksFiles;
1195 for (
size_t i = 0; i < stepCount; ++i)
1197 wxString stepString(wxT(
"/Step"));
1199 if (!settings.HasGroup(stepString))
1201 errStream <<
"ERROR: Output specifies " << stepCount <<
" steps, but step " << i <<
" is missing in configuration." << std::endl;
1205 settings.SetPath(stepString);
1207 if (stepType.IsEmpty())
1209 errStream <<
"ERROR: \"" << stepString.mb_str(wxConvLocal) <<
"\" has no type defined." << std::endl;
1216 errStream <<
"ERROR: Step " << i <<
" has no arguments given." << std::endl;
1221 if (stepType.CmpNoCase(wxT(
"remap")) == 0)
1224 const bool outputLayers = (settings.Read(wxT(
"OutputExposureLayers"), 0l) == 1l);
1227 args.Append(wxT(
" --create-exposure-layers -o ") +
wxEscapeFilename(prefix + wxT(
"_layer")));
1235 args, description));
1238 if (exposureLayers.empty())
1242 fill_set(exposureLayersNumber, 0, exposureLayers.size() - 1);
1246 if (settings.Read(wxT(
"Keep"), 0l) == 0l)
1254 const bool hdrOutput = args.MakeLower().Find(wxT(
"-r hdr")) != wxNOT_FOUND;
1260 if (settings.Read(wxT(
"Keep"), 0l) == 0l)
1272 if (stepType.CmpNoCase(wxT(
"merge")) == 0)
1276 if (resultFile.IsEmpty())
1278 errStream <<
"ERROR: Step " << i <<
" has no result file specified." << std::endl;
1282 resultFile.Replace(wxT(
"%prefix%"), prefix,
true);
1283 if (args.Replace(wxT(
"%result%"),
wxEscapeFilename(resultFile),
true) == 0)
1285 errStream <<
"ERROR: Step " << i <<
" has missing %result% placeholder in arguments." << std::endl;
1289 const wxString BlenderInput =
GetSettingString(&settings, wxT(
"Input"), wxT(
"all"));
1291 if (BlenderInput.CmpNoCase(wxT(
"all")) == 0)
1295 errStream <<
"ERROR: Step " << i <<
" has missing %input% placeholder in arguments." << std::endl;
1302 if (BlenderInput.CmpNoCase(wxT(
"stacks")) == 0)
1308 fill_set(stackNumbers, 0, stacks.size() - 1);
1313 errStream <<
"ERROR: Step " << i <<
" has missing %input% placeholder in arguments." << std::endl;
1320 if (BlenderInput.CmpNoCase(wxT(
"layers")) == 0)
1322 if (exposureLayers.empty())
1326 fill_set(exposureLayersNumber, 0, exposureLayers.size() - 1);
1331 errStream <<
"ERROR: Step " << i <<
" has missing %input% placeholder in arguments." << std::endl;
1338 errStream <<
"ERROR: Step " << i <<
" has invalid input type: \"" << BlenderInput.mb_str(wxConvLocal) <<
"\"." << std::endl;
1344 args.Replace(wxT(
"%size%"), sizeString,
true);
1346 if (needsWrapSwitch && !wrapSwitch.IsEmpty())
1348 args.Prepend(wrapSwitch + wxT(
" "));
1351 args, description, errStream))
1355 outputFiles.Add(resultFile);
1356 if (settings.Read(wxT(
"Keep"), 1l) == 0l)
1358 tempFilesDelete.Add(resultFile);
1363 if (stepType.CmpNoCase(wxT(
"stack")) == 0)
1370 fill_set(stackNumbers, 0, stacks.size() - 1);
1373 const bool clean = (settings.Read(wxT(
"Keep"), 0l) == 0l);
1374 args.Replace(wxT(
"%size%"), sizeString,
true);
1376 for (
size_t stackNr = 0; stackNr < stacks.size(); ++stackNr)
1378 wxString finalArgs(args);
1382 errStream <<
"ERROR: Step " << i <<
" has missing %input% placeholder in arguments." << std::endl;
1386 if (finalArgs.Replace(wxT(
"%output%"),
wxEscapeFilename(stacksFiles[stackNr]),
true) == 0)
1388 errStream <<
"ERROR: Step " << i <<
" has missing %output% placeholder in arguments." << std::endl;
1393 finalArgs, description, errStream))
1397 outputFiles.Add(stacksFiles[stackNr]);
1400 tempFilesDelete.Add(stacksFiles[stackNr]);
1406 if (stepType.CmpNoCase(wxT(
"layer")) == 0)
1409 if (exposureLayers.empty())
1413 fill_set(exposureLayersNumber, 0, exposureLayers.size() - 1);
1416 const bool clean = (settings.Read(wxT(
"Keep"), 0l) == 0l);
1417 args.Replace(wxT(
"%size%"), sizeString,
true);
1419 for (
size_t exposureLayerNr = 0; exposureLayerNr < exposureLayers.size(); ++exposureLayerNr)
1421 wxString finalArgs(args);
1425 errStream <<
"ERROR: Step " << i <<
" has missing %input% placeholder in arguments." << std::endl;
1429 if (finalArgs.Replace(wxT(
"%output%"),
wxEscapeFilename(exposureLayersFiles[exposureLayerNr]),
true) == 0)
1431 errStream <<
"ERROR: Step " << i <<
" has missing %output% placeholder in arguments." << std::endl;
1436 finalArgs, description, errStream))
1440 outputFiles.Add(exposureLayersFiles[exposureLayerNr]);
1443 tempFilesDelete.Add(exposureLayersFiles[exposureLayerNr]);
1449 if (stepType.CmpNoCase(wxT(
"modify")) == 0)
1453 if (inputFiles.IsEmpty())
1455 errStream <<
"ERROR: Step " << i <<
" has no input/output file specified." << std::endl;
1459 if (args.Find(wxT(
"%file%")) == wxNOT_FOUND)
1461 errStream <<
"ERROR: Step " << i <<
" has missing %file% placeholder in arguments." << std::endl;
1468 errStream <<
"ERROR: Step " << i <<
" has invalid %prefix% placeholder in arguments." << std::endl;
1474 errStream <<
"ERROR: Step " << i <<
" has invalid %width% placeholder in arguments." << std::endl;
1480 errStream <<
"ERROR: Step " << i <<
" has invalid %height% placeholder in arguments." << std::endl;
1485 if (progName.IsEmpty())
1487 errStream <<
"ERROR: Step " << i <<
" has no program name specified." << std::endl;
1494 #elif defined __WXMSW__
1496 const wxString prog = MSWGetProgname(ExePath, progName);
1498 const wxString prog = progName;
1500 if (inputFiles.CmpNoCase(wxT(
"all")) == 0)
1502 for (
size_t imgNr = 0; imgNr < remappedImages.size(); ++imgNr)
1504 wxString finalArgs(args);
1505 finalArgs.Replace(wxT(
"%file%"),
wxEscapeFilename(remappedImages[imgNr]),
true);
1506 finalArgs.Replace(wxT(
"%sourceimage%"),
wxEscapeFilename(inputImages[imgNr]),
true);
1507 commands->push_back(
new NormalCommand(prog, finalArgs, description));
1512 if (inputFiles.CmpNoCase(wxT(
"stacks")) == 0)
1516 errStream <<
"ERROR: Step " << i <<
" requests to modify stacks, but no stack was created before." << std::endl;
1520 for (
size_t stackNr = 0; stackNr < stacksFiles.size(); ++stackNr)
1522 wxString finalArgs(args);
1523 finalArgs.Replace(wxT(
"%file%"),
wxEscapeFilename(stacksFiles[stackNr]),
true);
1524 commands->push_back(
new NormalCommand(prog, finalArgs, description));
1529 if (inputFiles.CmpNoCase(wxT(
"layers")) == 0)
1531 if (exposureLayers.empty())
1533 errStream <<
"ERROR: Step " << i <<
" requests to modify exposure layers, but no exposure layer was created before." << std::endl;
1537 for (
size_t layerNr = 0; layerNr < exposureLayersFiles.size(); ++layerNr)
1539 wxString finalArgs(args);
1540 finalArgs.Replace(wxT(
"%file%"),
wxEscapeFilename(exposureLayersFiles[layerNr]),
true);
1541 commands->push_back(
new NormalCommand(prog, finalArgs, description));
1546 inputFiles.Replace(wxT(
"%prefix%"), prefix,
true);
1548 commands->push_back(
new NormalCommand(prog , args, description));
1555 if (stepType.CmpNoCase(wxT(
"exiftool")) == 0)
1558 if (resultFile.IsEmpty())
1560 errStream <<
"ERROR: Step " << i <<
" has no result file specified." << std::endl;
1564 resultFile.Replace(wxT(
"%prefix%"), prefix,
true);
1565 if (args.Replace(wxT(
"%result%"),
wxEscapeFilename(resultFile),
true) == 0)
1567 errStream <<
"ERROR: Step " << i <<
" has missing %result% placeholder in arguments." << std::endl;
1573 args, description));
1577 errStream <<
"ERROR: Step " << i <<
" has unknown Type \"" << stepType.mb_str(wxConvLocal) <<
"\"." << std::endl;
1594 for (
size_t i = 0; i < files.size(); ++i)
const wxString GetSettingString(wxConfigBase *setting, const wxString &name, const wxString defaultValue)
read a string from setting and remove all whitespaces
normal command for queue, processing is stopped if an error occurred in program
PanoramaOptions::ProjectionFormat getProjection() const
std::vector< UIntSet > getHDRStacks(const PanoramaData &pano, UIntSet allImgs, PanoramaOptions opts)
returns vector of set of output stacks
declaration of functions to handle stacks and layers
EdgeFillMode edgeFillMode
std::vector< UIntSet > UIntSetVector
std::vector< UIntSet > getExposureLayers(const PanoramaData &pano, UIntSet allImgs, PanoramaOptions opts)
returns vector of set of output exposure layers
const wxString GetConfigTempDir(const wxConfigBase *config)
return the temp dir from the preferences, ensure that it ends with path separator ...
PanoramaData * getNewSubset(const UIntSet &imgs) const
bool outputLDRLayers
save remapped layers (LDR)
optional command for queue, processing of queue is always continued, also if an error occurred ...
interface of CommandQueue creating for stitching engine
std::string outputLayersCompression
EdgeFillMode
edge fill modes
bool outputHDRLayers
save remapped layers (HDR)
unsigned int getHeight() const
get panorama height
#define HUGIN_EXIFTOOL_CREATE_GPANO
UIntSet getImagesinROI(const PanoramaData &pano, const UIntSet activeImages)
returns set of images which are visible in output ROI
void AddToArray(const wxArrayString &input, wxArrayString &output)
append all strings from input array to output array
#define HUGIN_USE_EXIFTOOL
wxString GetInternalProgram(const wxString &bindir, const wxString &name)
return path and name of external program, which comes bundled with Hugin
bool set_contains(const _Container &c, const typename _Container::key_type &key)
std::string enfuseOptions
int getHeight() const
Get the height of the image in pixels.
std::string outputImageTypeCompression
unsigned int colorReferenceImage
bool outputLDRExposureBlended
< save exposure fused stacks (no exposure adjustment)
void AddEdgeFillCommand(HuginQueue::CommandQueue *commands, const HuginBase::PanoramaOptions::EdgeFillMode &edgeFillMode, const wxString &ExePath, const wxString &inputFilename, const wxString &outputFilename, const wxString &compression)
adds the command for the edge fill option
UIntVector getEstimatedBlendingOrder(const PanoramaData &pano, const UIntSet &images, const unsigned int referenceImage)
returns vector of image numbers for blending in approbiate order
std::set< unsigned int > UIntSet
wxString GenerateFinalArgfile(const HuginBase::Panorama &pano, const wxString &projectName, const wxConfigBase *config, const HuginBase::UIntSet &images, const double exifToolVersion)
generate the final argfile
const vigra::Rect2D & getROI() const
std::string GetDataDir()
returns the full path to the data directory
bool outputLDRBlended
save blended panorama (LDR)
std::size_t getNrOfImages() const
number of images.
bool ReplaceWidthHeightPlaceHolder(wxString &args, const wxString name, int value)
std::vector< unsigned int > UIntVector
int getWidth() const
Get the width of the image in pixels.
wxString GetQuotedFilenamesString(const wxArrayString &files)
return a wxString with all files in files quoted
bool outputHDRBlended
save blended panorama (HDR)
#define HUGIN_NONA_USEGPU
bool ReplacePrefixPlaceholder(wxString &args, const wxString prefix)
UIntSet getActiveImages() const
get active images
std::string tiffCompression
std::string verdandiOptions
bool outputLDRExposureLayers
save blended exposure layers, do not perform fusion (no exposure adjustment)
void CleanQueue(CommandQueue *queue)
clean the queue, delete all entries, but not the queue itself
unsigned int getWidth() const
bool AddBlenderCommand(CommandQueue *queue, const wxString &ExePath, const wxString &prog, const int &stepNr, const wxString &arguments, const wxString &description, std::ostream &errStream)
std::string enblendOptions
const PanoramaOptions & getOptions() const
returns the options for this panorama
HDRMergeType hdrMergeMode
const wxString GetSettingStringTranslated(wxConfigBase *setting, const wxString &name, const wxString defaultValue)
read a translated string from settings and remove all whitespaces
str wxEscapeFilename(const str &arg)
special escaping routine for CommandQueues
bool outputLDRExposureRemapped
save remapped layers (no exposure adjustment)
bool outputLDRExposureLayersFused
save blended exposure layers which are then fused (no exposure adjustment)
std::string outputImageType
std::string outputImageTypeHDR
std::string GetHuginVersion()
return a string with version numbers
std::string hdrmergeOptions
const SrcPanoImage & getImage(std::size_t nr) const
get a panorama image, counting starts with 0
void fill_set(_Container &c, typename _Container::key_type begin, typename _Container::key_type end)
All variables of a source image.
bool outputHDRStacks
save image stacks (HDR)
CommandQueue * GetStitchingCommandQueueUserOutput(const HuginBase::Panorama &pano, const wxString &ExePath, const wxString &project, const wxString &prefix, const wxString &outputSettings, wxString &statusText, wxArrayString &outputFiles, wxArrayString &tempFilesDelete, std::ostream &errStream)
generates the command queue for stitching a pano, the commands are parsed from the given executor out...
vigra::Size2D getSize() const
get size of output image
CommandQueue * GetStitchingCommandQueue(const HuginBase::Panorama &pano, const wxString &ExePath, const wxString &project, const wxString &prefix, wxString &statusText, wxArrayString &outputFiles, wxArrayString &tempFilesDelete, std::ostream &errStream)
generates the command queue for stitching a pano it will also generate the necessary exiftool argfile...
BlendingMechanism blendMode
wxString GetExternalProgram(wxConfigBase *config, const wxString &bindir, const wxString &name)
return path and name of external program, which can be overwritten by the user
std::vector< NormalCommand * > CommandQueue
wxString GetQuotedFilenamesStringForVerdandi(const wxArrayString &files, const HuginBase::Panorama &pano, const HuginBase::UIntSetVector &stacks, const int referenceImage, const bool hardSeam)
build quoted filename list for verdandi
wxString PrintDetailInfo(const HuginBase::Panorama &pano, const HuginBase::PanoramaOptions &opts, const HuginBase::UIntSet &allActiveImages, const wxString &prefix, const wxString &bindir, wxConfigBase *config, double &exiftoolVersion)
double outputExposureValue
wxArrayString GetNumberedFilename(const wxString &prefix, const wxString &postfix, const HuginBase::UIntSet &img)
returns an array of filenames with numbers