i wanted to script it in an assistant so here it is just put cptune.cpp in ./src/hugin1/icpfind/ (couldn't get the includes to be built in time properly from the tools folder, feel free to put it wherever if you know better how to configure CMake, lol), and cptune.pod in ./docs/, patch the CMakeLists (or fix 'em yourself) and optionally patch Correlation.h to silence the "maxima to close to the border" non-errors from the CLI output (at least, PointFineTune just prints a debug line there while PointFineTuneRotSearch annoyingly prints an error)
seems to work as intended from my testing, have only tested in linux however, and not particularly thoroughly -- A list of frequently asked questions is available at: http://wiki.panotools.org/Hugin_FAQ --- You received this message because you are subscribed to the Google Groups "hugin and other free panoramic software" group. To unsubscribe from this group and stop receiving emails from it, send an email to [email protected]. To view this discussion on the web visit https://groups.google.com/d/msgid/hugin-ptx/f16ce6a1-9578-4058-8622-589f3a27efd1n%40googlegroups.com.
diff --git a/hugin/src/hugin_base/vigra_ext/Correlation.h b/hugin/src/hugin_base/vigra_ext/Correlation.h
index 5e25bdf..3ddc849 100644
--- a/hugin/src/hugin_base/vigra_ext/Correlation.h
+++ b/hugin/src/hugin_base/vigra_ext/Correlation.h
@@ -810,7 +810,7 @@ CorrelationResult PointFineTuneRotSearch(const IMAGET & templImg,
<< " at " << bestRes.maxpos << " under angle: " << bestAngle/M_PI*180);
} else {
// not enough values for subpixel estimation.
- DEBUG_ERROR("subpixel estimation not done, maxima to close to border");
+ DEBUG_DEBUG("subpixel estimation not done, maxima to close to border");
}
bestRes.maxpos = bestRes.maxpos + searchUL;
@@ -1056,7 +1056,7 @@ CorrelationResult PointFineTuneRotSearch(const IMAGET & templImg,
else {
// not enough values for subpixel estimation.
res = resCorrelate;
- DEBUG_ERROR("subpixel estimation not done, maxima to close to border");
+ DEBUG_DEBUG("subpixel estimation not done, maxima to close to border");
}
res.maxpos = res.maxpos + searchUL;
cptune.pod
Description: Perl program
diff --git a/hugin/CMakeLists.txt b/hugin/CMakeLists.txt
index be3db97..4ea1dad 100644
--- a/hugin/CMakeLists.txt
+++ b/hugin/CMakeLists.txt
@@ -714,7 +714,7 @@ IF(APPLE)
POST_BUILD
COMMAND ${CMAKE_SOURCE_DIR}/mac/PackageCreateToolsLibs.sh
ARGS ${CMAKE_CURRENT_BINARY_DIR}/tools_mac
- align_image_stack autooptimiser celeste_standalone checkpto cpclean cpfind
+ align_image_stack autooptimiser celeste_standalone checkpto cpclean cptune cpfind
deghosting_mask fulla geocpset hugin_executor hugin_lensdb hugin_stacker
icpfind linefind pano_modify pano_trafo pto_gen pto_lensstack pto_mask pto_merge
pto_move pto_template pto_var tca_correct vig_optimize
diff --git a/hugin/src/hugin1/hugin/CMakeLists.txt b/hugin/src/hugin1/hugin/CMakeLists.txt
index 58a974c..4160f9a 100644
--- a/hugin/src/hugin1/hugin/CMakeLists.txt
+++ b/hugin/src/hugin1/hugin/CMakeLists.txt
@@ -66,7 +66,7 @@ IF(APPLE)
# Tools
set( TOOLS ${CMAKE_BINARY_DIR}/src/tools/align_image_stack ${CMAKE_BINARY_DIR}/src/tools/autooptimiser ${CMAKE_BINARY_DIR}/src/tools/checkpto ${CMAKE_BINARY_DIR}/src/celeste/celeste_standalone
- ${CMAKE_BINARY_DIR}/src/tools/cpclean ${CMAKE_BINARY_DIR}/src/tools/linefind ${CMAKE_BINARY_DIR}/src/tools/geocpset
+ ${CMAKE_BINARY_DIR}/src/tools/cpclean ${CMAKE_BINARY_DIR}/src/hugin1/icpfind/cptune ${CMAKE_BINARY_DIR}/src/tools/linefind ${CMAKE_BINARY_DIR}/src/tools/geocpset
${CMAKE_BINARY_DIR}/src/tools/pano_modify ${CMAKE_BINARY_DIR}/src/hugin1/icpfind/icpfind ${CMAKE_BINARY_DIR}/src/hugin_cpfind/cpfind/cpfind
# bins for external use
${CMAKE_BINARY_DIR}/src/tools/fulla ${CMAKE_BINARY_DIR}/src/tools/tca_correct ${CMAKE_BINARY_DIR}/src/tools/vig_optimize
@@ -186,7 +186,7 @@ IF(APPLE)
WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/${PROGNAME}.app/Contents/Resources/"
)
- ADD_DEPENDENCIES(${PROGNAME} align_image_stack autooptimiser checkpto cpclean linefind geocpset pano_modify icpfind cpfind celeste_standalone
+ ADD_DEPENDENCIES(${PROGNAME} align_image_stack autooptimiser checkpto cpclean cptune linefind geocpset pano_modify icpfind cpfind celeste_standalone
fulla tca_correct vig_optimize hugin_lensdb hugin_executor pano_trafo pto_gen pto_lensstack pto_mask pto_merge
pto_move pto_template pto_var deghosting_mask hugin_stacker
translations)
diff --git a/hugin/src/hugin1/icpfind/CMakeLists.txt b/hugin/src/hugin1/icpfind/CMakeLists.txt
index 97481b0..c518e15 100644
--- a/hugin/src/hugin1/icpfind/CMakeLists.txt
+++ b/hugin/src/hugin1/icpfind/CMakeLists.txt
@@ -19,9 +19,12 @@ ENDIF (${HUGIN_SHARED_LIBS})
TARGET_LINK_LIBRARIES(icpfindlib huginbase ${wxWidgets_LIBRARIES} huginbasewx)
ADD_EXECUTABLE(icpfind icpfind.h icpfind.cpp)
+TARGET_LINK_LIBRARIES(icpfind icpfindlib ${wxWidgets_LIBRARIES} ${common_libs})
+
+ADD_EXECUTABLE(cptune cptune.cpp)
+TARGET_LINK_LIBRARIES(cptune ${common_libs} ${image_libs} huginbase ${wxWidgets_LIBRARIES} huginbasewx)
-TARGET_LINK_LIBRARIES(icpfind icpfindlib ${wxWidgets_LIBRARIES} ${common_libs})
-INSTALL(TARGETS icpfind DESTINATION ${BINDIR})
+INSTALL(TARGETS icpfind cptune DESTINATION ${BINDIR})
IF(WIN32)
INSTALL(FILES default.win DESTINATION ${HUGINDATADIR}/data RENAME default.setting)
// -*- c-basic-offset: 4 -*- /** @file cptune.cpp * * @brief program to fine tune control points * * sloppy hackjob ripping the guts of the FT functions out of the GUI * * @author jak * * $Id$ * */ /* This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this software. If not, see * <http://www.gnu.org/licenses/>. * */ #include <fstream> #include <sstream> #include <getopt.h> #include "base_wx/platform.h" #include "base_wx/wxPlatform.h" #include "base_wx/CommandHistory.h" #include "base_wx/wxImageCache.h" #include "base_wx/wxPanoCommand.h" #include "algorithms/basic/CalculateOptimalScale.h" #include "vigra/imageinfo.hxx" #include "vigra_ext/Correlation.h" #include "panotools/PanoToolsInterface.h" //#include "hugin/CPEditorPanel.h" //if this had GetImageRotatedTo accesible in the header we wouldn't need to copy it here #include "hugin/config_defaults.h" static void usage(const char* name) { std::cout << name << ": fine-tune control points" << std::endl << "cptune version " << hugin_utils::GetHuginVersion() << std::endl << std::endl << "Usage: " << name << " [options] input.pto" << std::endl << std::endl << "Fine-tunes all control points and optionally removes points outside set ranges" << std::endl << std::endl << " General Options:" << std::endl << " -o|--output file.pto output Hugin PTO file (default: '<filename>_tuned.pto')" << std::endl << " -v|--verbose output details for each tuned point"<<std::endl << " -d|--debug output even more details, implies verbose"<<std::endl << " -q|--silent no output to command line (disables verbose & debug)"<<std::endl << " -h|--help shows help" << std::endl << std::endl << " Tuning Options" << std::endl << " -p|--patch-width <l> width of template patch (in px) (default: 21)"<<std::endl << " -s|--search-width <l> width of search area (in px) (default: 10)"<<std::endl << " -t|--corr-thresh <d> correlation threshold (default: 0.8)"<<std::endl << " -u|--curv-thresh <d> curvature threshold (default: 0.0)"<<std::endl << " -r|--rotation-search <0/1> rotate images for better fine tuning (0=off/1=on) (default: on)"<<std::endl << " -b|--start-angle <d> starting angle for rotation search (default: -30.0)"<<std::endl << " -e|--stop-angle <d> stopping angle for rotation search (default: 30.0)"<<std::endl << " -l|--angles <d> set start and stop angles to -<d>/<d>"<<std::endl << " -n|--steps <i> steps between angles for rotation search (default: 12)"<<std::endl << " -j|--reproject <-1/0/1> reproject during tuning for better results between wildly different lenses (default: -1/auto, set 1/0 to force on/off)"<<std::endl << " -f|--alternate swap search and template image for every other point"<<std::endl << std::endl << " Cleaning Options:" << std::endl << " -c|--clean remove points outside of acceptable correlation range (set below)"<<std::endl << " -i|--corr-min <d> minimum acceptable correlation (default: correlation threshold)"<<std::endl << " -a|--corr-max <d> maximum acceptable correlation (default: 1.0)"<<std::endl << " -k|--curv-min <d> minimum acceptable curvature (default: curvature threshold)"<<std::endl << " -g|--curv-max <d> maximum acceptable curvature (default: 2.0)"<<std::endl << " -z|--only-both-curvs only mark a point for cleaning if both curv.x and curv.y exceed cleaning threshholds (default: either)"<<std::endl << std::endl; } /* not sure what these do but everything seems to work fine without it? // dummy panotools progress functions static int ptProgress(int command, char* argument) { return 1; } static int ptinfoDlg(int command, char* argument) { return 1; } */ // return a SrcPanoImage so that the given point is in the center (from CPEditorPanel, since it isn't declared in the header) HuginBase::SrcPanoImage GetImageRotatedTo(const HuginBase::SrcPanoImage& img, const vigra::Diff2D& point, int testWidth, double& neededHFOV) { // copy only necessary information into temporary SrcPanoImage HuginBase::SrcPanoImage imgMod; imgMod.setSize(img.getSize()); imgMod.setProjection(img.getProjection()); imgMod.setHFOV(img.getHFOV()); // calculate, where the interest point lies HuginBase::PanoramaOptions opt; opt.setProjection(HuginBase::PanoramaOptions::EQUIRECTANGULAR); opt.setHFOV(360); opt.setWidth(360); opt.setHeight(180); HuginBase::PTools::Transform transform; transform.createInvTransform(imgMod, opt); double x1, y1; if (!transform.transformImgCoord(x1, y1, point.x, point.y)) { neededHFOV = -1; return imgMod; } // equirect image coordinates -> equirectangular coordinates // transformImgCoord places the origin at the upper left corner and negate Matrix3 rotY; rotY.SetRotationPT(DEG_TO_RAD(180 - (x1 + 0.5)), 0, 0); Matrix3 rotP; rotP.SetRotationPT(0, DEG_TO_RAD((y1 + 0.5) - 90), 0); double y, p, r; // calculate the necessary rotation angles and remember Matrix3 rot = rotP * rotY; rot.GetRotationPT(y, p, r); imgMod.setYaw(RAD_TO_DEG(y)); imgMod.setPitch(RAD_TO_DEG(p)); imgMod.setRoll(RAD_TO_DEG(r)); // new we calculate the needed HFOV for template/search area width double x2, y2; // check a point left from our interest point if (transform.transformImgCoord(x2, y2, point.x - testWidth / 2.0, point.y)) { if (x2 < x1) { neededHFOV = 2.0 * (x1 - x2); } else { // we crossed the 360 deg border neededHFOV = 2.0 * (360 - x2 + x1); }; // limit maximum HFOV for remapping to stereographic projection done as next step if (neededHFOV > 90) { neededHFOV = 90; }; return imgMod; }; // this goes wrong, maybe the tested point is outside the image area of a fisheye image // now test the right point if (transform.transformImgCoord(x2, y2, point.x + testWidth / 2.0, point.y)) { if (x1 < x2) { neededHFOV = 2.0 * (x2 - x1); } else { // we crossed the 360 deg border neededHFOV = 2.0 * (360 + x2 - x1); }; // limit maximum HFOV for remapping to stereographic projection done as next step if (neededHFOV > 90) { neededHFOV = 90; }; return imgMod; }; // we can't calculate the needed HFOV, return -1 neededHFOV = -1; return imgMod; }; //from CPEditorPanel, need to copy it here so options don't get overwritten by Hugin settings vigra_ext::CorrelationResult PointFineTuneProjectionAware(const HuginBase::SrcPanoImage& templ, const vigra::UInt8RGBImage& templImg, vigra::Diff2D templPos, int templSize, const HuginBase::SrcPanoImage& search, const vigra::UInt8RGBImage& searchImg, vigra::Diff2D searchPos, int sWidth, bool rotatingFinetune, double& startAngle, double& stopAngle, int nSteps, int reproject) { bool doReprojection; if (templ.getProjection() == search.getProjection() && templ.getHFOV() < 65 && search.getHFOV() < 65 && fabs(templ.getHFOV() - search.getHFOV()) < 5) { doReprojection = false; } else { doReprojection = true; }; if (reproject==0) { doReprojection = false; }; if (reproject==1) { doReprojection = true; }; // if both images have the same projection and the angle does not differ to much use normal point fine-tune if (!doReprojection) { vigra_ext::CorrelationResult res; if (rotatingFinetune) { res = vigra_ext::PointFineTuneRotSearch(templImg, templPos, templSize, searchImg, searchPos, sWidth, startAngle, stopAngle, nSteps); } else { res = vigra_ext::PointFineTune(templImg, vigra::RGBToGrayAccessor<vigra::RGBValue<vigra::UInt8> >(), templPos, templSize, searchImg, vigra::RGBToGrayAccessor<vigra::RGBValue<vigra::UInt8> >(), searchPos, sWidth); }; res.corrPos = templPos; return res; }; // images have different projections or the HFOV is different // so we reproject the image to stereographic projection and fine tune point there // rotate image so that interest point is in the center double templHFOV = 0; double searchHFOV = 0; HuginBase::SrcPanoImage templMod = GetImageRotatedTo(templ, templPos, templSize, templHFOV); HuginBase::SrcPanoImage searchMod = GetImageRotatedTo(search, searchPos, sWidth + templSize + 5, searchHFOV); vigra_ext::CorrelationResult res; res.maxpos = hugin_utils::FDiff2D(-1, -1); res.corrPos = hugin_utils::FDiff2D(-1, -1); if (templHFOV < 0 || searchHFOV < 0) { //something went wrong, e.g. image outside of projection circle for fisheye lenses return res; } // populate PanoramaOptions HuginBase::PanoramaOptions opts; opts.setProjection(HuginBase::PanoramaOptions::STEREOGRAPHIC); opts.setHFOV(std::max(templHFOV, searchHFOV)); // calculate a sensible scale factor double scaleTempl = HuginBase::CalculateOptimalScale::calcOptimalPanoScale(templMod, opts); double scaleSearch = HuginBase::CalculateOptimalScale::calcOptimalPanoScale(searchMod, opts); opts.setWidth(std::max<unsigned int>(opts.getWidth()*std::min(scaleTempl, scaleSearch), 3 * templSize)); opts.setHeight(opts.getWidth()); // transform coordinates to transform system double templX, templY, searchX, searchY; HuginBase::PTools::Transform transform; transform.createInvTransform(templMod, opts); transform.transformImgCoord(templX, templY, templPos.x, templPos.y); transform.createInvTransform(searchMod, opts); transform.transformImgCoord(searchX, searchY, searchPos.x, searchPos.y); // now transform the images vigra_ext::PassThroughFunctor<vigra::UInt8> ptf; AppBase::DummyProgressDisplay dummy; transform.createTransform(searchMod, opts); vigra::UInt8RGBImage searchImgMod(opts.getSize()); vigra::BImage alpha(opts.getSize()); vigra_ext::transformImage(srcImageRange(searchImg), destImageRange(searchImgMod), destImage(alpha), vigra::Diff2D(0, 0), transform, ptf, false, vigra_ext::INTERP_CUBIC, &dummy); // now remap template, we need to remap a little bigger area to have enough information when the template // is rotated in PointFineTuneRotSearch vigra::Diff2D templPointInt(hugin_utils::roundi(templX), hugin_utils::roundi(templY)); vigra::Rect2D rect(templPointInt.x - templSize - 2, templPointInt.y - templSize - 2, templPointInt.x + templSize + 2, templPointInt.y + templSize + 2); rect &= vigra::Rect2D(opts.getSize()); transform.createTransform(templMod, opts); vigra::UInt8RGBImage templImgMod(opts.getSize()); vigra_ext::transformImage(srcImageRange(templImg), destImageRange(templImgMod, rect), destImage(alpha), vigra::Diff2D(rect.left(), rect.top()), transform, ptf, false, vigra_ext::INTERP_CUBIC, &dummy); #if defined DEBUG_EXPORT_FINE_TUNE_REMAPPING { vigra::ImageExportInfo templExport("template_remapped.tif"); vigra::exportImage(srcImageRange(templImgMod), templExport.setPixelType("UINT8")); vigra::ImageExportInfo searchExport("search_remapped.tif"); vigra::exportImage(srcImageRange(searchImgMod), searchExport.setPixelType("UINT8")); } #endif // now we can finetune the point in stereographic projection // we are always using the rotate fine-tune algorithm, because for this case // often a rotation is involved res = vigra_ext::PointFineTuneRotSearch(templImgMod, templPointInt, templSize, searchImgMod, vigra::Diff2D(hugin_utils::roundi(searchX), hugin_utils::roundi(searchY)), sWidth, startAngle, stopAngle, nSteps); // we transfer also the new found template position back to the original image transform.createTransform(templMod, opts); transform.transformImgCoord(res.corrPos.x, res.corrPos.y, templPointInt.x + 0.00001, templPointInt.y + 0.00001); // we need to move the finetune point back to position in original image transform.createTransform(searchMod, opts); transform.transformImgCoord(res.maxpos.x, res.maxpos.y, res.maxpos.x, res.maxpos.y); return res; }; int main(int argc, char* argv[]) { //set baked in defaults in case hugin defaults/settings can't be found (or i can't figure out how to load them...) long patchWidth = 21; long searchWidth = 10; double corrThresh = 0.8; double curvThresh = 0.0; bool rotatingFinetune = true; double startAngle = -30.0; double stopAngle = 30.0; int nSteps = 12; /* //load defaults & settings from hugin (if i could) wxConfigBase *cfg = wxConfigBase::Get(); patchWidth = cfg->Read(wxT("/Finetune/TemplateSize"), HUGIN_FT_TEMPLATE_SIZE); searchWidth = cfg->Read(wxT("/Finetune/LocalSearchWidth"), HUGIN_FT_LOCAL_SEARCH_WIDTH); corrThresh = HUGIN_FT_CORR_THRESHOLD; cfg->Read(wxT("/Finetune/CorrThreshold"), &corrThresh, HUGIN_FT_CORR_THRESHOLD); curvThresh = HUGIN_FT_CURV_THRESHOLD; cfg->Read(wxT("/Finetune/CurvThreshold"), &curvThresh, HUGIN_FT_CURV_THRESHOLD); rotatingFinetune = cfg->Read(wxT("/Finetune/RotationSearch"), HUGIN_FT_ROTATION_SEARCH) == 1; startAngle = HUGIN_FT_ROTATION_START_ANGLE; cfg->Read(wxT("/Finetune/RotationStartAngle"), &startAngle, HUGIN_FT_ROTATION_START_ANGLE); stopAngle = HUGIN_FT_ROTATION_STOP_ANGLE; cfg->Read(wxT("/Finetune/RotationStopAngle"), &stopAngle, HUGIN_FT_ROTATION_STOP_ANGLE); nSteps = cfg->Read(wxT("/Finetune/RotationSteps"), HUGIN_FT_ROTATION_STEPS); */ //initialize other options std::string output; bool verbose = false; bool debug = false; bool silent = false; bool flipFlop = false; bool cleanPoints = false; bool bothCurvs = false; int reproject = -1; double corrMin = corrThresh; double corrMax = 1.0; //values above 1.0 usually tend to be garbage, doesn't seem like that should even happen but it does? double curvMin = curvThresh; double curvMax = 9.9; //set absurdly high to functionally disable by default, probably unnecesary but might as well give the option // parse arguments const char* optstring = "o:vdqhp:s:t:u:r:b:e:l:n:j:fci:a:k:g:z"; static struct option longOptions[] = { { "output", required_argument, NULL, 'o'}, { "verbose", no_argument, NULL, 'v'}, { "debug", no_argument, NULL, 'd'}, { "silent", no_argument, NULL, 'q'}, { "help", no_argument, NULL, 'h' }, { "patch-width", required_argument, NULL, 'p'}, { "search-width", required_argument, NULL, 's'}, { "corr-thresh", required_argument, NULL, 't'}, { "curv-thresh", required_argument, NULL, 'u'}, { "rotation-search", required_argument, NULL, 'r'}, { "start-angle", required_argument, NULL, 'b'}, { "stop-angle", required_argument, NULL, 'e'}, { "angles", required_argument, NULL, 'l'}, { "steps", required_argument, NULL, 'n'}, { "reproject", required_argument, NULL, 'j'}, { "alternate", no_argument, NULL, 'f'}, { "clean", no_argument, NULL, 'c'}, { "corr-min", required_argument, NULL, 'i'}, { "corr-max", required_argument, NULL, 'a'}, { "curv-min", required_argument, NULL, 'k'}, { "curv-max", required_argument, NULL, 'g'}, { "only-both-curvs", no_argument, NULL, 'z'}, 0 }; int c; while ((c = getopt_long(argc, argv, optstring, longOptions, nullptr)) != -1) { switch (c) { case 'o': output = optarg; break; case 'v': verbose = true; break; case 'd': debug = true; break; case 'q': silent = true; break; case 'h': usage(hugin_utils::stripPath(argv[0]).c_str()); return 0; case 'p': patchWidth = atof(optarg); if(patchWidth < 0) { std::cerr << hugin_utils::stripPath(argv[0]) << ": Invalid parameter: -p/--patch-width must be greater than 0" << std::endl; return 1; }; break; case 's': searchWidth = atof(optarg); if(searchWidth < 0) { std::cerr << hugin_utils::stripPath(argv[0]) << ": Invalid parameter: -s/--search-width must be greater than 0" << std::endl; return 1; }; break; case 't': corrThresh = atof(optarg); if(corrThresh < 0) { std::cerr << hugin_utils::stripPath(argv[0]) << ": Invalid parameter: -t/--corr-thresh must be greater than 0.0 (and probably less than 1.0)" << std::endl; return 1; }; break; case 'u': curvThresh = atof(optarg); if(curvThresh < 0) { std::cerr << hugin_utils::stripPath(argv[0]) << ": Invalid parameter: -u/--curv-thresh must be greater than 0.0 (and probably less than 1.0)" << std::endl; return 1; }; break; case 'r': if(atof(optarg)==0) { rotatingFinetune = false; break; } else if (atof(optarg)==1) { rotatingFinetune = true; break; } else { std::cerr << hugin_utils::stripPath(argv[0]) << ": Invalid parameter: -r/--rotation-search must be 0 or 1" << std::endl; return 1; }; case 'b': startAngle = atof(optarg); if(startAngle < -360 || startAngle > 360) { std::cerr << hugin_utils::stripPath(argv[0]) << ": Invalid parameter: angle should be between -360.0 and 360.0" << std::endl; return 1; }; break; case 'e': stopAngle = atof(optarg); if(stopAngle < -360 || stopAngle > 360) { std::cerr << hugin_utils::stripPath(argv[0]) << ": Invalid parameter: angle should be between -360.0 and 360.0" << std::endl; return 1; }; break; case 'l': if(atof(optarg) < 0 || atof(optarg) > 360) { std::cerr << hugin_utils::stripPath(argv[0]) << ": Invalid parameter: angle should be between 0.0 and 360.0 (will be set to negative for start)" << std::endl; return 1; }; startAngle = - atof(optarg); stopAngle = atof(optarg); break; case 'n': nSteps = atof(optarg); break; case 'j': reproject = atof(optarg); if(reproject < -1 || reproject > 1) { std::cerr << hugin_utils::stripPath(argv[0]) << ": Invalid parameter: -j/--reproject must be -1, 0, or 1" << std::endl; return 1; }; break; case 'f': flipFlop = true; break; case 'c': cleanPoints = true; break; case 'i': if(atof(optarg) < 0) { std::cerr << hugin_utils::stripPath(argv[0]) << ": Invalid parameter: -i/--corr-min must be greater than 0.0 (and probably less than 1.0)" << std::endl; return 1; }; corrMin = atof(optarg); break; case 'a': if(atof(optarg) < 0) { std::cerr << hugin_utils::stripPath(argv[0]) << ": Invalid parameter: -a/--corr-max must be greater than 0.0 (and probably less than 1.0)" << std::endl; return 1; }; corrMax = atof(optarg); break; case 'k': if(atof(optarg) < 0) { std::cerr << hugin_utils::stripPath(argv[0]) << ": Invalid parameter: -k/--curv-min must be greater than 0.0 (and probably less than 1.0)" << std::endl; return 1; }; curvMin = atof(optarg); break; case 'g': if(atof(optarg) < 0) { std::cerr << hugin_utils::stripPath(argv[0]) << ": Invalid parameter: -g/--curv-max must be greater than 0.0 (and probably less than 1.0)" << std::endl; return 1; }; curvMax = atof(optarg); break; case 'z': bothCurvs = true; break; case ':': case '?': // missing argument or invalid switch usage(hugin_utils::stripPath(argv[0]).c_str()); return 1; break; default: // this should not happen abort (); } } if (debug) verbose = true; if (silent) { verbose = false; debug = false; }; if (!silent) std::cout << std::endl << "Hugin's CPTune version " << hugin_utils::GetHuginVersion() << std::endl; //sanity checks if (argc - optind != 1) { if (argc - optind < 1) { std::cerr << hugin_utils::stripPath(argv[0]) << ": No project file given." << std::endl; } else { std::cerr << hugin_utils::stripPath(argv[0]) << ": Only one project file expected." << std::endl; }; return 1; }; std::string input=argv[optind]; HuginBase::Panorama pano; if (!pano.ReadPTOFile(input, hugin_utils::getPathPrefix(input))) { std::cerr << "Unable to read panorama PTOFile" << std::endl; return 1; }; const size_t nImgs=pano.getNrOfImages(); const size_t nPoints=pano.getNrOfCtrlPoints(); if (nImgs < 2) { std::cerr << "Panorama should consist of at least two images" << std::endl; return 1; }; if (nPoints < 2) { std::cerr << "Panorama should contain at least 2 control points" << std::endl; return 1; }; if (debug) std::cout << std::endl << "Running with settings: " << std::endl << "patchWidth = " << patchWidth << std::endl << "searchWidth = " << searchWidth << std::endl << "corrThresh = " << corrThresh << std::endl << "curvThresh = " << curvThresh << std::endl << "rotatingFinetune = " << rotatingFinetune << std::endl << "startAngle = " << startAngle << std::endl << "stopAngle = " << stopAngle << std::endl; //RotSearch needs these in rads startAngle = DEG_TO_RAD(startAngle); stopAngle = DEG_TO_RAD(stopAngle); if (debug) std::cout << "startAngle (rad) = " << startAngle << std::endl << "stopAngle (rad) = " << stopAngle << std::endl << "nSteps = " << nSteps << std::endl << "flipFlop = " << flipFlop << std::endl << "reproject = " << reproject << std::endl << "cleanPoints = " << cleanPoints << std::endl << "corrMin = " << corrMin << std::endl << "corrMax = " << corrMax << std::endl << "curvMin = " << curvMin << std::endl << "curvMax = " << curvMax << std::endl << "bothCurvs = " << bothCurvs << std::endl << "verbose = " << verbose << std::endl << "debug = " << debug << std::endl << "silent = " << silent << std::endl; //start MainFrame::OnFineTuneAll if (!silent) std::cout << std::endl << " -> Loaded project " << hugin_utils::stripPath(input) << ", found " << nImgs << " images with " << nPoints << " control points <-" << std::endl << std::endl; unsigned int toTune=0; unsigned int toClean=0; unsigned int nGood=0; unsigned int nBad=0; unsigned int nSkip=0; unsigned int nCorrUnder=0; unsigned int nCorrOver=0; unsigned int nCurvUnder=0; unsigned int nCurvOver=0; ImageCache & imgCache = ImageCache::getInstance(); HuginBase::UIntSet CPtoRemove; // create a map of all control points. HuginBase::CPVector cps = pano.getCtrlPoints(); std::set<unsigned int> unoptimized; for (unsigned int i=0; i < cps.size(); i++) { // create all control points. unoptimized.insert(i); } // do not process the control points in random order, // but walk from image to image, to reduce image reloading // in low mem situations. for (unsigned int imgNr = 0 ; imgNr < nImgs; imgNr++) { std::set<unsigned int>::iterator it=unoptimized.begin(); imgCache.softFlush(); toTune=0; toClean=0; if (!silent) std::cout << "-> Loading image #" << imgNr << ": " << hugin_utils::stripPath(pano.getImage(imgNr).getFilename()) << std::endl; while (it != unoptimized.end()) { if (cps[*it].image1Nr == imgNr || cps[*it].image2Nr == imgNr) { // finetune only normal points if (cps[*it].mode == HuginBase::ControlPoint::X_Y) { if (verbose) std::cout << " - Fine-tuning point " << *it << " between image #" << cps[*it].image1Nr << " and image #" << cps[*it].image2Nr << std::endl; wxImage wxSearchImg; ImageCache::ImageCacheRGB8Ptr img1 = imgCache.getImage(pano.getImage(cps[*it].image1Nr).getFilename())->get8BitImage(); ImageCache::ImageCacheRGB8Ptr img2 = imgCache.getImage(pano.getImage(cps[*it].image2Nr).getFilename())->get8BitImage(); vigra_ext::CorrelationResult res; //not sure rounding these is neccesary? //vigra::Diff2D point1(hugin_utils::roundi(cps[*it].x1), hugin_utils::roundi(cps[*it].y1)); //vigra::Diff2D point2(hugin_utils::roundi(cps[*it].x2), hugin_utils::roundi(cps[*it].y2)); //try without rounding /shrug vigra::Diff2D point1(cps[*it].x1, cps[*it].y1); vigra::Diff2D point2(cps[*it].x2, cps[*it].y2); const long templWidth = patchWidth; const long sWidth = templWidth + searchWidth; if (flipFlop && *it%2==1) { if (debug) std::cout << " -- [Debug] Swapping template and search images" << std::endl; res = PointFineTuneProjectionAware(pano.getImage(cps[*it].image2Nr), *img2, point2, templWidth, pano.getImage(cps[*it].image1Nr), *img1, point1, sWidth, rotatingFinetune, startAngle, stopAngle, nSteps, reproject); } else { res = PointFineTuneProjectionAware(pano.getImage(cps[*it].image1Nr), *img1, point1, templWidth, pano.getImage(cps[*it].image2Nr), *img2, point2, sWidth, rotatingFinetune, startAngle, stopAngle, nSteps, reproject); } // invert curvature. we always assume its a maxima, the curvature there is negative // however, we allow the user to specify a positive threshold, so we need to // invert it res.curv.x = - res.curv.x; res.curv.y = - res.curv.y; if (debug) std::cout << " -- [Debug] Results: maxi(corr)=" << res.maxi << ", curv.x=" << res.curv.x << ", curv.y=" << res.curv.y << ", maxpos.x=" << res.maxpos.x << ", maxpos.y=" << res.maxpos.y << ", corrPos.x=" << res.corrPos.x << ", corrPos.y=" << res.corrPos.y << std::endl; if (res.maxi < corrThresh || res.curv.x < curvThresh || res.curv.y < curvThresh || res.maxpos.x < 0 || res.maxpos.y < 0 || res.corrPos.x < 0 || res.corrPos.y < 0) { // Bad correlation result. nBad++; if (verbose) std::cout << " -- [Tune] BAD Point: Correlation ( " << res.maxi << " ) or curvature " << res.curv << " below thresholds, not tuning" << std::endl; } else { nGood++; toTune++; if (verbose) std::cout << " -- [Tune] Good Point! Correlation ( " << res.maxi << " ), Curvature " << res.curv << ", tuning point" << std::endl; // only update if a good correlation was found if (flipFlop && *it%2==1) { cps[*it].x2 = res.corrPos.x; cps[*it].y2 = res.corrPos.y; cps[*it].x1 = res.maxpos.x; cps[*it].y1 = res.maxpos.y; //cps[*it].error = res.maxi; } else { cps[*it].x1 = res.corrPos.x; cps[*it].y1 = res.corrPos.y; cps[*it].x2 = res.maxpos.x; cps[*it].y2 = res.maxpos.y; //cps[*it].error = res.maxi; } } //cleanlist screening, could probably be optimized if (cleanPoints) { if (res.maxi < corrMin) { if (verbose) std::cout << " -- [Clean] Correlation below acceptable range, adding to cleanlist" << std::endl; nCorrUnder++; toClean++; CPtoRemove.insert(*it); } else if (res.maxi > corrMax) { if (verbose) std::cout << " -- [Clean] Correlation above acceptable range, adding to cleanlist" << std::endl; nCorrOver++; toClean++; CPtoRemove.insert(*it); } else if (res.curv.x < curvMin && res.curv.y < curvMin) { if (verbose) std::cout << " -- [Clean] X&Y curvature below acceptable range, adding to cleanlist" << std::endl; nCurvUnder++; toClean++; CPtoRemove.insert(*it); } else if (res.curv.x > curvMax && res.curv.y > curvMax) { if (verbose) std::cout << " -- [Clean] X&Y curvature above acceptable range, adding to cleanlist" << std::endl; nCurvOver++; toClean++; CPtoRemove.insert(*it); } else if ((res.curv.x > curvMax || res.curv.y > curvMax) && (res.curv.x < curvMin || res.curv.y < curvMin)) { if (verbose) std::cout << " -- [Clean] X&Y curvature outside acceptable range, adding to cleanlist" << std::endl; nCurvOver++; nCurvUnder++; toClean++; CPtoRemove.insert(*it); } else if (!bothCurvs && res.curv.x < curvMin) { if (verbose) std::cout << " -- [Clean] X curvature below acceptable range, adding to cleanlist" << std::endl; nCurvUnder++; toClean++; CPtoRemove.insert(*it); } else if (!bothCurvs && res.curv.y < curvMin) { if (verbose) std::cout << " -- [Clean] Y curvature below acceptable range, adding to cleanlist" << std::endl; nCurvUnder++; toClean++; CPtoRemove.insert(*it); } else if (!bothCurvs && res.curv.x > curvMax) { if (verbose) std::cout << " -- [Clean] X curvature above acceptable range, adding to cleanlist" << std::endl; nCurvOver++; toClean++; CPtoRemove.insert(*it); } else if (!bothCurvs && res.curv.y > curvMax) { if (verbose) std::cout << " -- [Clean] Y curvature above acceptable range, adding to cleanlist" << std::endl; nCurvOver++; toClean++; CPtoRemove.insert(*it); } } } else { // for line control points just skip if (verbose) std::cout << " - Point is probably a line, skipping" << std::endl; nSkip++; //cps[*it].error = 1.0; }; unsigned int rm = *it; ++it; unoptimized.erase(rm); } else { ++it; } } if (!silent) std::cout << " - Found " << toTune << " tunable points" << std::endl; if (!silent && cleanPoints) std::cout << " - Found " << toClean << " points to be cleaned" << std::endl; if (verbose) std::cout << std::endl; } //report results if (!silent) std::cout << std::endl << "[Tune] Results: " << nGood << " points fine-tuned, " << nBad << " points not tuned due to low correlation, " << nSkip << " points skipped (probably lines)" << std::endl; if (!silent && cleanPoints) std::cout << "[Clean] Results: " << nCorrUnder << " points below and " << nCorrOver << " points above acceptable correlation range" << std::endl << " -- " << nCurvUnder << " points below and " << nCurvOver << " points above acceptable curvature range" << std::endl; if (!silent) std::cout << std::endl; // set newly optimized points if (!silent) std::cout << "Setting new positions for " << nGood << " tuned points..." << std::endl; PanoCommand::GlobalCmdHist::getInstance().addCommand(new PanoCommand::UpdateCPsCmd(pano, cps, false)); //clean bad points if enabled if (cleanPoints && !CPtoRemove.empty()) { if (!silent) std::cout << "Cleaning " << CPtoRemove.size() << " points outside acceptable ranges..." << std::endl; for (HuginBase::UIntSet::reverse_iterator cprit = CPtoRemove.rbegin(); cprit != CPtoRemove.rend(); ++cprit) { if (debug) std::cout << "--removing point (" << *cprit << ")" << std::endl; pano.removeCtrlPoint(*cprit); } }; //write output // Set output .pto filename if not given output = hugin_utils::GetOutputFilename(output, input, "tuned"); if (pano.WritePTOFile(output, hugin_utils::getPathPrefix(output))) { if (!silent) std::cout << std::endl << "Output file written to " << output << std::endl << std::endl; return 0; } else { std::cerr << std::endl << "Error trying to write file to " << output << std::endl << std::endl; return 1; }; }
