From caf512802fbbfc49cfd51853ee886486d30c77d1 Mon Sep 17 00:00:00 2001 From: Fabien Date: Thu, 11 Mar 2021 15:38:41 +0100 Subject: [PATCH 1/5] [utils] update to 360 splitting --- src/software/utils/CMakeLists.txt | 1 + src/software/utils/main_split360Images.cpp | 17 ++++++++++------- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/software/utils/CMakeLists.txt b/src/software/utils/CMakeLists.txt index e243bd12dd..46d2651b07 100644 --- a/src/software/utils/CMakeLists.txt +++ b/src/software/utils/CMakeLists.txt @@ -219,6 +219,7 @@ alicevision_add_software(aliceVision_utils_split360Images LINKS aliceVision_system aliceVision_numeric aliceVision_image + aliceVision_panorama ${OPENIMAGEIO_LIBRARIES} Boost::program_options Boost::filesystem diff --git a/src/software/utils/main_split360Images.cpp b/src/software/utils/main_split360Images.cpp index fe58504ed4..9e9e8f037d 100644 --- a/src/software/utils/main_split360Images.cpp +++ b/src/software/utils/main_split360Images.cpp @@ -13,6 +13,7 @@ #include #include +#include #include #include @@ -74,7 +75,7 @@ class PinholeCameraR /** * @brief Function to map 3D coordinates onto a 2D image according a spherical projection */ -class SphericalMapping +class SphericalMappingLocal { public: @@ -182,7 +183,9 @@ bool splitEquirectangular(const std::string& imagePath, const std::string& outpu const double twoPi = M_PI * 2.0; const double alpha = twoPi / static_cast(nbSplits); - const double focal = focalFromPinholeHeight(inHeight, degreeToRadian(60.0)); + + const double fov = degreeToRadian(110.0); + const double focal = (inWidth / 2.0) / tan(fov / 2.0); double angle = 0.0; for(std::size_t i = 0; i < nbSplits; ++i) @@ -206,7 +209,7 @@ bool splitEquirectangular(const std::string& imagePath, const std::string& outpu for(int i = 0; i < splitResolution; ++i) { const Vec3 ray = camera.getRay(i, j); - const Vec2 x = SphericalMapping::get2DPoint(ray, inWidth, inHeight); + const Vec2 x = SphericalMapping::toEquirectangular(ray, inWidth, inHeight); imaOut(j,i) = sampler(imageSource, x(1), x(0)); } } @@ -276,12 +279,12 @@ bool splitEquirectangularDemo(const std::string& imagePath, const std::string& o { Vec2 pt(0.,j); ray = camera.getRay(pt(0), pt(1)); - Vec2 x = SphericalMapping::get2DPoint( ray, inWidth, inHeight); + Vec2 x = SphericalMappingLocal::get2DPoint( ray, inWidth, inHeight); svgStream.drawCircle(x(0), x(1), 8, svg::svgStyle().fill("magenta").stroke("white", 4)); pt[0] = splitResolution; ray = camera.getRay(pt(0), pt(1)); - x = SphericalMapping::get2DPoint( ray, inWidth, inHeight); + x = SphericalMappingLocal::get2DPoint( ray, inWidth, inHeight); svgStream.drawCircle(x(0), x(1), 8, svg::svgStyle().fill("magenta").stroke("white", 4)); } // Horizontal rectilinear image border: @@ -289,12 +292,12 @@ bool splitEquirectangularDemo(const std::string& imagePath, const std::string& o { Vec2 pt(j,0.); ray = camera.getRay(pt(0), pt(1)); - Vec2 x = SphericalMapping::get2DPoint( ray, inWidth, inHeight); + Vec2 x = SphericalMappingLocal::get2DPoint( ray, inWidth, inHeight); svgStream.drawCircle(x(0), x(1), 8, svg::svgStyle().fill("lime").stroke("white", 4)); pt[1] = splitResolution; ray = camera.getRay(pt(0), pt(1)); - x = SphericalMapping::get2DPoint( ray, inWidth, inHeight); + x = SphericalMappingLocal::get2DPoint( ray, inWidth, inHeight); svgStream.drawCircle(x(0), x(1), 8, svg::svgStyle().fill("lime").stroke("white", 4)); } } From 90a8da4b110b93993cd134eb16b24d3ebc35cfce Mon Sep 17 00:00:00 2001 From: Fabien Date: Thu, 11 Mar 2021 17:46:55 +0100 Subject: [PATCH 2/5] [utils] fix bug --- src/software/utils/main_split360Images.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/software/utils/main_split360Images.cpp b/src/software/utils/main_split360Images.cpp index 9e9e8f037d..c1ce3cbe78 100644 --- a/src/software/utils/main_split360Images.cpp +++ b/src/software/utils/main_split360Images.cpp @@ -185,7 +185,7 @@ bool splitEquirectangular(const std::string& imagePath, const std::string& outpu const double alpha = twoPi / static_cast(nbSplits); const double fov = degreeToRadian(110.0); - const double focal = (inWidth / 2.0) / tan(fov / 2.0); + const double focal = (splitResolution / 2.0) / tan(fov / 2.0); double angle = 0.0; for(std::size_t i = 0; i < nbSplits; ++i) From 61413e9a660e6b801bd96dc777b1dbfbcdcd682f Mon Sep 17 00:00:00 2001 From: Fabien Date: Thu, 11 Mar 2021 17:52:27 +0100 Subject: [PATCH 3/5] [utils] split 360 demo mode fix --- src/software/utils/main_split360Images.cpp | 47 +++++----------------- 1 file changed, 9 insertions(+), 38 deletions(-) diff --git a/src/software/utils/main_split360Images.cpp b/src/software/utils/main_split360Images.cpp index c1ce3cbe78..6c5498b64f 100644 --- a/src/software/utils/main_split360Images.cpp +++ b/src/software/utils/main_split360Images.cpp @@ -72,39 +72,6 @@ class PinholeCameraR Mat3 _K; }; -/** - * @brief Function to map 3D coordinates onto a 2D image according a spherical projection - */ -class SphericalMappingLocal -{ -public: - - static Vec2 get2DPoint(const Vec3& X, int width, int height) - { - const Vec3 polarCoord = get3DPointPolar(X); - - const double phi = polarCoord(0); - const double theta = polarCoord(1); - - const double x = ((phi * width) / M_PI + width) / 2.0; // between 0 and width - const double y = theta * height / M_PI; // between 0 and height - - return Vec2(x, y); - } - - static Vec3 get3DPointPolar(const Vec3& pos3d) - { - const double x = pos3d(0); - const double y = pos3d(1); - const double z = pos3d(2); - - const double theta = atan2(y, sqrt(Square(x) + Square(z))); - const double phi = atan2(x, z); - - return Vec3 (phi, theta + M_PI/2.0, 1.0); - } -}; - /** * @brief Compute a rectilinear camera focal from an angular FoV * @param h @@ -251,7 +218,9 @@ bool splitEquirectangularDemo(const std::string& imagePath, const std::string& o const double twoPi = M_PI * 2.0; const double alpha = twoPi / static_cast(nbSplits); - const double focal = focalFromPinholeHeight(inHeight, degreeToRadian(60.0)); + + const double fov = degreeToRadian(110.0); + const double focal = (splitResolution / 2.0) / tan(fov / 2.0); double angle = 0.0; for(std::size_t i = 0; i < nbSplits; ++i) @@ -279,28 +248,30 @@ bool splitEquirectangularDemo(const std::string& imagePath, const std::string& o { Vec2 pt(0.,j); ray = camera.getRay(pt(0), pt(1)); - Vec2 x = SphericalMappingLocal::get2DPoint( ray, inWidth, inHeight); + Vec2 x = SphericalMapping::toEquirectangular( ray, inWidth, inHeight); svgStream.drawCircle(x(0), x(1), 8, svg::svgStyle().fill("magenta").stroke("white", 4)); pt[0] = splitResolution; ray = camera.getRay(pt(0), pt(1)); - x = SphericalMappingLocal::get2DPoint( ray, inWidth, inHeight); + x = SphericalMapping::toEquirectangular( ray, inWidth, inHeight); svgStream.drawCircle(x(0), x(1), 8, svg::svgStyle().fill("magenta").stroke("white", 4)); } + // Horizontal rectilinear image border: for (double j = 0; j <= splitResolution; j += splitResolution/(double)step) { Vec2 pt(j,0.); ray = camera.getRay(pt(0), pt(1)); - Vec2 x = SphericalMappingLocal::get2DPoint( ray, inWidth, inHeight); + Vec2 x = SphericalMapping::toEquirectangular( ray, inWidth, inHeight); svgStream.drawCircle(x(0), x(1), 8, svg::svgStyle().fill("lime").stroke("white", 4)); pt[1] = splitResolution; ray = camera.getRay(pt(0), pt(1)); - x = SphericalMappingLocal::get2DPoint( ray, inWidth, inHeight); + x = SphericalMapping::toEquirectangular( ray, inWidth, inHeight); svgStream.drawCircle(x(0), x(1), 8, svg::svgStyle().fill("lime").stroke("white", 4)); } } + boost::filesystem::path path(imagePath); std::ofstream svgFile(outputFolder + std::string("/") + path.stem().string() + std::string(".svg")); svgFile << svgStream.closeSvgFile().str(); From 14e5efaffd911199e20371ceae1ba01f2d131b38 Mon Sep 17 00:00:00 2001 From: Fabien Castan Date: Thu, 11 Mar 2021 19:35:43 +0100 Subject: [PATCH 4/5] [software] split360: add fov param and set focal length in mm focal length was set in the metadata in pixels which is not conform to exif (and how it is later interpretated in other parts of the library) --- src/software/utils/main_split360Images.cpp | 27 +++++++++++++--------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/src/software/utils/main_split360Images.cpp b/src/software/utils/main_split360Images.cpp index 6c5498b64f..aeda9fe77c 100644 --- a/src/software/utils/main_split360Images.cpp +++ b/src/software/utils/main_split360Images.cpp @@ -138,7 +138,7 @@ bool splitDualFisheye(const std::string& imagePath, const std::string& outputFol return true; } -bool splitEquirectangular(const std::string& imagePath, const std::string& outputFolder, std::size_t nbSplits, std::size_t splitResolution) +bool splitEquirectangular(const std::string& imagePath, const std::string& outputFolder, std::size_t nbSplits, std::size_t splitResolution, double fovDegree) { image::Image imageSource; image::readImage(imagePath, imageSource, image::EImageColorSpace::LINEAR); @@ -151,13 +151,13 @@ bool splitEquirectangular(const std::string& imagePath, const std::string& outpu const double twoPi = M_PI * 2.0; const double alpha = twoPi / static_cast(nbSplits); - const double fov = degreeToRadian(110.0); - const double focal = (splitResolution / 2.0) / tan(fov / 2.0); + const double fov = degreeToRadian(fovDegree); + const double focal_px = (splitResolution / 2.0) / tan(fov / 2.0); double angle = 0.0; for(std::size_t i = 0; i < nbSplits; ++i) { - cameras.emplace_back(focal, splitResolution, splitResolution, RotationAroundY(angle)); + cameras.emplace_back(focal_px, splitResolution, splitResolution, RotationAroundY(angle)); angle += alpha; } @@ -193,7 +193,8 @@ bool splitEquirectangular(const std::string& imagePath, const std::string& outpu // Ooerride make and model in order to force camera model in SfM outMetadataSpec.attribute("Make", "Custom"); outMetadataSpec.attribute("Model", "Pinhole"); - outMetadataSpec.attribute("Exif:FocalLength", static_cast(focal)); + const float focal_mm = focal_px / splitResolution; // muliplied by sensorWidth (which is 1 for "Custom") + outMetadataSpec.attribute("Exif:FocalLength", focal_mm); boost::filesystem::path path(imagePath); image::writeImage(outputFolder + std::string("/") + path.stem().string() + std::string("_") + std::to_string(index) + path.extension().string(), @@ -206,7 +207,7 @@ bool splitEquirectangular(const std::string& imagePath, const std::string& outpu } -bool splitEquirectangularDemo(const std::string& imagePath, const std::string& outputFolder, std::size_t nbSplits, std::size_t splitResolution) +bool splitEquirectangularDemo(const std::string& imagePath, const std::string& outputFolder, std::size_t nbSplits, std::size_t splitResolution, double fovDegree) { image::Image imageSource; image::readImage(imagePath, imageSource, image::EImageColorSpace::LINEAR); @@ -218,8 +219,8 @@ bool splitEquirectangularDemo(const std::string& imagePath, const std::string& o const double twoPi = M_PI * 2.0; const double alpha = twoPi / static_cast(nbSplits); - - const double fov = degreeToRadian(110.0); + + const double fov = degreeToRadian(fovDegree); const double focal = (splitResolution / 2.0) / tan(fov / 2.0); double angle = 0.0; @@ -289,6 +290,7 @@ int aliceVision_main(int argc, char** argv) std::size_t equirectangularNbSplits; // nb splits for equirectangular image std::size_t equirectangularSplitResolution; // split resolution for equirectangular image bool equirectangularDemoMode; + double fov = 110.0; // Field of View in degree po::options_description allParams("This program is used to extract multiple images from equirectangular or dualfisheye images or image folder\n" "AliceVision split360Images"); @@ -313,7 +315,10 @@ int aliceVision_main(int argc, char** argv) ("equirectangularSplitResolution", po::value(&equirectangularSplitResolution)->default_value(1200), "Equirectangular split resolution") ("equirectangularDemoMode", po::value(&equirectangularDemoMode)->default_value(false), - "Export a SVG file that simulate the split"); + "Export a SVG file that simulate the split") + ("fov", po::value(&fov)->default_value(fov), + "Field of View to extract (in degree).") + ; po::options_description logParams("Log parameters"); logParams.add_options() @@ -422,9 +427,9 @@ int aliceVision_main(int argc, char** argv) if(splitMode == "equirectangular") { if(equirectangularDemoMode) - hasCorrectPath = splitEquirectangularDemo(imagePath, outputFolder, equirectangularNbSplits, equirectangularSplitResolution); + hasCorrectPath = splitEquirectangularDemo(imagePath, outputFolder, equirectangularNbSplits, equirectangularSplitResolution, fov); else - hasCorrectPath = splitEquirectangular(imagePath, outputFolder, equirectangularNbSplits, equirectangularSplitResolution); + hasCorrectPath = splitEquirectangular(imagePath, outputFolder, equirectangularNbSplits, equirectangularSplitResolution, fov); } else if(splitMode == "dualfisheye") { From 4dc7163d34e8b8acc9c9f0ea94d3aa719bdbcab9 Mon Sep 17 00:00:00 2001 From: Fabien Castan Date: Thu, 11 Mar 2021 19:36:27 +0100 Subject: [PATCH 5/5] [software] split360: add nbThreads param --- src/software/utils/main_split360Images.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/software/utils/main_split360Images.cpp b/src/software/utils/main_split360Images.cpp index aeda9fe77c..4df043a108 100644 --- a/src/software/utils/main_split360Images.cpp +++ b/src/software/utils/main_split360Images.cpp @@ -291,6 +291,7 @@ int aliceVision_main(int argc, char** argv) std::size_t equirectangularSplitResolution; // split resolution for equirectangular image bool equirectangularDemoMode; double fov = 110.0; // Field of View in degree + int nbThreads = 3; po::options_description allParams("This program is used to extract multiple images from equirectangular or dualfisheye images or image folder\n" "AliceVision split360Images"); @@ -318,6 +319,8 @@ int aliceVision_main(int argc, char** argv) "Export a SVG file that simulate the split") ("fov", po::value(&fov)->default_value(fov), "Field of View to extract (in degree).") + ("nbThreads", po::value(&nbThreads)->default_value(nbThreads), + "Number of threads.") ; po::options_description logParams("Log parameters"); @@ -420,8 +423,10 @@ int aliceVision_main(int argc, char** argv) } } - for(const std::string& imagePath : imagePaths) +#pragma omp parallel for num_threads(nbThreads) + for(int i = 0; i < imagePaths.size(); ++i) { + const std::string& imagePath = imagePaths[i]; bool hasCorrectPath = true; if(splitMode == "equirectangular") @@ -441,7 +446,10 @@ int aliceVision_main(int argc, char** argv) } if(!hasCorrectPath) + { +#pragma omp critical badPaths.push_back(imagePath); + } } if(!badPaths.empty())