From 9152f4b1cb699021f837bb047ab63a041860a442 Mon Sep 17 00:00:00 2001 From: Unknown Date: Fri, 22 Mar 2019 11:12:56 +0100 Subject: [PATCH 01/51] [mvsData] Create Image class Use an Image class instead of vector and width, height. --- src/aliceVision/mesh/Texturing.hpp | 2 +- src/aliceVision/mvsData/CMakeLists.txt | 5 +- src/aliceVision/mvsData/Color.hpp | 8 ++ src/aliceVision/mvsData/Image.hpp | 122 ++++++++++++++++++ .../mvsData/{image.cpp => imageIO.cpp} | 2 +- .../mvsData/{image.hpp => imageIO.hpp} | 0 src/aliceVision/mvsUtils/MultiViewParams.cpp | 2 +- src/software/pipeline/main_texturing.cpp | 2 +- 8 files changed, 137 insertions(+), 6 deletions(-) create mode 100644 src/aliceVision/mvsData/Image.hpp rename src/aliceVision/mvsData/{image.cpp => imageIO.cpp} (98%) rename src/aliceVision/mvsData/{image.hpp => imageIO.hpp} (100%) diff --git a/src/aliceVision/mesh/Texturing.hpp b/src/aliceVision/mesh/Texturing.hpp index a18b144f5e..ce7f48a31b 100644 --- a/src/aliceVision/mesh/Texturing.hpp +++ b/src/aliceVision/mesh/Texturing.hpp @@ -6,7 +6,7 @@ #pragma once -#include +#include #include #include #include diff --git a/src/aliceVision/mvsData/CMakeLists.txt b/src/aliceVision/mvsData/CMakeLists.txt index 441fbc7768..0d30eab299 100644 --- a/src/aliceVision/mvsData/CMakeLists.txt +++ b/src/aliceVision/mvsData/CMakeLists.txt @@ -1,9 +1,10 @@ # Headers set(mvsData_files_headers Color.hpp + Image.hpp geometry.hpp geometryTriTri.hpp - image.hpp + imageIO.hpp jetColorMap.hpp Matrix3x3.hpp Matrix3x4.hpp @@ -23,7 +24,7 @@ set(mvsData_files_headers # Sources set(mvsData_files_sources jetColorMap.cpp - image.cpp + imageIO.cpp geometry.cpp geometryTriTri.cpp Stat3d.cpp diff --git a/src/aliceVision/mvsData/Color.hpp b/src/aliceVision/mvsData/Color.hpp index e0d7bd021c..0d7cf273ef 100644 --- a/src/aliceVision/mvsData/Color.hpp +++ b/src/aliceVision/mvsData/Color.hpp @@ -82,6 +82,14 @@ class Color return *this; } + inline Color& operator-=(const Color& _p) + { + r -= _p.r; + g -= _p.g; + b -= _p.b; + return *this; + } + inline Color& operator/=(const int d) { r /= d; diff --git a/src/aliceVision/mvsData/Image.hpp b/src/aliceVision/mvsData/Image.hpp new file mode 100644 index 0000000000..b66f5244ec --- /dev/null +++ b/src/aliceVision/mvsData/Image.hpp @@ -0,0 +1,122 @@ +// This file is part of the AliceVision project. +// Copyright (c) 2019 AliceVision contributors. +// This Source Code Form is subject to the terms of the Mozilla Public License, +// v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at https://mozilla.org/MPL/2.0/. + +#pragma once + +#include +#include +#include +#include + +namespace aliceVision { + +class Image +{ +private: + std::vector _img; + int _width = 0; + int _height = 0; + +public: + Image() + { + _width = 0; + _height = 0; + } + + Image(int width, int height) + : _width(width) + , _height(height) + { + _img.resize(width*height); + } + + Image(Color* data, int width, int height) + { + _width = width; + _height = height; + _img.resize(_width * _height); + + for(int i = 0; i < _width*_height; ++i) + _img[i] = data[i]; + } + + Image(const Image& other) + : _width(other._width) + , _height(other._height) + , _img(other._img) + { + } + + void resize(int width, int height) + { + _width = width; + _height = height; + _img.resize(_width*_height); + } + + void swap(Image& other) + { + std::swap(_width, other._width); + std::swap(_height, other._height); + _img.swap(other._img); + } + + Image& operator=(const Image& param) + { + _width = param._width; + _height = param._height; + _img = param._img; + return *this; + } + + //outImg = inImga - inImgb + static void imageDiff(const Image& inImga, const Image& inImgb, Image& outImg) + { + if(inImga._width != inImgb._width || inImga._height != inImgb._height) + throw std::runtime_error("Incompatible image sizes"); + + outImg.resize(inImga._width, inImga._height); + for(int i = 0; i < inImga._width*inImga._height; ++i) + { + outImg._img[i] = inImga._img.at(i) - inImgb._img.at(i); + } + } + + int width() const { return _width; } + int height() const { return _height; } + + std::vector& data() { return _img; } + const std::vector& data() const { return _img; } + + Color getInterpolateColor(const Point2d& pix) const + { + const int xp = static_cast(pix.x); + const int yp = static_cast(pix.y); + + // precision to 4 decimal places + const float ui = pix.x - static_cast(xp); + const float vi = pix.y - static_cast(yp); + + const Color lu = _img.at( yp * _width + xp ); + const Color ru = _img.at( yp * _width + (xp+1) ); + const Color rd = _img.at( (yp+1) * _width + (xp+1) ); + const Color ld = _img.at( (yp+1) * _width + xp ); + + // bilinear interpolation of the pixel intensity value + const Color u = lu + (ru - lu) * ui; + const Color d = ld + (rd - ld) * ui; + const Color out = u + (d - u) * vi; + return out; + } + +}; + + +} + + + diff --git a/src/aliceVision/mvsData/image.cpp b/src/aliceVision/mvsData/imageIO.cpp similarity index 98% rename from src/aliceVision/mvsData/image.cpp rename to src/aliceVision/mvsData/imageIO.cpp index 04c0325394..dcfe600986 100644 --- a/src/aliceVision/mvsData/image.cpp +++ b/src/aliceVision/mvsData/imageIO.cpp @@ -4,7 +4,7 @@ // v. 2.0. If a copy of the MPL was not distributed with this file, // You can obtain one at https://mozilla.org/MPL/2.0/. -#include "image.hpp" +#include "imageIO.hpp" #include diff --git a/src/aliceVision/mvsData/image.hpp b/src/aliceVision/mvsData/imageIO.hpp similarity index 100% rename from src/aliceVision/mvsData/image.hpp rename to src/aliceVision/mvsData/imageIO.hpp diff --git a/src/aliceVision/mvsUtils/MultiViewParams.cpp b/src/aliceVision/mvsUtils/MultiViewParams.cpp index 6d85f5a1a3..b4786bbb62 100644 --- a/src/aliceVision/mvsUtils/MultiViewParams.cpp +++ b/src/aliceVision/mvsUtils/MultiViewParams.cpp @@ -10,7 +10,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/src/software/pipeline/main_texturing.cpp b/src/software/pipeline/main_texturing.cpp index 52614ab98d..a626625b0a 100644 --- a/src/software/pipeline/main_texturing.cpp +++ b/src/software/pipeline/main_texturing.cpp @@ -9,7 +9,7 @@ #include #include #include -#include +#include #include #include #include From 5dfaf6276e841cef785e5d962478e302049bde77 Mon Sep 17 00:00:00 2001 From: Unknown Date: Fri, 22 Mar 2019 11:13:37 +0100 Subject: [PATCH 02/51] [mesh] Texturing: add missing reference --- src/aliceVision/mesh/Texturing.cpp | 4 ++-- src/aliceVision/mesh/Texturing.hpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/aliceVision/mesh/Texturing.cpp b/src/aliceVision/mesh/Texturing.cpp index 4396c7b7e0..44acad8610 100644 --- a/src/aliceVision/mesh/Texturing.cpp +++ b/src/aliceVision/mesh/Texturing.cpp @@ -299,7 +299,7 @@ void Texturing::generateTextures(const mvsUtils::MultiViewParams &mp, } void Texturing::generateTexturesSubSet(const mvsUtils::MultiViewParams& mp, - std::vector atlasIDs, mvsUtils::ImagesCache& imageCache, const bfs::path& outPath, EImageFileType textureFileType) + const std::vector& atlasIDs, mvsUtils::ImagesCache& imageCache, const bfs::path& outPath, EImageFileType textureFileType) { if(atlasIDs.size() > _atlases.size()) throw std::runtime_error("Invalid atlas IDs "); @@ -313,7 +313,7 @@ void Texturing::generateTexturesSubSet(const mvsUtils::MultiViewParams& mp, // List of triangle IDs (selected to contribute to the final texturing) per image. std::vector> contributionsPerCamera(mp.ncams); - //for each atlasID + //for each atlasID, calculate contributionPerCamera for(const size_t atlasID : atlasIDs) { ALICEVISION_LOG_INFO("Generating texture for atlas " << atlasID + 1 << "/" << _atlases.size() diff --git a/src/aliceVision/mesh/Texturing.hpp b/src/aliceVision/mesh/Texturing.hpp index ce7f48a31b..14848b2355 100644 --- a/src/aliceVision/mesh/Texturing.hpp +++ b/src/aliceVision/mesh/Texturing.hpp @@ -151,7 +151,7 @@ struct Texturing /// Generate texture files for the given sub-set of texture atlases void generateTexturesSubSet(const mvsUtils::MultiViewParams& mp, - std::vector atlasIDs, mvsUtils::ImagesCache& imageCache, + const std::vector& atlasIDs, mvsUtils::ImagesCache& imageCache, const bfs::path &outPath, EImageFileType textureFileType = EImageFileType::PNG); /// Save textured mesh as an OBJ + MTL file From 84ee7ba321a985d4d2bed622d6d2338e043edd80 Mon Sep 17 00:00:00 2001 From: Unknown Date: Fri, 22 Mar 2019 11:16:15 +0100 Subject: [PATCH 03/51] [mesh] texturing: create laplacian pyramid and recompose the image Does not change the results yet, but start to use the multiband. --- src/aliceVision/mesh/CMakeLists.txt | 2 + src/aliceVision/mesh/MultiBandBlending.cpp | 50 ++++++++++++++++++++++ src/aliceVision/mesh/MultiBandBlending.hpp | 25 +++++++++++ src/aliceVision/mesh/Texturing.cpp | 22 +++++++++- 4 files changed, 97 insertions(+), 2 deletions(-) create mode 100644 src/aliceVision/mesh/MultiBandBlending.cpp create mode 100644 src/aliceVision/mesh/MultiBandBlending.hpp diff --git a/src/aliceVision/mesh/CMakeLists.txt b/src/aliceVision/mesh/CMakeLists.txt index 2697ea9b6c..a5c318c5e0 100644 --- a/src/aliceVision/mesh/CMakeLists.txt +++ b/src/aliceVision/mesh/CMakeLists.txt @@ -7,6 +7,7 @@ set(mesh_files_headers MeshEnergyOpt.hpp meshPostProcessing.hpp meshVisibility.hpp + MultiBandBlending.hpp Texturing.hpp UVAtlas.hpp ) @@ -19,6 +20,7 @@ set(mesh_files_sources MeshEnergyOpt.cpp meshPostProcessing.cpp meshVisibility.cpp + MultiBandBlending.cpp Texturing.cpp UVAtlas.cpp ) diff --git a/src/aliceVision/mesh/MultiBandBlending.cpp b/src/aliceVision/mesh/MultiBandBlending.cpp new file mode 100644 index 0000000000..24bc275428 --- /dev/null +++ b/src/aliceVision/mesh/MultiBandBlending.cpp @@ -0,0 +1,50 @@ +// This file is part of the AliceVision project. +// Copyright (c) 2019 AliceVision contributors. +// This Source Code Form is subject to the terms of the Mozilla Public License, +// v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at https://mozilla.org/MPL/2.0/. + +#include "MultiBandBlending.hpp" + +#include +#include + + + +namespace aliceVision { +namespace mesh { + +void MultiBandBlending::laplacianPyramid(std::vector& out_pyramidL, const Image& inImg, int camId, int nbBand, float sizeKernel) +{ + assert(nbBand >= 1); + + const int width = inImg.width(); + const int height = inImg.height(); + + Image base(inImg); // gaussian + Image baseG(width, height); // gaussian + out_pyramidL.resize(nbBand); // laplacian + + float s = sizeKernel / std::pow(2, nbBand-1); + + //Create Laplacian pyramid + for(int b = 0; b < nbBand-1; ++b) + { + imageIO::convolveImage(width, height, base.data(), baseG.data(), "gaussian", s, s); //baseG = base * gaussianKernel + Image::imageDiff(base, baseG, out_pyramidL.at(b)); + base.swap(baseG); //newBase = oldBaseG + s *= 2; + + /*//TODO : REMOVE + const std::string outPathG = std::string("/datas/rave/tmp/") + std::string("cam") + std::to_string(camId) + std::string("G") + std::to_string(b) + ".exr"; + imageIO::writeImage(outPathG, width, height, baseG.data()); + const std::string outPathL = std::string("/datas/rave/tmp/") + std::string("cam") + std::to_string(camId) + std::string("L") + std::to_string(b) + ".exr"; + imageIO::writeImage(outPathL, width, height, out_pyramidL.at(b).data());*/ + + } + out_pyramidL.at(nbBand-1) = base; +} + + +} +} diff --git a/src/aliceVision/mesh/MultiBandBlending.hpp b/src/aliceVision/mesh/MultiBandBlending.hpp new file mode 100644 index 0000000000..14e280174c --- /dev/null +++ b/src/aliceVision/mesh/MultiBandBlending.hpp @@ -0,0 +1,25 @@ +// This file is part of the AliceVision project. +// Copyright (c) 2019 AliceVision contributors. +// This Source Code Form is subject to the terms of the Mozilla Public License, +// v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at https://mozilla.org/MPL/2.0/. + +#pragma once + +#include +#include + +namespace aliceVision { +namespace mesh { + +class MultiBandBlending +{ +public: + + ///Generate the pyramid of the differences of the successives gaussian convolutions of the input image + void laplacianPyramid(std::vector& out_pyramidL, const Image& inImg, int camId, int nbBand, float sizeKernel); + +}; + +} +} diff --git a/src/aliceVision/mesh/Texturing.cpp b/src/aliceVision/mesh/Texturing.cpp index 44acad8610..926d0f8cbf 100644 --- a/src/aliceVision/mesh/Texturing.cpp +++ b/src/aliceVision/mesh/Texturing.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -462,6 +463,17 @@ void Texturing::generateTexturesSubSet(const mvsUtils::MultiViewParams& mp, imageCache.refreshData(camId); + //conversion ImagesCache::Img -> Image + mvsUtils::ImagesCache::ImgPtr imgPtr = imageCache.getImg_sync(camId); + Image camImg(imgPtr->data, imgPtr->getWidth(), imgPtr->getHeight()); + + ALICEVISION_LOG_INFO(" - camera " << camId + 1 << " multiBandBlending"); + + //Calculate laplacianPyramid + MultiBandBlending multiBandBlending; + std::vector pyramidL; //laplacian pyramid + multiBandBlending.laplacianPyramid(pyramidL, camImg, camId, 3, 40.0f); //sizeKernel = final blur + // for each output texture file for(const auto& c : cameraContributions) { @@ -535,7 +547,14 @@ void Texturing::generateTexturesSubSet(const mvsUtils::MultiViewParams& mp, // exclude out of bounds pixels if(!mp.isPixelInImage(pixRC, camId)) continue; - Color color = imageCache.getPixelValueInterpolated(&pixRC, camId); + + //Color color = imageCache.getPixelValueInterpolated(&pixRC, camId); + Color color(0.f,0.f,0.f); + for(const Image& bandImage : pyramidL) + { + color += bandImage.getInterpolateColor(pixRC); + } + // If the color is pure zero, we consider it as an invalid pixel. // After correction of radial distortion, some pixels are invalid. // TODO: use an alpha channel instead. @@ -647,7 +666,6 @@ void Texturing::generateTexturesSubSet(const mvsUtils::MultiViewParams& mp, } } - void Texturing::clear() { trisMtlIds.clear(); From cf020027d833632f1e1f37a6a5bde8dd0654568a Mon Sep 17 00:00:00 2001 From: Unknown Date: Fri, 29 Mar 2019 11:05:32 +0100 Subject: [PATCH 04/51] [mvsUtils] ImageCache : function to setCacheSize --- src/aliceVision/mvsUtils/ImagesCache.cpp | 15 ++++++++++----- src/aliceVision/mvsUtils/ImagesCache.hpp | 1 + 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/aliceVision/mvsUtils/ImagesCache.cpp b/src/aliceVision/mvsUtils/ImagesCache.cpp index b2b9250ade..8a915091ac 100644 --- a/src/aliceVision/mvsUtils/ImagesCache.cpp +++ b/src/aliceVision/mvsUtils/ImagesCache.cpp @@ -37,18 +37,15 @@ void ImagesCache::initIC( std::vector& _imagesNames ) float oneimagemb = (sizeof(Color) * mp->getMaxImageWidth() * mp->getMaxImageHeight()) / 1024.f / 1024.f; float maxmbCPU = (float)mp->userParams.get("images_cache.maxmbCPU", 5000); int _npreload = std::max((int)(maxmbCPU / oneimagemb), 5); // image cache has a minimum size of 5 - N_PRELOADED_IMAGES = std::min(mp->ncams, _npreload); + _npreload = std::min(mp->ncams, _npreload); for(int rc = 0; rc < mp->ncams; rc++) { imagesNames.push_back(_imagesNames[rc]); } - imgs.resize(N_PRELOADED_IMAGES); // = new Color*[N_PRELOADED_IMAGES]; - camIdMapId.resize( mp->ncams, -1 ); - mapIdCamId.resize( N_PRELOADED_IMAGES, -1 ); - mapIdClock.resize( N_PRELOADED_IMAGES, clock() ); + setCacheSize(_npreload); { // Cannot resize the vector directly, as mutex class is not move-constructible. @@ -61,6 +58,14 @@ void ImagesCache::initIC( std::vector& _imagesNames ) } +void ImagesCache::setCacheSize(int nbPreload) +{ + N_PRELOADED_IMAGES = nbPreload; + imgs.resize(N_PRELOADED_IMAGES); + mapIdCamId.resize( N_PRELOADED_IMAGES, -1 ); + mapIdClock.resize( N_PRELOADED_IMAGES, clock() ); +} + ImagesCache::~ImagesCache() { } diff --git a/src/aliceVision/mvsUtils/ImagesCache.hpp b/src/aliceVision/mvsUtils/ImagesCache.hpp index 0b8cda40c7..9fffe78447 100644 --- a/src/aliceVision/mvsUtils/ImagesCache.hpp +++ b/src/aliceVision/mvsUtils/ImagesCache.hpp @@ -81,6 +81,7 @@ class ImagesCache ImagesCache( const MultiViewParams* _mp, int _bandType); ImagesCache( const MultiViewParams* _mp, int _bandType, std::vector& _imagesNames); void initIC( std::vector& _imagesNames ); + void setCacheSize(int nbPreload); ~ImagesCache(); inline ImgPtr getImg_sync( int camId ) From 885d38d38c265c00e13c57ff614e0cc88814ac92 Mon Sep 17 00:00:00 2001 From: Unknown Date: Fri, 29 Mar 2019 11:23:07 +0100 Subject: [PATCH 05/51] [mesh] Texturing : create laplacian pyramid from downscaled images Calculate the laplacian pyramid of an image with successives downscales (instead of successives blurs) -> memory saving --- src/aliceVision/mesh/MultiBandBlending.cpp | 33 +++++++++++++++++++++- src/aliceVision/mesh/MultiBandBlending.hpp | 4 +++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/src/aliceVision/mesh/MultiBandBlending.cpp b/src/aliceVision/mesh/MultiBandBlending.cpp index 24bc275428..e80db7d5ec 100644 --- a/src/aliceVision/mesh/MultiBandBlending.cpp +++ b/src/aliceVision/mesh/MultiBandBlending.cpp @@ -31,7 +31,7 @@ void MultiBandBlending::laplacianPyramid(std::vector& out_pyramidL, const for(int b = 0; b < nbBand-1; ++b) { imageIO::convolveImage(width, height, base.data(), baseG.data(), "gaussian", s, s); //baseG = base * gaussianKernel - Image::imageDiff(base, baseG, out_pyramidL.at(b)); + Image::imageDiff(base, baseG, out_pyramidL[b]); base.swap(baseG); //newBase = oldBaseG s *= 2; @@ -45,6 +45,37 @@ void MultiBandBlending::laplacianPyramid(std::vector& out_pyramidL, const out_pyramidL.at(nbBand-1) = base; } +void MultiBandBlending::laplacianDownscalePyramid(std::vector& out_pyramidL, const Image& inImg, int camId, int nbBand, float sizeKernel) +{ + assert(nbBand >= 1); + + Image img(inImg); + int outW = static_cast(img.width()/2); + int outH = static_cast(img.height()/2); + + Image imgDownscaled(outW, outH); + out_pyramidL.resize(nbBand); + + //Create Laplacian pyramid + for(int b = 0; b < nbBand-1; ++b) + { + imageIO::resizeImage(img.width(), img.height(), 2, img.data(), imgDownscaled.data(), "gaussian", sizeKernel); + Image::imageDownscaleDiff(img, imgDownscaled, out_pyramidL[b]); + + img.swap(imgDownscaled); + + outW = static_cast(outW/2); + outH = static_cast(outH/2); + imgDownscaled.resize(outW, outH); + + + } + out_pyramidL[nbBand-1] = img; + + for(std::size_t i = 0; i < out_pyramidL.size(); ++i) + ALICEVISION_LOG_INFO("laplacianDownscalePyramid: Size level " << i << " : " << out_pyramidL[i].width() << "x" << out_pyramidL[i].height()); +} + } } diff --git a/src/aliceVision/mesh/MultiBandBlending.hpp b/src/aliceVision/mesh/MultiBandBlending.hpp index 14e280174c..32ebce9c21 100644 --- a/src/aliceVision/mesh/MultiBandBlending.hpp +++ b/src/aliceVision/mesh/MultiBandBlending.hpp @@ -19,6 +19,10 @@ class MultiBandBlending ///Generate the pyramid of the differences of the successives gaussian convolutions of the input image void laplacianPyramid(std::vector& out_pyramidL, const Image& inImg, int camId, int nbBand, float sizeKernel); + ///Generate the gaussian pyramid given the input image + void laplacianDownscalePyramid(std::vector& out_pyramidL, const Image& inImg, int camId, int nbBand, float sizeKernel); + + }; } From 6ea330674f4ea1d485113c0604f96d491e856993 Mon Sep 17 00:00:00 2001 From: Unknown Date: Fri, 29 Mar 2019 11:28:32 +0100 Subject: [PATCH 06/51] [mvsData] Image : Color interpolation valid on borders --- src/aliceVision/mvsData/Image.hpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/aliceVision/mvsData/Image.hpp b/src/aliceVision/mvsData/Image.hpp index b66f5244ec..53227429a1 100644 --- a/src/aliceVision/mvsData/Image.hpp +++ b/src/aliceVision/mvsData/Image.hpp @@ -97,10 +97,15 @@ class Image const int xp = static_cast(pix.x); const int yp = static_cast(pix.y); + if(xp == _width - 1 || yp == _height - 1 ) + return _img.at(yp * _width + xp); + // precision to 4 decimal places const float ui = pix.x - static_cast(xp); const float vi = pix.y - static_cast(yp); + + const Color lu = _img.at( yp * _width + xp ); const Color ru = _img.at( yp * _width + (xp+1) ); const Color rd = _img.at( (yp+1) * _width + (xp+1) ); From f4e9ebb8df543fc2950a0fbba71af2affccf8223 Mon Sep 17 00:00:00 2001 From: Unknown Date: Fri, 29 Mar 2019 11:32:38 +0100 Subject: [PATCH 07/51] [mvsData] Image : operators valid operators =, [] & resize without copying elements --- src/aliceVision/mvsData/Image.hpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/aliceVision/mvsData/Image.hpp b/src/aliceVision/mvsData/Image.hpp index 53227429a1..7cea55d990 100644 --- a/src/aliceVision/mvsData/Image.hpp +++ b/src/aliceVision/mvsData/Image.hpp @@ -55,6 +55,7 @@ class Image { _width = width; _height = height; + _img.resize(0); _img.resize(_width*_height); } @@ -69,6 +70,7 @@ class Image { _width = param._width; _height = param._height; + _img.resize(_width*_height); _img = param._img; return *this; } @@ -92,6 +94,9 @@ class Image std::vector& data() { return _img; } const std::vector& data() const { return _img; } + const Color& operator[](std::size_t index) const { return _img[index]; } + Color& operator[](std::size_t index) { return _img[index]; } + Color getInterpolateColor(const Point2d& pix) const { const int xp = static_cast(pix.x); From b1242973eec712c6565ce60e45cc255b9a239928 Mon Sep 17 00:00:00 2001 From: Unknown Date: Fri, 29 Mar 2019 11:40:53 +0100 Subject: [PATCH 08/51] [mvsData] Image : difference between different sizes images calculate difference between an image of size s and an other of size s/2 --- src/aliceVision/mvsData/Image.hpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/aliceVision/mvsData/Image.hpp b/src/aliceVision/mvsData/Image.hpp index 7cea55d990..84764be8f1 100644 --- a/src/aliceVision/mvsData/Image.hpp +++ b/src/aliceVision/mvsData/Image.hpp @@ -88,6 +88,22 @@ class Image } } + //WARNING : size(inImga) = 2*size(inImgb) + static void imageDownscaleDiff(Image& inImga, Image& inImgb, Image& outImg) + { + //inImga = largest image + if(inImga.height() < inImgb.height()) + inImga.swap(inImgb); + + outImg.resize(inImga._width, inImga._height); + for(int i = 0; i < inImga._width*inImga._height; ++i) + { + Point2d pix(i%inImga.width(), static_cast(i/inImga.width())); + outImg._img[i] = inImga._img[i] - inImgb.getInterpolateColor(pix/2); + } + + } + int width() const { return _width; } int height() const { return _height; } From 94ac0534f6f8ff5f51749521990e2ea3319757c0 Mon Sep 17 00:00:00 2001 From: Unknown Date: Fri, 29 Mar 2019 11:46:15 +0100 Subject: [PATCH 09/51] [mesh] Texturing : new function to write a texture + fix padding --- src/aliceVision/mesh/Texturing.cpp | 102 ++++++++++++++++++++++------- src/aliceVision/mesh/Texturing.hpp | 28 ++++++++ 2 files changed, 108 insertions(+), 22 deletions(-) diff --git a/src/aliceVision/mesh/Texturing.cpp b/src/aliceVision/mesh/Texturing.cpp index 926d0f8cbf..12ea9a4e7a 100644 --- a/src/aliceVision/mesh/Texturing.cpp +++ b/src/aliceVision/mesh/Texturing.cpp @@ -630,40 +630,98 @@ void Texturing::generateTexturesSubSet(const mvsUtils::MultiViewParams& mp, const std::string textureName = "texture_" + std::to_string(1001 + atlasID) + "." + EImageFileType_enumToString(textureFileType); // starts at '1001' for UDIM compatibility bfs::path texturePath = outPath / textureName; ALICEVISION_LOG_INFO(" - Writing texture file: " << texturePath.string()); +void Texturing::writeTexture(AccuImage& atlasTexture, const std::size_t atlasID, const boost::filesystem::path &outPath, + EImageFileType textureFileType, const int level) +{ + // WARNING: we modify the "imgCount" to apply the padding (to avoid the creation of a new buffer) + if(!texParams.fillHoles && texParams.padding > 0) + { + ALICEVISION_LOG_INFO(" - Edge padding (" << texParams.padding << " pixels)."); + // edge padding (dilate gutter) + for(unsigned int g = 0; g < texParams.padding; ++g) + { + for(unsigned int y = 1; y < texParams.textureSide-1; ++y) + { + unsigned int yoffset = y * texParams.textureSide; + for(unsigned int x = 1; x < texParams.textureSide-1; ++x) + { + unsigned int xyoffset = yoffset + x; - unsigned int outTextureSide = texParams.textureSide; + if(atlasTexture.imgCount[xyoffset] > 0) + continue; - // texture holes filling - if(texParams.fillHoles) - { - ALICEVISION_LOG_INFO(" - Filling texture holes."); - std::vector alphaBuffer(accuImage.img.size()); - for(unsigned int yp = 0; yp < texParams.textureSide; ++yp) + if(atlasTexture.imgCount[xyoffset-1] > 0) + { + atlasTexture.img[xyoffset] = atlasTexture.img[xyoffset-1]; + atlasTexture.imgCount[xyoffset] = -1; + } + else if(atlasTexture.imgCount[xyoffset+1] > 0) + { + atlasTexture.img[xyoffset] = atlasTexture.img[xyoffset+1]; + atlasTexture.imgCount[xyoffset] = -1; + } + else if(atlasTexture.imgCount[xyoffset+texParams.textureSide] > 0) + { + atlasTexture.img[xyoffset] = atlasTexture.img[xyoffset+texParams.textureSide]; + atlasTexture.imgCount[xyoffset] = -1; + } + else if(atlasTexture.imgCount[xyoffset-texParams.textureSide] > 0) + { + atlasTexture.img[xyoffset] = atlasTexture.img[xyoffset-texParams.textureSide]; + atlasTexture.imgCount[xyoffset] = -1; + } + } + } + for(unsigned int y = 1; y < texParams.textureSide-1; ++y) { - unsigned int yoffset = yp * texParams.textureSide; - for(unsigned int xp = 0; xp < texParams.textureSide; ++xp) + unsigned int yoffset = y * texParams.textureSide; + for(unsigned int x = 1; x < texParams.textureSide-1; ++x) { - unsigned int xyoffset = yoffset + xp; - alphaBuffer[xyoffset] = accuImage.imgCount[xyoffset] ? 1.0f : 0.0f; + unsigned int xyoffset = yoffset + x; + if(atlasTexture.imgCount[xyoffset] == -1) + atlasTexture.imgCount[xyoffset] = 1; } } - imageIO::fillHoles(texParams.textureSide, texParams.textureSide, accuImage.img, alphaBuffer); - alphaBuffer.clear(); } + } - // downscale texture if required - if(texParams.downscale > 1) - { - std::vector resizedColorBuffer; - outTextureSide = texParams.textureSide / texParams.downscale; + const std::string textureName = "texture_" + std::to_string(1001 + atlasID) + (level < 0 ? "" : "_" + std::to_string(level)) + "." + EImageFileType_enumToString(textureFileType); // starts at '1001' for UDIM compatibility + bfs::path texturePath = outPath / textureName; + ALICEVISION_LOG_INFO(" - Writing texture file: " << texturePath.string()); - ALICEVISION_LOG_INFO(" - Downscaling texture (" << texParams.downscale << "x)."); - imageIO::resizeImage(texParams.textureSide, texParams.textureSide, texParams.downscale, accuImage.img, resizedColorBuffer); - std::swap(resizedColorBuffer, accuImage.img); + unsigned int outTextureSide = texParams.textureSide; + + // texture holes filling + if(texParams.fillHoles) + { + ALICEVISION_LOG_INFO(" - Filling texture holes."); + std::vector alphaBuffer(atlasTexture.img.size()); + for(unsigned int yp = 0; yp < texParams.textureSide; ++yp) + { + unsigned int yoffset = yp * texParams.textureSide; + for(unsigned int xp = 0; xp < texParams.textureSide; ++xp) + { + unsigned int xyoffset = yoffset + xp; + alphaBuffer[xyoffset] = atlasTexture.imgCount[xyoffset] ? 1.0f : 0.0f; + } } + imageIO::fillHoles(texParams.textureSide, texParams.textureSide, atlasTexture.img, alphaBuffer); + alphaBuffer.clear(); + } - imageIO::writeImage(texturePath.string(), outTextureSide, outTextureSide, accuImage.img); + // downscale texture if required + if(texParams.downscale > 1) + { + std::vector resizedColorBuffer; + outTextureSide = texParams.textureSide / texParams.downscale; + + ALICEVISION_LOG_INFO(" - Downscaling texture (" << texParams.downscale << "x)."); + imageIO::resizeImage(texParams.textureSide, texParams.textureSide, texParams.downscale, atlasTexture.img, resizedColorBuffer); + std::swap(resizedColorBuffer, atlasTexture.img); } + + imageIO::writeImage(texturePath.string(), outTextureSide, outTextureSide, atlasTexture.img); + } void Texturing::clear() diff --git a/src/aliceVision/mesh/Texturing.hpp b/src/aliceVision/mesh/Texturing.hpp index 14848b2355..03ea00e87d 100644 --- a/src/aliceVision/mesh/Texturing.hpp +++ b/src/aliceVision/mesh/Texturing.hpp @@ -145,6 +145,30 @@ struct Texturing */ void generateUVsBasicMethod(mvsUtils::MultiViewParams &mp); + // Create buffer for the set of output textures + struct AccuImage + { + std::vector img; + std::vector imgCount; + + void resize(std::size_t s) + { + img.resize(s); + imgCount.resize(s); + } + }; + struct AccuPyramid + { + std::vector pyramid; + + void init(std::size_t nbLevels, std::size_t imageSize) + { + pyramid.resize(nbLevels); + for(auto& p : pyramid) + p.resize(imageSize); + } + }; + /// Generate texture files for all texture atlases void generateTextures(const mvsUtils::MultiViewParams& mp, const bfs::path &outPath, EImageFileType textureFileType = EImageFileType::PNG); @@ -154,6 +178,10 @@ struct Texturing const std::vector& atlasIDs, mvsUtils::ImagesCache& imageCache, const bfs::path &outPath, EImageFileType textureFileType = EImageFileType::PNG); + ///Fill holes and write texture files for the given texture atlas + void writeTexture(AccuImage& atlasTexture, const std::size_t atlasID, const bfs::path& outPath, + EImageFileType textureFileType, const int level); + /// Save textured mesh as an OBJ + MTL file void saveAsOBJ(const bfs::path& dir, const std::string& basename, EImageFileType textureFileType = EImageFileType::PNG); }; From d7b38d1efb7abc85270ba936affb77229f780aa6 Mon Sep 17 00:00:00 2001 From: Unknown Date: Fri, 29 Mar 2019 11:48:19 +0100 Subject: [PATCH 10/51] [mesh] Texturing : memory management --- src/aliceVision/mesh/Texturing.cpp | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/aliceVision/mesh/Texturing.cpp b/src/aliceVision/mesh/Texturing.cpp index 12ea9a4e7a..95acfb2520 100644 --- a/src/aliceVision/mesh/Texturing.cpp +++ b/src/aliceVision/mesh/Texturing.cpp @@ -255,30 +255,30 @@ void Texturing::generateTextures(const mvsUtils::MultiViewParams &mp, const boost::filesystem::path &outPath, EImageFileType textureFileType) { mvsUtils::ImagesCache imageCache(&mp, 0); + imageCache.setCacheSize(2); system::MemoryInfo memInfo = system::getMemoryInfo(); //calculate the maximum number of atlases in memory in Mb - unsigned int atlasSize = texParams.textureSide * texParams.textureSide; - size_t atlasMemSize = atlasSize * 3 * sizeof(float) / std::pow(2,20); //Mb - int imageMaxSize = mp.getMaxImageWidth() * mp.getMaxImageHeight(); - size_t imageMaxMemSize = imageMaxSize * 3 * sizeof(float) / std::pow(2,20); //Mb - - int freeMem = int(memInfo.freeRam / std::pow(2,20)); - int availableMem = freeMem - int(imageMaxMemSize); // keep some memory for the input image buffers - int nbAtlasMax = availableMem / atlasMemSize; //maximum number of textures in RAM - int nbAtlas = _atlases.size(); + const std::size_t atlasMemSize = texParams.textureSide * texParams.textureSide * sizeof(Color) / std::pow(2,20); //Mb + const std::size_t imageMaxMemSize = mp.getMaxImageWidth() * mp.getMaxImageHeight() * sizeof(Color) / std::pow(2,20); //Mb + const std::size_t pyramidMaxMemSize = texParams.multiBandNbContrib.size() * atlasMemSize; + + const int freeMem = int(memInfo.freeRam / std::pow(2,20)); + const int availableMem = freeMem - 2 * imageMaxMemSize; // keep some memory for the input image buffer TODO : BESON DE + DE BUFFERS INTERMEDIAIRES ??? + int nbAtlasMax = availableMem / pyramidMaxMemSize; //maximum number of textures in RAM + const int nbAtlas = _atlases.size(); nbAtlasMax = std::max(1, nbAtlasMax); //if not enough memory, do it one by one nbAtlasMax = std::min(nbAtlas, nbAtlasMax); //if enough memory, do it with all atlases - div_t divresult = div(nbAtlas, nbAtlasMax); - int nquot = divresult.quot; - int nrem = divresult.rem; + std::div_t divresult = div(nbAtlas, nbAtlasMax); + const int nquot = divresult.quot; + const int nrem = divresult.rem; ALICEVISION_LOG_INFO("Total amount of free memory : " << freeMem << " Mb."); ALICEVISION_LOG_INFO("Total amount of an image in memory : " << imageMaxMemSize << " Mb."); ALICEVISION_LOG_INFO("Total amount of memory available : " << availableMem << " Mb."); ALICEVISION_LOG_INFO("Size of an atlas : " << atlasMemSize << " Mb."); - ALICEVISION_LOG_INFO("Processing " << nbAtlas << " atlases by chunks of " << nbAtlasMax << " (" << nquot + 1 << " iterations)."); + ALICEVISION_LOG_INFO("Processing " << nbAtlas << " atlases by chunks of " << nbAtlasMax); //generateTexture for the maximum number of atlases, and iterate std::vector atlasIDs; From 9160841028aea6df9e0dfd2fc32eb0d23b6e6588 Mon Sep 17 00:00:00 2001 From: Unknown Date: Fri, 29 Mar 2019 12:23:27 +0100 Subject: [PATCH 11/51] [mesh] Texturing : multiBandBlending For each camera, calculate the corresponding laplacian pyramid. For each texture triangle, fuse the contributions of frequencies bands from the best scoring cameras --- src/aliceVision/mesh/Texturing.cpp | 299 +++++++++++++++-------------- src/aliceVision/mesh/Texturing.hpp | 3 +- 2 files changed, 156 insertions(+), 146 deletions(-) diff --git a/src/aliceVision/mesh/Texturing.cpp b/src/aliceVision/mesh/Texturing.cpp index 95acfb2520..4a7ddbd14b 100644 --- a/src/aliceVision/mesh/Texturing.cpp +++ b/src/aliceVision/mesh/Texturing.cpp @@ -254,6 +254,12 @@ void Texturing::generateUVsBasicMethod(mvsUtils::MultiViewParams& mp) void Texturing::generateTextures(const mvsUtils::MultiViewParams &mp, const boost::filesystem::path &outPath, EImageFileType textureFileType) { + ALICEVISION_LOG_INFO("Texturing: Use multiband blending with the following contributions per band:"); + for(int c: texParams.multiBandNbContrib) + { + ALICEVISION_LOG_INFO(c << ", "); + } + mvsUtils::ImagesCache imageCache(&mp, 0); imageCache.setCacheSize(2); system::MemoryInfo memInfo = system::getMemoryInfo(); @@ -294,7 +300,7 @@ void Texturing::generateTextures(const mvsUtils::MultiViewParams &mp, size_t atlasID = size_t(n*nbAtlasMax + i); atlasIDs.push_back(atlasID); } - ALICEVISION_LOG_INFO("Generating texture for atlases " << n*nbAtlasMax << " to " << n*nbAtlasMax+imax-1 << " (process a chunk of " << atlasIDs.size() << " atlases within " << _atlases.size() << " atlases)."); + ALICEVISION_LOG_INFO("Generating texture for atlases " << n*nbAtlasMax + 1 << " to " << n*nbAtlasMax+imax << " (process a chunk of " << atlasIDs.size() << " atlases within " << _atlases.size() << " atlases)."); generateTexturesSubSet(mp, atlasIDs, imageCache, outPath, textureFileType); } } @@ -306,13 +312,14 @@ void Texturing::generateTexturesSubSet(const mvsUtils::MultiViewParams& mp, throw std::runtime_error("Invalid atlas IDs "); unsigned int textureSize = texParams.textureSide * texParams.textureSide; + int nbBand = texParams.multiBandNbContrib.size(); using AtlasIndex = size_t; using TrianglesId = std::vector; // We select the best cameras for each triangle and store it per camera for each output texture files. // List of triangle IDs (selected to contribute to the final texturing) per image. - std::vector> contributionsPerCamera(mp.ncams); + std::vector>> contributionsPerCamera(mp.ncams); //for each atlasID, calculate contributionPerCamera for(const size_t atlasID : atlasIDs) @@ -410,50 +417,49 @@ void Texturing::generateTexturesSubSet(const mvsUtils::MultiViewParams& mp, const double minScore = texParams.bestScoreThreshold * std::get<1>(scorePerCamId.front()); // bestScoreThreshold * bestScore const bool bestIsPartial = (std::get<0>(scorePerCamId.front()) < 3); int nbCumulatedVertices = 0; - const int maxNbVerticesForFusion = texParams.maxNbImagesForFusion * 3; - for(int i = 0; i < scorePerCamId.size(); ++i) + + int levelContrib = 0; + for(int i = 0; i < scorePerCamId.size() && levelContrib < nbBand; ++i) { + const int maxNbVerticesForFusion = texParams.multiBandNbContrib[levelContrib] * 3; if (!bestIsPartial && i > 0) { - bool triVisIsPartial = (std::get<0>(scorePerCamId[i]) < 3); nbCumulatedVertices += std::get<0>(scorePerCamId[i]); if(maxNbVerticesForFusion != 0 && nbCumulatedVertices > maxNbVerticesForFusion) - break; + { + ++levelContrib; + nbCumulatedVertices = 0; + continue; + } if(std::get<1>(scorePerCamId[i]) < minScore) + { // The best image fully see the triangle and has a much better score, so only rely on the first ones break; + } } - //add triangleID to the corresponding texture for this camera + //for the camera camId : add triangleID to the corresponding texture at the right level of frequency const int camId = std::get<2>(scorePerCamId[i]); - contributionsPerCamera[camId][atlasID].push_back(triangleID); + auto& camContribution = contributionsPerCamera[camId]; + if(camContribution.find(atlasID) == camContribution.end()) + camContribution[atlasID].resize(nbBand); + camContribution.at(atlasID)[levelContrib].push_back(triangleID); } } } ALICEVISION_LOG_INFO("Reading pixel color."); - // Create buffer for the set of output textures - struct AccuImage - { - std::vector img; - std::vector imgCount; - - void resize(std::size_t s) - { - img.resize(s); - imgCount.resize(s); - } - }; - - std::map accuImages; + std::map accuPyramids; for(std::size_t atlasID: atlasIDs) - accuImages[atlasID].resize(textureSize); + accuPyramids[atlasID].init(nbBand, textureSize); - //for each camera, for each texture, iterate over triangles and fill the colorID map - int camId = 0; - for(const std::map& cameraContributions : contributionsPerCamera) + //for each camera, for each texture, iterate over triangles and fill the accuPyramids map + + for(int camId = 0; camId < contributionsPerCamera.size(); ++camId) { + const std::map>& cameraContributions = contributionsPerCamera[camId]; + if(cameraContributions.empty()) { ALICEVISION_LOG_INFO(" - camera " << camId + 1 << "/" << mp.ncams << " unused."); @@ -461,7 +467,7 @@ void Texturing::generateTexturesSubSet(const mvsUtils::MultiViewParams& mp, } ALICEVISION_LOG_INFO(" - camera " << camId + 1 << "/" << mp.ncams << " with contributions to " << cameraContributions.size() << " texture files:"); - imageCache.refreshData(camId); + imageCache.refreshData(camId); //conversion ImagesCache::Img -> Image mvsUtils::ImagesCache::ImgPtr imgPtr = imageCache.getImg_sync(camId); @@ -472,125 +478,136 @@ void Texturing::generateTexturesSubSet(const mvsUtils::MultiViewParams& mp, //Calculate laplacianPyramid MultiBandBlending multiBandBlending; std::vector pyramidL; //laplacian pyramid - multiBandBlending.laplacianPyramid(pyramidL, camImg, camId, 3, 40.0f); //sizeKernel = final blur + multiBandBlending.laplacianDownscalePyramid(pyramidL, camImg, camId, nbBand, texParams.multiBandKernelSize); // for each output texture file for(const auto& c : cameraContributions) { AtlasIndex atlasID = c.first; - AccuImage& accuImage = accuImages.at(atlasID); - const TrianglesId& trianglesId = c.second; - - ALICEVISION_LOG_INFO(" Texture file: " << atlasID << ", number of triangles: " << trianglesId.size() << "."); - - // for each triangle - #pragma omp parallel for - for(int ti = 0; ti < trianglesId.size(); ++ti) - { - const unsigned int triangleId = trianglesId[ti]; - // retrieve triangle 3D and UV coordinates - Point2d triPixs[3]; - Point3d triPts[3]; - - for(int k = 0; k < 3; k++) - { - const int pointIndex = (*me->tris)[triangleId].v[k]; - triPts[k] = (*me->pts)[pointIndex]; // 3D coordinates - const int uvPointIndex = trisUvIds[triangleId].m[k]; - Point2d uv = uvCoords[uvPointIndex]; - uv.x -= std::floor(uv.x); - uv.y -= std::floor(uv.y); - - triPixs[k] = uv * texParams.textureSide; // UV coordinates - } - - // compute triangle bounding box in pixel indexes - // min values: floor(value) - // max values: ceil(value) - Pixel LU, RD; - LU.x = static_cast(std::floor(std::min(std::min(triPixs[0].x, triPixs[1].x), triPixs[2].x))); - LU.y = static_cast(std::floor(std::min(std::min(triPixs[0].y, triPixs[1].y), triPixs[2].y))); - RD.x = static_cast(std::ceil(std::max(std::max(triPixs[0].x, triPixs[1].x), triPixs[2].x))); - RD.y = static_cast(std::ceil(std::max(std::max(triPixs[0].y, triPixs[1].y), triPixs[2].y))); - - // sanity check: clamp values to [0; textureSide] - int texSide = static_cast(texParams.textureSide); - LU.x = clamp(LU.x, 0, texSide); - LU.y = clamp(LU.y, 0, texSide); - RD.x = clamp(RD.x, 0, texSide); - RD.y = clamp(RD.y, 0, texSide); - - // iterate over pixels of the triangle's bounding box - for(int y = LU.y; y < RD.y; y++) - { - for(int x = LU.x; x < RD.x; x++) - { - Pixel pix(x, y); // top-left corner of the pixel - Point2d barycCoords; - - // test if the pixel is inside triangle - // and retrieve its barycentric coordinates - if(!isPixelInTriangle(triPixs, pix, barycCoords)) - { - continue; - } + for(int level = 0; level < c.second.size(); ++level) + { + const TrianglesId& trianglesId = c.second[level]; + ALICEVISION_LOG_INFO(" Texture file: " << atlasID << ", number of triangles: " << trianglesId.size() << "."); + + // for each triangle + #pragma omp parallel for + for(int ti = 0; ti < trianglesId.size(); ++ti) + { + const unsigned int triangleId = trianglesId[ti]; + // retrieve triangle 3D and UV coordinates + Point2d triPixs[3]; + Point3d triPts[3]; - // remap 'y' to image coordinates system (inverted Y axis) - const unsigned int y_ = (texParams.textureSide - 1) - y; - // 1D pixel index - unsigned int xyoffset = y_ * texParams.textureSide + x; - // get 3D coordinates - Point3d pt3d = barycentricToCartesian(triPts, barycCoords); - // get 2D coordinates in source image - Point2d pixRC; - mp.getPixelFor3DPoint(&pixRC, pt3d, camId); - // exclude out of bounds pixels - if(!mp.isPixelInImage(pixRC, camId)) - continue; - - //Color color = imageCache.getPixelValueInterpolated(&pixRC, camId); - Color color(0.f,0.f,0.f); - for(const Image& bandImage : pyramidL) + for(int k = 0; k < 3; k++) + { + const int pointIndex = (*me->tris)[triangleId].v[k]; + triPts[k] = (*me->pts)[pointIndex]; // 3D coordinates + const int uvPointIndex = trisUvIds[triangleId].m[k]; + Point2d uv = uvCoords[uvPointIndex]; + uv.x -= std::floor(uv.x); + uv.y -= std::floor(uv.y); + + triPixs[k] = uv * texParams.textureSide; // UV coordinates + } + + // compute triangle bounding box in pixel indexes + // min values: floor(value) + // max values: ceil(value) + Pixel LU, RD; + LU.x = static_cast(std::floor(std::min(std::min(triPixs[0].x, triPixs[1].x), triPixs[2].x))); + LU.y = static_cast(std::floor(std::min(std::min(triPixs[0].y, triPixs[1].y), triPixs[2].y))); + RD.x = static_cast(std::ceil(std::max(std::max(triPixs[0].x, triPixs[1].x), triPixs[2].x))); + RD.y = static_cast(std::ceil(std::max(std::max(triPixs[0].y, triPixs[1].y), triPixs[2].y))); + + // sanity check: clamp values to [0; textureSide] + int texSide = static_cast(texParams.textureSide); + LU.x = clamp(LU.x, 0, texSide); + LU.y = clamp(LU.y, 0, texSide); + RD.x = clamp(RD.x, 0, texSide); + RD.y = clamp(RD.y, 0, texSide); + + // iterate over pixels of the triangle's bounding box + for(int y = LU.y; y < RD.y; y++) + { + for(int x = LU.x; x < RD.x; x++) { - color += bandImage.getInterpolateColor(pixRC); + Pixel pix(x, y); // top-left corner of the pixel + Point2d barycCoords; + + // test if the pixel is inside triangle + // and retrieve its barycentric coordinates + if(!isPixelInTriangle(triPixs, pix, barycCoords)) + { + continue; + } + + // remap 'y' to image coordinates system (inverted Y axis) + const unsigned int y_ = (texParams.textureSide - 1) - y; + // 1D pixel index + unsigned int xyoffset = y_ * texParams.textureSide + x; + // get 3D coordinates + Point3d pt3d = barycentricToCartesian(triPts, barycCoords); + // get 2D coordinates in source image + Point2d pixRC; + mp.getPixelFor3DPoint(&pixRC, pt3d, camId); + // exclude out of bounds pixels + if(!mp.isPixelInImage(pixRC, camId)) + continue; + + // If the color is pure zero, we consider it as an invalid pixel. + // After correction of radial distortion, some pixels are invalid. + // TODO: use an alpha channel instead. + if(pyramidL.back().getInterpolateColor(pixRC/std::pow(2,nbBand-1)) == Color(0.f, 0.f, 0.f)) + continue; + + //each level also contributes to lower frequencies levels + AccuPyramid& accuPyramid = accuPyramids.at(atlasID); + for(std::size_t levelC = level; levelC < pyramidL.size(); ++levelC) + { + int downscaleCoef = std::pow(2, levelC); + AccuImage& accuImage = accuPyramid.pyramid[levelC]; + + // fill the accumulated color map for this pixel + accuImage.img[xyoffset] += pyramidL[levelC].getInterpolateColor(pixRC/downscaleCoef); + accuImage.imgCount[xyoffset] += 1; + } } - - // If the color is pure zero, we consider it as an invalid pixel. - // After correction of radial distortion, some pixels are invalid. - // TODO: use an alpha channel instead. - if(color == Color(0.f, 0.f, 0.f)) - continue; - // fill the accumulated color map for this pixel - accuImage.img[xyoffset] += color; - accuImage.imgCount[xyoffset] += 1; - } - } - } + } + } + } } - camId++; // increment current cam index } - for(int atlasID = 0; atlasID < accuImages.size(); ++atlasID) + //calculate atlas texture in the first level of the pyramid (avoid creating a new buffer) + //debug mode : write all the frequencies levels for each texture + for(std::size_t atlasID : atlasIDs) { - AccuImage& accuImage = accuImages.at(atlasID); - + AccuPyramid& accuPyramid = accuPyramids.at(atlasID); + AccuImage& atlasTexture = accuPyramid.pyramid[0]; ALICEVISION_LOG_INFO("Create texture " << atlasID); ALICEVISION_LOG_INFO(" - Computing final (average) color."); - for(unsigned int yp = 0; yp < texParams.textureSide; ++yp) { unsigned int yoffset = yp * texParams.textureSide; for(unsigned int xp = 0; xp < texParams.textureSide; ++xp) { unsigned int xyoffset = yoffset + xp; - if(accuImage.imgCount[xyoffset]) - accuImage.img[xyoffset] /= accuImage.imgCount[xyoffset]; + + // If the imgCount is valid on the first band, it will be valid on all the other bands + if(atlasTexture.imgCount[xyoffset] == 0) + continue; + + atlasTexture.img[xyoffset] /= atlasTexture.imgCount[xyoffset]; + + for(std::size_t level = 1; level < accuPyramid.pyramid.size(); ++level) + { + AccuImage& atlasLevelTexture = accuPyramid.pyramid[level]; + atlasLevelTexture.img[xyoffset] /= atlasLevelTexture.imgCount[xyoffset]; + } } } - // WARNING: we modify the "imgCount" to apply the padding (to avoid the creation of a new buffer) - if(!texParams.fillHoles && texParams.padding > 0) { ALICEVISION_LOG_INFO(" - Edge padding (" << texParams.padding << " pixels)."); // edge padding (dilate gutter) @@ -603,33 +620,24 @@ void Texturing::generateTexturesSubSet(const mvsUtils::MultiViewParams& mp, { unsigned int xyoffset = yoffset + x; - if(accuImage.imgCount[xyoffset] > 0) - continue; - else if(accuImage.imgCount[xyoffset-1] > 0) - { - accuImage.img[xyoffset] = accuImage.img[xyoffset-1]; - } - else if(accuImage.imgCount[xyoffset+1] > 0) - { - accuImage.img[xyoffset] = accuImage.img[xyoffset+1]; - } - else if(accuImage.imgCount[xyoffset+texParams.textureSide] > 0) - { - accuImage.img[xyoffset] = accuImage.img[xyoffset+texParams.textureSide]; - } - else if(accuImage.imgCount[xyoffset-texParams.textureSide] > 0) - { - accuImage.img[xyoffset] = accuImage.img[xyoffset-texParams.textureSide]; - } - accuImage.imgCount[xyoffset] += 1; - } + // Fuse frequency bands into the first buffer + for(unsigned int yp = 0; yp < texParams.textureSide; ++yp) + { + unsigned int yoffset = yp * texParams.textureSide; + for(unsigned int xp = 0; xp < texParams.textureSide; ++xp) + { + unsigned int xyoffset = yoffset + xp; + for(std::size_t level = 1; level < accuPyramid.pyramid.size(); ++level) + { + AccuImage& atlasLevelTexture = accuPyramid.pyramid[level]; + atlasTexture.img[xyoffset] += atlasLevelTexture.img[xyoffset]; } } } + writeTexture(atlasTexture, atlasID, outPath, textureFileType, -1); + } +} - const std::string textureName = "texture_" + std::to_string(1001 + atlasID) + "." + EImageFileType_enumToString(textureFileType); // starts at '1001' for UDIM compatibility - bfs::path texturePath = outPath / textureName; - ALICEVISION_LOG_INFO(" - Writing texture file: " << texturePath.string()); void Texturing::writeTexture(AccuImage& atlasTexture, const std::size_t atlasID, const boost::filesystem::path &outPath, EImageFileType textureFileType, const int level) { @@ -724,6 +732,7 @@ void Texturing::writeTexture(AccuImage& atlasTexture, const std::size_t atlasID, } + void Texturing::clear() { trisMtlIds.clear(); diff --git a/src/aliceVision/mesh/Texturing.hpp b/src/aliceVision/mesh/Texturing.hpp index 03ea00e87d..60dbb5c20b 100644 --- a/src/aliceVision/mesh/Texturing.hpp +++ b/src/aliceVision/mesh/Texturing.hpp @@ -64,7 +64,8 @@ EVisibilityRemappingMethod EVisibilityRemappingMethod_stringToEnum(const std::st struct TexturingParams { - int maxNbImagesForFusion = 3; //< max number of images to combine to create the final texture + float multiBandKernelSize = 40.0f; + std::vector multiBandNbContrib = {1, 5, 10}; // number of contributions per level for the multi-band blending double bestScoreThreshold = 0.0; //< 0.0 to disable filtering based on threshold to relative best score double angleHardThreshold = 90.0; //< 0.0 to disable angle hard threshold filtering bool forceVisibleByAllVertices = false; //< triangle visibility is based on the union of vertices visiblity From 553e178d9c6552093473f064cc8af81f7b21e55e Mon Sep 17 00:00:00 2001 From: Unknown Date: Fri, 29 Mar 2019 12:24:43 +0100 Subject: [PATCH 12/51] [mesh] Texturing : debug mode save all frequency bands for all textures --- src/aliceVision/mesh/Texturing.cpp | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/aliceVision/mesh/Texturing.cpp b/src/aliceVision/mesh/Texturing.cpp index 4a7ddbd14b..e9fa3819ac 100644 --- a/src/aliceVision/mesh/Texturing.cpp +++ b/src/aliceVision/mesh/Texturing.cpp @@ -608,17 +608,15 @@ void Texturing::generateTexturesSubSet(const mvsUtils::MultiViewParams& mp, } } + bool debug = 1; + if(debug) { - ALICEVISION_LOG_INFO(" - Edge padding (" << texParams.padding << " pixels)."); - // edge padding (dilate gutter) - for(unsigned int g = 0; g < texParams.padding; ++g) + for(std::size_t level = 0; level < accuPyramid.pyramid.size(); ++level) { - for(unsigned int y = 1; y < texParams.textureSide-1; ++y) - { - unsigned int yoffset = y * texParams.textureSide; - for(unsigned int x = 1; x < texParams.textureSide-1; ++x) - { - unsigned int xyoffset = yoffset + x; + AccuImage& atlasLevelTexture = accuPyramid.pyramid[level]; + writeTexture(atlasLevelTexture, atlasID, outPath, textureFileType, level); + } + } // Fuse frequency bands into the first buffer for(unsigned int yp = 0; yp < texParams.textureSide; ++yp) From 4e3383df103b068b34efbc0b10169e90f996e637 Mon Sep 17 00:00:00 2001 From: Clara Date: Fri, 29 Mar 2019 12:40:17 +0100 Subject: [PATCH 13/51] [mesh] Texturing : delete sizeKernel & maxNbImagesForFusion Let openImageIO choose the size of the convolution kernel when downscaling by two & replace maxNbImagesForFusion by multiBandNbContrib --- src/aliceVision/mesh/MultiBandBlending.cpp | 4 ++-- src/aliceVision/mesh/MultiBandBlending.hpp | 2 +- src/aliceVision/mesh/Texturing.cpp | 2 +- src/aliceVision/mesh/Texturing.hpp | 1 - src/software/pipeline/main_texturing.cpp | 4 ++-- 5 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/aliceVision/mesh/MultiBandBlending.cpp b/src/aliceVision/mesh/MultiBandBlending.cpp index e80db7d5ec..f2342cae83 100644 --- a/src/aliceVision/mesh/MultiBandBlending.cpp +++ b/src/aliceVision/mesh/MultiBandBlending.cpp @@ -45,7 +45,7 @@ void MultiBandBlending::laplacianPyramid(std::vector& out_pyramidL, const out_pyramidL.at(nbBand-1) = base; } -void MultiBandBlending::laplacianDownscalePyramid(std::vector& out_pyramidL, const Image& inImg, int camId, int nbBand, float sizeKernel) +void MultiBandBlending::laplacianDownscalePyramid(std::vector& out_pyramidL, const Image& inImg, int nbBand) { assert(nbBand >= 1); @@ -59,7 +59,7 @@ void MultiBandBlending::laplacianDownscalePyramid(std::vector& out_pyrami //Create Laplacian pyramid for(int b = 0; b < nbBand-1; ++b) { - imageIO::resizeImage(img.width(), img.height(), 2, img.data(), imgDownscaled.data(), "gaussian", sizeKernel); + imageIO::resizeImage(img.width(), img.height(), 2, img.data(), imgDownscaled.data(), "gaussian"); Image::imageDownscaleDiff(img, imgDownscaled, out_pyramidL[b]); img.swap(imgDownscaled); diff --git a/src/aliceVision/mesh/MultiBandBlending.hpp b/src/aliceVision/mesh/MultiBandBlending.hpp index 32ebce9c21..9e99574c8b 100644 --- a/src/aliceVision/mesh/MultiBandBlending.hpp +++ b/src/aliceVision/mesh/MultiBandBlending.hpp @@ -20,7 +20,7 @@ class MultiBandBlending void laplacianPyramid(std::vector& out_pyramidL, const Image& inImg, int camId, int nbBand, float sizeKernel); ///Generate the gaussian pyramid given the input image - void laplacianDownscalePyramid(std::vector& out_pyramidL, const Image& inImg, int camId, int nbBand, float sizeKernel); + void laplacianDownscalePyramid(std::vector& out_pyramidL, const Image& inImg, int nbBand); }; diff --git a/src/aliceVision/mesh/Texturing.cpp b/src/aliceVision/mesh/Texturing.cpp index e9fa3819ac..2edb3782a6 100644 --- a/src/aliceVision/mesh/Texturing.cpp +++ b/src/aliceVision/mesh/Texturing.cpp @@ -478,7 +478,7 @@ void Texturing::generateTexturesSubSet(const mvsUtils::MultiViewParams& mp, //Calculate laplacianPyramid MultiBandBlending multiBandBlending; std::vector pyramidL; //laplacian pyramid - multiBandBlending.laplacianDownscalePyramid(pyramidL, camImg, camId, nbBand, texParams.multiBandKernelSize); + multiBandBlending.laplacianDownscalePyramid(pyramidL, camImg, nbBand); // for each output texture file for(const auto& c : cameraContributions) diff --git a/src/aliceVision/mesh/Texturing.hpp b/src/aliceVision/mesh/Texturing.hpp index 60dbb5c20b..22f71816cf 100644 --- a/src/aliceVision/mesh/Texturing.hpp +++ b/src/aliceVision/mesh/Texturing.hpp @@ -64,7 +64,6 @@ EVisibilityRemappingMethod EVisibilityRemappingMethod_stringToEnum(const std::st struct TexturingParams { - float multiBandKernelSize = 40.0f; std::vector multiBandNbContrib = {1, 5, 10}; // number of contributions per level for the multi-band blending double bestScoreThreshold = 0.0; //< 0.0 to disable filtering based on threshold to relative best score double angleHardThreshold = 90.0; //< 0.0 to disable angle hard threshold filtering diff --git a/src/software/pipeline/main_texturing.cpp b/src/software/pipeline/main_texturing.cpp index a626625b0a..13cc661fcd 100644 --- a/src/software/pipeline/main_texturing.cpp +++ b/src/software/pipeline/main_texturing.cpp @@ -85,8 +85,8 @@ int main(int argc, char* argv[]) "Texture edge padding size in pixel") ("flipNormals", po::value(&flipNormals)->default_value(flipNormals), "Option to flip face normals. It can be needed as it depends on the vertices order in triangles and the convention change from one software to another.") - ("maxNbImagesForFusion", po::value(&texParams.maxNbImagesForFusion)->default_value(texParams.maxNbImagesForFusion), - "Max number of images to combine to create the final texture.") + ("multiBandNbContrib", po::value>(&texParams.multiBandNbContrib)->default_value(texParams.multiBandNbContrib), + "Number of images to combine per band of frequencies to create the final texture.") ("bestScoreThreshold", po::value(&texParams.bestScoreThreshold)->default_value(texParams.bestScoreThreshold), "(0.0 to disable filtering based on threshold to relative best score).") ("angleHardThreshold", po::value(&texParams.angleHardThreshold)->default_value(texParams.angleHardThreshold), From 95f373d7781e29d8fd5fb97a52c2fe62b6914aa9 Mon Sep 17 00:00:00 2001 From: Clara Date: Fri, 29 Mar 2019 16:23:32 +0100 Subject: [PATCH 14/51] [mesh] Texturing : optimize padding Create the texture padding with only two passes of the image instead of 2*padding --- src/aliceVision/mesh/Texturing.cpp | 101 ++++++++++++++++++----------- 1 file changed, 64 insertions(+), 37 deletions(-) diff --git a/src/aliceVision/mesh/Texturing.cpp b/src/aliceVision/mesh/Texturing.cpp index 2edb3782a6..4ccba00703 100644 --- a/src/aliceVision/mesh/Texturing.cpp +++ b/src/aliceVision/mesh/Texturing.cpp @@ -639,53 +639,82 @@ void Texturing::generateTexturesSubSet(const mvsUtils::MultiViewParams& mp, void Texturing::writeTexture(AccuImage& atlasTexture, const std::size_t atlasID, const boost::filesystem::path &outPath, EImageFileType textureFileType, const int level) { + unsigned int outTextureSide = texParams.textureSide; // WARNING: we modify the "imgCount" to apply the padding (to avoid the creation of a new buffer) + // edge padding (dilate gutter) if(!texParams.fillHoles && texParams.padding > 0) { ALICEVISION_LOG_INFO(" - Edge padding (" << texParams.padding << " pixels)."); - // edge padding (dilate gutter) - for(unsigned int g = 0; g < texParams.padding; ++g) + + //up-left to bottom-right + for(unsigned int y = 1; y < outTextureSide-1; ++y) { - for(unsigned int y = 1; y < texParams.textureSide-1; ++y) + unsigned int yoffset = y * outTextureSide; + for(unsigned int x = 1; x < outTextureSide-1; ++x) { - unsigned int yoffset = y * texParams.textureSide; - for(unsigned int x = 1; x < texParams.textureSide-1; ++x) - { - unsigned int xyoffset = yoffset + x; + unsigned int xyoffset = yoffset + x; + const int leftCount = atlasTexture.imgCount[xyoffset-1]; + const int upCount = atlasTexture.imgCount[xyoffset-outTextureSide]; - if(atlasTexture.imgCount[xyoffset] > 0) - continue; + if(atlasTexture.imgCount[xyoffset] > 0) + continue; - if(atlasTexture.imgCount[xyoffset-1] > 0) - { - atlasTexture.img[xyoffset] = atlasTexture.img[xyoffset-1]; - atlasTexture.imgCount[xyoffset] = -1; - } - else if(atlasTexture.imgCount[xyoffset+1] > 0) - { - atlasTexture.img[xyoffset] = atlasTexture.img[xyoffset+1]; - atlasTexture.imgCount[xyoffset] = -1; - } - else if(atlasTexture.imgCount[xyoffset+texParams.textureSide] > 0) - { - atlasTexture.img[xyoffset] = atlasTexture.img[xyoffset+texParams.textureSide]; - atlasTexture.imgCount[xyoffset] = -1; - } - else if(atlasTexture.imgCount[xyoffset-texParams.textureSide] > 0) - { - atlasTexture.img[xyoffset] = atlasTexture.img[xyoffset-texParams.textureSide]; - atlasTexture.imgCount[xyoffset] = -1; - } + if(leftCount > 0) + { + atlasTexture.img[xyoffset] = atlasTexture.img[xyoffset-1]; + atlasTexture.imgCount[xyoffset] = - 1; + } + if(leftCount < 0 && leftCount > -texParams.padding) + { + atlasTexture.img[xyoffset] = atlasTexture.img[xyoffset-1]; + atlasTexture.imgCount[xyoffset] = leftCount - 1; + } + if(upCount > 0) + { + atlasTexture.img[xyoffset] = atlasTexture.img[xyoffset-1]; + atlasTexture.imgCount[xyoffset] = - 1; + } + if(upCount < 0 && upCount > -texParams.padding) + { + atlasTexture.img[xyoffset] = atlasTexture.img[xyoffset-1]; + atlasTexture.imgCount[xyoffset] = upCount - 1; } } - for(unsigned int y = 1; y < texParams.textureSide-1; ++y) + } + //bottom-right to up-left + for(unsigned int y = 1; y < outTextureSide-1; ++y) //change + { + unsigned int yoffset = (outTextureSide - y) * outTextureSide; + for(unsigned int x = 1; x < outTextureSide-1; ++x) { - unsigned int yoffset = y * texParams.textureSide; - for(unsigned int x = 1; x < texParams.textureSide-1; ++x) + unsigned int xyoffset = yoffset + (outTextureSide - x); + const int rightCount = atlasTexture.imgCount[xyoffset+1]; + const int leftCount = atlasTexture.imgCount[xyoffset-1]; + const int downCount = atlasTexture.imgCount[xyoffset+outTextureSide]; + const int upCount = atlasTexture.imgCount[xyoffset-outTextureSide]; + + if(atlasTexture.imgCount[xyoffset] > 0) + continue; + + if(rightCount > 0) { - unsigned int xyoffset = yoffset + x; - if(atlasTexture.imgCount[xyoffset] == -1) - atlasTexture.imgCount[xyoffset] = 1; + atlasTexture.img[xyoffset] = atlasTexture.img[xyoffset-1]; + atlasTexture.imgCount[xyoffset] = - 1; + } + if(rightCount < 0 && rightCount > -texParams.padding && rightCount < leftCount) + { + atlasTexture.img[xyoffset] = atlasTexture.img[xyoffset-1]; + atlasTexture.imgCount[xyoffset] = rightCount - 1; + } + if(downCount > 0) + { + atlasTexture.img[xyoffset] = atlasTexture.img[xyoffset-1]; + atlasTexture.imgCount[xyoffset] = - 1; + } + if(downCount < 0 && downCount > -texParams.padding && downCount < upCount) + { + atlasTexture.img[xyoffset] = atlasTexture.img[xyoffset-1]; + atlasTexture.imgCount[xyoffset] = downCount - 1; } } } @@ -695,8 +724,6 @@ void Texturing::writeTexture(AccuImage& atlasTexture, const std::size_t atlasID, bfs::path texturePath = outPath / textureName; ALICEVISION_LOG_INFO(" - Writing texture file: " << texturePath.string()); - unsigned int outTextureSide = texParams.textureSide; - // texture holes filling if(texParams.fillHoles) { From 7dc8eb68822d89c22745a4a19caafdee1f796da1 Mon Sep 17 00:00:00 2001 From: Clara Date: Fri, 5 Apr 2019 11:54:21 +0200 Subject: [PATCH 15/51] [mesh] Texturing: add downscale param for multiband blending Add param to control the level of blur between each level of the laplacian pyramid (previously fixed to 2) --- src/aliceVision/mesh/MultiBandBlending.cpp | 16 +++++++--------- src/aliceVision/mesh/MultiBandBlending.hpp | 2 +- src/aliceVision/mesh/Texturing.cpp | 7 ++++--- src/aliceVision/mesh/Texturing.hpp | 1 + src/aliceVision/mvsData/Image.hpp | 7 ++++--- src/software/pipeline/main_texturing.cpp | 2 ++ 6 files changed, 19 insertions(+), 16 deletions(-) diff --git a/src/aliceVision/mesh/MultiBandBlending.cpp b/src/aliceVision/mesh/MultiBandBlending.cpp index f2342cae83..e673061e6a 100644 --- a/src/aliceVision/mesh/MultiBandBlending.cpp +++ b/src/aliceVision/mesh/MultiBandBlending.cpp @@ -45,13 +45,13 @@ void MultiBandBlending::laplacianPyramid(std::vector& out_pyramidL, const out_pyramidL.at(nbBand-1) = base; } -void MultiBandBlending::laplacianDownscalePyramid(std::vector& out_pyramidL, const Image& inImg, int nbBand) +void MultiBandBlending::laplacianDownscalePyramid(std::vector& out_pyramidL, const Image& inImg, int nbBand, unsigned int downscale) { assert(nbBand >= 1); Image img(inImg); - int outW = static_cast(img.width()/2); - int outH = static_cast(img.height()/2); + int outW = static_cast(img.width()/downscale); + int outH = static_cast(img.height()/downscale); Image imgDownscaled(outW, outH); out_pyramidL.resize(nbBand); @@ -59,16 +59,14 @@ void MultiBandBlending::laplacianDownscalePyramid(std::vector& out_pyrami //Create Laplacian pyramid for(int b = 0; b < nbBand-1; ++b) { - imageIO::resizeImage(img.width(), img.height(), 2, img.data(), imgDownscaled.data(), "gaussian"); - Image::imageDownscaleDiff(img, imgDownscaled, out_pyramidL[b]); - + imageIO::resizeImage(img.width(), img.height(), static_cast(downscale), img.data(), imgDownscaled.data(), "gaussian"); + Image::imageDownscaleDiff(img, imgDownscaled, out_pyramidL[b], downscale); img.swap(imgDownscaled); - outW = static_cast(outW/2); - outH = static_cast(outH/2); + outW = static_cast(outW/downscale); + outH = static_cast(outH/downscale); imgDownscaled.resize(outW, outH); - } out_pyramidL[nbBand-1] = img; diff --git a/src/aliceVision/mesh/MultiBandBlending.hpp b/src/aliceVision/mesh/MultiBandBlending.hpp index 9e99574c8b..1233fc8395 100644 --- a/src/aliceVision/mesh/MultiBandBlending.hpp +++ b/src/aliceVision/mesh/MultiBandBlending.hpp @@ -20,7 +20,7 @@ class MultiBandBlending void laplacianPyramid(std::vector& out_pyramidL, const Image& inImg, int camId, int nbBand, float sizeKernel); ///Generate the gaussian pyramid given the input image - void laplacianDownscalePyramid(std::vector& out_pyramidL, const Image& inImg, int nbBand); + void laplacianDownscalePyramid(std::vector& out_pyramidL, const Image& inImg, int nbBand, unsigned int downscale); }; diff --git a/src/aliceVision/mesh/Texturing.cpp b/src/aliceVision/mesh/Texturing.cpp index 4ccba00703..b2b328d9f0 100644 --- a/src/aliceVision/mesh/Texturing.cpp +++ b/src/aliceVision/mesh/Texturing.cpp @@ -313,6 +313,7 @@ void Texturing::generateTexturesSubSet(const mvsUtils::MultiViewParams& mp, unsigned int textureSize = texParams.textureSide * texParams.textureSide; int nbBand = texParams.multiBandNbContrib.size(); + unsigned int multiBandDownScale = texParams.multiBandDownscale; using AtlasIndex = size_t; using TrianglesId = std::vector; @@ -478,7 +479,7 @@ void Texturing::generateTexturesSubSet(const mvsUtils::MultiViewParams& mp, //Calculate laplacianPyramid MultiBandBlending multiBandBlending; std::vector pyramidL; //laplacian pyramid - multiBandBlending.laplacianDownscalePyramid(pyramidL, camImg, nbBand); + multiBandBlending.laplacianDownscalePyramid(pyramidL, camImg, nbBand, multiBandDownScale); // for each output texture file for(const auto& c : cameraContributions) @@ -557,14 +558,14 @@ void Texturing::generateTexturesSubSet(const mvsUtils::MultiViewParams& mp, // If the color is pure zero, we consider it as an invalid pixel. // After correction of radial distortion, some pixels are invalid. // TODO: use an alpha channel instead. - if(pyramidL.back().getInterpolateColor(pixRC/std::pow(2,nbBand-1)) == Color(0.f, 0.f, 0.f)) + if(pyramidL.back().getInterpolateColor(pixRC/std::pow(multiBandDownScale,nbBand-1)) == Color(0.f, 0.f, 0.f)) continue; //each level also contributes to lower frequencies levels AccuPyramid& accuPyramid = accuPyramids.at(atlasID); for(std::size_t levelC = level; levelC < pyramidL.size(); ++levelC) { - int downscaleCoef = std::pow(2, levelC); + int downscaleCoef = std::pow(multiBandDownScale, levelC); AccuImage& accuImage = accuPyramid.pyramid[levelC]; // fill the accumulated color map for this pixel diff --git a/src/aliceVision/mesh/Texturing.hpp b/src/aliceVision/mesh/Texturing.hpp index 22f71816cf..95a2a898b1 100644 --- a/src/aliceVision/mesh/Texturing.hpp +++ b/src/aliceVision/mesh/Texturing.hpp @@ -64,6 +64,7 @@ EVisibilityRemappingMethod EVisibilityRemappingMethod_stringToEnum(const std::st struct TexturingParams { + unsigned int multiBandDownscale = 2; std::vector multiBandNbContrib = {1, 5, 10}; // number of contributions per level for the multi-band blending double bestScoreThreshold = 0.0; //< 0.0 to disable filtering based on threshold to relative best score double angleHardThreshold = 90.0; //< 0.0 to disable angle hard threshold filtering diff --git a/src/aliceVision/mvsData/Image.hpp b/src/aliceVision/mvsData/Image.hpp index 84764be8f1..45d4f6badd 100644 --- a/src/aliceVision/mvsData/Image.hpp +++ b/src/aliceVision/mvsData/Image.hpp @@ -88,8 +88,7 @@ class Image } } - //WARNING : size(inImga) = 2*size(inImgb) - static void imageDownscaleDiff(Image& inImga, Image& inImgb, Image& outImg) + static void imageDownscaleDiff(Image& inImga, Image& inImgb, Image& outImg, unsigned int downscale) { //inImga = largest image if(inImga.height() < inImgb.height()) @@ -99,7 +98,9 @@ class Image for(int i = 0; i < inImga._width*inImga._height; ++i) { Point2d pix(i%inImga.width(), static_cast(i/inImga.width())); - outImg._img[i] = inImga._img[i] - inImgb.getInterpolateColor(pix/2); + Point2d pixd = pix/downscale; + + outImg._img[i] = inImga._img[i] - inImgb.getInterpolateColor(pixd); } } diff --git a/src/software/pipeline/main_texturing.cpp b/src/software/pipeline/main_texturing.cpp index 13cc661fcd..ec6bc45377 100644 --- a/src/software/pipeline/main_texturing.cpp +++ b/src/software/pipeline/main_texturing.cpp @@ -87,6 +87,8 @@ int main(int argc, char* argv[]) "Option to flip face normals. It can be needed as it depends on the vertices order in triangles and the convention change from one software to another.") ("multiBandNbContrib", po::value>(&texParams.multiBandNbContrib)->default_value(texParams.multiBandNbContrib), "Number of images to combine per band of frequencies to create the final texture.") + ("multiBandDownscale", po::value(&texParams.multiBandDownscale)->default_value(texParams.multiBandDownscale), + "Width of frequency bands.") ("bestScoreThreshold", po::value(&texParams.bestScoreThreshold)->default_value(texParams.bestScoreThreshold), "(0.0 to disable filtering based on threshold to relative best score).") ("angleHardThreshold", po::value(&texParams.angleHardThreshold)->default_value(texParams.angleHardThreshold), From f41101ed0c9eb6761911f554930591d5b3bee9d1 Mon Sep 17 00:00:00 2001 From: Clara Date: Fri, 5 Apr 2019 12:01:43 +0200 Subject: [PATCH 16/51] [mesh] Texturing: add nbBand param for multiband blending Add number of bands param, to control the number of level of the laplacian pyramid --- src/aliceVision/mesh/Texturing.cpp | 2 +- src/aliceVision/mesh/Texturing.hpp | 1 + src/software/pipeline/main_texturing.cpp | 4 ++-- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/aliceVision/mesh/Texturing.cpp b/src/aliceVision/mesh/Texturing.cpp index b2b328d9f0..ac77dd24c8 100644 --- a/src/aliceVision/mesh/Texturing.cpp +++ b/src/aliceVision/mesh/Texturing.cpp @@ -312,7 +312,7 @@ void Texturing::generateTexturesSubSet(const mvsUtils::MultiViewParams& mp, throw std::runtime_error("Invalid atlas IDs "); unsigned int textureSize = texParams.textureSide * texParams.textureSide; - int nbBand = texParams.multiBandNbContrib.size(); + int nbBand = texParams.nbBand; unsigned int multiBandDownScale = texParams.multiBandDownscale; using AtlasIndex = size_t; diff --git a/src/aliceVision/mesh/Texturing.hpp b/src/aliceVision/mesh/Texturing.hpp index 95a2a898b1..6c2834ce91 100644 --- a/src/aliceVision/mesh/Texturing.hpp +++ b/src/aliceVision/mesh/Texturing.hpp @@ -64,6 +64,7 @@ EVisibilityRemappingMethod EVisibilityRemappingMethod_stringToEnum(const std::st struct TexturingParams { + unsigned int nbBand = 3; unsigned int multiBandDownscale = 2; std::vector multiBandNbContrib = {1, 5, 10}; // number of contributions per level for the multi-band blending double bestScoreThreshold = 0.0; //< 0.0 to disable filtering based on threshold to relative best score diff --git a/src/software/pipeline/main_texturing.cpp b/src/software/pipeline/main_texturing.cpp index ec6bc45377..01d4b9b77c 100644 --- a/src/software/pipeline/main_texturing.cpp +++ b/src/software/pipeline/main_texturing.cpp @@ -85,10 +85,10 @@ int main(int argc, char* argv[]) "Texture edge padding size in pixel") ("flipNormals", po::value(&flipNormals)->default_value(flipNormals), "Option to flip face normals. It can be needed as it depends on the vertices order in triangles and the convention change from one software to another.") - ("multiBandNbContrib", po::value>(&texParams.multiBandNbContrib)->default_value(texParams.multiBandNbContrib), - "Number of images to combine per band of frequencies to create the final texture.") ("multiBandDownscale", po::value(&texParams.multiBandDownscale)->default_value(texParams.multiBandDownscale), "Width of frequency bands.") + ("nbBand", po::value(&texParams.nbBand)->default_value(texParams.nbBand), + "Number of bands to combine for multi-band blending.") ("bestScoreThreshold", po::value(&texParams.bestScoreThreshold)->default_value(texParams.bestScoreThreshold), "(0.0 to disable filtering based on threshold to relative best score).") ("angleHardThreshold", po::value(&texParams.angleHardThreshold)->default_value(texParams.angleHardThreshold), From 35d33a7f2cb2d5a58093fc0886d40d9ccb45d01f Mon Sep 17 00:00:00 2001 From: Clara Date: Fri, 5 Apr 2019 12:06:33 +0200 Subject: [PATCH 17/51] [mesh] Texturing: keep margin in memory use Refine calculus of the number of atlases simultaneously computable --- src/aliceVision/mesh/Texturing.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/aliceVision/mesh/Texturing.cpp b/src/aliceVision/mesh/Texturing.cpp index ac77dd24c8..a6959170f9 100644 --- a/src/aliceVision/mesh/Texturing.cpp +++ b/src/aliceVision/mesh/Texturing.cpp @@ -265,13 +265,13 @@ void Texturing::generateTextures(const mvsUtils::MultiViewParams &mp, system::MemoryInfo memInfo = system::getMemoryInfo(); //calculate the maximum number of atlases in memory in Mb - const std::size_t atlasMemSize = texParams.textureSide * texParams.textureSide * sizeof(Color) / std::pow(2,20); //Mb + const std::size_t atlasContribMemSize = texParams.textureSide * texParams.textureSide * (sizeof(Color)+sizeof(int)) / std::pow(2,20); //Mb const std::size_t imageMaxMemSize = mp.getMaxImageWidth() * mp.getMaxImageHeight() * sizeof(Color) / std::pow(2,20); //Mb - const std::size_t pyramidMaxMemSize = texParams.multiBandNbContrib.size() * atlasMemSize; + const std::size_t pyramidMaxMemSize = texParams.nbBand * atlasContribMemSize; const int freeMem = int(memInfo.freeRam / std::pow(2,20)); - const int availableMem = freeMem - 2 * imageMaxMemSize; // keep some memory for the input image buffer TODO : BESON DE + DE BUFFERS INTERMEDIAIRES ??? - int nbAtlasMax = availableMem / pyramidMaxMemSize; //maximum number of textures in RAM + const int availableMem = freeMem - 2 * imageMaxMemSize * (texParams.nbBand + 1); // keep some memory for the input image buffer and its laplacian pyramid + int nbAtlasMax = std::floor(availableMem / pyramidMaxMemSize) - 1; //maximum number of textures in RAM const int nbAtlas = _atlases.size(); nbAtlasMax = std::max(1, nbAtlasMax); //if not enough memory, do it one by one nbAtlasMax = std::min(nbAtlas, nbAtlasMax); //if enough memory, do it with all atlases @@ -283,7 +283,7 @@ void Texturing::generateTextures(const mvsUtils::MultiViewParams &mp, ALICEVISION_LOG_INFO("Total amount of free memory : " << freeMem << " Mb."); ALICEVISION_LOG_INFO("Total amount of an image in memory : " << imageMaxMemSize << " Mb."); ALICEVISION_LOG_INFO("Total amount of memory available : " << availableMem << " Mb."); - ALICEVISION_LOG_INFO("Size of an atlas : " << atlasMemSize << " Mb."); + ALICEVISION_LOG_INFO("Size of an atlas : " << atlasContribMemSize << " Mb."); ALICEVISION_LOG_INFO("Processing " << nbAtlas << " atlases by chunks of " << nbAtlasMax); //generateTexture for the maximum number of atlases, and iterate From fa0f07cf780866aacd95e978f0aadcf77c79ece2 Mon Sep 17 00:00:00 2001 From: Clara Date: Fri, 5 Apr 2019 12:11:10 +0200 Subject: [PATCH 18/51] [mesh] Texturing : optimize texture padding Texture padding computed in 2 passes instead of padding*2 passes --- src/aliceVision/mesh/Texturing.cpp | 83 ++++++++++++++++++------------ src/aliceVision/mesh/Texturing.hpp | 2 +- 2 files changed, 50 insertions(+), 35 deletions(-) diff --git a/src/aliceVision/mesh/Texturing.cpp b/src/aliceVision/mesh/Texturing.cpp index a6959170f9..7dafb62b76 100644 --- a/src/aliceVision/mesh/Texturing.cpp +++ b/src/aliceVision/mesh/Texturing.cpp @@ -643,9 +643,14 @@ void Texturing::writeTexture(AccuImage& atlasTexture, const std::size_t atlasID, unsigned int outTextureSide = texParams.textureSide; // WARNING: we modify the "imgCount" to apply the padding (to avoid the creation of a new buffer) // edge padding (dilate gutter) - if(!texParams.fillHoles && texParams.padding > 0) + if(!texParams.fillHoles && texParams.padding > 0 && level < 0) { - ALICEVISION_LOG_INFO(" - Edge padding (" << texParams.padding << " pixels)."); + const unsigned int padding = texParams.padding * 3; + ALICEVISION_LOG_INFO(" - Edge padding (" << padding << " pixels)."); + + /* const std::string textureName0 = "texture_" + std::to_string(1001 + atlasID) + "prePadd." + EImageFileType_enumToString(textureFileType); // starts at '1001' for UDIM compatibility + bfs::path texturePath0 = outPath / textureName0; + imageIO::writeImage(texturePath0.string(), outTextureSide, outTextureSide, atlasTexture.img);*/ //up-left to bottom-right for(unsigned int y = 1; y < outTextureSide-1; ++y) @@ -654,77 +659,84 @@ void Texturing::writeTexture(AccuImage& atlasTexture, const std::size_t atlasID, for(unsigned int x = 1; x < outTextureSide-1; ++x) { unsigned int xyoffset = yoffset + x; - const int leftCount = atlasTexture.imgCount[xyoffset-1]; - const int upCount = atlasTexture.imgCount[xyoffset-outTextureSide]; - if(atlasTexture.imgCount[xyoffset] > 0) continue; + const int upCount = atlasTexture.imgCount[xyoffset - outTextureSide]; + const int leftCount = atlasTexture.imgCount[xyoffset - 1]; + //if pixel on the edge of a chart if(leftCount > 0) { - atlasTexture.img[xyoffset] = atlasTexture.img[xyoffset-1]; + atlasTexture.img[xyoffset] = atlasTexture.img[xyoffset - 1]; atlasTexture.imgCount[xyoffset] = - 1; } - if(leftCount < 0 && leftCount > -texParams.padding) + else if(upCount > 0) { - atlasTexture.img[xyoffset] = atlasTexture.img[xyoffset-1]; - atlasTexture.imgCount[xyoffset] = leftCount - 1; + atlasTexture.img[xyoffset] = atlasTexture.img[xyoffset - outTextureSide]; + atlasTexture.imgCount[xyoffset] = - 1; } - if(upCount > 0) + // + else if (leftCount < 0 && - leftCount < padding && (upCount == 0 || leftCount > upCount)) { - atlasTexture.img[xyoffset] = atlasTexture.img[xyoffset-1]; - atlasTexture.imgCount[xyoffset] = - 1; + atlasTexture.img[xyoffset] = atlasTexture.img[xyoffset - 1]; + atlasTexture.imgCount[xyoffset] = leftCount - 1; } - if(upCount < 0 && upCount > -texParams.padding) + else if (upCount < 0 && - upCount < padding) { - atlasTexture.img[xyoffset] = atlasTexture.img[xyoffset-1]; + atlasTexture.img[xyoffset] = atlasTexture.img[xyoffset - outTextureSide]; atlasTexture.imgCount[xyoffset] = upCount - 1; } } } + /* const std::string textureName1 = "texture_" + std::to_string(1001 + atlasID) + "Interpadd." + EImageFileType_enumToString(textureFileType); // starts at '1001' for UDIM compatibility + bfs::path texturePath1 = outPath / textureName1; + imageIO::writeImage(texturePath1.string(), outTextureSide, outTextureSide, atlasTexture.img);*/ //bottom-right to up-left - for(unsigned int y = 1; y < outTextureSide-1; ++y) //change + for(unsigned int y = 1; y < outTextureSide-1; ++y) { unsigned int yoffset = (outTextureSide - y) * outTextureSide; for(unsigned int x = 1; x < outTextureSide-1; ++x) { unsigned int xyoffset = yoffset + (outTextureSide - x); - const int rightCount = atlasTexture.imgCount[xyoffset+1]; - const int leftCount = atlasTexture.imgCount[xyoffset-1]; - const int downCount = atlasTexture.imgCount[xyoffset+outTextureSide]; - const int upCount = atlasTexture.imgCount[xyoffset-outTextureSide]; - if(atlasTexture.imgCount[xyoffset] > 0) continue; + const int upCount = atlasTexture.imgCount[xyoffset - outTextureSide]; + const int downCount = atlasTexture.imgCount[xyoffset + outTextureSide]; + const int rightCount = atlasTexture.imgCount[xyoffset + 1]; + const int leftCount = atlasTexture.imgCount[xyoffset - 1]; if(rightCount > 0) { - atlasTexture.img[xyoffset] = atlasTexture.img[xyoffset-1]; + atlasTexture.img[xyoffset] = atlasTexture.img[xyoffset + 1]; atlasTexture.imgCount[xyoffset] = - 1; } - if(rightCount < 0 && rightCount > -texParams.padding && rightCount < leftCount) + else if(downCount > 0) { - atlasTexture.img[xyoffset] = atlasTexture.img[xyoffset-1]; - atlasTexture.imgCount[xyoffset] = rightCount - 1; + atlasTexture.img[xyoffset] = atlasTexture.img[xyoffset + outTextureSide]; + atlasTexture.imgCount[xyoffset] = - 1; } - if(downCount > 0) + else if ((rightCount < 0 && - rightCount < padding) && + (leftCount == 0 || rightCount > leftCount) && + (downCount == 0 || rightCount >= downCount) + ) { - atlasTexture.img[xyoffset] = atlasTexture.img[xyoffset-1]; - atlasTexture.imgCount[xyoffset] = - 1; + atlasTexture.img[xyoffset] = atlasTexture.img[xyoffset + 1]; + atlasTexture.imgCount[xyoffset] = rightCount - 1; } - if(downCount < 0 && downCount > -texParams.padding && downCount < upCount) + else if ((downCount < 0 && - downCount < padding) && + (upCount == 0 || downCount > upCount) + ) { - atlasTexture.img[xyoffset] = atlasTexture.img[xyoffset-1]; + atlasTexture.img[xyoffset] = atlasTexture.img[xyoffset + outTextureSide]; atlasTexture.imgCount[xyoffset] = downCount - 1; } } } + /* const std::string textureName2 = "texture_" + std::to_string(1001 + atlasID) + "finalPadd." + EImageFileType_enumToString(textureFileType); // starts at '1001' for UDIM compatibility + bfs::path texturePath2 = outPath / textureName2; + imageIO::writeImage(texturePath2.string(), outTextureSide, outTextureSide, atlasTexture.img);*/ } - const std::string textureName = "texture_" + std::to_string(1001 + atlasID) + (level < 0 ? "" : "_" + std::to_string(level)) + "." + EImageFileType_enumToString(textureFileType); // starts at '1001' for UDIM compatibility - bfs::path texturePath = outPath / textureName; - ALICEVISION_LOG_INFO(" - Writing texture file: " << texturePath.string()); - // texture holes filling if(texParams.fillHoles) { @@ -754,8 +766,11 @@ void Texturing::writeTexture(AccuImage& atlasTexture, const std::size_t atlasID, std::swap(resizedColorBuffer, atlasTexture.img); } - imageIO::writeImage(texturePath.string(), outTextureSide, outTextureSide, atlasTexture.img); + const std::string textureName = "texture_" + std::to_string(1001 + atlasID) + (level < 0 ? "" : "_" + std::to_string(level)) + "." + EImageFileType_enumToString(textureFileType); // starts at '1001' for UDIM compatibility + bfs::path texturePath = outPath / textureName; + ALICEVISION_LOG_INFO(" - Writing texture file: " << texturePath.string()); + imageIO::writeImage(texturePath.string(), outTextureSide, outTextureSide, atlasTexture.img); } diff --git a/src/aliceVision/mesh/Texturing.hpp b/src/aliceVision/mesh/Texturing.hpp index 6c2834ce91..e2eae79636 100644 --- a/src/aliceVision/mesh/Texturing.hpp +++ b/src/aliceVision/mesh/Texturing.hpp @@ -73,7 +73,7 @@ struct TexturingParams EVisibilityRemappingMethod visibilityRemappingMethod = EVisibilityRemappingMethod::PullPush; unsigned int textureSide = 8192; - unsigned int padding = 15; + unsigned int padding = 5; unsigned int downscale = 2; bool fillHoles = false; bool useUDIM = true; From d2f29ddf31cc824b0a1eb9d70f26514bd8b60a3f Mon Sep 17 00:00:00 2001 From: Clara Date: Fri, 5 Apr 2019 12:15:13 +0200 Subject: [PATCH 19/51] [mvsData] Image: handle invalid pixels in color interpolation --- src/aliceVision/mvsData/Image.hpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/aliceVision/mvsData/Image.hpp b/src/aliceVision/mvsData/Image.hpp index 45d4f6badd..b21a625a38 100644 --- a/src/aliceVision/mvsData/Image.hpp +++ b/src/aliceVision/mvsData/Image.hpp @@ -119,15 +119,13 @@ class Image const int xp = static_cast(pix.x); const int yp = static_cast(pix.y); - if(xp == _width - 1 || yp == _height - 1 ) - return _img.at(yp * _width + xp); + if(xp >= _width - 1 || yp >= _height - 1 ) //invalid pixel for interpolation + return _img.at((_height-1) * _width + (_width-1)); // precision to 4 decimal places const float ui = pix.x - static_cast(xp); const float vi = pix.y - static_cast(yp); - - const Color lu = _img.at( yp * _width + xp ); const Color ru = _img.at( yp * _width + (xp+1) ); const Color rd = _img.at( (yp+1) * _width + (xp+1) ); From 8e6b83066f4e7bbd79a7a9eb2cc05f0564a55ca6 Mon Sep 17 00:00:00 2001 From: Clara Date: Fri, 12 Apr 2019 11:17:41 +0200 Subject: [PATCH 20/51] [mesh] Texturing : error fix --- src/aliceVision/mesh/Texturing.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/aliceVision/mesh/Texturing.cpp b/src/aliceVision/mesh/Texturing.cpp index b6fec229fe..14110a55c1 100644 --- a/src/aliceVision/mesh/Texturing.cpp +++ b/src/aliceVision/mesh/Texturing.cpp @@ -694,10 +694,10 @@ void Texturing::writeTexture(AccuImage& atlasTexture, const std::size_t atlasID, //bottom-right to up-left for(unsigned int y = 1; y < outTextureSide-1; ++y) { - unsigned int yoffset = (outTextureSide - y) * outTextureSide; + unsigned int yoffset = (outTextureSide - 1 - y) * outTextureSide; for(unsigned int x = 1; x < outTextureSide-1; ++x) { - unsigned int xyoffset = yoffset + (outTextureSide - x); + unsigned int xyoffset = yoffset + (outTextureSide - 1 - x); if(atlasTexture.imgCount[xyoffset] > 0) continue; From 19dac6ff2f4ebb136fc43c6fc997904e4ed80682 Mon Sep 17 00:00:00 2001 From: Clara Date: Fri, 12 Apr 2019 15:06:32 +0200 Subject: [PATCH 21/51] [mesh] Texturing : final memory optimization --- src/aliceVision/mesh/Texturing.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/aliceVision/mesh/Texturing.cpp b/src/aliceVision/mesh/Texturing.cpp index 14110a55c1..04d1674e89 100644 --- a/src/aliceVision/mesh/Texturing.cpp +++ b/src/aliceVision/mesh/Texturing.cpp @@ -271,10 +271,12 @@ void Texturing::generateTextures(const mvsUtils::MultiViewParams &mp, const int freeMem = int(memInfo.freeRam / std::pow(2,20)); const int availableMem = freeMem - 2 * imageMaxMemSize * (texParams.nbBand + 1); // keep some memory for the input image buffer and its laplacian pyramid - int nbAtlasMax = std::floor(availableMem / pyramidMaxMemSize) - 1; //maximum number of textures in RAM + int nbAtlasMax = std::floor(availableMem / pyramidMaxMemSize); //maximum number of textures in RAM const int nbAtlas = _atlases.size(); nbAtlasMax = std::max(1, nbAtlasMax); //if not enough memory, do it one by one nbAtlasMax = std::min(nbAtlas, nbAtlasMax); //if enough memory, do it with all atlases + if (availableMem - nbAtlasMax*pyramidMaxMemSize < 2000) //keep margin in memory + nbAtlasMax -= 1; std::div_t divresult = div(nbAtlas, nbAtlasMax); const int nquot = divresult.quot; @@ -283,7 +285,7 @@ void Texturing::generateTextures(const mvsUtils::MultiViewParams &mp, ALICEVISION_LOG_INFO("Total amount of free memory : " << freeMem << " Mb."); ALICEVISION_LOG_INFO("Total amount of an image in memory : " << imageMaxMemSize << " Mb."); ALICEVISION_LOG_INFO("Total amount of memory available : " << availableMem << " Mb."); - ALICEVISION_LOG_INFO("Size of an atlas : " << atlasContribMemSize << " Mb."); + ALICEVISION_LOG_INFO("Size of an atlas : " << pyramidMaxMemSize << " Mb."); ALICEVISION_LOG_INFO("Processing " << nbAtlas << " atlases by chunks of " << nbAtlasMax); //generateTexture for the maximum number of atlases, and iterate From 770499e96e257004ceb08af541016d0e5a1d8acd Mon Sep 17 00:00:00 2001 From: Clara Date: Mon, 15 Apr 2019 11:35:52 +0200 Subject: [PATCH 22/51] [mesh] Texturing: taking triangles scores into account Triangles contribute to atlas textures according to their score (ie. the area of reprojection on the camera) -> less artefacts, especially on low frequencies band --- src/aliceVision/mesh/Texturing.cpp | 47 +++++++++++++++++------------- 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/src/aliceVision/mesh/Texturing.cpp b/src/aliceVision/mesh/Texturing.cpp index 04d1674e89..ce21b934e7 100644 --- a/src/aliceVision/mesh/Texturing.cpp +++ b/src/aliceVision/mesh/Texturing.cpp @@ -323,6 +323,9 @@ void Texturing::generateTexturesSubSet(const mvsUtils::MultiViewParams& mp, // We select the best cameras for each triangle and store it per camera for each output texture files. // List of triangle IDs (selected to contribute to the final texturing) per image. std::vector>> contributionsPerCamera(mp.ncams); + using AtlasIndex = size_t; + using ScoreTriangleId = std::vector>; //list of + std::vector>> contributionsPerCamera(mp.ncams); //for each atlasID, calculate contributionPerCamera for(const size_t atlasID : atlasIDs) @@ -419,35 +422,34 @@ void Texturing::generateTexturesSubSet(const mvsUtils::MultiViewParams& mp, std::sort(scorePerCamId.begin(), scorePerCamId.end(), std::greater()); const double minScore = texParams.bestScoreThreshold * std::get<1>(scorePerCamId.front()); // bestScoreThreshold * bestScore const bool bestIsPartial = (std::get<0>(scorePerCamId.front()) < 3); - int nbCumulatedVertices = 0; - int levelContrib = 0; - for(int i = 0; i < scorePerCamId.size() && levelContrib < nbBand; ++i) + int maxNbContrib = std::min(texParams.multiBandNbContrib.back(), static_cast(scorePerCamId.size())); + int nbCumulatedVertices = 0; + int level = 0; + for(int contrib = 0; nbCumulatedVertices < 3 * maxNbContrib && contrib < maxNbContrib; ++contrib) { - const int maxNbVerticesForFusion = texParams.multiBandNbContrib[levelContrib] * 3; - if (!bestIsPartial && i > 0) + nbCumulatedVertices += std::get<0>(scorePerCamId[contrib]); + if (!bestIsPartial && contrib != 0) { - nbCumulatedVertices += std::get<0>(scorePerCamId[i]); - if(maxNbVerticesForFusion != 0 && nbCumulatedVertices > maxNbVerticesForFusion) - { - ++levelContrib; - nbCumulatedVertices = 0; - continue; - } - if(std::get<1>(scorePerCamId[i]) < minScore) + if(std::get<1>(scorePerCamId[contrib]) < minScore) { // The best image fully see the triangle and has a much better score, so only rely on the first ones break; } } - //for the camera camId : add triangleID to the corresponding texture at the right level of frequency - const int camId = std::get<2>(scorePerCamId[i]); + //for the camera camId : add triangleID to the corresponding texture at the right level of frequencies + const int camId = std::get<2>(scorePerCamId[contrib]); + const int triangleScore = std::get<1>(scorePerCamId[contrib]); auto& camContribution = contributionsPerCamera[camId]; if(camContribution.find(atlasID) == camContribution.end()) camContribution[atlasID].resize(nbBand); - camContribution.at(atlasID)[levelContrib].push_back(triangleID); + camContribution.at(atlasID)[level].emplace_back(triangleID, triangleScore); + + if(contrib + 1 >= texParams.multiBandNbContrib[level]) + ++level; } + } } @@ -461,7 +463,7 @@ void Texturing::generateTexturesSubSet(const mvsUtils::MultiViewParams& mp, for(int camId = 0; camId < contributionsPerCamera.size(); ++camId) { - const std::map>& cameraContributions = contributionsPerCamera[camId]; + const std::map>& cameraContributions = contributionsPerCamera[camId]; if(cameraContributions.empty()) { @@ -487,16 +489,18 @@ void Texturing::generateTexturesSubSet(const mvsUtils::MultiViewParams& mp, for(const auto& c : cameraContributions) { AtlasIndex atlasID = c.first; + //for each level for(int level = 0; level < c.second.size(); ++level) { - const TrianglesId& trianglesId = c.second[level]; + const ScoreTriangleId& trianglesId = c.second[level]; ALICEVISION_LOG_INFO(" Texture file: " << atlasID << ", number of triangles: " << trianglesId.size() << "."); // for each triangle #pragma omp parallel for for(int ti = 0; ti < trianglesId.size(); ++ti) { - const unsigned int triangleId = trianglesId[ti]; + const unsigned int triangleId = std::get<0>(trianglesId[ti]); + const int triangleScore = std::get<1>(trianglesId[ti]); // retrieve triangle 3D and UV coordinates Point2d triPixs[3]; Point3d triPts[3]; @@ -571,8 +575,8 @@ void Texturing::generateTexturesSubSet(const mvsUtils::MultiViewParams& mp, AccuImage& accuImage = accuPyramid.pyramid[levelC]; // fill the accumulated color map for this pixel - accuImage.img[xyoffset] += pyramidL[levelC].getInterpolateColor(pixRC/downscaleCoef); - accuImage.imgCount[xyoffset] += 1; + accuImage.img[xyoffset] += pyramidL[levelC].getInterpolateColor(pixRC/downscaleCoef) * triangleScore; + accuImage.imgCount[xyoffset] += triangleScore; } } } @@ -602,6 +606,7 @@ void Texturing::generateTexturesSubSet(const mvsUtils::MultiViewParams& mp, continue; atlasTexture.img[xyoffset] /= atlasTexture.imgCount[xyoffset]; + atlasTexture.imgCount[xyoffset] = 1; for(std::size_t level = 1; level < accuPyramid.pyramid.size(); ++level) { From 000aba76355be1801510740e3bc45f4c4deef520 Mon Sep 17 00:00:00 2001 From: Clara Date: Fri, 26 Apr 2019 11:02:25 +0200 Subject: [PATCH 23/51] [utils] New main to test multi-band blending Create new file to test multi-band blending on one image --- src/software/utils/CMakeLists.txt | 11 ++ src/software/utils/main_multibandtest.cpp | 133 ++++++++++++++++++++++ 2 files changed, 144 insertions(+) create mode 100644 src/software/utils/main_multibandtest.cpp diff --git a/src/software/utils/CMakeLists.txt b/src/software/utils/CMakeLists.txt index 4abc0740c4..72221bb5cf 100644 --- a/src/software/utils/CMakeLists.txt +++ b/src/software/utils/CMakeLists.txt @@ -171,3 +171,14 @@ alicevision_add_software(aliceVision_utils_split360Images ${OPENIMAGEIO_LIBRARIES} ${Boost_LIBRARIES} ) + +# Multi-Band Blending +alicevision_add_software(aliceVision_utils_multibandtest + SOURCE main_multibandtest.cpp + FOLDER ${FOLDER_SOFTWARE_UTILS} + LINKS aliceVision_system + aliceVision_mesh + aliceVision_imageIO + aliceVision_mvsData + ${Boost_LIBRARIES} +) diff --git a/src/software/utils/main_multibandtest.cpp b/src/software/utils/main_multibandtest.cpp new file mode 100644 index 0000000000..384ff73f85 --- /dev/null +++ b/src/software/utils/main_multibandtest.cpp @@ -0,0 +1,133 @@ +// This file is part of the AliceVision project. +// Copyright (c) 2016 AliceVision contributors. +// This Source Code Form is subject to the terms of the Mozilla Public License, +// v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at https://mozilla.org/MPL/2.0/. + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +// These constants define the current software version. +// They must be updated when the command line is changed. +#define ALICEVISION_SOFTWARE_VERSION_MAJOR 1 +#define ALICEVISION_SOFTWARE_VERSION_MINOR 0 + +using namespace aliceVision; +using namespace aliceVision::mesh; + +namespace po = boost::program_options; + +int main(int argc, char **argv) +{ + // command-line parameters + + std::string verboseLevel = system::EVerboseLevel_enumToString(system::Logger::getDefaultVerboseLevel()); + std::string filesPath; + std::string inputImgPath; + unsigned int downscaleMBB = 2; + unsigned int nbBand = 3; + + po::options_description allParams("AliceVision multibandtest"); + + po::options_description requiredParams("Required parameters"); + requiredParams.add_options() + ("input,i", po::value(&inputImgPath)->required(), + "Input image path.") + ("output,o", po::value(&filesPath)->required(), + "Path to save files in."); + + po::options_description logParams("Log parameters"); + logParams.add_options() + ("verboseLevel,v", po::value(&verboseLevel)->default_value(verboseLevel), + "verbosity level (fatal, error, warning, info, debug, trace).") + ("nbBand", po::value(&nbBand)->default_value(nbBand), + "number of frequency bands") + ("downscaleMBB", po::value(&downscaleMBB)->default_value(downscaleMBB), + "size of frequency bands"); + + + allParams.add(requiredParams).add(logParams); + + po::variables_map vm; + try + { + po::store(po::parse_command_line(argc, argv, allParams), vm); + + if(vm.count("help") || (argc == 1)) + { + ALICEVISION_COUT(allParams); + return EXIT_SUCCESS; + } + po::notify(vm); + } + catch(boost::program_options::required_option& e) + { + ALICEVISION_CERR("ERROR: " << e.what()); + ALICEVISION_COUT("Usage:\n\n" << allParams); + return EXIT_FAILURE; + } + catch(boost::program_options::error& e) + { + ALICEVISION_CERR("ERROR: " << e.what()); + ALICEVISION_COUT("Usage:\n\n" << allParams); + return EXIT_FAILURE; + } + + ALICEVISION_COUT("Program called with the following parameters:"); + ALICEVISION_COUT(vm); + + // set verbose level + system::Logger::get()->setLogLevel(verboseLevel); + + ALICEVISION_LOG_INFO("Load input image."); + //Load input image + Image inImg; + int w, h; + imageIO::readImage(inputImgPath, w, h, inImg.data(), imageIO::EImageColorSpace::SRGB); + ALICEVISION_LOG_INFO("Input image loaded: " << inImg.data().size()); + ALICEVISION_LOG_INFO("Input image loaded: " << w*h); + inImg.resize(w, h); + ALICEVISION_LOG_INFO("Input image loaded: " << w << "x" << h); + + const std::string inImgFilename = filesPath + "inImg.exr"; + ALICEVISION_LOG_INFO("Write input image: " << inImgFilename); + imageIO::writeImage(inImgFilename, inImg.width(), inImg.height(), inImg.data(), imageIO::EImageQuality::LOSSLESS, imageIO::EImageColorSpace::AUTO); + + ALICEVISION_LOG_INFO("Compute MBB."); + //Calculate bands + MultiBandBlending multiBandBlending; + std::vector pyramidL; //laplacian pyramid + multiBandBlending.laplacianDownscalePyramid(pyramidL, inImg, nbBand, downscaleMBB); + + ALICEVISION_LOG_INFO("Write bands"); + //Write bands + reconstitution + Image outImg(inImg.width(), inImg.height()); + for(int b = 0; b < nbBand; ++b) + { + ALICEVISION_LOG_INFO("Writing band :" + std::to_string(b)); + int downscaleBand = std::pow(downscaleMBB, b); + const std::string bandFilename = filesPath + std::string("band") + std::to_string(b) + ".exr"; + imageIO::writeImage(bandFilename, pyramidL[b].width(), pyramidL[b].height(), pyramidL[b].data(), imageIO::EImageQuality::LOSSLESS, imageIO::EImageColorSpace::AUTO); + for(int i = 0; i < inImg.width() * inImg.height(); ++i) + { + Point2d pix(i%inImg.width(), static_cast(i/inImg.width())); + Point2d pixd = pix/downscaleBand; + + outImg[i] += pyramidL[b].getInterpolateColor(pixd); + } + } + const std::string outImgFilename = filesPath + "outImg.exr"; + ALICEVISION_LOG_INFO("Write output image: " << outImgFilename); + imageIO::writeImage(outImgFilename, outImg.width(), outImg.height(), outImg.data(), imageIO::EImageQuality::LOSSLESS, imageIO::EImageColorSpace::AUTO); + + return EXIT_SUCCESS; +} From 22096c7df21a2393fa67dc670edde07b29fc125e Mon Sep 17 00:00:00 2001 From: Clara Date: Fri, 26 Apr 2019 11:22:42 +0200 Subject: [PATCH 24/51] [mvsUtils] load images from cache in a chosen colorspace Add colorspace attribute to ImagesCache and read images in that colorspace (LINEAR for depthMap, SRGB for texturing) --- src/aliceVision/depthMap/RefineRc.cpp | 4 ++-- src/aliceVision/mesh/Texturing.cpp | 1 + src/aliceVision/mvsUtils/ImagesCache.cpp | 8 +++++--- src/aliceVision/mvsUtils/ImagesCache.hpp | 6 ++++-- src/aliceVision/mvsUtils/fileIO.cpp | 4 ++-- src/aliceVision/mvsUtils/fileIO.hpp | 3 ++- 6 files changed, 16 insertions(+), 10 deletions(-) diff --git a/src/aliceVision/depthMap/RefineRc.cpp b/src/aliceVision/depthMap/RefineRc.cpp index e687f79b3a..4f8fb8eec1 100644 --- a/src/aliceVision/depthMap/RefineRc.cpp +++ b/src/aliceVision/depthMap/RefineRc.cpp @@ -355,7 +355,7 @@ void estimateAndRefineDepthMaps(int cudaDeviceNo, mvsUtils::MultiViewParams* mp, const int bandType = 0; // load images from files into RAM - mvsUtils::ImagesCache ic(mp, bandType); + mvsUtils::ImagesCache ic(mp, bandType, imageIO::EImageColorSpace::LINEAR); // load stuff on GPU memory and creates multi-level images and computes gradients PlaneSweepingCuda cps(cudaDeviceNo, ic, mp, sgmScale); // init plane sweeping parameters @@ -388,7 +388,7 @@ void computeNormalMaps(int CUDADeviceNo, mvsUtils::MultiViewParams* mp, const St const int bandType = 0; const int wsh = 3; - mvsUtils::ImagesCache ic(mp, bandType); + mvsUtils::ImagesCache ic(mp, bandType, imageIO::EImageColorSpace::LINEAR); PlaneSweepingCuda cps(CUDADeviceNo, ic, mp, 1); for(const int rc : cams) diff --git a/src/aliceVision/mesh/Texturing.cpp b/src/aliceVision/mesh/Texturing.cpp index ce21b934e7..3f98daa3bb 100644 --- a/src/aliceVision/mesh/Texturing.cpp +++ b/src/aliceVision/mesh/Texturing.cpp @@ -261,6 +261,7 @@ void Texturing::generateTextures(const mvsUtils::MultiViewParams &mp, } mvsUtils::ImagesCache imageCache(&mp, 0); + mvsUtils::ImagesCache imageCache(&mp, 0, imageIO::EImageColorSpace::SRGB); imageCache.setCacheSize(2); system::MemoryInfo memInfo = system::getMemoryInfo(); diff --git a/src/aliceVision/mvsUtils/ImagesCache.cpp b/src/aliceVision/mvsUtils/ImagesCache.cpp index 8a915091ac..bf403db33d 100644 --- a/src/aliceVision/mvsUtils/ImagesCache.cpp +++ b/src/aliceVision/mvsUtils/ImagesCache.cpp @@ -13,9 +13,10 @@ namespace aliceVision { namespace mvsUtils { -ImagesCache::ImagesCache(const MultiViewParams* _mp, int _bandType) +ImagesCache::ImagesCache(const MultiViewParams* _mp, int _bandType, imageIO::EImageColorSpace colorspace) : mp(_mp) , bandType( _bandType ) + , _colorspace(colorspace) { std::vector _imagesNames; for(int rc = 0; rc < _mp->getNbCameras(); rc++) @@ -25,9 +26,10 @@ ImagesCache::ImagesCache(const MultiViewParams* _mp, int _bandType) initIC( _imagesNames ); } -ImagesCache::ImagesCache(const MultiViewParams* _mp, int _bandType, std::vector& _imagesNames) +ImagesCache::ImagesCache(const MultiViewParams* _mp, int _bandType, imageIO::EImageColorSpace colorspace, std::vector& _imagesNames) : mp(_mp) , bandType( _bandType ) + , _colorspace(colorspace) { initIC( _imagesNames ); } @@ -97,7 +99,7 @@ void ImagesCache::refreshData(int camId) } const std::string imagePath = imagesNames.at(camId); - memcpyRGBImageFromFileToArr(camId, imgs[mapId]->data, imagePath, mp, bandType); + memcpyRGBImageFromFileToArr(camId, imgs[mapId]->data, imagePath, mp, bandType, _colorspace); imgs[mapId]->setWidth( mp->getWidth(camId) ); imgs[mapId]->setHeight( mp->getHeight(camId) ); diff --git a/src/aliceVision/mvsUtils/ImagesCache.hpp b/src/aliceVision/mvsUtils/ImagesCache.hpp index 9fffe78447..4c4fea4205 100644 --- a/src/aliceVision/mvsUtils/ImagesCache.hpp +++ b/src/aliceVision/mvsUtils/ImagesCache.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -76,10 +77,11 @@ class ImagesCache std::vector imagesNames; const int bandType; + const imageIO::EImageColorSpace _colorspace; public: - ImagesCache( const MultiViewParams* _mp, int _bandType); - ImagesCache( const MultiViewParams* _mp, int _bandType, std::vector& _imagesNames); + ImagesCache( const MultiViewParams* _mp, int _bandType, imageIO::EImageColorSpace colorspace); + ImagesCache( const MultiViewParams* _mp, int _bandType, imageIO::EImageColorSpace colorspace, std::vector& _imagesNames); void initIC( std::vector& _imagesNames ); void setCacheSize(int nbPreload); ~ImagesCache(); diff --git a/src/aliceVision/mvsUtils/fileIO.cpp b/src/aliceVision/mvsUtils/fileIO.cpp index 46d0f83348..868424414c 100644 --- a/src/aliceVision/mvsUtils/fileIO.cpp +++ b/src/aliceVision/mvsUtils/fileIO.cpp @@ -322,11 +322,11 @@ Matrix3x4 load3x4MatrixFromFile(FILE* fi) return m; } -void memcpyRGBImageFromFileToArr(int camId, Color* imgArr, const std::string& fileNameOrigStr, const MultiViewParams* mp, int bandType) +void memcpyRGBImageFromFileToArr(int camId, Color* imgArr, const std::string& fileNameOrigStr, const MultiViewParams* mp, int bandType, imageIO::EImageColorSpace colorspace) { int origWidth, origHeight; std::vector cimg; - imageIO::readImage(fileNameOrigStr, origWidth, origHeight, cimg, imageIO::EImageColorSpace::LINEAR); + imageIO::readImage(fileNameOrigStr, origWidth, origHeight, cimg, colorspace); // check image size if((mp->getOriginalWidth(camId) != origWidth) || (mp->getOriginalHeight(camId) != origHeight)) diff --git a/src/aliceVision/mvsUtils/fileIO.hpp b/src/aliceVision/mvsUtils/fileIO.hpp index 196d0b3bcc..7494dfe006 100644 --- a/src/aliceVision/mvsUtils/fileIO.hpp +++ b/src/aliceVision/mvsUtils/fileIO.hpp @@ -12,6 +12,7 @@ #include #include #include +#include #include @@ -26,7 +27,7 @@ std::string getFileNameFromIndex(const MultiViewParams* mp, int index, EFileType FILE* mv_openFile(const MultiViewParams* mp, int index, EFileType mv_file_type, const char* readWrite); Matrix3x4 load3x4MatrixFromFile(FILE* fi); void memcpyRGBImageFromFileToArr(int camId, Color* imgArr, const std::string& fileNameOrigStr, const MultiViewParams* mp, - int bandType); + int bandType, imageIO::EImageColorSpace colorspace); bool DeleteDirectory(const std::string& sPath); From 9a85cd00fd578268e93b5f642ad4a545dec880f5 Mon Sep 17 00:00:00 2001 From: Clara Date: Fri, 26 Apr 2019 11:36:16 +0200 Subject: [PATCH 25/51] [mesh] texturing: write textures from sRGB colorspace New colorspace type (AUTO_FROM_SRGB) -> calculate texture atlases in sRGB for multi-band blending accuracy --- src/aliceVision/imageIO/image.cpp | 29 +++++++++++++++++++++++------ src/aliceVision/imageIO/image.hpp | 3 +++ src/aliceVision/mesh/Texturing.cpp | 2 +- 3 files changed, 27 insertions(+), 7 deletions(-) diff --git a/src/aliceVision/imageIO/image.cpp b/src/aliceVision/imageIO/image.cpp index 2eb873655d..08bac7b1f5 100644 --- a/src/aliceVision/imageIO/image.cpp +++ b/src/aliceVision/imageIO/image.cpp @@ -19,12 +19,25 @@ #include #include +#include namespace fs = boost::filesystem; namespace aliceVision { namespace imageIO { +std::string EImageColorSpace_enumToString(const EImageColorSpace colorSpace) +{ + switch(colorSpace) + { + case EImageColorSpace::SRGB: return "sRGB"; // WARNING: string should match with OIIO definitions + case EImageColorSpace::LINEAR: return "Linear"; + default: ; + } + throw std::out_of_range("No string defined for EImageColorSpace: " + std::to_string(int(colorSpace))); +} + + std::string EImageQuality_informations() { return "Image quality :\n" @@ -238,7 +251,7 @@ void writeImage(const std::string& path, int nchannels, const std::vector& buffer, EImageQuality imageQuality, - EImageColorSpace imageColorSpace, + EImageColorSpace outputColorSpace, const oiio::ParamValueList& metadata) { const fs::path bPath = fs::path(path); @@ -249,13 +262,17 @@ void writeImage(const std::string& path, const bool isJPG = (extension == ".jpg"); const bool isPNG = (extension == ".png"); - if(imageColorSpace == EImageColorSpace::AUTO) + EImageColorSpace fromColorspace = EImageColorSpace::LINEAR; + EImageColorSpace toColorspace = EImageColorSpace::LINEAR; + if(outputColorSpace == EImageColorSpace::AUTO || outputColorSpace == EImageColorSpace::AUTO_FROM_SRGB) { if(isJPG || isPNG) - imageColorSpace = EImageColorSpace::SRGB; + toColorspace = EImageColorSpace::SRGB; else - imageColorSpace = EImageColorSpace::LINEAR; + toColorspace = EImageColorSpace::LINEAR; } + if(outputColorSpace == EImageColorSpace::AUTO_FROM_SRGB) + fromColorspace = EImageColorSpace::SRGB; ALICEVISION_LOG_DEBUG("[IO] Write Image: " << path << std::endl << "\t- width: " << width << std::endl @@ -273,9 +290,9 @@ void writeImage(const std::string& path, const oiio::ImageBuf* outBuf = &imgBuf; // buffer to write oiio::ImageBuf colorspaceBuf; // buffer for image colorspace modification - if(imageColorSpace == EImageColorSpace::SRGB) + if(fromColorspace != toColorspace) { - oiio::ImageBufAlgo::colorconvert(colorspaceBuf, *outBuf, "Linear", "sRGB"); + oiio::ImageBufAlgo::colorconvert(colorspaceBuf, *outBuf, EImageColorSpace_enumToString(fromColorspace), EImageColorSpace_enumToString(toColorspace)); outBuf = &colorspaceBuf; } diff --git a/src/aliceVision/imageIO/image.hpp b/src/aliceVision/imageIO/image.hpp index 83b8ba6ad0..d9c52c5cae 100644 --- a/src/aliceVision/imageIO/image.hpp +++ b/src/aliceVision/imageIO/image.hpp @@ -26,11 +26,14 @@ namespace imageIO { enum class EImageColorSpace { AUTO, + AUTO_FROM_SRGB, LINEAR, SRGB, NO_CONVERSION }; +std::string EImageColorSpace_enumToString(const EImageColorSpace colorSpace); + /** * @brief Available image qualities for pipeline output */ diff --git a/src/aliceVision/mesh/Texturing.cpp b/src/aliceVision/mesh/Texturing.cpp index 3f98daa3bb..77a689d5fe 100644 --- a/src/aliceVision/mesh/Texturing.cpp +++ b/src/aliceVision/mesh/Texturing.cpp @@ -778,7 +778,7 @@ void Texturing::writeTexture(AccuImage& atlasTexture, const std::size_t atlasID, bfs::path texturePath = outPath / textureName; ALICEVISION_LOG_INFO(" - Writing texture file: " << texturePath.string()); - imageIO::writeImage(texturePath.string(), outTextureSide, outTextureSide, atlasTexture.img, imageIO::EImageQuality::OPTIMIZED, imageIO::EImageColorSpace::AUTO); + imageIO::writeImage(texturePath.string(), outTextureSide, outTextureSide, atlasTexture.img, imageIO::EImageQuality::OPTIMIZED, imageIO::EImageColorSpace::AUTO_FROM_SRGB); } From c7929e889acfa5e7a332d918c5e2481990835581 Mon Sep 17 00:00:00 2001 From: Clara Date: Fri, 26 Apr 2019 11:38:00 +0200 Subject: [PATCH 26/51] [imageIO] image: fix error readImage kept to much space in memory --- src/aliceVision/imageIO/image.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/aliceVision/imageIO/image.cpp b/src/aliceVision/imageIO/image.cpp index 08bac7b1f5..cc2c7796b3 100644 --- a/src/aliceVision/imageIO/image.cpp +++ b/src/aliceVision/imageIO/image.cpp @@ -207,7 +207,7 @@ void readImage(const std::string& path, width = inSpec.width; height = inSpec.height; - buffer.resize(inSpec.width * inSpec.height * nchannels); + buffer.resize(inSpec.width * inSpec.height); { oiio::ROI exportROI = inBuf.roi(); From e841b644e480513f06fa92caad442bd5c076673e Mon Sep 17 00:00:00 2001 From: Clara Date: Fri, 26 Apr 2019 12:00:06 +0200 Subject: [PATCH 27/51] [mvsData] Image: fix color interpolation and resize functions --- src/aliceVision/mvsData/Image.hpp | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/src/aliceVision/mvsData/Image.hpp b/src/aliceVision/mvsData/Image.hpp index b21a625a38..d64a9e3975 100644 --- a/src/aliceVision/mvsData/Image.hpp +++ b/src/aliceVision/mvsData/Image.hpp @@ -11,6 +11,8 @@ #include #include +#include + namespace aliceVision { class Image @@ -55,8 +57,11 @@ class Image { _width = width; _height = height; - _img.resize(0); - _img.resize(_width*_height); + if(_width*_height != _img.size()) + { + _img.resize(0); // no need to copy image content + _img.resize(_width*_height); + } } void swap(Image& other) @@ -116,11 +121,8 @@ class Image Color getInterpolateColor(const Point2d& pix) const { - const int xp = static_cast(pix.x); - const int yp = static_cast(pix.y); - - if(xp >= _width - 1 || yp >= _height - 1 ) //invalid pixel for interpolation - return _img.at((_height-1) * _width + (_width-1)); + const int xp = std::min(static_cast(pix.x), _width-2); + const int yp = std::min(static_cast(pix.y), _height-2); // precision to 4 decimal places const float ui = pix.x - static_cast(xp); @@ -138,6 +140,13 @@ class Image return out; } + Color getNearestPixelColor(const Point2d& pix) const + { + const int xp = std::min(static_cast(pix.x), _width-1); + const int yp = std::min(static_cast(pix.y), _height-1); + const Color lu = _img.at( yp * _width + xp ); + return lu; + } }; From 70f7a754832f9e628717b1dba03f23f4a5518ba7 Mon Sep 17 00:00:00 2001 From: Clara Date: Fri, 26 Apr 2019 16:35:28 +0200 Subject: [PATCH 28/51] [mesh] texturing: taking triangles scores into account Bool to choose to use scores + scores in float type + simple rename --- src/aliceVision/mesh/Texturing.cpp | 15 +++++++-------- src/aliceVision/mesh/Texturing.hpp | 3 ++- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/aliceVision/mesh/Texturing.cpp b/src/aliceVision/mesh/Texturing.cpp index 77a689d5fe..7b52a6c881 100644 --- a/src/aliceVision/mesh/Texturing.cpp +++ b/src/aliceVision/mesh/Texturing.cpp @@ -317,16 +317,15 @@ void Texturing::generateTexturesSubSet(const mvsUtils::MultiViewParams& mp, unsigned int textureSize = texParams.textureSide * texParams.textureSide; int nbBand = texParams.nbBand; unsigned int multiBandDownScale = texParams.multiBandDownscale; + bool useScore = texParams.useScore; using AtlasIndex = size_t; - using TrianglesId = std::vector; // We select the best cameras for each triangle and store it per camera for each output texture files. - // List of triangle IDs (selected to contribute to the final texturing) per image. - std::vector>> contributionsPerCamera(mp.ncams); + // Triangles contributions are stored per frequency bands for multi-band blending. using AtlasIndex = size_t; - using ScoreTriangleId = std::vector>; //list of - std::vector>> contributionsPerCamera(mp.ncams); + using ScorePerTriangle = std::vector>; //list of + std::vector>> contributionsPerCamera(mp.ncams); //for each atlasID, calculate contributionPerCamera for(const size_t atlasID : atlasIDs) @@ -464,7 +463,7 @@ void Texturing::generateTexturesSubSet(const mvsUtils::MultiViewParams& mp, for(int camId = 0; camId < contributionsPerCamera.size(); ++camId) { - const std::map>& cameraContributions = contributionsPerCamera[camId]; + const std::map>& cameraContributions = contributionsPerCamera[camId]; if(cameraContributions.empty()) { @@ -493,7 +492,7 @@ void Texturing::generateTexturesSubSet(const mvsUtils::MultiViewParams& mp, //for each level for(int level = 0; level < c.second.size(); ++level) { - const ScoreTriangleId& trianglesId = c.second[level]; + const ScorePerTriangle& trianglesId = c.second[level]; ALICEVISION_LOG_INFO(" Texture file: " << atlasID << ", number of triangles: " << trianglesId.size() << "."); // for each triangle @@ -501,7 +500,7 @@ void Texturing::generateTexturesSubSet(const mvsUtils::MultiViewParams& mp, for(int ti = 0; ti < trianglesId.size(); ++ti) { const unsigned int triangleId = std::get<0>(trianglesId[ti]); - const int triangleScore = std::get<1>(trianglesId[ti]); + const float triangleScore = useScore ? std::get<1>(trianglesId[ti]) : 1.0f; // retrieve triangle 3D and UV coordinates Point2d triPixs[3]; Point3d triPts[3]; diff --git a/src/aliceVision/mesh/Texturing.hpp b/src/aliceVision/mesh/Texturing.hpp index e2eae79636..76b54f6094 100644 --- a/src/aliceVision/mesh/Texturing.hpp +++ b/src/aliceVision/mesh/Texturing.hpp @@ -64,6 +64,7 @@ EVisibilityRemappingMethod EVisibilityRemappingMethod_stringToEnum(const std::st struct TexturingParams { + bool useScore = true; unsigned int nbBand = 3; unsigned int multiBandDownscale = 2; std::vector multiBandNbContrib = {1, 5, 10}; // number of contributions per level for the multi-band blending @@ -151,7 +152,7 @@ struct Texturing struct AccuImage { std::vector img; - std::vector imgCount; + std::vector imgCount; void resize(std::size_t s) { From 3202d80892b1c80b34c6e7c42cf47f0da99d0a56 Mon Sep 17 00:00:00 2001 From: Clara Date: Fri, 26 Apr 2019 16:38:28 +0200 Subject: [PATCH 29/51] [mesh] Texturing: fix number of contributions for multi-band blending texParams.multibandNbContrib holds contributions to each frequency band, not the sum of contributions to all higher frequency bands as before --- src/aliceVision/mesh/Texturing.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/aliceVision/mesh/Texturing.cpp b/src/aliceVision/mesh/Texturing.cpp index 7b52a6c881..cf7475953c 100644 --- a/src/aliceVision/mesh/Texturing.cpp +++ b/src/aliceVision/mesh/Texturing.cpp @@ -423,10 +423,11 @@ void Texturing::generateTexturesSubSet(const mvsUtils::MultiViewParams& mp, const double minScore = texParams.bestScoreThreshold * std::get<1>(scorePerCamId.front()); // bestScoreThreshold * bestScore const bool bestIsPartial = (std::get<0>(scorePerCamId.front()) < 3); - int maxNbContrib = std::min(texParams.multiBandNbContrib.back(), static_cast(scorePerCamId.size())); + int nbContribMax = std::min(texParams.multiBandNbContrib.back(), static_cast(scorePerCamId.size())); int nbCumulatedVertices = 0; int level = 0; - for(int contrib = 0; nbCumulatedVertices < 3 * maxNbContrib && contrib < maxNbContrib; ++contrib) + int nbContribLevel = texParams.multiBandNbContrib[0]; + for(int contrib = 0; nbCumulatedVertices < 3 * nbContribMax && contrib < nbContribMax; ++contrib) { nbCumulatedVertices += std::get<0>(scorePerCamId[contrib]); if (!bestIsPartial && contrib != 0) @@ -446,10 +447,12 @@ void Texturing::generateTexturesSubSet(const mvsUtils::MultiViewParams& mp, camContribution[atlasID].resize(nbBand); camContribution.at(atlasID)[level].emplace_back(triangleID, triangleScore); - if(contrib + 1 >= texParams.multiBandNbContrib[level]) + if(contrib + 1 == nbContribLevel) + { ++level; + nbContribLevel += texParams.multiBandNbContrib[level]; + } } - } } From 788705acae62e71b5d837e418326809e720b9a17 Mon Sep 17 00:00:00 2001 From: Clara Date: Fri, 26 Apr 2019 16:41:26 +0200 Subject: [PATCH 30/51] [imageIO] add OutputFileColorSpace struct New struct - holds "from" and "to" colorspaces, used to write images (from sRGB to AUTO for texturing) --- src/aliceVision/imageIO/image.cpp | 38 +++++++++++++----------------- src/aliceVision/imageIO/image.hpp | 35 ++++++++++++++++++++++----- src/aliceVision/mesh/Texturing.cpp | 3 ++- 3 files changed, 48 insertions(+), 28 deletions(-) diff --git a/src/aliceVision/imageIO/image.cpp b/src/aliceVision/imageIO/image.cpp index cc2c7796b3..c6b47bbc70 100644 --- a/src/aliceVision/imageIO/image.cpp +++ b/src/aliceVision/imageIO/image.cpp @@ -251,7 +251,7 @@ void writeImage(const std::string& path, int nchannels, const std::vector& buffer, EImageQuality imageQuality, - EImageColorSpace outputColorSpace, + OutputFileColorSpace colorspace, const oiio::ParamValueList& metadata) { const fs::path bPath = fs::path(path); @@ -262,17 +262,13 @@ void writeImage(const std::string& path, const bool isJPG = (extension == ".jpg"); const bool isPNG = (extension == ".png"); - EImageColorSpace fromColorspace = EImageColorSpace::LINEAR; - EImageColorSpace toColorspace = EImageColorSpace::LINEAR; - if(outputColorSpace == EImageColorSpace::AUTO || outputColorSpace == EImageColorSpace::AUTO_FROM_SRGB) + if(colorspace.to == EImageColorSpace::AUTO) { if(isJPG || isPNG) - toColorspace = EImageColorSpace::SRGB; + colorspace.to = EImageColorSpace::SRGB; else - toColorspace = EImageColorSpace::LINEAR; + colorspace.to = EImageColorSpace::LINEAR; } - if(outputColorSpace == EImageColorSpace::AUTO_FROM_SRGB) - fromColorspace = EImageColorSpace::SRGB; ALICEVISION_LOG_DEBUG("[IO] Write Image: " << path << std::endl << "\t- width: " << width << std::endl @@ -290,14 +286,14 @@ void writeImage(const std::string& path, const oiio::ImageBuf* outBuf = &imgBuf; // buffer to write oiio::ImageBuf colorspaceBuf; // buffer for image colorspace modification - if(fromColorspace != toColorspace) + if(colorspace.from != colorspace.to) { - oiio::ImageBufAlgo::colorconvert(colorspaceBuf, *outBuf, EImageColorSpace_enumToString(fromColorspace), EImageColorSpace_enumToString(toColorspace)); + oiio::ImageBufAlgo::colorconvert(colorspaceBuf, *outBuf, EImageColorSpace_enumToString(colorspace.from), EImageColorSpace_enumToString(colorspace.to)); outBuf = &colorspaceBuf; } oiio::ImageBuf formatBuf; // buffer for image format modification - if(imageQuality ==EImageQuality::OPTIMIZED && isEXR) + if(imageQuality == EImageQuality::OPTIMIZED && isEXR) { formatBuf.copy(*outBuf, oiio::TypeDesc::HALF); // override format, use half instead of float outBuf = &formatBuf; @@ -311,29 +307,29 @@ void writeImage(const std::string& path, fs::rename(tmpPath, path); } -void writeImage(const std::string& path, int width, int height, const std::vector& buffer, EImageQuality imageQuality, EImageColorSpace imageColorSpace, const oiio::ParamValueList& metadata) +void writeImage(const std::string& path, int width, int height, const std::vector& buffer, EImageQuality imageQuality, OutputFileColorSpace colorspace, const oiio::ParamValueList& metadata) { - writeImage(path, oiio::TypeDesc::UCHAR, width, height, 1, buffer, imageQuality, imageColorSpace, metadata); + writeImage(path, oiio::TypeDesc::UCHAR, width, height, 1, buffer, imageQuality, colorspace, metadata); } -void writeImage(const std::string& path, int width, int height, const std::vector& buffer, EImageQuality imageQuality, EImageColorSpace imageColorSpace, const oiio::ParamValueList& metadata) +void writeImage(const std::string& path, int width, int height, const std::vector& buffer, EImageQuality imageQuality, OutputFileColorSpace colorspace, const oiio::ParamValueList& metadata) { - writeImage(path, oiio::TypeDesc::UINT16, width, height, 1, buffer, imageQuality, imageColorSpace, metadata); + writeImage(path, oiio::TypeDesc::UINT16, width, height, 1, buffer, imageQuality, colorspace, metadata); } -void writeImage(const std::string& path, int width, int height, const std::vector& buffer, EImageQuality imageQuality, EImageColorSpace imageColorSpace, const oiio::ParamValueList& metadata) +void writeImage(const std::string& path, int width, int height, const std::vector& buffer, EImageQuality imageQuality, OutputFileColorSpace colorspace, const oiio::ParamValueList& metadata) { - writeImage(path, oiio::TypeDesc::UCHAR, width, height, 3, buffer, imageQuality, imageColorSpace, metadata); + writeImage(path, oiio::TypeDesc::UCHAR, width, height, 3, buffer, imageQuality, colorspace, metadata); } -void writeImage(const std::string& path, int width, int height, const std::vector& buffer, EImageQuality imageQuality, EImageColorSpace imageColorSpace, const oiio::ParamValueList& metadata) +void writeImage(const std::string& path, int width, int height, const std::vector& buffer, EImageQuality imageQuality, OutputFileColorSpace colorspace, const oiio::ParamValueList& metadata) { - writeImage(path, oiio::TypeDesc::FLOAT, width, height, 1, buffer, imageQuality, imageColorSpace, metadata); + writeImage(path, oiio::TypeDesc::FLOAT, width, height, 1, buffer, imageQuality, colorspace, metadata); } -void writeImage(const std::string& path, int width, int height, const std::vector& buffer, EImageQuality imageQuality, EImageColorSpace imageColorSpace, const oiio::ParamValueList& metadata) +void writeImage(const std::string& path, int width, int height, const std::vector& buffer, EImageQuality imageQuality, OutputFileColorSpace colorspace, const oiio::ParamValueList& metadata) { - writeImage(path, oiio::TypeDesc::FLOAT, width, height, 3, buffer, imageQuality, imageColorSpace, metadata); + writeImage(path, oiio::TypeDesc::FLOAT, width, height, 3, buffer, imageQuality, colorspace, metadata); } template diff --git a/src/aliceVision/imageIO/image.hpp b/src/aliceVision/imageIO/image.hpp index d9c52c5cae..2b3c39fdc4 100644 --- a/src/aliceVision/imageIO/image.hpp +++ b/src/aliceVision/imageIO/image.hpp @@ -26,12 +26,35 @@ namespace imageIO { enum class EImageColorSpace { AUTO, - AUTO_FROM_SRGB, LINEAR, SRGB, NO_CONVERSION }; + +struct OutputFileColorSpace +{ + EImageColorSpace from = EImageColorSpace::LINEAR; + EImageColorSpace to = EImageColorSpace::AUTO; + + OutputFileColorSpace(EImageColorSpace from_, EImageColorSpace to_) + : from(from_) + , to(to_) + { + } + /// @brief Assumes that @p from is LINEAR + OutputFileColorSpace(EImageColorSpace to_) + { + if(to_ == EImageColorSpace::NO_CONVERSION) + to = from; + else + to = to_; + } + OutputFileColorSpace() + { + } +}; + std::string EImageColorSpace_enumToString(const EImageColorSpace colorSpace); /** @@ -123,11 +146,11 @@ void readImage(const std::string& path, int& width, int& height, std::vector& buffer, EImageQuality imageQuality, EImageColorSpace imageColorSpace, const oiio::ParamValueList& metadata = oiio::ParamValueList()); -void writeImage(const std::string& path, int width, int height, const std::vector& buffer, EImageQuality imageQuality, EImageColorSpace imageColorSpace, const oiio::ParamValueList& metadata = oiio::ParamValueList()); -void writeImage(const std::string& path, int width, int height, const std::vector& buffer, EImageQuality imageQuality, EImageColorSpace imageColorSpace, const oiio::ParamValueList& metadata = oiio::ParamValueList()); -void writeImage(const std::string& path, int width, int height, const std::vector& buffer, EImageQuality imageQuality, EImageColorSpace imageColorSpace, const oiio::ParamValueList& metadata = oiio::ParamValueList()); -void writeImage(const std::string& path, int width, int height, const std::vector& buffer, EImageQuality imageQuality, EImageColorSpace imageColorSpace, const oiio::ParamValueList& metadata = oiio::ParamValueList()); +void writeImage(const std::string& path, int width, int height, const std::vector& buffer, EImageQuality imageQuality, OutputFileColorSpace colorspace, const oiio::ParamValueList& metadata = oiio::ParamValueList()); +void writeImage(const std::string& path, int width, int height, const std::vector& buffer, EImageQuality imageQuality, OutputFileColorSpace colorspace, const oiio::ParamValueList& metadata = oiio::ParamValueList()); +void writeImage(const std::string& path, int width, int height, const std::vector& buffer, EImageQuality imageQuality, OutputFileColorSpace colorspace, const oiio::ParamValueList& metadata = oiio::ParamValueList()); +void writeImage(const std::string& path, int width, int height, const std::vector& buffer, EImageQuality imageQuality, OutputFileColorSpace colorspace, const oiio::ParamValueList& metadata = oiio::ParamValueList()); +void writeImage(const std::string& path, int width, int height, const std::vector& buffer, EImageQuality imageQuality, OutputFileColorSpace colorspace, const oiio::ParamValueList& metadata = oiio::ParamValueList()); /** * @brief transpose a given image buffer diff --git a/src/aliceVision/mesh/Texturing.cpp b/src/aliceVision/mesh/Texturing.cpp index cf7475953c..ddec54065e 100644 --- a/src/aliceVision/mesh/Texturing.cpp +++ b/src/aliceVision/mesh/Texturing.cpp @@ -780,7 +780,8 @@ void Texturing::writeTexture(AccuImage& atlasTexture, const std::size_t atlasID, bfs::path texturePath = outPath / textureName; ALICEVISION_LOG_INFO(" - Writing texture file: " << texturePath.string()); - imageIO::writeImage(texturePath.string(), outTextureSide, outTextureSide, atlasTexture.img, imageIO::EImageQuality::OPTIMIZED, imageIO::EImageColorSpace::AUTO_FROM_SRGB); + using namespace imageIO; + imageIO::writeImage(texturePath.string(), outTextureSide, outTextureSide, atlasTexture.img, EImageQuality::OPTIMIZED, OutputFileColorSpace(EImageColorSpace::SRGB, EImageColorSpace::AUTO)); } From 3b0681ee68b29e4a0cdc50c38727b58e3296ac17 Mon Sep 17 00:00:00 2001 From: Clara Date: Fri, 26 Apr 2019 17:24:55 +0200 Subject: [PATCH 31/51] [mesh] texturing: debug mode debug mode to write the number of contributions in frequency bands of each atlas + some code cleaning --- src/aliceVision/mesh/Texturing.cpp | 38 +++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/src/aliceVision/mesh/Texturing.cpp b/src/aliceVision/mesh/Texturing.cpp index ddec54065e..ed693b156c 100644 --- a/src/aliceVision/mesh/Texturing.cpp +++ b/src/aliceVision/mesh/Texturing.cpp @@ -260,7 +260,6 @@ void Texturing::generateTextures(const mvsUtils::MultiViewParams &mp, ALICEVISION_LOG_INFO(c << ", "); } - mvsUtils::ImagesCache imageCache(&mp, 0); mvsUtils::ImagesCache imageCache(&mp, 0, imageIO::EImageColorSpace::SRGB); imageCache.setCacheSize(2); system::MemoryInfo memInfo = system::getMemoryInfo(); @@ -596,6 +595,33 @@ void Texturing::generateTexturesSubSet(const mvsUtils::MultiViewParams& mp, AccuImage& atlasTexture = accuPyramid.pyramid[0]; ALICEVISION_LOG_INFO("Create texture " << atlasID); + bool debug = 1; + + if(debug) + { + for(std::size_t level = 0; level < accuPyramid.pyramid.size(); ++level) + { + AccuImage& atlasLevelTexture = accuPyramid.pyramid[level]; + + //write the number of contributions for each texture + std::vector imgContrib(texParams.textureSide*texParams.textureSide); + + for(unsigned int yp = 0; yp < texParams.textureSide; ++yp) + { + unsigned int yoffset = yp * texParams.textureSide; + for(unsigned int xp = 0; xp < texParams.textureSide; ++xp) + { + unsigned int xyoffset = yoffset + xp; + imgContrib[xyoffset] = atlasLevelTexture.imgCount[xyoffset]; + } + } + + const std::string textureName = "contrib_" + std::to_string(1001 + atlasID) + std::string("_") + std::to_string(level) + std::string(".") + EImageFileType_enumToString(textureFileType); // starts at '1001' for UDIM compatibility + bfs::path texturePath = outPath / textureName; + imageIO::writeImage(texturePath.string(), texParams.textureSide, texParams.textureSide, imgContrib, imageIO::EImageQuality::OPTIMIZED, imageIO::EImageColorSpace::AUTO); + } + } + ALICEVISION_LOG_INFO(" - Computing final (average) color."); for(unsigned int yp = 0; yp < texParams.textureSide; ++yp) { @@ -619,14 +645,15 @@ void Texturing::generateTexturesSubSet(const mvsUtils::MultiViewParams& mp, } } - bool debug = 1; if(debug) { + //write each frequency band, for each texture for(std::size_t level = 0; level < accuPyramid.pyramid.size(); ++level) { AccuImage& atlasLevelTexture = accuPyramid.pyramid[level]; writeTexture(atlasLevelTexture, atlasID, outPath, textureFileType, level); } + } // Fuse frequency bands into the first buffer @@ -698,9 +725,7 @@ void Texturing::writeTexture(AccuImage& atlasTexture, const std::size_t atlasID, } } } - /* const std::string textureName1 = "texture_" + std::to_string(1001 + atlasID) + "Interpadd." + EImageFileType_enumToString(textureFileType); // starts at '1001' for UDIM compatibility - bfs::path texturePath1 = outPath / textureName1; - imageIO::writeImage(texturePath1.string(), outTextureSide, outTextureSide, atlasTexture.img);*/ + //bottom-right to up-left for(unsigned int y = 1; y < outTextureSide-1; ++y) { @@ -742,9 +767,6 @@ void Texturing::writeTexture(AccuImage& atlasTexture, const std::size_t atlasID, } } } - /* const std::string textureName2 = "texture_" + std::to_string(1001 + atlasID) + "finalPadd." + EImageFileType_enumToString(textureFileType); // starts at '1001' for UDIM compatibility - bfs::path texturePath2 = outPath / textureName2; - imageIO::writeImage(texturePath2.string(), outTextureSide, outTextureSide, atlasTexture.img);*/ } // texture holes filling From d3c8ca7f81fa66650687080a2658c1d431a14fdb Mon Sep 17 00:00:00 2001 From: Clara Date: Fri, 26 Apr 2019 17:30:54 +0200 Subject: [PATCH 32/51] [mesh] texturing: fix padding adapt padding when using triangle scores --- src/aliceVision/mesh/Texturing.cpp | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/aliceVision/mesh/Texturing.cpp b/src/aliceVision/mesh/Texturing.cpp index ed693b156c..9f8ace81a0 100644 --- a/src/aliceVision/mesh/Texturing.cpp +++ b/src/aliceVision/mesh/Texturing.cpp @@ -685,9 +685,17 @@ void Texturing::writeTexture(AccuImage& atlasTexture, const std::size_t atlasID, const unsigned int padding = texParams.padding * 3; ALICEVISION_LOG_INFO(" - Edge padding (" << padding << " pixels)."); - /* const std::string textureName0 = "texture_" + std::to_string(1001 + atlasID) + "prePadd." + EImageFileType_enumToString(textureFileType); // starts at '1001' for UDIM compatibility - bfs::path texturePath0 = outPath / textureName0; - imageIO::writeImage(texturePath0.string(), outTextureSide, outTextureSide, atlasTexture.img);*/ + // Init valid values to 1 + for(unsigned int y = 0; y < outTextureSide; ++y) + { + unsigned int yoffset = y * outTextureSide; + for(unsigned int x = 0; x < outTextureSide; ++x) + { + unsigned int xyoffset = yoffset + x; + if(atlasTexture.imgCount[xyoffset] > 0) + atlasTexture.imgCount[xyoffset] = 1; + } + } //up-left to bottom-right for(unsigned int y = 1; y < outTextureSide-1; ++y) @@ -780,7 +788,7 @@ void Texturing::writeTexture(AccuImage& atlasTexture, const std::size_t atlasID, for(unsigned int xp = 0; xp < texParams.textureSide; ++xp) { unsigned int xyoffset = yoffset + xp; - alphaBuffer[xyoffset] = atlasTexture.imgCount[xyoffset] ? 1.0f : 0.0f; + alphaBuffer[xyoffset] = atlasTexture.imgCount[xyoffset] ? 1 : 0; } } imageIO::fillHoles(texParams.textureSide, texParams.textureSide, atlasTexture.img, alphaBuffer); From 69998ac8b226620c7c6e905bd98b0711464cd646 Mon Sep 17 00:00:00 2001 From: Clara Date: Fri, 26 Apr 2019 18:16:51 +0200 Subject: [PATCH 33/51] [mesh] texturing: change memory estimation --- src/aliceVision/mesh/Texturing.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/aliceVision/mesh/Texturing.cpp b/src/aliceVision/mesh/Texturing.cpp index 9f8ace81a0..d13b29b52c 100644 --- a/src/aliceVision/mesh/Texturing.cpp +++ b/src/aliceVision/mesh/Texturing.cpp @@ -270,12 +270,12 @@ void Texturing::generateTextures(const mvsUtils::MultiViewParams &mp, const std::size_t pyramidMaxMemSize = texParams.nbBand * atlasContribMemSize; const int freeMem = int(memInfo.freeRam / std::pow(2,20)); - const int availableMem = freeMem - 2 * imageMaxMemSize * (texParams.nbBand + 1); // keep some memory for the input image buffer and its laplacian pyramid - int nbAtlasMax = std::floor(availableMem / pyramidMaxMemSize); //maximum number of textures in RAM + const int availableMem = freeMem - 2 * imageMaxMemSize - imageMaxMemSize * texParams.nbBand; // keep some memory for the input image buffer and its laplacian pyramid + int nbAtlasMax = std::floor((availableMem - 1000) / pyramidMaxMemSize); //maximum number of textures in RAM const int nbAtlas = _atlases.size(); nbAtlasMax = std::max(1, nbAtlasMax); //if not enough memory, do it one by one nbAtlasMax = std::min(nbAtlas, nbAtlasMax); //if enough memory, do it with all atlases - if (availableMem - nbAtlasMax*pyramidMaxMemSize < 2000) //keep margin in memory + if (availableMem - nbAtlasMax*pyramidMaxMemSize < 1000 && nbAtlasMax > 1) //keep margin in memory nbAtlasMax -= 1; std::div_t divresult = div(nbAtlas, nbAtlasMax); @@ -283,9 +283,9 @@ void Texturing::generateTextures(const mvsUtils::MultiViewParams &mp, const int nrem = divresult.rem; ALICEVISION_LOG_INFO("Total amount of free memory : " << freeMem << " Mb."); - ALICEVISION_LOG_INFO("Total amount of an image in memory : " << imageMaxMemSize << " Mb."); ALICEVISION_LOG_INFO("Total amount of memory available : " << availableMem << " Mb."); - ALICEVISION_LOG_INFO("Size of an atlas : " << pyramidMaxMemSize << " Mb."); + ALICEVISION_LOG_INFO("Total amount of an image in memory : " << imageMaxMemSize << " Mb."); + ALICEVISION_LOG_INFO("Total amount of an atlas in memory: " << pyramidMaxMemSize << " Mb."); ALICEVISION_LOG_INFO("Processing " << nbAtlas << " atlases by chunks of " << nbAtlasMax); //generateTexture for the maximum number of atlases, and iterate From 94546a22708d487ccd7b36210f8c29b983a9908f Mon Sep 17 00:00:00 2001 From: Clara Date: Fri, 26 Apr 2019 18:23:37 +0200 Subject: [PATCH 34/51] [mesh] texturing: add comments --- src/aliceVision/mesh/Texturing.cpp | 45 ++++++++++++++---------------- 1 file changed, 21 insertions(+), 24 deletions(-) diff --git a/src/aliceVision/mesh/Texturing.cpp b/src/aliceVision/mesh/Texturing.cpp index d13b29b52c..77c4ecd91a 100644 --- a/src/aliceVision/mesh/Texturing.cpp +++ b/src/aliceVision/mesh/Texturing.cpp @@ -318,8 +318,6 @@ void Texturing::generateTexturesSubSet(const mvsUtils::MultiViewParams& mp, unsigned int multiBandDownScale = texParams.multiBandDownscale; bool useScore = texParams.useScore; - using AtlasIndex = size_t; - // We select the best cameras for each triangle and store it per camera for each output texture files. // Triangles contributions are stored per frequency bands for multi-band blending. using AtlasIndex = size_t; @@ -381,8 +379,8 @@ void Texturing::generateTexturesSubSet(const mvsUtils::MultiViewParams& mp, triangleNormal = me->computeTriangleNormal(triangleID); triangleCenter = me->computeTriangleCenterOfGravity(triangleID); } - using ScoreCamId = std::tuple; - std::vector scorePerCamId; // + using ScoreCamId = std::tuple; // + std::vector scorePerCamId; for (const auto& itCamVis: selectedTriCams) { const int camId = itCamVis.first; @@ -424,7 +422,7 @@ void Texturing::generateTexturesSubSet(const mvsUtils::MultiViewParams& mp, int nbContribMax = std::min(texParams.multiBandNbContrib.back(), static_cast(scorePerCamId.size())); int nbCumulatedVertices = 0; - int level = 0; + int band = 0; int nbContribLevel = texParams.multiBandNbContrib[0]; for(int contrib = 0; nbCumulatedVertices < 3 * nbContribMax && contrib < nbContribMax; ++contrib) { @@ -438,18 +436,18 @@ void Texturing::generateTexturesSubSet(const mvsUtils::MultiViewParams& mp, } } - //for the camera camId : add triangleID to the corresponding texture at the right level of frequencies + //for the camera camId : add triangle score to the corresponding texture, at the right frequency band const int camId = std::get<2>(scorePerCamId[contrib]); const int triangleScore = std::get<1>(scorePerCamId[contrib]); auto& camContribution = contributionsPerCamera[camId]; if(camContribution.find(atlasID) == camContribution.end()) camContribution[atlasID].resize(nbBand); - camContribution.at(atlasID)[level].emplace_back(triangleID, triangleScore); + camContribution.at(atlasID)[band].emplace_back(triangleID, triangleScore); if(contrib + 1 == nbContribLevel) { - ++level; - nbContribLevel += texParams.multiBandNbContrib[level]; + ++band; + nbContribLevel += texParams.multiBandNbContrib[band]; } } } @@ -457,12 +455,12 @@ void Texturing::generateTexturesSubSet(const mvsUtils::MultiViewParams& mp, ALICEVISION_LOG_INFO("Reading pixel color."); + //pyramid of atlases frequency bands std::map accuPyramids; for(std::size_t atlasID: atlasIDs) accuPyramids[atlasID].init(nbBand, textureSize); //for each camera, for each texture, iterate over triangles and fill the accuPyramids map - for(int camId = 0; camId < contributionsPerCamera.size(); ++camId) { const std::map>& cameraContributions = contributionsPerCamera[camId]; @@ -491,11 +489,11 @@ void Texturing::generateTexturesSubSet(const mvsUtils::MultiViewParams& mp, for(const auto& c : cameraContributions) { AtlasIndex atlasID = c.first; - //for each level - for(int level = 0; level < c.second.size(); ++level) + //for each frequency band + for(int band = 0; band < c.second.size(); ++band) { - const ScorePerTriangle& trianglesId = c.second[level]; - ALICEVISION_LOG_INFO(" Texture file: " << atlasID << ", number of triangles: " << trianglesId.size() << "."); + const ScorePerTriangle& trianglesId = c.second[band]; + ALICEVISION_LOG_INFO(" Texture file: " << atlasID << "- band: " << band << "- number of triangles: " << trianglesId.size() << "."); // for each triangle #pragma omp parallel for @@ -563,21 +561,20 @@ void Texturing::generateTexturesSubSet(const mvsUtils::MultiViewParams& mp, if(!mp.isPixelInImage(pixRC, camId)) continue; - // If the color is pure zero, we consider it as an invalid pixel. - // After correction of radial distortion, some pixels are invalid. - // TODO: use an alpha channel instead. - if(pyramidL.back().getInterpolateColor(pixRC/std::pow(multiBandDownScale,nbBand-1)) == Color(0.f, 0.f, 0.f)) + // If the color is pure zero (ie. no contributions), we consider it as an invalid pixel. + if(camImg.getInterpolateColor(pixRC) == Color(0.f, 0.f, 0.f)) continue; - //each level also contributes to lower frequencies levels + // Fill the accumulated pyramid for this pixel + // each frequency band also contributes to lower frequencies (higher band indexes) AccuPyramid& accuPyramid = accuPyramids.at(atlasID); - for(std::size_t levelC = level; levelC < pyramidL.size(); ++levelC) + for(std::size_t bandContrib = band; bandContrib < pyramidL.size(); ++bandContrib) { - int downscaleCoef = std::pow(multiBandDownScale, levelC); - AccuImage& accuImage = accuPyramid.pyramid[levelC]; + int downscaleCoef = std::pow(multiBandDownScale, bandContrib); + AccuImage& accuImage = accuPyramid.pyramid[bandContrib]; // fill the accumulated color map for this pixel - accuImage.img[xyoffset] += pyramidL[levelC].getInterpolateColor(pixRC/downscaleCoef) * triangleScore; + accuImage.img[xyoffset] += pyramidL[bandContrib].getInterpolateColor(pixRC/downscaleCoef) * triangleScore; accuImage.imgCount[xyoffset] += triangleScore; } } @@ -656,7 +653,7 @@ void Texturing::generateTexturesSubSet(const mvsUtils::MultiViewParams& mp, } - // Fuse frequency bands into the first buffer + // Fuse frequency bands into the first buffer, calculate final texture for(unsigned int yp = 0; yp < texParams.textureSide; ++yp) { unsigned int yoffset = yp * texParams.textureSide; From 4d126bfc16919b4ed8af6b2f8184205963bcfcaf Mon Sep 17 00:00:00 2001 From: Yann Lanthony Date: Mon, 13 May 2019 15:55:27 +0200 Subject: [PATCH 35/51] [samples] Move main_multibandtest to samples Delete main_multibandtest from [utils] and add it to [samples] --- src/samples/CMakeLists.txt | 1 + src/samples/multiBandBlending/CMakeLists.txt | 10 ++++++++++ .../multiBandBlending}/main_multibandtest.cpp | 0 src/software/utils/CMakeLists.txt | 11 +---------- 4 files changed, 12 insertions(+), 10 deletions(-) create mode 100644 src/samples/multiBandBlending/CMakeLists.txt rename src/{software/utils => samples/multiBandBlending}/main_multibandtest.cpp (100%) diff --git a/src/samples/CMakeLists.txt b/src/samples/CMakeLists.txt index f1b03d69c6..f946a455e7 100644 --- a/src/samples/CMakeLists.txt +++ b/src/samples/CMakeLists.txt @@ -10,6 +10,7 @@ add_subdirectory(featuresRepeatability) # add_subdirectory(imageData) add_subdirectory(imageDescriberMatches) add_subdirectory(kvldFilter) +add_subdirectory(multiBandBlending) add_subdirectory(robustEssential) add_subdirectory(robustEssentialBA) add_subdirectory(robustEssentialSpherical) diff --git a/src/samples/multiBandBlending/CMakeLists.txt b/src/samples/multiBandBlending/CMakeLists.txt new file mode 100644 index 0000000000..e9c098187e --- /dev/null +++ b/src/samples/multiBandBlending/CMakeLists.txt @@ -0,0 +1,10 @@ +# Multi-Band Blending +alicevision_add_software(aliceVision_samples_multibandtest + SOURCE main_multibandtest.cpp + FOLDER ${FOLDER_SAMPLES} + LINKS aliceVision_system + aliceVision_mesh + aliceVision_imageIO + aliceVision_mvsData + ${Boost_LIBRARIES} +) \ No newline at end of file diff --git a/src/software/utils/main_multibandtest.cpp b/src/samples/multiBandBlending/main_multibandtest.cpp similarity index 100% rename from src/software/utils/main_multibandtest.cpp rename to src/samples/multiBandBlending/main_multibandtest.cpp diff --git a/src/software/utils/CMakeLists.txt b/src/software/utils/CMakeLists.txt index 72221bb5cf..03eab4d2df 100644 --- a/src/software/utils/CMakeLists.txt +++ b/src/software/utils/CMakeLists.txt @@ -172,13 +172,4 @@ alicevision_add_software(aliceVision_utils_split360Images ${Boost_LIBRARIES} ) -# Multi-Band Blending -alicevision_add_software(aliceVision_utils_multibandtest - SOURCE main_multibandtest.cpp - FOLDER ${FOLDER_SOFTWARE_UTILS} - LINKS aliceVision_system - aliceVision_mesh - aliceVision_imageIO - aliceVision_mvsData - ${Boost_LIBRARIES} -) + From 29c9e1d8cfff56fbf0db861fa87a538d3149b293 Mon Sep 17 00:00:00 2001 From: Clara Date: Tue, 7 May 2019 11:12:48 +0200 Subject: [PATCH 36/51] [mesh] Texturing: set parameters for multi-band blending Set parameters : useScore and multiBandNbContrib --- src/software/pipeline/main_texturing.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/software/pipeline/main_texturing.cpp b/src/software/pipeline/main_texturing.cpp index 01d4b9b77c..c527c168d7 100644 --- a/src/software/pipeline/main_texturing.cpp +++ b/src/software/pipeline/main_texturing.cpp @@ -85,10 +85,12 @@ int main(int argc, char* argv[]) "Texture edge padding size in pixel") ("flipNormals", po::value(&flipNormals)->default_value(flipNormals), "Option to flip face normals. It can be needed as it depends on the vertices order in triangles and the convention change from one software to another.") + ("useScore", po::value(&texParams.useScore)->default_value(texParams.useScore), + "Use triangles scores for multiband blending.") ("multiBandDownscale", po::value(&texParams.multiBandDownscale)->default_value(texParams.multiBandDownscale), "Width of frequency bands.") - ("nbBand", po::value(&texParams.nbBand)->default_value(texParams.nbBand), - "Number of bands to combine for multi-band blending.") + ("multiBandNbContrib", po::value>(&texParams.multiBandNbContrib)->default_value(texParams.multiBandNbContrib), + "Number of contributions per frequency band.") ("bestScoreThreshold", po::value(&texParams.bestScoreThreshold)->default_value(texParams.bestScoreThreshold), "(0.0 to disable filtering based on threshold to relative best score).") ("angleHardThreshold", po::value(&texParams.angleHardThreshold)->default_value(texParams.angleHardThreshold), From da08a26ce707a0192c8f488151aba3d8db0f30bb Mon Sep 17 00:00:00 2001 From: Clara Date: Mon, 13 May 2019 11:44:44 +0200 Subject: [PATCH 37/51] [mesh] Texturing: add multiBandNbContrib param Add a parameter to choose the number of contributions per frequency band for multi-band blending --- src/aliceVision/mesh/Texturing.cpp | 6 ++++++ src/aliceVision/mesh/Texturing.hpp | 4 ++-- src/software/pipeline/main_texturing.cpp | 2 +- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/aliceVision/mesh/Texturing.cpp b/src/aliceVision/mesh/Texturing.cpp index 77c4ecd91a..eb8b45b67e 100644 --- a/src/aliceVision/mesh/Texturing.cpp +++ b/src/aliceVision/mesh/Texturing.cpp @@ -255,11 +255,17 @@ void Texturing::generateTextures(const mvsUtils::MultiViewParams &mp, const boost::filesystem::path &outPath, EImageFileType textureFileType) { ALICEVISION_LOG_INFO("Texturing: Use multiband blending with the following contributions per band:"); + texParams.nbBand = texParams.multiBandNbContrib.size(); for(int c: texParams.multiBandNbContrib) { ALICEVISION_LOG_INFO(c << ", "); + if(c == 0) + texParams.nbBand -= 1; } + texParams.multiBandNbContrib.resize(texParams.nbBand); + ALICEVISION_LOG_INFO("Size multibandnnbcontrib : " + std::to_string(texParams.multiBandNbContrib.size())); + mvsUtils::ImagesCache imageCache(&mp, 0, imageIO::EImageColorSpace::SRGB); imageCache.setCacheSize(2); system::MemoryInfo memInfo = system::getMemoryInfo(); diff --git a/src/aliceVision/mesh/Texturing.hpp b/src/aliceVision/mesh/Texturing.hpp index 76b54f6094..f707e9dde4 100644 --- a/src/aliceVision/mesh/Texturing.hpp +++ b/src/aliceVision/mesh/Texturing.hpp @@ -65,9 +65,9 @@ EVisibilityRemappingMethod EVisibilityRemappingMethod_stringToEnum(const std::st struct TexturingParams { bool useScore = true; - unsigned int nbBand = 3; + unsigned int nbBand = 4; unsigned int multiBandDownscale = 2; - std::vector multiBandNbContrib = {1, 5, 10}; // number of contributions per level for the multi-band blending + std::vector multiBandNbContrib = {1, 3, 15, 0}; // number of contributions per frequency band for the multi-band blending double bestScoreThreshold = 0.0; //< 0.0 to disable filtering based on threshold to relative best score double angleHardThreshold = 90.0; //< 0.0 to disable angle hard threshold filtering bool forceVisibleByAllVertices = false; //< triangle visibility is based on the union of vertices visiblity diff --git a/src/software/pipeline/main_texturing.cpp b/src/software/pipeline/main_texturing.cpp index c527c168d7..ec473f0bc3 100644 --- a/src/software/pipeline/main_texturing.cpp +++ b/src/software/pipeline/main_texturing.cpp @@ -89,7 +89,7 @@ int main(int argc, char* argv[]) "Use triangles scores for multiband blending.") ("multiBandDownscale", po::value(&texParams.multiBandDownscale)->default_value(texParams.multiBandDownscale), "Width of frequency bands.") - ("multiBandNbContrib", po::value>(&texParams.multiBandNbContrib)->default_value(texParams.multiBandNbContrib), + ("multiBandNbContrib", po::value>(&texParams.multiBandNbContrib)->default_value(texParams.multiBandNbContrib)->multitoken(), "Number of contributions per frequency band.") ("bestScoreThreshold", po::value(&texParams.bestScoreThreshold)->default_value(texParams.bestScoreThreshold), "(0.0 to disable filtering based on threshold to relative best score).") From e503f75a22f5fb1760db67c991b3fb27c3583885 Mon Sep 17 00:00:00 2001 From: Clara Date: Tue, 14 May 2019 15:51:33 +0200 Subject: [PATCH 38/51] [mesh] Texturing: add log infos, comments and delete useless variables Change some log informations and some comments, and use texParams variables instead of declaring intermediate variables --- src/aliceVision/mesh/MultiBandBlending.cpp | 10 +----- src/aliceVision/mesh/MultiBandBlending.hpp | 3 +- src/aliceVision/mesh/Texturing.cpp | 41 +++++++++------------- src/software/pipeline/main_texturing.cpp | 2 +- 4 files changed, 20 insertions(+), 36 deletions(-) diff --git a/src/aliceVision/mesh/MultiBandBlending.cpp b/src/aliceVision/mesh/MultiBandBlending.cpp index e673061e6a..f64ceb3b56 100644 --- a/src/aliceVision/mesh/MultiBandBlending.cpp +++ b/src/aliceVision/mesh/MultiBandBlending.cpp @@ -34,13 +34,6 @@ void MultiBandBlending::laplacianPyramid(std::vector& out_pyramidL, const Image::imageDiff(base, baseG, out_pyramidL[b]); base.swap(baseG); //newBase = oldBaseG s *= 2; - - /*//TODO : REMOVE - const std::string outPathG = std::string("/datas/rave/tmp/") + std::string("cam") + std::to_string(camId) + std::string("G") + std::to_string(b) + ".exr"; - imageIO::writeImage(outPathG, width, height, baseG.data()); - const std::string outPathL = std::string("/datas/rave/tmp/") + std::string("cam") + std::to_string(camId) + std::string("L") + std::to_string(b) + ".exr"; - imageIO::writeImage(outPathL, width, height, out_pyramidL.at(b).data());*/ - } out_pyramidL.at(nbBand-1) = base; } @@ -71,9 +64,8 @@ void MultiBandBlending::laplacianDownscalePyramid(std::vector& out_pyrami out_pyramidL[nbBand-1] = img; for(std::size_t i = 0; i < out_pyramidL.size(); ++i) - ALICEVISION_LOG_INFO("laplacianDownscalePyramid: Size level " << i << " : " << out_pyramidL[i].width() << "x" << out_pyramidL[i].height()); + ALICEVISION_LOG_DEBUG("laplacianDownscalePyramid: Size level " << i << " : " << out_pyramidL[i].width() << "x" << out_pyramidL[i].height()); } - } } diff --git a/src/aliceVision/mesh/MultiBandBlending.hpp b/src/aliceVision/mesh/MultiBandBlending.hpp index 1233fc8395..dbdb769872 100644 --- a/src/aliceVision/mesh/MultiBandBlending.hpp +++ b/src/aliceVision/mesh/MultiBandBlending.hpp @@ -19,10 +19,9 @@ class MultiBandBlending ///Generate the pyramid of the differences of the successives gaussian convolutions of the input image void laplacianPyramid(std::vector& out_pyramidL, const Image& inImg, int camId, int nbBand, float sizeKernel); - ///Generate the gaussian pyramid given the input image + ///Generate the laplacian pyramid given the input image void laplacianDownscalePyramid(std::vector& out_pyramidL, const Image& inImg, int nbBand, unsigned int downscale); - }; } diff --git a/src/aliceVision/mesh/Texturing.cpp b/src/aliceVision/mesh/Texturing.cpp index eb8b45b67e..d51cf208c2 100644 --- a/src/aliceVision/mesh/Texturing.cpp +++ b/src/aliceVision/mesh/Texturing.cpp @@ -277,30 +277,27 @@ void Texturing::generateTextures(const mvsUtils::MultiViewParams &mp, const int freeMem = int(memInfo.freeRam / std::pow(2,20)); const int availableMem = freeMem - 2 * imageMaxMemSize - imageMaxMemSize * texParams.nbBand; // keep some memory for the input image buffer and its laplacian pyramid - int nbAtlasMax = std::floor((availableMem - 1000) / pyramidMaxMemSize); //maximum number of textures in RAM + int nbAtlasMax = std::floor(availableMem / pyramidMaxMemSize); //maximum number of textures in RAM const int nbAtlas = _atlases.size(); nbAtlasMax = std::max(1, nbAtlasMax); //if not enough memory, do it one by one nbAtlasMax = std::min(nbAtlas, nbAtlasMax); //if enough memory, do it with all atlases - if (availableMem - nbAtlasMax*pyramidMaxMemSize < 1000 && nbAtlasMax > 1) //keep margin in memory - nbAtlasMax -= 1; - - std::div_t divresult = div(nbAtlas, nbAtlasMax); - const int nquot = divresult.quot; - const int nrem = divresult.rem; + // if (availableMem - nbAtlasMax*pyramidMaxMemSize < 1000 && nbAtlasMax > 1) //keep margin in memory + // nbAtlasMax -= 1; ALICEVISION_LOG_INFO("Total amount of free memory : " << freeMem << " Mb."); ALICEVISION_LOG_INFO("Total amount of memory available : " << availableMem << " Mb."); ALICEVISION_LOG_INFO("Total amount of an image in memory : " << imageMaxMemSize << " Mb."); - ALICEVISION_LOG_INFO("Total amount of an atlas in memory: " << pyramidMaxMemSize << " Mb."); + ALICEVISION_LOG_INFO("Total amount of an atlas pyramid in memory: " << pyramidMaxMemSize << " Mb."); ALICEVISION_LOG_INFO("Processing " << nbAtlas << " atlases by chunks of " << nbAtlasMax); //generateTexture for the maximum number of atlases, and iterate + const std::div_t divresult = div(nbAtlas, nbAtlasMax); std::vector atlasIDs; atlasIDs.reserve(nbAtlasMax); - for(int n = 0; n <= nquot; ++n) + for(int n = 0; n <= divresult.quot; ++n) { atlasIDs.clear(); - int imax = (n < nquot ? nbAtlasMax : nrem); + int imax = (n < divresult.quot ? nbAtlasMax : divresult.rem); if(!imax) continue; for(int i = 0; i < imax; ++i) @@ -308,7 +305,7 @@ void Texturing::generateTextures(const mvsUtils::MultiViewParams &mp, size_t atlasID = size_t(n*nbAtlasMax + i); atlasIDs.push_back(atlasID); } - ALICEVISION_LOG_INFO("Generating texture for atlases " << n*nbAtlasMax + 1 << " to " << n*nbAtlasMax+imax << " (process a chunk of " << atlasIDs.size() << " atlases within " << _atlases.size() << " atlases)."); + ALICEVISION_LOG_INFO("Generating texture for atlases " << n*nbAtlasMax + 1 << " to " << n*nbAtlasMax+imax ); generateTexturesSubSet(mp, atlasIDs, imageCache, outPath, textureFileType); } } @@ -320,9 +317,6 @@ void Texturing::generateTexturesSubSet(const mvsUtils::MultiViewParams& mp, throw std::runtime_error("Invalid atlas IDs "); unsigned int textureSize = texParams.textureSide * texParams.textureSide; - int nbBand = texParams.nbBand; - unsigned int multiBandDownScale = texParams.multiBandDownscale; - bool useScore = texParams.useScore; // We select the best cameras for each triangle and store it per camera for each output texture files. // Triangles contributions are stored per frequency bands for multi-band blending. @@ -447,7 +441,7 @@ void Texturing::generateTexturesSubSet(const mvsUtils::MultiViewParams& mp, const int triangleScore = std::get<1>(scorePerCamId[contrib]); auto& camContribution = contributionsPerCamera[camId]; if(camContribution.find(atlasID) == camContribution.end()) - camContribution[atlasID].resize(nbBand); + camContribution[atlasID].resize(texParams.nbBand); camContribution.at(atlasID)[band].emplace_back(triangleID, triangleScore); if(contrib + 1 == nbContribLevel) @@ -464,7 +458,7 @@ void Texturing::generateTexturesSubSet(const mvsUtils::MultiViewParams& mp, //pyramid of atlases frequency bands std::map accuPyramids; for(std::size_t atlasID: atlasIDs) - accuPyramids[atlasID].init(nbBand, textureSize); + accuPyramids[atlasID].init(texParams.nbBand, textureSize); //for each camera, for each texture, iterate over triangles and fill the accuPyramids map for(int camId = 0; camId < contributionsPerCamera.size(); ++camId) @@ -473,10 +467,10 @@ void Texturing::generateTexturesSubSet(const mvsUtils::MultiViewParams& mp, if(cameraContributions.empty()) { - ALICEVISION_LOG_INFO(" - camera " << camId + 1 << "/" << mp.ncams << " unused."); + ALICEVISION_LOG_INFO("- camera " << camId + 1 << "/" << mp.ncams << " unused."); continue; } - ALICEVISION_LOG_INFO(" - camera " << camId + 1 << "/" << mp.ncams << " with contributions to " << cameraContributions.size() << " texture files:"); + ALICEVISION_LOG_INFO("- camera " << camId + 1 << "/" << mp.ncams << " with contributions to " << cameraContributions.size() << " texture files:"); imageCache.refreshData(camId); @@ -484,29 +478,28 @@ void Texturing::generateTexturesSubSet(const mvsUtils::MultiViewParams& mp, mvsUtils::ImagesCache::ImgPtr imgPtr = imageCache.getImg_sync(camId); Image camImg(imgPtr->data, imgPtr->getWidth(), imgPtr->getHeight()); - ALICEVISION_LOG_INFO(" - camera " << camId + 1 << " multiBandBlending"); - //Calculate laplacianPyramid MultiBandBlending multiBandBlending; std::vector pyramidL; //laplacian pyramid - multiBandBlending.laplacianDownscalePyramid(pyramidL, camImg, nbBand, multiBandDownScale); + multiBandBlending.laplacianDownscalePyramid(pyramidL, camImg, texParams.nbBand, texParams.multiBandDownscale); // for each output texture file for(const auto& c : cameraContributions) { AtlasIndex atlasID = c.first; + ALICEVISION_LOG_INFO(" - Texture file: " << atlasID); //for each frequency band for(int band = 0; band < c.second.size(); ++band) { const ScorePerTriangle& trianglesId = c.second[band]; - ALICEVISION_LOG_INFO(" Texture file: " << atlasID << "- band: " << band << "- number of triangles: " << trianglesId.size() << "."); + ALICEVISION_LOG_INFO(" - band " << band << ": " << trianglesId.size() << " triangles."); // for each triangle #pragma omp parallel for for(int ti = 0; ti < trianglesId.size(); ++ti) { const unsigned int triangleId = std::get<0>(trianglesId[ti]); - const float triangleScore = useScore ? std::get<1>(trianglesId[ti]) : 1.0f; + const float triangleScore = texParams.useScore ? std::get<1>(trianglesId[ti]) : 1.0f; // retrieve triangle 3D and UV coordinates Point2d triPixs[3]; Point3d triPts[3]; @@ -576,7 +569,7 @@ void Texturing::generateTexturesSubSet(const mvsUtils::MultiViewParams& mp, AccuPyramid& accuPyramid = accuPyramids.at(atlasID); for(std::size_t bandContrib = band; bandContrib < pyramidL.size(); ++bandContrib) { - int downscaleCoef = std::pow(multiBandDownScale, bandContrib); + int downscaleCoef = std::pow(texParams.multiBandDownscale, bandContrib); AccuImage& accuImage = accuPyramid.pyramid[bandContrib]; // fill the accumulated color map for this pixel diff --git a/src/software/pipeline/main_texturing.cpp b/src/software/pipeline/main_texturing.cpp index ec473f0bc3..7b134ff4d9 100644 --- a/src/software/pipeline/main_texturing.cpp +++ b/src/software/pipeline/main_texturing.cpp @@ -86,7 +86,7 @@ int main(int argc, char* argv[]) ("flipNormals", po::value(&flipNormals)->default_value(flipNormals), "Option to flip face normals. It can be needed as it depends on the vertices order in triangles and the convention change from one software to another.") ("useScore", po::value(&texParams.useScore)->default_value(texParams.useScore), - "Use triangles scores for multiband blending.") + "Use triangles scores (based on observations and re-projected areas in source images) for weighting contributions.") ("multiBandDownscale", po::value(&texParams.multiBandDownscale)->default_value(texParams.multiBandDownscale), "Width of frequency bands.") ("multiBandNbContrib", po::value>(&texParams.multiBandNbContrib)->default_value(texParams.multiBandNbContrib)->multitoken(), From 624981999a673d0fe1bb89f15a931901b48fe306 Mon Sep 17 00:00:00 2001 From: Clara Date: Tue, 14 May 2019 15:53:16 +0200 Subject: [PATCH 39/51] [mesh] Texturing: add debug mode Add debug mode to save atlases decomposition in frequency bands and the number of contribution in each band --- src/aliceVision/mesh/Texturing.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/aliceVision/mesh/Texturing.cpp b/src/aliceVision/mesh/Texturing.cpp index d51cf208c2..793497635d 100644 --- a/src/aliceVision/mesh/Texturing.cpp +++ b/src/aliceVision/mesh/Texturing.cpp @@ -28,6 +28,10 @@ #include #include +// Debug mode: save atlases decomposition in frequency bands and +// the number of contribution in each band (if useScore is set to false) +#define TEXTURING_MBB_DEBUG 0 + namespace aliceVision { namespace mesh { @@ -591,9 +595,7 @@ void Texturing::generateTexturesSubSet(const mvsUtils::MultiViewParams& mp, AccuImage& atlasTexture = accuPyramid.pyramid[0]; ALICEVISION_LOG_INFO("Create texture " << atlasID); - bool debug = 1; - - if(debug) +#if TEXTURING_MBB_DEBUG { for(std::size_t level = 0; level < accuPyramid.pyramid.size(); ++level) { @@ -617,6 +619,7 @@ void Texturing::generateTexturesSubSet(const mvsUtils::MultiViewParams& mp, imageIO::writeImage(texturePath.string(), texParams.textureSide, texParams.textureSide, imgContrib, imageIO::EImageQuality::OPTIMIZED, imageIO::EImageColorSpace::AUTO); } } +#endif ALICEVISION_LOG_INFO(" - Computing final (average) color."); for(unsigned int yp = 0; yp < texParams.textureSide; ++yp) @@ -641,7 +644,7 @@ void Texturing::generateTexturesSubSet(const mvsUtils::MultiViewParams& mp, } } - if(debug) +#if TEXTURING_MBB_DEBUG { //write each frequency band, for each texture for(std::size_t level = 0; level < accuPyramid.pyramid.size(); ++level) @@ -651,6 +654,7 @@ void Texturing::generateTexturesSubSet(const mvsUtils::MultiViewParams& mp, } } +#endif // Fuse frequency bands into the first buffer, calculate final texture for(unsigned int yp = 0; yp < texParams.textureSide; ++yp) From b488f2ca26fe85b2a84eba31618092c723c0c4bd Mon Sep 17 00:00:00 2001 From: Clara Date: Tue, 14 May 2019 15:54:38 +0200 Subject: [PATCH 40/51] [mesh] Texturing: fix number of contributions per band --- src/aliceVision/mesh/Texturing.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/aliceVision/mesh/Texturing.cpp b/src/aliceVision/mesh/Texturing.cpp index 793497635d..a302cbfaf8 100644 --- a/src/aliceVision/mesh/Texturing.cpp +++ b/src/aliceVision/mesh/Texturing.cpp @@ -451,7 +451,7 @@ void Texturing::generateTexturesSubSet(const mvsUtils::MultiViewParams& mp, if(contrib + 1 == nbContribLevel) { ++band; - nbContribLevel += texParams.multiBandNbContrib[band]; + nbContribLevel = texParams.multiBandNbContrib[band]; } } } From bd89d50b9845b92be06f19e21e0857a87106bb82 Mon Sep 17 00:00:00 2001 From: Clara Date: Tue, 14 May 2019 16:02:28 +0200 Subject: [PATCH 41/51] [mesh] Texturing: handle "0"or illogical contributions for MBB Handle cases where the user asks for 0 contributions for frequency bands or illogical (decreasant) order of contributions --- src/aliceVision/mesh/Texturing.cpp | 17 +++++++++++------ src/aliceVision/mesh/Texturing.hpp | 3 ++- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/aliceVision/mesh/Texturing.cpp b/src/aliceVision/mesh/Texturing.cpp index a302cbfaf8..0c41e8be96 100644 --- a/src/aliceVision/mesh/Texturing.cpp +++ b/src/aliceVision/mesh/Texturing.cpp @@ -259,17 +259,22 @@ void Texturing::generateTextures(const mvsUtils::MultiViewParams &mp, const boost::filesystem::path &outPath, EImageFileType textureFileType) { ALICEVISION_LOG_INFO("Texturing: Use multiband blending with the following contributions per band:"); + + texParams.multiBandNbContrib.erase(std::remove(std::begin(texParams.multiBandNbContrib), std::end(texParams.multiBandNbContrib), 0), + std::end(texParams.multiBandNbContrib)); texParams.nbBand = texParams.multiBandNbContrib.size(); + + if(!std::is_sorted(std::begin(texParams.multiBandNbContrib), std::end(texParams.multiBandNbContrib))) + { + ALICEVISION_LOG_INFO("Sorting contributions per band (necessary)."); + std::sort(std::begin(texParams.multiBandNbContrib), std::end(texParams.multiBandNbContrib)); + + } for(int c: texParams.multiBandNbContrib) { - ALICEVISION_LOG_INFO(c << ", "); - if(c == 0) - texParams.nbBand -= 1; + ALICEVISION_LOG_INFO(" - " << c); } - texParams.multiBandNbContrib.resize(texParams.nbBand); - ALICEVISION_LOG_INFO("Size multibandnnbcontrib : " + std::to_string(texParams.multiBandNbContrib.size())); - mvsUtils::ImagesCache imageCache(&mp, 0, imageIO::EImageColorSpace::SRGB); imageCache.setCacheSize(2); system::MemoryInfo memInfo = system::getMemoryInfo(); diff --git a/src/aliceVision/mesh/Texturing.hpp b/src/aliceVision/mesh/Texturing.hpp index f707e9dde4..29a1195472 100644 --- a/src/aliceVision/mesh/Texturing.hpp +++ b/src/aliceVision/mesh/Texturing.hpp @@ -67,7 +67,8 @@ struct TexturingParams bool useScore = true; unsigned int nbBand = 4; unsigned int multiBandDownscale = 2; - std::vector multiBandNbContrib = {1, 3, 15, 0}; // number of contributions per frequency band for the multi-band blending + std::vector multiBandNbContrib = {1, 5, 10, 0}; // number of contributions per frequency band for the multi-band blending + double bestScoreThreshold = 0.0; //< 0.0 to disable filtering based on threshold to relative best score double angleHardThreshold = 90.0; //< 0.0 to disable angle hard threshold filtering bool forceVisibleByAllVertices = false; //< triangle visibility is based on the union of vertices visiblity From 01d9862691447cbd542ae3b76995b1eb067fb83a Mon Sep 17 00:00:00 2001 From: Clara Date: Tue, 4 Jun 2019 15:35:26 +0200 Subject: [PATCH 42/51] Texturing: merge MultiBandBlending class in Image class + clean code Delete MultiBandBlending class & calculate laplacian pyramid directly in the image class. Delete useless functions and add comments [mvsData] Image: some cleaning + split into .hpp / .cpp Create a .cpp file to write the implementation of elaborate functions --- src/aliceVision/mesh/CMakeLists.txt | 2 - src/aliceVision/mesh/MultiBandBlending.cpp | 71 ---------------- src/aliceVision/mesh/MultiBandBlending.hpp | 28 ------- src/aliceVision/mesh/Texturing.cpp | 5 +- src/aliceVision/mvsData/CMakeLists.txt | 3 + src/aliceVision/mvsData/Image.cpp | 87 ++++++++++++++++++++ src/aliceVision/mvsData/Image.hpp | 95 ++++++---------------- src/samples/CMakeLists.txt | 1 - 8 files changed, 115 insertions(+), 177 deletions(-) delete mode 100644 src/aliceVision/mesh/MultiBandBlending.cpp delete mode 100644 src/aliceVision/mesh/MultiBandBlending.hpp create mode 100644 src/aliceVision/mvsData/Image.cpp diff --git a/src/aliceVision/mesh/CMakeLists.txt b/src/aliceVision/mesh/CMakeLists.txt index a5c318c5e0..2697ea9b6c 100644 --- a/src/aliceVision/mesh/CMakeLists.txt +++ b/src/aliceVision/mesh/CMakeLists.txt @@ -7,7 +7,6 @@ set(mesh_files_headers MeshEnergyOpt.hpp meshPostProcessing.hpp meshVisibility.hpp - MultiBandBlending.hpp Texturing.hpp UVAtlas.hpp ) @@ -20,7 +19,6 @@ set(mesh_files_sources MeshEnergyOpt.cpp meshPostProcessing.cpp meshVisibility.cpp - MultiBandBlending.cpp Texturing.cpp UVAtlas.cpp ) diff --git a/src/aliceVision/mesh/MultiBandBlending.cpp b/src/aliceVision/mesh/MultiBandBlending.cpp deleted file mode 100644 index f64ceb3b56..0000000000 --- a/src/aliceVision/mesh/MultiBandBlending.cpp +++ /dev/null @@ -1,71 +0,0 @@ -// This file is part of the AliceVision project. -// Copyright (c) 2019 AliceVision contributors. -// This Source Code Form is subject to the terms of the Mozilla Public License, -// v. 2.0. If a copy of the MPL was not distributed with this file, -// You can obtain one at https://mozilla.org/MPL/2.0/. - -#include "MultiBandBlending.hpp" - -#include -#include - - - -namespace aliceVision { -namespace mesh { - -void MultiBandBlending::laplacianPyramid(std::vector& out_pyramidL, const Image& inImg, int camId, int nbBand, float sizeKernel) -{ - assert(nbBand >= 1); - - const int width = inImg.width(); - const int height = inImg.height(); - - Image base(inImg); // gaussian - Image baseG(width, height); // gaussian - out_pyramidL.resize(nbBand); // laplacian - - float s = sizeKernel / std::pow(2, nbBand-1); - - //Create Laplacian pyramid - for(int b = 0; b < nbBand-1; ++b) - { - imageIO::convolveImage(width, height, base.data(), baseG.data(), "gaussian", s, s); //baseG = base * gaussianKernel - Image::imageDiff(base, baseG, out_pyramidL[b]); - base.swap(baseG); //newBase = oldBaseG - s *= 2; - } - out_pyramidL.at(nbBand-1) = base; -} - -void MultiBandBlending::laplacianDownscalePyramid(std::vector& out_pyramidL, const Image& inImg, int nbBand, unsigned int downscale) -{ - assert(nbBand >= 1); - - Image img(inImg); - int outW = static_cast(img.width()/downscale); - int outH = static_cast(img.height()/downscale); - - Image imgDownscaled(outW, outH); - out_pyramidL.resize(nbBand); - - //Create Laplacian pyramid - for(int b = 0; b < nbBand-1; ++b) - { - imageIO::resizeImage(img.width(), img.height(), static_cast(downscale), img.data(), imgDownscaled.data(), "gaussian"); - Image::imageDownscaleDiff(img, imgDownscaled, out_pyramidL[b], downscale); - img.swap(imgDownscaled); - - outW = static_cast(outW/downscale); - outH = static_cast(outH/downscale); - imgDownscaled.resize(outW, outH); - - } - out_pyramidL[nbBand-1] = img; - - for(std::size_t i = 0; i < out_pyramidL.size(); ++i) - ALICEVISION_LOG_DEBUG("laplacianDownscalePyramid: Size level " << i << " : " << out_pyramidL[i].width() << "x" << out_pyramidL[i].height()); -} - -} -} diff --git a/src/aliceVision/mesh/MultiBandBlending.hpp b/src/aliceVision/mesh/MultiBandBlending.hpp deleted file mode 100644 index dbdb769872..0000000000 --- a/src/aliceVision/mesh/MultiBandBlending.hpp +++ /dev/null @@ -1,28 +0,0 @@ -// This file is part of the AliceVision project. -// Copyright (c) 2019 AliceVision contributors. -// This Source Code Form is subject to the terms of the Mozilla Public License, -// v. 2.0. If a copy of the MPL was not distributed with this file, -// You can obtain one at https://mozilla.org/MPL/2.0/. - -#pragma once - -#include -#include - -namespace aliceVision { -namespace mesh { - -class MultiBandBlending -{ -public: - - ///Generate the pyramid of the differences of the successives gaussian convolutions of the input image - void laplacianPyramid(std::vector& out_pyramidL, const Image& inImg, int camId, int nbBand, float sizeKernel); - - ///Generate the laplacian pyramid given the input image - void laplacianDownscalePyramid(std::vector& out_pyramidL, const Image& inImg, int nbBand, unsigned int downscale); - -}; - -} -} diff --git a/src/aliceVision/mesh/Texturing.cpp b/src/aliceVision/mesh/Texturing.cpp index 0c41e8be96..08c1e336c9 100644 --- a/src/aliceVision/mesh/Texturing.cpp +++ b/src/aliceVision/mesh/Texturing.cpp @@ -14,8 +14,8 @@ #include #include #include +#include #include -#include #include #include @@ -488,9 +488,8 @@ void Texturing::generateTexturesSubSet(const mvsUtils::MultiViewParams& mp, Image camImg(imgPtr->data, imgPtr->getWidth(), imgPtr->getHeight()); //Calculate laplacianPyramid - MultiBandBlending multiBandBlending; std::vector pyramidL; //laplacian pyramid - multiBandBlending.laplacianDownscalePyramid(pyramidL, camImg, texParams.nbBand, texParams.multiBandDownscale); + camImg.laplacianPyramid(pyramidL, texParams.nbBand, texParams.multiBandDownscale); // for each output texture file for(const auto& c : cameraContributions) diff --git a/src/aliceVision/mvsData/CMakeLists.txt b/src/aliceVision/mvsData/CMakeLists.txt index 0d30eab299..ba8d4f5443 100644 --- a/src/aliceVision/mvsData/CMakeLists.txt +++ b/src/aliceVision/mvsData/CMakeLists.txt @@ -24,6 +24,7 @@ set(mvsData_files_headers # Sources set(mvsData_files_sources jetColorMap.cpp + Image.cpp imageIO.cpp geometry.cpp geometryTriTri.cpp @@ -39,7 +40,9 @@ alicevision_add_library(aliceVision_mvsData aliceVision_system ${ZLIB_LIBRARIES} ${Boost_FILESYSTEM_LIBRARY} + ${OPENIMAGEIO_LIBRARIES} PUBLIC_INCLUDE_DIRS ${ZLIB_INCLUDE_DIR} ${Boost_INCLUDE_DIR} + ${OPENIMAGEIO_INCLUDE_DIRS} ) diff --git a/src/aliceVision/mvsData/Image.cpp b/src/aliceVision/mvsData/Image.cpp new file mode 100644 index 0000000000..82cf4d96f1 --- /dev/null +++ b/src/aliceVision/mvsData/Image.cpp @@ -0,0 +1,87 @@ +// This file is part of the AliceVision project. +// Copyright (c) 2019 AliceVision contributors. +// This Source Code Form is subject to the terms of the Mozilla Public License, +// v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at https://mozilla.org/MPL/2.0/. + +#include "Image.hpp" + +#include + + +namespace aliceVision{ + + +void Image::imageDiff(Image& inImgDownscaled, Image& outImg, unsigned int downscale) +{ + outImg.resize(_width, _height); + for(int i = 0; i < _width*_height; ++i) + { + Point2d pix(i%_width, static_cast(i/_width)); + Point2d pixd = pix/downscale; + + outImg._img[i] = _img[i] - inImgDownscaled.getInterpolateColor(pixd); + } + +} + +void Image::laplacianPyramid(std::vector& out_pyramidL, int nbBand, unsigned int downscale) +{ + assert(nbBand >= 1); + + Image img(*this); + int outW = static_cast(img.width()/downscale); + int outH = static_cast(img.height()/downscale); + + Image imgDownscaled(outW, outH); + out_pyramidL.resize(nbBand); + + //Create Laplacian pyramid + for(int b = 0; b < nbBand-1; ++b) + { + imageIO::resizeImage(img.width(), img.height(), static_cast(downscale), img.data(), imgDownscaled.data(), "gaussian"); + img.imageDiff(imgDownscaled, out_pyramidL[b], downscale); + img.swap(imgDownscaled); + + outW = static_cast(outW/downscale); + outH = static_cast(outH/downscale); + imgDownscaled.resize(outW, outH); + + } + out_pyramidL[nbBand-1] = img; + + for(std::size_t i = 0; i < out_pyramidL.size(); ++i) + ALICEVISION_LOG_DEBUG("laplacianDownscalePyramid: Size level " << i << " : " << out_pyramidL[i].width() << "x" << out_pyramidL[i].height()); +} + +Color Image::getInterpolateColor(const Point2d& pix) const +{ + const int xp = std::min(static_cast(pix.x), _width-2); + const int yp = std::min(static_cast(pix.y), _height-2); + + // precision to 4 decimal places + const float ui = pix.x - static_cast(xp); + const float vi = pix.y - static_cast(yp); + + const Color lu = _img.at( yp * _width + xp ); + const Color ru = _img.at( yp * _width + (xp+1) ); + const Color rd = _img.at( (yp+1) * _width + (xp+1) ); + const Color ld = _img.at( (yp+1) * _width + xp ); + + // bilinear interpolation of the pixel intensity value + const Color u = lu + (ru - lu) * ui; + const Color d = ld + (rd - ld) * ui; + const Color out = u + (d - u) * vi; + return out; +} + +Color Image::getNearestPixelColor(const Point2d& pix) const +{ + const int xp = std::min(static_cast(pix.x), _width-1); + const int yp = std::min(static_cast(pix.y), _height-1); + const Color lu = _img.at( yp * _width + xp ); + return lu; +} + + +} diff --git a/src/aliceVision/mvsData/Image.hpp b/src/aliceVision/mvsData/Image.hpp index d64a9e3975..b9cda3df6b 100644 --- a/src/aliceVision/mvsData/Image.hpp +++ b/src/aliceVision/mvsData/Image.hpp @@ -8,7 +8,6 @@ #include #include -#include #include #include @@ -19,15 +18,11 @@ class Image { private: std::vector _img; - int _width = 0; - int _height = 0; + int _width{0}; + int _height{0}; public: - Image() - { - _width = 0; - _height = 0; - } + Image() = default; Image(int width, int height) : _width(width) @@ -36,12 +31,8 @@ class Image _img.resize(width*height); } - Image(Color* data, int width, int height) + Image(Color* data, int width, int height) : Image(width, height) { - _width = width; - _height = height; - _img.resize(_width * _height); - for(int i = 0; i < _width*_height; ++i) _img[i] = data[i]; } @@ -80,35 +71,6 @@ class Image return *this; } - //outImg = inImga - inImgb - static void imageDiff(const Image& inImga, const Image& inImgb, Image& outImg) - { - if(inImga._width != inImgb._width || inImga._height != inImgb._height) - throw std::runtime_error("Incompatible image sizes"); - - outImg.resize(inImga._width, inImga._height); - for(int i = 0; i < inImga._width*inImga._height; ++i) - { - outImg._img[i] = inImga._img.at(i) - inImgb._img.at(i); - } - } - - static void imageDownscaleDiff(Image& inImga, Image& inImgb, Image& outImg, unsigned int downscale) - { - //inImga = largest image - if(inImga.height() < inImgb.height()) - inImga.swap(inImgb); - - outImg.resize(inImga._width, inImga._height); - for(int i = 0; i < inImga._width*inImga._height; ++i) - { - Point2d pix(i%inImga.width(), static_cast(i/inImga.width())); - Point2d pixd = pix/downscale; - - outImg._img[i] = inImga._img[i] - inImgb.getInterpolateColor(pixd); - } - - } int width() const { return _width; } int height() const { return _height; } @@ -119,38 +81,27 @@ class Image const Color& operator[](std::size_t index) const { return _img[index]; } Color& operator[](std::size_t index) { return _img[index]; } - Color getInterpolateColor(const Point2d& pix) const - { - const int xp = std::min(static_cast(pix.x), _width-2); - const int yp = std::min(static_cast(pix.y), _height-2); - - // precision to 4 decimal places - const float ui = pix.x - static_cast(xp); - const float vi = pix.y - static_cast(yp); - - const Color lu = _img.at( yp * _width + xp ); - const Color ru = _img.at( yp * _width + (xp+1) ); - const Color rd = _img.at( (yp+1) * _width + (xp+1) ); - const Color ld = _img.at( (yp+1) * _width + xp ); - - // bilinear interpolation of the pixel intensity value - const Color u = lu + (ru - lu) * ui; - const Color d = ld + (rd - ld) * ui; - const Color out = u + (d - u) * vi; - return out; - } - - Color getNearestPixelColor(const Point2d& pix) const - { - const int xp = std::min(static_cast(pix.x), _width-1); - const int yp = std::min(static_cast(pix.y), _height-1); - const Color lu = _img.at( yp * _width + xp ); - return lu; - } -}; + Color getInterpolateColor(const Point2d& pix) const; + Color getNearestPixelColor(const Point2d& pix) const; + /** + * @brief Calculate the difference between images of different sizes + * @param [inImgDownscaled] the smaller image + * @param [outImg] the difference + * @param [downscale] the downscale coefficient between image sizes + */ + void imageDiff(Image& inImgDownscaled, Image& outImg, unsigned int downscale); -} + /** + * @brief Calculate the laplacian pyramid of a given image, + * ie. its decomposition in frequency bands + * @param [out_pyramidL] the laplacian pyramid + * @param [nbBand] the number of frequency bands + * @param [downscale] the downscale coefficient between floors of the pyramid + */ + void laplacianPyramid(std::vector& out_pyramidL, int nbBand, unsigned int downscale); +}; +} diff --git a/src/samples/CMakeLists.txt b/src/samples/CMakeLists.txt index f946a455e7..f1b03d69c6 100644 --- a/src/samples/CMakeLists.txt +++ b/src/samples/CMakeLists.txt @@ -10,7 +10,6 @@ add_subdirectory(featuresRepeatability) # add_subdirectory(imageData) add_subdirectory(imageDescriberMatches) add_subdirectory(kvldFilter) -add_subdirectory(multiBandBlending) add_subdirectory(robustEssential) add_subdirectory(robustEssentialBA) add_subdirectory(robustEssentialSpherical) From c4d3678da7c72b37b064b769cc8e54872e4d1113 Mon Sep 17 00:00:00 2001 From: Clara Date: Wed, 5 Jun 2019 12:08:02 +0200 Subject: [PATCH 43/51] Merge imageIO module into mvsData/imageIO Simplify imageIO management in MVS by having only one imageIO file. Override imageIO methods to take directly mvsData::Image class in argument (instead of width, height and buffer) --- src/aliceVision/CMakeLists.txt | 1 - src/aliceVision/depthMap/CMakeLists.txt | 1 - src/aliceVision/depthMap/DepthSimMap.cpp | 2 +- src/aliceVision/depthMap/RefineRc.cpp | 2 +- .../depthMap/SemiGlobalMatchingRc.cpp | 3 +- src/aliceVision/fuseCut/CMakeLists.txt | 1 - src/aliceVision/fuseCut/DelaunayGraphCut.cpp | 2 +- src/aliceVision/fuseCut/Fuser.cpp | 3 +- src/aliceVision/imageIO/CMakeLists.txt | 23 - src/aliceVision/imageIO/image.cpp | 478 ----------------- src/aliceVision/imageIO/image.hpp | 210 -------- src/aliceVision/imageIO/imageScaledColors.cpp | 78 --- src/aliceVision/imageIO/imageScaledColors.hpp | 28 - src/aliceVision/mesh/CMakeLists.txt | 1 - src/aliceVision/mesh/Texturing.cpp | 14 +- src/aliceVision/mesh/Texturing.hpp | 8 +- src/aliceVision/mvsData/Image.cpp | 2 +- src/aliceVision/mvsData/imageIO.cpp | 504 ++++++++++++++++++ src/aliceVision/mvsData/imageIO.hpp | 203 +++++++ src/aliceVision/mvsUtils/CMakeLists.txt | 1 - src/aliceVision/mvsUtils/ImagesCache.hpp | 2 +- src/aliceVision/mvsUtils/MultiViewParams.cpp | 4 +- src/aliceVision/mvsUtils/fileIO.cpp | 2 +- src/aliceVision/mvsUtils/fileIO.hpp | 2 +- src/samples/multiBandBlending/CMakeLists.txt | 3 +- .../multiBandBlending/main_multibandtest.cpp | 2 +- src/software/pipeline/main_texturing.cpp | 6 +- 27 files changed, 734 insertions(+), 852 deletions(-) delete mode 100644 src/aliceVision/imageIO/CMakeLists.txt delete mode 100644 src/aliceVision/imageIO/image.cpp delete mode 100644 src/aliceVision/imageIO/image.hpp delete mode 100644 src/aliceVision/imageIO/imageScaledColors.cpp delete mode 100644 src/aliceVision/imageIO/imageScaledColors.hpp diff --git a/src/aliceVision/CMakeLists.txt b/src/aliceVision/CMakeLists.txt index 59a4b2d9e8..0a564b2a5a 100644 --- a/src/aliceVision/CMakeLists.txt +++ b/src/aliceVision/CMakeLists.txt @@ -38,7 +38,6 @@ endif() # MVS modules if(ALICEVISION_BUILD_MVS) - add_subdirectory(imageIO) add_subdirectory(lightingEstimation) add_subdirectory(mesh) add_subdirectory(mvsData) diff --git a/src/aliceVision/depthMap/CMakeLists.txt b/src/aliceVision/depthMap/CMakeLists.txt index 7a7aebb9c8..6600981d7e 100644 --- a/src/aliceVision/depthMap/CMakeLists.txt +++ b/src/aliceVision/depthMap/CMakeLists.txt @@ -66,7 +66,6 @@ alicevision_add_library(aliceVision_depthMap ${depthMap_cuda_files_sources} PUBLIC_LINKS aliceVision_mvsData - aliceVision_imageIO aliceVision_mvsUtils aliceVision_system ${Boost_FILESYSTEM_LIBRARY} diff --git a/src/aliceVision/depthMap/DepthSimMap.cpp b/src/aliceVision/depthMap/DepthSimMap.cpp index dc58c5c3ca..d446ef8539 100644 --- a/src/aliceVision/depthMap/DepthSimMap.cpp +++ b/src/aliceVision/depthMap/DepthSimMap.cpp @@ -11,7 +11,7 @@ #include #include #include -#include +#include #include diff --git a/src/aliceVision/depthMap/RefineRc.cpp b/src/aliceVision/depthMap/RefineRc.cpp index 4f8fb8eec1..013c12083e 100644 --- a/src/aliceVision/depthMap/RefineRc.cpp +++ b/src/aliceVision/depthMap/RefineRc.cpp @@ -12,7 +12,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/aliceVision/depthMap/SemiGlobalMatchingRc.cpp b/src/aliceVision/depthMap/SemiGlobalMatchingRc.cpp index 80075445af..ff549cd9d5 100644 --- a/src/aliceVision/depthMap/SemiGlobalMatchingRc.cpp +++ b/src/aliceVision/depthMap/SemiGlobalMatchingRc.cpp @@ -14,8 +14,7 @@ #include #include #include -#include -#include +#include #include #include diff --git a/src/aliceVision/fuseCut/CMakeLists.txt b/src/aliceVision/fuseCut/CMakeLists.txt index 38e5b67981..28ed9b479f 100644 --- a/src/aliceVision/fuseCut/CMakeLists.txt +++ b/src/aliceVision/fuseCut/CMakeLists.txt @@ -28,7 +28,6 @@ alicevision_add_library(aliceVision_fuseCut PUBLIC_LINKS aliceVision_mvsData aliceVision_mvsUtils - aliceVision_imageIO aliceVision_mesh aliceVision_system Geogram::geogram diff --git a/src/aliceVision/fuseCut/DelaunayGraphCut.cpp b/src/aliceVision/fuseCut/DelaunayGraphCut.cpp index 20cadd2dc6..2d333242e6 100644 --- a/src/aliceVision/fuseCut/DelaunayGraphCut.cpp +++ b/src/aliceVision/fuseCut/DelaunayGraphCut.cpp @@ -14,7 +14,7 @@ #include #include #include -#include +#include #include #include "nanoflann.hpp" diff --git a/src/aliceVision/fuseCut/Fuser.cpp b/src/aliceVision/fuseCut/Fuser.cpp index e5f57e1a0e..3d43efac2e 100644 --- a/src/aliceVision/fuseCut/Fuser.cpp +++ b/src/aliceVision/fuseCut/Fuser.cpp @@ -13,8 +13,7 @@ #include #include #include -#include -#include +#include #include #include diff --git a/src/aliceVision/imageIO/CMakeLists.txt b/src/aliceVision/imageIO/CMakeLists.txt deleted file mode 100644 index d74885a729..0000000000 --- a/src/aliceVision/imageIO/CMakeLists.txt +++ /dev/null @@ -1,23 +0,0 @@ -# Headers -set(imageio_files_headers - image.hpp - imageScaledColors.hpp -) - -# Sources -set(imageio_files_sources - image.cpp - imageScaledColors.cpp -) - -alicevision_add_library(aliceVision_imageIO - SOURCES ${imageio_files_headers} ${imageio_files_sources} - PUBLIC_LINKS - ${OPENIMAGEIO_LIBRARIES} - PRIVATE_LINKS - aliceVision_mvsData - aliceVision_system - ${Boost_FILESYSTEM_LIBRARY} - PUBLIC_INCLUDE_DIRS - ${OPENIMAGEIO_INCLUDE_DIRS} -) diff --git a/src/aliceVision/imageIO/image.cpp b/src/aliceVision/imageIO/image.cpp deleted file mode 100644 index c6b47bbc70..0000000000 --- a/src/aliceVision/imageIO/image.cpp +++ /dev/null @@ -1,478 +0,0 @@ -// This file is part of the AliceVision project. -// Copyright (c) 2017 AliceVision contributors. -// This Source Code Form is subject to the terms of the Mozilla Public License, -// v. 2.0. If a copy of the MPL was not distributed with this file, -// You can obtain one at https://mozilla.org/MPL/2.0/. - -#include "image.hpp" -#include -#include -#include - -#include -#include -#include - -#include - -#include - -#include -#include -#include - -namespace fs = boost::filesystem; - -namespace aliceVision { -namespace imageIO { - -std::string EImageColorSpace_enumToString(const EImageColorSpace colorSpace) -{ - switch(colorSpace) - { - case EImageColorSpace::SRGB: return "sRGB"; // WARNING: string should match with OIIO definitions - case EImageColorSpace::LINEAR: return "Linear"; - default: ; - } - throw std::out_of_range("No string defined for EImageColorSpace: " + std::to_string(int(colorSpace))); -} - - -std::string EImageQuality_informations() -{ - return "Image quality :\n" - "* optimized \n" - "* lossless "; -} - -EImageQuality EImageQuality_stringToEnum(const std::string& imageQuality) -{ - std::string type = imageQuality; - std::transform(type.begin(), type.end(), type.begin(), ::tolower); //tolower - - if(type == "optimized") return EImageQuality::OPTIMIZED; - if(type == "lossless") return EImageQuality::LOSSLESS; - - throw std::out_of_range("Invalid image quality : " + imageQuality); -} - -std::string EImageQuality_enumToString(const EImageQuality imageQuality) -{ - switch(imageQuality) - { - case EImageQuality::OPTIMIZED: return "optimized"; - case EImageQuality::LOSSLESS: return "lossless"; - } - throw std::out_of_range("Invalid EImageQuality enum"); -} - -std::ostream& operator<<(std::ostream& os, EImageQuality imageQuality) -{ - return os << EImageQuality_enumToString(imageQuality); -} - -std::istream& operator>>(std::istream& in, EImageQuality& imageQuality) -{ - std::string token; - in >> token; - imageQuality = EImageQuality_stringToEnum(token); - return in; -} - -oiio::ParamValueList getMetadataFromMap(const std::map& metadataMap) -{ - oiio::ParamValueList metadata; - for(const auto& metadataPair : metadataMap) - metadata.push_back(oiio::ParamValue(metadataPair.first, metadataPair.second)); - return metadata; -} - -void readImageSpec(const std::string& path, - int& width, - int& height, - int& nchannels) -{ - ALICEVISION_LOG_DEBUG("[IO] Read Image Spec: " << path); - std::unique_ptr in(oiio::ImageInput::open(path)); - - if(!in) - throw std::runtime_error("Can't find/open image file '" + path + "'."); - - const oiio::ImageSpec &spec = in->spec(); - - width = spec.width; - height = spec.height; - nchannels = spec.nchannels; - - in->close(); -} - -void readImageMetadata(const std::string& path, oiio::ParamValueList& metadata) -{ - ALICEVISION_LOG_DEBUG("[IO] Read Image Metadata: " << path); - std::unique_ptr in(oiio::ImageInput::open(path)); - - if(!in) - throw std::runtime_error("Can't find/open image file '" + path + "'."); - - metadata = in->spec().extra_attribs; - - in->close(); -} - -template -void readImage(const std::string& path, - oiio::TypeDesc typeDesc, - int nchannels, - int& width, - int& height, - std::vector& buffer, - EImageColorSpace imageColorSpace) -{ - ALICEVISION_LOG_DEBUG("[IO] Read Image: " << path); - - // check requested channels number - assert(nchannels == 1 || nchannels >= 3); - - oiio::ImageSpec configSpec; - - // libRAW configuration - configSpec.attribute("raw:auto_bright", 0); // don't want exposure correction - configSpec.attribute("raw:use_camera_wb", 1); // want white balance correction - configSpec.attribute("raw:use_camera_matrix", 3); // want to use embeded color profile - - oiio::ImageBuf inBuf(path, 0, 0, NULL, &configSpec); - - inBuf.read(0, 0, true, oiio::TypeDesc::FLOAT); // force image convertion to float (for grayscale and color space convertion) - - if(!inBuf.initialized()) - throw std::runtime_error("Can't find/open image file '" + path + "'."); - - const oiio::ImageSpec& inSpec = inBuf.spec(); - - // check picture channels number - if(inSpec.nchannels != 1 && inSpec.nchannels < 3) - throw std::runtime_error("Can't load channels of image file '" + path + "'."); - - // color conversion - if(imageColorSpace == EImageColorSpace::AUTO) - throw std::runtime_error("You must specify a requested color space for image file '" + path + "'."); - - if(imageColorSpace == EImageColorSpace::SRGB) // color conversion to sRGB - { - const std::string& colorSpace = inSpec.get_string_attribute("oiio:ColorSpace", "sRGB"); // default image color space is sRGB - if(colorSpace != "sRGB") - oiio::ImageBufAlgo::colorconvert(inBuf, inBuf, colorSpace, "sRGB"); - } - else if(imageColorSpace == EImageColorSpace::LINEAR) // color conversion to linear - { - const std::string& colorSpace = inSpec.get_string_attribute("oiio:ColorSpace", "sRGB"); - if(colorSpace != "Linear") - oiio::ImageBufAlgo::colorconvert(inBuf, inBuf, colorSpace, "Linear"); - } - - // convert to grayscale if needed - if(nchannels == 1 && inSpec.nchannels >= 3) - { - // convertion region of interest (for inSpec.nchannels > 3) - oiio::ROI convertionROI = inBuf.roi(); - convertionROI.chbegin = 0; - convertionROI.chend = 3; - - // compute luminance via a weighted sum of R,G,B - // (assuming Rec709 primaries and a linear scale) - const float weights[3] = {.2126, .7152, .0722}; - oiio::ImageBuf grayscaleBuf; - oiio::ImageBufAlgo::channel_sum(grayscaleBuf, inBuf, weights, convertionROI); - inBuf.copy(grayscaleBuf); - } - - // add missing channels - if(nchannels > inSpec.nchannels) - { - oiio::ImageSpec requestedSpec(inSpec.width, inSpec.height, nchannels, typeDesc); - oiio::ImageBuf requestedBuf(requestedSpec); - - // duplicate first channel for RGB - if(requestedSpec.nchannels >= 3 && inSpec.nchannels < 3) - { - oiio::ImageBufAlgo::paste(requestedBuf, 0, 0, 0, 0, inBuf); - oiio::ImageBufAlgo::paste(requestedBuf, 0, 0, 0, 1, inBuf); - oiio::ImageBufAlgo::paste(requestedBuf, 0, 0, 0, 2, inBuf); - } - - inBuf.copy(requestedBuf); - } - - width = inSpec.width; - height = inSpec.height; - - buffer.resize(inSpec.width * inSpec.height); - - { - oiio::ROI exportROI = inBuf.roi(); - exportROI.chbegin = 0; - exportROI.chend = nchannels; - - inBuf.get_pixels(exportROI, typeDesc, buffer.data()); - } -} - -void readImage(const std::string& path, int& width, int& height, std::vector& buffer, EImageColorSpace imageColorSpace) -{ - readImage(path, oiio::TypeDesc::UCHAR, 1, width, height, buffer, imageColorSpace); -} - -void readImage(const std::string& path, int& width, int& height, std::vector& buffer, EImageColorSpace imageColorSpace) -{ - readImage(path, oiio::TypeDesc::UINT16, 1, width, height, buffer, imageColorSpace); -} - -void readImage(const std::string& path, int& width, int& height, std::vector& buffer, EImageColorSpace imageColorSpace) -{ - readImage(path, oiio::TypeDesc::UCHAR, 3, width, height, buffer, imageColorSpace); -} - -void readImage(const std::string& path, int& width, int& height, std::vector& buffer, EImageColorSpace imageColorSpace) -{ - readImage(path, oiio::TypeDesc::FLOAT, 1, width, height, buffer, imageColorSpace); -} - -void readImage(const std::string& path, int& width, int& height, std::vector& buffer, EImageColorSpace imageColorSpace) -{ - readImage(path, oiio::TypeDesc::FLOAT, 3, width, height, buffer, imageColorSpace); -} - -template -void writeImage(const std::string& path, - oiio::TypeDesc typeDesc, - int width, - int height, - int nchannels, - const std::vector& buffer, - EImageQuality imageQuality, - OutputFileColorSpace colorspace, - const oiio::ParamValueList& metadata) -{ - const fs::path bPath = fs::path(path); - const std::string extension = bPath.extension().string(); - const std::string tmpPath = (bPath.parent_path() / bPath.stem()).string() + "." + fs::unique_path().string() + extension; - const bool isEXR = (extension == ".exr"); - //const bool isTIF = (extension == ".tif"); - const bool isJPG = (extension == ".jpg"); - const bool isPNG = (extension == ".png"); - - if(colorspace.to == EImageColorSpace::AUTO) - { - if(isJPG || isPNG) - colorspace.to = EImageColorSpace::SRGB; - else - colorspace.to = EImageColorSpace::LINEAR; - } - - ALICEVISION_LOG_DEBUG("[IO] Write Image: " << path << std::endl - << "\t- width: " << width << std::endl - << "\t- height: " << height << std::endl - << "\t- channels: " << nchannels); - - oiio::ImageSpec imageSpec(width, height, nchannels, typeDesc); - imageSpec.extra_attribs = metadata; // add custom metadata - - imageSpec.attribute("jpeg:subsampling", "4:4:4"); // if possible, always subsampling 4:4:4 for jpeg - imageSpec.attribute("CompressionQuality", 100); // if possible, best compression quality - imageSpec.attribute("compression", isEXR ? "piz" : "none"); // if possible, set compression (piz for EXR, none for the other) - - const oiio::ImageBuf imgBuf = oiio::ImageBuf(imageSpec, const_cast(buffer.data())); // original image buffer - const oiio::ImageBuf* outBuf = &imgBuf; // buffer to write - - oiio::ImageBuf colorspaceBuf; // buffer for image colorspace modification - if(colorspace.from != colorspace.to) - { - oiio::ImageBufAlgo::colorconvert(colorspaceBuf, *outBuf, EImageColorSpace_enumToString(colorspace.from), EImageColorSpace_enumToString(colorspace.to)); - outBuf = &colorspaceBuf; - } - - oiio::ImageBuf formatBuf; // buffer for image format modification - if(imageQuality == EImageQuality::OPTIMIZED && isEXR) - { - formatBuf.copy(*outBuf, oiio::TypeDesc::HALF); // override format, use half instead of float - outBuf = &formatBuf; - } - - // write image - if(!outBuf->write(tmpPath)) - throw std::runtime_error("Can't write output image file '" + path + "'."); - - // rename temporay filename - fs::rename(tmpPath, path); -} - -void writeImage(const std::string& path, int width, int height, const std::vector& buffer, EImageQuality imageQuality, OutputFileColorSpace colorspace, const oiio::ParamValueList& metadata) -{ - writeImage(path, oiio::TypeDesc::UCHAR, width, height, 1, buffer, imageQuality, colorspace, metadata); -} - -void writeImage(const std::string& path, int width, int height, const std::vector& buffer, EImageQuality imageQuality, OutputFileColorSpace colorspace, const oiio::ParamValueList& metadata) -{ - writeImage(path, oiio::TypeDesc::UINT16, width, height, 1, buffer, imageQuality, colorspace, metadata); -} - -void writeImage(const std::string& path, int width, int height, const std::vector& buffer, EImageQuality imageQuality, OutputFileColorSpace colorspace, const oiio::ParamValueList& metadata) -{ - writeImage(path, oiio::TypeDesc::UCHAR, width, height, 3, buffer, imageQuality, colorspace, metadata); -} - -void writeImage(const std::string& path, int width, int height, const std::vector& buffer, EImageQuality imageQuality, OutputFileColorSpace colorspace, const oiio::ParamValueList& metadata) -{ - writeImage(path, oiio::TypeDesc::FLOAT, width, height, 1, buffer, imageQuality, colorspace, metadata); -} - -void writeImage(const std::string& path, int width, int height, const std::vector& buffer, EImageQuality imageQuality, OutputFileColorSpace colorspace, const oiio::ParamValueList& metadata) -{ - writeImage(path, oiio::TypeDesc::FLOAT, width, height, 3, buffer, imageQuality, colorspace, metadata); -} - -template -void transposeImage(oiio::TypeDesc typeDesc, - int width, - int height, - int nchannels, - std::vector& buffer) -{ - oiio::ImageSpec imageSpec(width, height, nchannels, typeDesc); - - oiio::ImageBuf inBuf(imageSpec, buffer.data()); - oiio::ImageBuf transposeBuf; - - oiio::ImageBufAlgo::transpose(transposeBuf, inBuf, oiio::ROI::All()); - - transposeBuf.get_pixels(oiio::ROI::All(), typeDesc, buffer.data()); -} - -void transposeImage(int width, int height, std::vector& buffer) -{ - transposeImage(oiio::TypeDesc::UCHAR, width, height, 1, buffer); -} - -void transposeImage(int width, int height, std::vector& buffer) -{ - transposeImage(oiio::TypeDesc::UCHAR, width, height, 3, buffer); -} - -void transposeImage(int width, int height, std::vector& buffer) -{ - transposeImage(oiio::TypeDesc::FLOAT, width, height, 1, buffer); -} - -void transposeImage(int width, int height, std::vector& buffer) -{ - transposeImage(oiio::TypeDesc::FLOAT, width, height, 3, buffer); -} - -template -void resizeImage(oiio::TypeDesc typeDesc, - int inWidth, - int inHeight, - int nchannels, - int downscale, - const std::vector& inBuffer, - std::vector& outBuffer, - const std::string& filter = "", - float filterSize = 0) -{ - const int outWidth = inWidth / downscale; - const int outHeight = inHeight / downscale; - - outBuffer.resize(outWidth * outHeight); - - const oiio::ImageBuf inBuf(oiio::ImageSpec(inWidth, inHeight, nchannels, typeDesc), const_cast(inBuffer.data())); - oiio::ImageBuf outBuf(oiio::ImageSpec(outWidth, outHeight, nchannels, typeDesc), outBuffer.data()); - - oiio::ImageBufAlgo::resize(outBuf, inBuf, filter, filterSize, oiio::ROI::All()); -} - -void resizeImage(int inWidth, int inHeight, int downscale, const std::vector& inBuffer, std::vector& outBuffer, const std::string& filter, float filterSize) -{ - resizeImage(oiio::TypeDesc::UCHAR, inWidth, inHeight, 1, downscale, inBuffer, outBuffer, filter, filterSize); -} - -void resizeImage(int inWidth, int inHeight, int downscale, const std::vector& inBuffer, std::vector& outBuffer, const std::string& filter, float filterSize) -{ - resizeImage(oiio::TypeDesc::UCHAR, inWidth, inHeight, 3, downscale, inBuffer, outBuffer, filter, filterSize); -} - -void resizeImage(int inWidth, int inHeight, int downscale, const std::vector& inBuffer, std::vector& outBuffer, const std::string& filter, float filterSize) -{ - resizeImage(oiio::TypeDesc::FLOAT, inWidth, inHeight, 1, downscale, inBuffer, outBuffer, filter, filterSize); -} - -void resizeImage(int inWidth, int inHeight, int downscale, const std::vector& inBuffer, std::vector& outBuffer, const std::string& filter, float filterSize) -{ - resizeImage(oiio::TypeDesc::FLOAT, inWidth, inHeight, 3, downscale, inBuffer, outBuffer, filter, filterSize); -} - -template -void convolveImage(oiio::TypeDesc typeDesc, - int inWidth, - int inHeight, - int nchannels, - const std::vector& inBuffer, - std::vector& outBuffer, - const std::string& kernel, - float kernelWidth, - float kernelHeight) -{ - outBuffer.resize(inBuffer.size()); - - const oiio::ImageBuf inBuf(oiio::ImageSpec(inWidth, inHeight, nchannels, typeDesc), const_cast(inBuffer.data())); - oiio::ImageBuf outBuf(oiio::ImageSpec(inWidth, inHeight, nchannels, typeDesc), outBuffer.data()); - - oiio::ImageBuf K; - oiio::ImageBufAlgo::make_kernel(K, kernel, kernelWidth, kernelHeight); - - oiio::ImageBufAlgo::convolve(outBuf, inBuf, K); -} - - -void convolveImage(int inWidth, int inHeight, const std::vector& inBuffer, std::vector& outBuffer, const std::string& kernel, float kernelWidth, float kernelHeight) -{ - convolveImage(oiio::TypeDesc::UCHAR, inWidth, inHeight, 1, inBuffer, outBuffer, kernel, kernelWidth, kernelHeight); -} - -void convolveImage(int inWidth, int inHeight, const std::vector& inBuffer, std::vector& outBuffer, const std::string& kernel, float kernelWidth, float kernelHeight) -{ - convolveImage(oiio::TypeDesc::UCHAR, inWidth, inHeight, 3, inBuffer, outBuffer, kernel, kernelWidth, kernelHeight); -} - -void convolveImage(int inWidth, int inHeight, const std::vector& inBuffer, std::vector& outBuffer, const std::string& kernel, float kernelWidth, float kernelHeight) -{ - convolveImage(oiio::TypeDesc::FLOAT, inWidth, inHeight, 1, inBuffer, outBuffer, kernel, kernelWidth, kernelHeight); -} - -void convolveImage(int inWidth, int inHeight, const std::vector& inBuffer, std::vector& outBuffer, const std::string& kernel, float kernelWidth, float kernelHeight) -{ - convolveImage(oiio::TypeDesc::FLOAT, inWidth, inHeight, 3, inBuffer, outBuffer, kernel, kernelWidth, kernelHeight); -} - -void fillHoles(int inWidth, int inHeight, std::vector& colorBuffer, const std::vector& alphaBuffer) -{ - oiio::ImageBuf rgbBuf(oiio::ImageSpec(inWidth, inHeight, 3, oiio::TypeDesc::FLOAT), colorBuffer.data()); - const oiio::ImageBuf alphaBuf(oiio::ImageSpec(inWidth, inHeight, 1, oiio::TypeDesc::FLOAT), const_cast(alphaBuffer.data())); - - // Create RGBA ImageBuf from source buffers with correct channel names - // (identified alpha channel is needed for fillholes_pushpull) - oiio::ImageBuf rgbaBuf; - oiio::ImageBufAlgo::channel_append(rgbaBuf, rgbBuf, alphaBuf); - rgbaBuf.specmod().default_channel_names(); - - // Temp RGBA buffer to store fillholes result - oiio::ImageBuf filledBuf; - oiio::ImageBufAlgo::fillholes_pushpull(filledBuf, rgbaBuf); - rgbaBuf.clear(); - - // Copy result to original RGB buffer - oiio::ImageBufAlgo::copy(rgbBuf, filledBuf); -} - -} // namespace imageIO -} // namespace aliceVision diff --git a/src/aliceVision/imageIO/image.hpp b/src/aliceVision/imageIO/image.hpp deleted file mode 100644 index 2b3c39fdc4..0000000000 --- a/src/aliceVision/imageIO/image.hpp +++ /dev/null @@ -1,210 +0,0 @@ -// This file is part of the AliceVision project. -// Copyright (c) 2017 AliceVision contributors. -// This Source Code Form is subject to the terms of the Mozilla Public License, -// v. 2.0. If a copy of the MPL was not distributed with this file, -// You can obtain one at https://mozilla.org/MPL/2.0/. - -#pragma once - -#include -#include - -#include - -namespace oiio = OIIO; - -namespace aliceVision { - -class rgb; -class Color; - -namespace imageIO { - -/** - * @brief Available image color space for pipeline input / output - */ -enum class EImageColorSpace -{ - AUTO, - LINEAR, - SRGB, - NO_CONVERSION -}; - - -struct OutputFileColorSpace -{ - EImageColorSpace from = EImageColorSpace::LINEAR; - EImageColorSpace to = EImageColorSpace::AUTO; - - OutputFileColorSpace(EImageColorSpace from_, EImageColorSpace to_) - : from(from_) - , to(to_) - { - } - /// @brief Assumes that @p from is LINEAR - OutputFileColorSpace(EImageColorSpace to_) - { - if(to_ == EImageColorSpace::NO_CONVERSION) - to = from; - else - to = to_; - } - OutputFileColorSpace() - { - } -}; - -std::string EImageColorSpace_enumToString(const EImageColorSpace colorSpace); - -/** - * @brief Available image qualities for pipeline output - */ -enum class EImageQuality -{ - OPTIMIZED, - LOSSLESS -}; - -/** - * @brief get informations about each image quality - * @return String - */ -std::string EImageQuality_informations(); - -/** - * @brief returns the EImageQuality enum from a string. - * @param[in] imageQuality the input string. - * @return the associated EImageQuality enum. - */ -EImageQuality EImageQuality_stringToEnum(const std::string& imageQuality); - -/** - * @brief converts an EImageQuality enum to a string. - * @param[in] imageQuality the EImageQuality enum to convert. - * @return the string associated to the EImageQuality enum. - */ -std::string EImageQuality_enumToString(const EImageQuality imageQuality); - -/** - * @brief write an EImageQuality enum into a stream by converting it to a string. - * @param[in] os the stream where to write the imageType. - * @param[in] imageQuality the EImageQuality enum to write. - * @return the modified stream. - */ -std::ostream& operator<<(std::ostream& os, EImageQuality imageQuality); - -/** - * @brief read a EImageQuality enum from a stream. - * @param[in] in the stream from which the enum is read. - * @param[out] imageQuality the EImageQuality enum read from the stream. - * @return the modified stream without the read enum. - */ -std::istream& operator>>(std::istream& in, EImageQuality& imageQuality); - -/** - * @brief convert a metadata string map into an oiio::ParamValueList - * @param[in] metadataMap string map - * @return oiio::ParamValueList - */ -oiio::ParamValueList getMetadataFromMap(const std::map& metadataMap); - -/** - * @brief read image dimension from a given path - * @param[in] path The given path to the image - * @param[out] width The image width - * @param[out] height The image height - * @param[out] nchannels The image channel number - */ -void readImageSpec(const std::string& path, int& width, int& height, int& nchannels); - -/** - * @brief read image metadata from a given path - * @param[in] path The given path to the image - * @param[out] metadata The image metadata - */ -void readImageMetadata(const std::string& path, oiio::ParamValueList& metadata); - -/** - * @brief read an image with a given path and buffer - * @param[in] path The given path to the image - * @param[out] width The output image width - * @param[out] height The output image height - * @param[out] buffer The output image buffer - * @param[in] image color space - */ -void readImage(const std::string& path, int& width, int& height, std::vector& buffer, EImageColorSpace imageColorSpace); -void readImage(const std::string& path, int& width, int& height, std::vector& buffer, EImageColorSpace imageColorSpace); -void readImage(const std::string& path, int& width, int& height, std::vector& buffer, EImageColorSpace imageColorSpace); -void readImage(const std::string& path, int& width, int& height, std::vector& buffer, EImageColorSpace imageColorSpace); -void readImage(const std::string& path, int& width, int& height, std::vector& buffer, EImageColorSpace imageColorSpace); - -/** - * @brief write an image with a given path and buffer - * @param[in] path The given path to the image - * @param[in] width The input image width - * @param[in] height The input image height - * @param[in] buffer The input image buffer - */ -void writeImage(const std::string& path, int width, int height, const std::vector& buffer, EImageQuality imageQuality, OutputFileColorSpace colorspace, const oiio::ParamValueList& metadata = oiio::ParamValueList()); -void writeImage(const std::string& path, int width, int height, const std::vector& buffer, EImageQuality imageQuality, OutputFileColorSpace colorspace, const oiio::ParamValueList& metadata = oiio::ParamValueList()); -void writeImage(const std::string& path, int width, int height, const std::vector& buffer, EImageQuality imageQuality, OutputFileColorSpace colorspace, const oiio::ParamValueList& metadata = oiio::ParamValueList()); -void writeImage(const std::string& path, int width, int height, const std::vector& buffer, EImageQuality imageQuality, OutputFileColorSpace colorspace, const oiio::ParamValueList& metadata = oiio::ParamValueList()); -void writeImage(const std::string& path, int width, int height, const std::vector& buffer, EImageQuality imageQuality, OutputFileColorSpace colorspace, const oiio::ParamValueList& metadata = oiio::ParamValueList()); - -/** - * @brief transpose a given image buffer - * @param[in] width The image buffer width - * @param[in] height The image buffer height - * @param[in,out] buffer The image buffer - */ -void transposeImage(int width, int height, std::vector& buffer); -void transposeImage(int width, int height, std::vector& buffer); -void transposeImage(int width, int height, std::vector& buffer); -void transposeImage(int width, int height, std::vector& buffer); - -/** - * @brief resize a given image buffer - * @param[in] inWidth The input image buffer width - * @param[in] inHeight The input image buffer height - * @param[in] downscale The resize downscale - * @param[in] inBuffer The input image buffer - * @param[out] outBuffer The output image buffer - * @param[in] filter The name of a high-quality filter to use when resampling - * Default is bilinear resampling - * See openImageIO documentation "ImageBufAlgo filtername" - * @param[in] filterSize The resize filter size - */ -void resizeImage(int inWidth, int inHeight, int downscale, const std::vector& inBuffer, std::vector& outBuffer, const std::string& filter = "", float filterSize = 0); -void resizeImage(int inWidth, int inHeight, int downscale, const std::vector& inBuffer, std::vector& outBuffer, const std::string& filter = "", float filterSize = 0); -void resizeImage(int inWidth, int inHeight, int downscale, const std::vector& inBuffer, std::vector& outBuffer, const std::string& filter = "", float filterSize = 0); -void resizeImage(int inWidth, int inHeight, int downscale, const std::vector& inBuffer, std::vector& outBuffer, const std::string& filter = "", float filterSize = 0); - -/** - * @brief convolve a given image buffer - * @param[in] inWidth The input image buffer width - * @param[in] inHeight The input image buffer heightt - * @param[in] inBuffer The input image buffer - * @param[out] outBuffer outBuffer The output image buffer - * @param[in] kernel The kernel name, can be "gaussian", "sharp-gaussian", "box", ... - * Default is gaussian kernel - * See openImageIO documentation "ImageBufAlgo.make_kernel" - * @param[in] kernelWidth The kernel width - * @param[in] kernelHeight The kernal height - */ -void convolveImage(int inWidth, int inHeight, const std::vector& inBuffer, std::vector& outBuffer, const std::string& kernel = "gaussian", float kernelWidth = 5.0f, float kernelHeight = 5.0f); -void convolveImage(int inWidth, int inHeight, const std::vector& inBuffer, std::vector& outBuffer, const std::string& kernel = "gaussian", float kernelWidth = 5.0f, float kernelHeight = 5.0f); -void convolveImage(int inWidth, int inHeight, const std::vector& inBuffer, std::vector& outBuffer, const std::string& kernel = "gaussian", float kernelWidth = 5.0f, float kernelHeight = 5.0f); -void convolveImage(int inWidth, int inHeight, const std::vector& inBuffer, std::vector& outBuffer, const std::string& kernel = "gaussian", float kernelWidth = 5.0f, float kernelHeight = 5.0f); - -/** - * @brief fill holes in a given image buffer with plausible values - * @param[in] inWidth The input image buffer width - * @param[in] inHeight The input image buffer height - * @param[in,out] colorBuffer The image buffer to fill - * @param[in] alphaBuffer The input alpha buffer containing 0.0/1.0 for empty/valid pixels - */ -void fillHoles(int inWidth, int inHeight, std::vector& colorBuffer, const std::vector& alphaBuffer); - -} // namespace imageIO -} // namespace aliceVision diff --git a/src/aliceVision/imageIO/imageScaledColors.cpp b/src/aliceVision/imageIO/imageScaledColors.cpp deleted file mode 100644 index b305355ce1..0000000000 --- a/src/aliceVision/imageIO/imageScaledColors.cpp +++ /dev/null @@ -1,78 +0,0 @@ -// This file is part of the AliceVision project. -// Copyright (c) 2017 AliceVision contributors. -// This Source Code Form is subject to the terms of the Mozilla Public License, -// v. 2.0. If a copy of the MPL was not distributed with this file, -// You can obtain one at https://mozilla.org/MPL/2.0/. - -#include "imageScaledColors.hpp" -#include -#include -#include - -namespace aliceVision { -namespace imageIO { - -template -void writeImageScaledColors(const std::string& savePath, T* buffer, int width, int height, - T minVal, T maxVal, bool transpose) -{ - std::vector colorBuffer(width * height); - std::size_t index = 0; - - for(int y = 0; y < height; y++) - { - for(int x = 0; x < width; x++) - { - float val; - - if(!transpose) - val = static_cast(buffer[x * height + y]); - else - val = static_cast(buffer[y * width + x]); - - const float s = 1.0f - (static_cast(maxVal) - std::max(static_cast(minVal), val) / static_cast(maxVal) - static_cast(minVal)); - colorBuffer.at(index++) = getColorFromJetColorMap(s); - } - } - writeImage(savePath, width, height, colorBuffer, EImageQuality::OPTIMIZED, EImageColorSpace::NO_CONVERSION); -} - -void writeImageScaledColors(const std::string& path, int width, int height, float* buffer, bool transpose) -{ - std::vector colorBuffer(width * height); - std::size_t index = 0; - - for(int y = 0; y < height; y++) - { - for(int x = 0; x < width; x++) - { - float val; - - if(!transpose) - val = buffer[x * height + y]; - else - val = buffer[y * width + x]; - - colorBuffer.at(index++) = Color(val, val, val); - } - } - writeImage(path, width, height, colorBuffer, EImageQuality::OPTIMIZED, EImageColorSpace::NO_CONVERSION); -} - -void writeImageScaledColors(const std::string& path, int width, int height, float minVal, float maxVal, float* buffer, bool transpose) -{ - writeImageScaledColors(path, buffer, width, height, minVal, maxVal, transpose); -} - -void writeImageScaledColors(const std::string& path, int width, int height, int minVal, int maxVal, int* buffer, bool transpose) -{ - writeImageScaledColors(path, buffer, width, height, minVal, maxVal, transpose); -} - -void writeImageScaledColors(const std::string& path, int width, int height, unsigned short minVal, unsigned short maxVal, unsigned short* buffer, bool transpose) -{ - writeImageScaledColors(path, buffer, width, height, minVal, maxVal, transpose); -} - -} // namespace imageIO -} // namespace aliceVision diff --git a/src/aliceVision/imageIO/imageScaledColors.hpp b/src/aliceVision/imageIO/imageScaledColors.hpp deleted file mode 100644 index 540a1c08b1..0000000000 --- a/src/aliceVision/imageIO/imageScaledColors.hpp +++ /dev/null @@ -1,28 +0,0 @@ -// This file is part of the AliceVision project. -// Copyright (c) 2017 AliceVision contributors. -// This Source Code Form is subject to the terms of the Mozilla Public License, -// v. 2.0. If a copy of the MPL was not distributed with this file, -// You can obtain one at https://mozilla.org/MPL/2.0/. - -#pragma once - -#include - -namespace aliceVision { -namespace imageIO { - -/** - * @brief Save an image with scaled colors - * @param[in] savePath The output image path - * @param[in] buffer The input image buffer - * @param[in] width The input image width - * @param[in] height The input image height - * @param[in] transpose - */ -void writeImageScaledColors(const std::string& path, int width, int height, float* buffer, bool transpose = false); -void writeImageScaledColors(const std::string& path, int width, int height, float minVal, float maxVal, float* buffer, bool transpose = false); -void writeImageScaledColors(const std::string& path, int width, int height, int minVal, int maxVal, int* buffer, bool transpose = false); -void writeImageScaledColors(const std::string& path, int width, int height, unsigned short minVal, unsigned short maxVal, unsigned short* buffer, bool transpose); - -} // namespace imageIO -} // namespace aliceVision diff --git a/src/aliceVision/mesh/CMakeLists.txt b/src/aliceVision/mesh/CMakeLists.txt index 2697ea9b6c..527344d926 100644 --- a/src/aliceVision/mesh/CMakeLists.txt +++ b/src/aliceVision/mesh/CMakeLists.txt @@ -28,7 +28,6 @@ alicevision_add_library(aliceVision_mesh PUBLIC_LINKS aliceVision_mvsData aliceVision_mvsUtils - aliceVision_imageIO Geogram::geogram ${Boost_FILESYSTEM_LIBRARY} PRIVATE_LINKS diff --git a/src/aliceVision/mesh/Texturing.cpp b/src/aliceVision/mesh/Texturing.cpp index 08c1e336c9..9e3a4709f2 100644 --- a/src/aliceVision/mesh/Texturing.cpp +++ b/src/aliceVision/mesh/Texturing.cpp @@ -15,7 +15,7 @@ #include #include #include -#include +#include #include #include @@ -256,7 +256,7 @@ void Texturing::generateUVsBasicMethod(mvsUtils::MultiViewParams& mp) } void Texturing::generateTextures(const mvsUtils::MultiViewParams &mp, - const boost::filesystem::path &outPath, EImageFileType textureFileType) + const boost::filesystem::path &outPath, imageIO::EImageFileType textureFileType) { ALICEVISION_LOG_INFO("Texturing: Use multiband blending with the following contributions per band:"); @@ -320,7 +320,7 @@ void Texturing::generateTextures(const mvsUtils::MultiViewParams &mp, } void Texturing::generateTexturesSubSet(const mvsUtils::MultiViewParams& mp, - const std::vector& atlasIDs, mvsUtils::ImagesCache& imageCache, const bfs::path& outPath, EImageFileType textureFileType) + const std::vector& atlasIDs, mvsUtils::ImagesCache& imageCache, const bfs::path& outPath, imageIO::EImageFileType textureFileType) { if(atlasIDs.size() > _atlases.size()) throw std::runtime_error("Invalid atlas IDs "); @@ -679,7 +679,7 @@ void Texturing::generateTexturesSubSet(const mvsUtils::MultiViewParams& mp, } void Texturing::writeTexture(AccuImage& atlasTexture, const std::size_t atlasID, const boost::filesystem::path &outPath, - EImageFileType textureFileType, const int level) + imageIO::EImageFileType textureFileType, const int level) { unsigned int outTextureSide = texParams.textureSide; // WARNING: we modify the "imgCount" to apply the padding (to avoid the creation of a new buffer) @@ -810,7 +810,7 @@ void Texturing::writeTexture(AccuImage& atlasTexture, const std::size_t atlasID, std::swap(resizedColorBuffer, atlasTexture.img); } - const std::string textureName = "texture_" + std::to_string(1001 + atlasID) + (level < 0 ? "" : "_" + std::to_string(level)) + "." + EImageFileType_enumToString(textureFileType); // starts at '1001' for UDIM compatibility + const std::string textureName = "texture_" + std::to_string(1001 + atlasID) + (level < 0 ? "" : "_" + std::to_string(level)) + "." + imageIO::EImageFileType_enumToString(textureFileType); // starts at '1001' for UDIM compatibility bfs::path texturePath = outPath / textureName; ALICEVISION_LOG_INFO(" - Writing texture file: " << texturePath.string()); @@ -942,7 +942,7 @@ void Texturing::unwrap(mvsUtils::MultiViewParams& mp, EUnwrapMethod method) } } -void Texturing::saveAsOBJ(const bfs::path& dir, const std::string& basename, EImageFileType textureFileType) +void Texturing::saveAsOBJ(const bfs::path& dir, const std::string& basename, imageIO::EImageFileType textureFileType) { ALICEVISION_LOG_INFO("Writing obj and mtl file."); @@ -1004,7 +1004,7 @@ void Texturing::saveAsOBJ(const bfs::path& dir, const std::string& basename, EIm for(size_t atlasId=0; atlasId < _atlases.size(); ++atlasId) { const std::size_t textureId = 1001 + atlasId; // starts at '1001' for UDIM compatibility - const std::string textureName = "texture_" + std::to_string(textureId) + "." + EImageFileType_enumToString(textureFileType); + const std::string textureName = "texture_" + std::to_string(textureId) + "." + imageIO::EImageFileType_enumToString(textureFileType); fprintf(fmtl, "newmtl TextureAtlas_%i\n", textureId); fprintf(fmtl, "Ka 0.6 0.6 0.6\n"); diff --git a/src/aliceVision/mesh/Texturing.hpp b/src/aliceVision/mesh/Texturing.hpp index 29a1195472..3bd1131867 100644 --- a/src/aliceVision/mesh/Texturing.hpp +++ b/src/aliceVision/mesh/Texturing.hpp @@ -175,19 +175,19 @@ struct Texturing /// Generate texture files for all texture atlases void generateTextures(const mvsUtils::MultiViewParams& mp, - const bfs::path &outPath, EImageFileType textureFileType = EImageFileType::PNG); + const bfs::path &outPath, imageIO::EImageFileType textureFileType = imageIO::EImageFileType::PNG); /// Generate texture files for the given sub-set of texture atlases void generateTexturesSubSet(const mvsUtils::MultiViewParams& mp, const std::vector& atlasIDs, mvsUtils::ImagesCache& imageCache, - const bfs::path &outPath, EImageFileType textureFileType = EImageFileType::PNG); + const bfs::path &outPath, imageIO::EImageFileType textureFileType = imageIO::EImageFileType::PNG); ///Fill holes and write texture files for the given texture atlas void writeTexture(AccuImage& atlasTexture, const std::size_t atlasID, const bfs::path& outPath, - EImageFileType textureFileType, const int level); + imageIO::EImageFileType textureFileType, const int level); /// Save textured mesh as an OBJ + MTL file - void saveAsOBJ(const bfs::path& dir, const std::string& basename, EImageFileType textureFileType = EImageFileType::PNG); + void saveAsOBJ(const bfs::path& dir, const std::string& basename, imageIO::EImageFileType textureFileType = imageIO::EImageFileType::PNG); }; } // namespace mesh diff --git a/src/aliceVision/mvsData/Image.cpp b/src/aliceVision/mvsData/Image.cpp index 82cf4d96f1..9e0b2ebf6b 100644 --- a/src/aliceVision/mvsData/Image.cpp +++ b/src/aliceVision/mvsData/Image.cpp @@ -6,7 +6,7 @@ #include "Image.hpp" -#include +#include namespace aliceVision{ diff --git a/src/aliceVision/mvsData/imageIO.cpp b/src/aliceVision/mvsData/imageIO.cpp index dcfe600986..f1f9e9ae1d 100644 --- a/src/aliceVision/mvsData/imageIO.cpp +++ b/src/aliceVision/mvsData/imageIO.cpp @@ -6,6 +6,18 @@ #include "imageIO.hpp" +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include #include #include @@ -13,10 +25,69 @@ #include #include +#include +#include +#include +namespace fs = boost::filesystem; namespace aliceVision { +namespace imageIO { + +std::string EImageColorSpace_enumToString(const EImageColorSpace colorSpace) +{ + switch(colorSpace) + { + case EImageColorSpace::SRGB: return "sRGB"; // WARNING: string should match with OIIO definitions + case EImageColorSpace::LINEAR: return "Linear"; + default: ; + } + throw std::out_of_range("No string defined for EImageColorSpace: " + std::to_string(int(colorSpace))); +} + + +std::string EImageQuality_informations() +{ + return "Image quality :\n" + "* optimized \n" + "* lossless "; +} + +EImageQuality EImageQuality_stringToEnum(const std::string& imageQuality) +{ + std::string type = imageQuality; + std::transform(type.begin(), type.end(), type.begin(), ::tolower); //tolower + + if(type == "optimized") return EImageQuality::OPTIMIZED; + if(type == "lossless") return EImageQuality::LOSSLESS; + + throw std::out_of_range("Invalid image quality : " + imageQuality); +} + +std::string EImageQuality_enumToString(const EImageQuality imageQuality) +{ + switch(imageQuality) + { + case EImageQuality::OPTIMIZED: return "optimized"; + case EImageQuality::LOSSLESS: return "lossless"; + } + throw std::out_of_range("Invalid EImageQuality enum"); +} + +std::ostream& operator<<(std::ostream& os, EImageQuality imageQuality) +{ + return os << EImageQuality_enumToString(imageQuality); +} + +std::istream& operator>>(std::istream& in, EImageQuality& imageQuality) +{ + std::string token; + in >> token; + imageQuality = EImageQuality_stringToEnum(token); + return in; +} + std::string EImageFileType_informations() { return "Image file type :\n" @@ -64,6 +135,47 @@ std::istream& operator>>(std::istream& in, EImageFileType& imageFileType) return in; } +oiio::ParamValueList getMetadataFromMap(const std::map& metadataMap) +{ + oiio::ParamValueList metadata; + for(const auto& metadataPair : metadataMap) + metadata.push_back(oiio::ParamValue(metadataPair.first, metadataPair.second)); + return metadata; +} + +void readImageSpec(const std::string& path, + int& width, + int& height, + int& nchannels) +{ + ALICEVISION_LOG_DEBUG("[IO] Read Image Spec: " << path); + std::unique_ptr in(oiio::ImageInput::open(path)); + + if(!in) + throw std::runtime_error("Can't find/open image file '" + path + "'."); + + const oiio::ImageSpec &spec = in->spec(); + + width = spec.width; + height = spec.height; + nchannels = spec.nchannels; + + in->close(); +} + +void readImageMetadata(const std::string& path, oiio::ParamValueList& metadata) +{ + ALICEVISION_LOG_DEBUG("[IO] Read Image Metadata: " << path); + std::unique_ptr in(oiio::ImageInput::open(path)); + + if(!in) + throw std::runtime_error("Can't find/open image file '" + path + "'."); + + metadata = in->spec().extra_attribs; + + in->close(); +} + bool isSupportedUndistortFormat(const std::string &ext) { static const std::array supportedExtensions = {".jpg", ".jpeg", ".png", ".tif", ".tiff", ".exr"}; @@ -72,4 +184,396 @@ bool isSupportedUndistortFormat(const std::string &ext) return(std::find(start, end, boost::to_lower_copy(ext)) != end); } +template +void readImage(const std::string& path, + oiio::TypeDesc typeDesc, + int nchannels, + int& width, + int& height, + std::vector& buffer, + EImageColorSpace imageColorSpace) +{ + ALICEVISION_LOG_DEBUG("[IO] Read Image: " << path); + + // check requested channels number + assert(nchannels == 1 || nchannels >= 3); + + oiio::ImageSpec configSpec; + + // libRAW configuration + configSpec.attribute("raw:auto_bright", 0); // don't want exposure correction + configSpec.attribute("raw:use_camera_wb", 1); // want white balance correction + configSpec.attribute("raw:use_camera_matrix", 3); // want to use embeded color profile + + oiio::ImageBuf inBuf(path, 0, 0, NULL, &configSpec); + + inBuf.read(0, 0, true, oiio::TypeDesc::FLOAT); // force image convertion to float (for grayscale and color space convertion) + + if(!inBuf.initialized()) + throw std::runtime_error("Can't find/open image file '" + path + "'."); + + const oiio::ImageSpec& inSpec = inBuf.spec(); + + // check picture channels number + if(inSpec.nchannels != 1 && inSpec.nchannels < 3) + throw std::runtime_error("Can't load channels of image file '" + path + "'."); + + // color conversion + if(imageColorSpace == EImageColorSpace::AUTO) + throw std::runtime_error("You must specify a requested color space for image file '" + path + "'."); + + if(imageColorSpace == EImageColorSpace::SRGB) // color conversion to sRGB + { + const std::string& colorSpace = inSpec.get_string_attribute("oiio:ColorSpace", "sRGB"); // default image color space is sRGB + if(colorSpace != "sRGB") + oiio::ImageBufAlgo::colorconvert(inBuf, inBuf, colorSpace, "sRGB"); + } + else if(imageColorSpace == EImageColorSpace::LINEAR) // color conversion to linear + { + const std::string& colorSpace = inSpec.get_string_attribute("oiio:ColorSpace", "sRGB"); + if(colorSpace != "Linear") + oiio::ImageBufAlgo::colorconvert(inBuf, inBuf, colorSpace, "Linear"); + } + + // convert to grayscale if needed + if(nchannels == 1 && inSpec.nchannels >= 3) + { + // convertion region of interest (for inSpec.nchannels > 3) + oiio::ROI convertionROI = inBuf.roi(); + convertionROI.chbegin = 0; + convertionROI.chend = 3; + + // compute luminance via a weighted sum of R,G,B + // (assuming Rec709 primaries and a linear scale) + const float weights[3] = {.2126, .7152, .0722}; + oiio::ImageBuf grayscaleBuf; + oiio::ImageBufAlgo::channel_sum(grayscaleBuf, inBuf, weights, convertionROI); + inBuf.copy(grayscaleBuf); + } + + // add missing channels + if(nchannels > inSpec.nchannels) + { + oiio::ImageSpec requestedSpec(inSpec.width, inSpec.height, nchannels, typeDesc); + oiio::ImageBuf requestedBuf(requestedSpec); + + // duplicate first channel for RGB + if(requestedSpec.nchannels >= 3 && inSpec.nchannels < 3) + { + oiio::ImageBufAlgo::paste(requestedBuf, 0, 0, 0, 0, inBuf); + oiio::ImageBufAlgo::paste(requestedBuf, 0, 0, 0, 1, inBuf); + oiio::ImageBufAlgo::paste(requestedBuf, 0, 0, 0, 2, inBuf); + } + + inBuf.copy(requestedBuf); + } + + width = inSpec.width; + height = inSpec.height; + + buffer.resize(inSpec.width * inSpec.height); + + { + oiio::ROI exportROI = inBuf.roi(); + exportROI.chbegin = 0; + exportROI.chend = nchannels; + + inBuf.get_pixels(exportROI, typeDesc, buffer.data()); + } +} + +void readImage(const std::string& path, int& width, int& height, std::vector& buffer, EImageColorSpace imageColorSpace) +{ + readImage(path, oiio::TypeDesc::UCHAR, 1, width, height, buffer, imageColorSpace); +} + +void readImage(const std::string& path, int& width, int& height, std::vector& buffer, EImageColorSpace imageColorSpace) +{ + readImage(path, oiio::TypeDesc::UINT16, 1, width, height, buffer, imageColorSpace); +} + +void readImage(const std::string& path, int& width, int& height, std::vector& buffer, EImageColorSpace imageColorSpace) +{ + readImage(path, oiio::TypeDesc::UCHAR, 3, width, height, buffer, imageColorSpace); +} + +void readImage(const std::string& path, int& width, int& height, std::vector& buffer, EImageColorSpace imageColorSpace) +{ + readImage(path, oiio::TypeDesc::FLOAT, 1, width, height, buffer, imageColorSpace); +} + +void readImage(const std::string& path, int& width, int& height, std::vector& buffer, EImageColorSpace imageColorSpace) +{ + readImage(path, oiio::TypeDesc::FLOAT, 3, width, height, buffer, imageColorSpace); +} + +void readImage(const std::string& path, Image& image, EImageColorSpace imageColorSpace) +{ + int width, height; + readImage(path, oiio::TypeDesc::FLOAT, 3, width, height, image.data(), imageColorSpace); + image.setWidth(width); + image.setHeight(height); +} + +template +void writeImage(const std::string& path, + oiio::TypeDesc typeDesc, + int width, + int height, + int nchannels, + const std::vector& buffer, + EImageQuality imageQuality, + OutputFileColorSpace colorspace, + const oiio::ParamValueList& metadata) +{ + const fs::path bPath = fs::path(path); + const std::string extension = bPath.extension().string(); + const std::string tmpPath = (bPath.parent_path() / bPath.stem()).string() + "." + fs::unique_path().string() + extension; + const bool isEXR = (extension == ".exr"); + //const bool isTIF = (extension == ".tif"); + const bool isJPG = (extension == ".jpg"); + const bool isPNG = (extension == ".png"); + + if(colorspace.to == EImageColorSpace::AUTO) + { + if(isJPG || isPNG) + colorspace.to = EImageColorSpace::SRGB; + else + colorspace.to = EImageColorSpace::LINEAR; + } + + ALICEVISION_LOG_DEBUG("[IO] Write Image: " << path << std::endl + << "\t- width: " << width << std::endl + << "\t- height: " << height << std::endl + << "\t- channels: " << nchannels); + + oiio::ImageSpec imageSpec(width, height, nchannels, typeDesc); + imageSpec.extra_attribs = metadata; // add custom metadata + + imageSpec.attribute("jpeg:subsampling", "4:4:4"); // if possible, always subsampling 4:4:4 for jpeg + imageSpec.attribute("CompressionQuality", 100); // if possible, best compression quality + imageSpec.attribute("compression", isEXR ? "piz" : "none"); // if possible, set compression (piz for EXR, none for the other) + + const oiio::ImageBuf imgBuf = oiio::ImageBuf(imageSpec, const_cast(buffer.data())); // original image buffer + const oiio::ImageBuf* outBuf = &imgBuf; // buffer to write + + oiio::ImageBuf colorspaceBuf; // buffer for image colorspace modification + if(colorspace.from != colorspace.to) + { + oiio::ImageBufAlgo::colorconvert(colorspaceBuf, *outBuf, EImageColorSpace_enumToString(colorspace.from), EImageColorSpace_enumToString(colorspace.to)); + outBuf = &colorspaceBuf; + } + + oiio::ImageBuf formatBuf; // buffer for image format modification + if(imageQuality == EImageQuality::OPTIMIZED && isEXR) + { + formatBuf.copy(*outBuf, oiio::TypeDesc::HALF); // override format, use half instead of float + outBuf = &formatBuf; + } + + // write image + if(!outBuf->write(tmpPath)) + throw std::runtime_error("Can't write output image file '" + path + "'."); + + // rename temporay filename + fs::rename(tmpPath, path); +} + +void writeImage(const std::string& path, int width, int height, const std::vector& buffer, EImageQuality imageQuality, OutputFileColorSpace colorspace, const oiio::ParamValueList& metadata) +{ + writeImage(path, oiio::TypeDesc::UCHAR, width, height, 1, buffer, imageQuality, colorspace, metadata); +} + +void writeImage(const std::string& path, int width, int height, const std::vector& buffer, EImageQuality imageQuality, OutputFileColorSpace colorspace, const oiio::ParamValueList& metadata) +{ + writeImage(path, oiio::TypeDesc::UINT16, width, height, 1, buffer, imageQuality, colorspace, metadata); +} + +void writeImage(const std::string& path, int width, int height, const std::vector& buffer, EImageQuality imageQuality, OutputFileColorSpace colorspace, const oiio::ParamValueList& metadata) +{ + writeImage(path, oiio::TypeDesc::UCHAR, width, height, 3, buffer, imageQuality, colorspace, metadata); +} + +void writeImage(const std::string& path, int width, int height, const std::vector& buffer, EImageQuality imageQuality, OutputFileColorSpace colorspace, const oiio::ParamValueList& metadata) +{ + writeImage(path, oiio::TypeDesc::FLOAT, width, height, 1, buffer, imageQuality, colorspace, metadata); +} + +void writeImage(const std::string& path, int width, int height, const std::vector& buffer, EImageQuality imageQuality, OutputFileColorSpace colorspace, const oiio::ParamValueList& metadata) +{ + writeImage(path, oiio::TypeDesc::FLOAT, width, height, 3, buffer, imageQuality, colorspace, metadata); +} + +void writeImage(const std::string &path, Image &image, EImageQuality imageQuality, OutputFileColorSpace colorspace, const OpenImageIO_v1_8::ParamValueList &metadata) +{ + writeImage(path, oiio::TypeDesc::FLOAT, image.width(), image.height(), 3, image.data(), imageQuality, colorspace, metadata); +} + +template +void transposeImage(oiio::TypeDesc typeDesc, + int width, + int height, + int nchannels, + std::vector& buffer) +{ + oiio::ImageSpec imageSpec(width, height, nchannels, typeDesc); + + oiio::ImageBuf inBuf(imageSpec, buffer.data()); + oiio::ImageBuf transposeBuf; + + oiio::ImageBufAlgo::transpose(transposeBuf, inBuf, oiio::ROI::All()); + + transposeBuf.get_pixels(oiio::ROI::All(), typeDesc, buffer.data()); +} + +void transposeImage(int width, int height, std::vector& buffer) +{ + transposeImage(oiio::TypeDesc::UCHAR, width, height, 1, buffer); +} + +void transposeImage(int width, int height, std::vector& buffer) +{ + transposeImage(oiio::TypeDesc::UCHAR, width, height, 3, buffer); +} + +void transposeImage(int width, int height, std::vector& buffer) +{ + transposeImage(oiio::TypeDesc::FLOAT, width, height, 1, buffer); +} + +void transposeImage(int width, int height, std::vector& buffer) +{ + transposeImage(oiio::TypeDesc::FLOAT, width, height, 3, buffer); +} + +void transposeImage(Image &image) +{ + transposeImage(oiio::TypeDesc::FLOAT, image.width(), image.height(), 3, image.data()); +} + +template +void resizeImage(oiio::TypeDesc typeDesc, + int inWidth, + int inHeight, + int nchannels, + int downscale, + const std::vector& inBuffer, + std::vector& outBuffer, + const std::string& filter = "", + float filterSize = 0) +{ + const int outWidth = inWidth / downscale; + const int outHeight = inHeight / downscale; + + outBuffer.resize(outWidth * outHeight); + + const oiio::ImageBuf inBuf(oiio::ImageSpec(inWidth, inHeight, nchannels, typeDesc), const_cast(inBuffer.data())); + oiio::ImageBuf outBuf(oiio::ImageSpec(outWidth, outHeight, nchannels, typeDesc), outBuffer.data()); + + oiio::ImageBufAlgo::resize(outBuf, inBuf, filter, filterSize, oiio::ROI::All()); +} + +void resizeImage(int inWidth, int inHeight, int downscale, const std::vector& inBuffer, std::vector& outBuffer, const std::string& filter, float filterSize) +{ + resizeImage(oiio::TypeDesc::UCHAR, inWidth, inHeight, 1, downscale, inBuffer, outBuffer, filter, filterSize); +} + +void resizeImage(int inWidth, int inHeight, int downscale, const std::vector& inBuffer, std::vector& outBuffer, const std::string& filter, float filterSize) +{ + resizeImage(oiio::TypeDesc::UCHAR, inWidth, inHeight, 3, downscale, inBuffer, outBuffer, filter, filterSize); +} + +void resizeImage(int inWidth, int inHeight, int downscale, const std::vector& inBuffer, std::vector& outBuffer, const std::string& filter, float filterSize) +{ + resizeImage(oiio::TypeDesc::FLOAT, inWidth, inHeight, 1, downscale, inBuffer, outBuffer, filter, filterSize); +} + +void resizeImage(int inWidth, int inHeight, int downscale, const std::vector& inBuffer, std::vector& outBuffer, const std::string& filter, float filterSize) +{ + resizeImage(oiio::TypeDesc::FLOAT, inWidth, inHeight, 3, downscale, inBuffer, outBuffer, filter, filterSize); +} + +void resizeImage(int downscale, const Image &inImage, Image &outImage, const std::string &filter, float filterSize) +{ + resizeImage(oiio::TypeDesc::FLOAT, inImage.width(), inImage.height(), 3, downscale, inImage.data(), outImage.data(), filter, filterSize); + outImage.setHeight(inImage.height() / downscale); + outImage.setWidth(inImage.width() / downscale); +} + +template +void convolveImage(oiio::TypeDesc typeDesc, + int inWidth, + int inHeight, + int nchannels, + const std::vector& inBuffer, + std::vector& outBuffer, + const std::string& kernel, + float kernelWidth, + float kernelHeight) +{ + outBuffer.resize(inBuffer.size()); + + const oiio::ImageBuf inBuf(oiio::ImageSpec(inWidth, inHeight, nchannels, typeDesc), const_cast(inBuffer.data())); + oiio::ImageBuf outBuf(oiio::ImageSpec(inWidth, inHeight, nchannels, typeDesc), outBuffer.data()); + + oiio::ImageBuf K; + oiio::ImageBufAlgo::make_kernel(K, kernel, kernelWidth, kernelHeight); + + oiio::ImageBufAlgo::convolve(outBuf, inBuf, K); +} + + +void convolveImage(int inWidth, int inHeight, const std::vector& inBuffer, std::vector& outBuffer, const std::string& kernel, float kernelWidth, float kernelHeight) +{ + convolveImage(oiio::TypeDesc::UCHAR, inWidth, inHeight, 1, inBuffer, outBuffer, kernel, kernelWidth, kernelHeight); +} + +void convolveImage(int inWidth, int inHeight, const std::vector& inBuffer, std::vector& outBuffer, const std::string& kernel, float kernelWidth, float kernelHeight) +{ + convolveImage(oiio::TypeDesc::UCHAR, inWidth, inHeight, 3, inBuffer, outBuffer, kernel, kernelWidth, kernelHeight); +} + +void convolveImage(int inWidth, int inHeight, const std::vector& inBuffer, std::vector& outBuffer, const std::string& kernel, float kernelWidth, float kernelHeight) +{ + convolveImage(oiio::TypeDesc::FLOAT, inWidth, inHeight, 1, inBuffer, outBuffer, kernel, kernelWidth, kernelHeight); +} + +void convolveImage(int inWidth, int inHeight, const std::vector& inBuffer, std::vector& outBuffer, const std::string& kernel, float kernelWidth, float kernelHeight) +{ + convolveImage(oiio::TypeDesc::FLOAT, inWidth, inHeight, 3, inBuffer, outBuffer, kernel, kernelWidth, kernelHeight); +} + +void convolveImage(const Image &inImage, Image &outImage, const std::string &kernel, float kernelWidth, float kernelHeight) +{ + convolveImage(oiio::TypeDesc::FLOAT, inImage.width(), inImage.height(), 3, inImage.data(), outImage.data(), kernel, kernelWidth, kernelHeight); + outImage.setHeight(inImage.height()); + outImage.setWidth(inImage.width()); +} + +void fillHoles(int inWidth, int inHeight, std::vector& colorBuffer, const std::vector& alphaBuffer) +{ + oiio::ImageBuf rgbBuf(oiio::ImageSpec(inWidth, inHeight, 3, oiio::TypeDesc::FLOAT), colorBuffer.data()); + const oiio::ImageBuf alphaBuf(oiio::ImageSpec(inWidth, inHeight, 1, oiio::TypeDesc::FLOAT), const_cast(alphaBuffer.data())); + + // Create RGBA ImageBuf from source buffers with correct channel names + // (identified alpha channel is needed for fillholes_pushpull) + oiio::ImageBuf rgbaBuf; + oiio::ImageBufAlgo::channel_append(rgbaBuf, rgbBuf, alphaBuf); + rgbaBuf.specmod().default_channel_names(); + + // Temp RGBA buffer to store fillholes result + oiio::ImageBuf filledBuf; + oiio::ImageBufAlgo::fillholes_pushpull(filledBuf, rgbaBuf); + rgbaBuf.clear(); + + // Copy result to original RGB buffer + oiio::ImageBufAlgo::copy(rgbBuf, filledBuf); +} + +void fillHoles(Image& image, const std::vector& alphaBuffer) +{ + fillHoles(image.width(), image.height(), image.data(), alphaBuffer); +} + +} // namespace imageIO } // namespace aliceVision diff --git a/src/aliceVision/mvsData/imageIO.hpp b/src/aliceVision/mvsData/imageIO.hpp index 98c38a1f94..3dfef7aa43 100644 --- a/src/aliceVision/mvsData/imageIO.hpp +++ b/src/aliceVision/mvsData/imageIO.hpp @@ -8,8 +8,18 @@ #include +#include + +namespace oiio = OIIO; + namespace aliceVision { +class rgb; +class Color; +class Image; + +namespace imageIO { + /** * @brief Available image file types for pipeline output */ @@ -21,6 +31,95 @@ enum class EImageFileType EXR }; +/** + * @brief Available image color space for pipeline input / output + */ +enum class EImageColorSpace +{ + AUTO, + LINEAR, + SRGB, + NO_CONVERSION +}; + + +struct OutputFileColorSpace +{ + EImageColorSpace from = EImageColorSpace::LINEAR; + EImageColorSpace to = EImageColorSpace::AUTO; + + OutputFileColorSpace(EImageColorSpace from_, EImageColorSpace to_) + : from(from_) + , to(to_) + { + } + /// @brief Assumes that @p from is LINEAR + OutputFileColorSpace(EImageColorSpace to_) + { + if(to_ == EImageColorSpace::NO_CONVERSION) + to = from; + else + to = to_; + } + OutputFileColorSpace() + { + } +}; + +/** + * @brief Available image qualities for pipeline output + */ +enum class EImageQuality +{ + OPTIMIZED, + LOSSLESS +}; + +std::string EImageColorSpace_enumToString(const EImageColorSpace colorSpace); + +/** + * @brief get informations about each image quality + * @return String + */ +std::string EImageQuality_informations(); + +/** + * @brief returns the EImageQuality enum from a string. + * @param[in] imageQuality the input string. + * @return the associated EImageQuality enum. + */ +EImageQuality EImageQuality_stringToEnum(const std::string& imageQuality); + +/** + * @brief converts an EImageQuality enum to a string. + * @param[in] imageQuality the EImageQuality enum to convert. + * @return the string associated to the EImageQuality enum. + */ +std::string EImageQuality_enumToString(const EImageQuality imageQuality); + +/** + * @brief write an EImageQuality enum into a stream by converting it to a string. + * @param[in] os the stream where to write the imageType. + * @param[in] imageQuality the EImageQuality enum to write. + * @return the modified stream. + */ +std::ostream& operator<<(std::ostream& os, EImageQuality imageQuality); + +/** + * @brief read a EImageQuality enum from a stream. + * @param[in] in the stream from which the enum is read. + * @param[out] imageQuality the EImageQuality enum read from the stream. + * @return the modified stream without the read enum. + */ +std::istream& operator>>(std::istream& in, EImageQuality& imageQuality); + +/** + * @brief convert a metadata string map into an oiio::ParamValueList + * @param[in] metadataMap string map + * @return oiio::ParamValueList + */ +oiio::ParamValueList getMetadataFromMap(const std::map& metadataMap); + /** * @brief get informations about each image file type * @return String @@ -57,6 +156,22 @@ std::ostream& operator<<(std::ostream& os, EImageFileType imageFileType); */ std::istream& operator>>(std::istream& in, EImageFileType& imageFileType); +/** + * @brief read image dimension from a given path + * @param[in] path The given path to the image + * @param[out] width The image width + * @param[out] height The image height + * @param[out] nchannels The image channel number + */ +void readImageSpec(const std::string& path, int& width, int& height, int& nchannels); + +/** + * @brief read image metadata from a given path + * @param[in] path The given path to the image + * @param[out] metadata The image metadata + */ +void readImageMetadata(const std::string& path, oiio::ParamValueList& metadata); + /** * @brief Test if the extension is supported for undistorted images. * @param[in] ext The extension with the dot (eg ".png") @@ -64,4 +179,92 @@ std::istream& operator>>(std::istream& in, EImageFileType& imageFileType); */ bool isSupportedUndistortFormat(const std::string &ext); +/** + * @brief read an image with a given path and buffer + * @param[in] path The given path to the image + * @param[out] width The output image width + * @param[out] height The output image height + * @param[out] buffer The output image buffer + * @param[in] image color space + */ +void readImage(const std::string& path, int& width, int& height, std::vector& buffer, EImageColorSpace imageColorSpace); +void readImage(const std::string& path, int& width, int& height, std::vector& buffer, EImageColorSpace imageColorSpace); +void readImage(const std::string& path, int& width, int& height, std::vector& buffer, EImageColorSpace imageColorSpace); +void readImage(const std::string& path, int& width, int& height, std::vector& buffer, EImageColorSpace imageColorSpace); +void readImage(const std::string& path, int& width, int& height, std::vector& buffer, EImageColorSpace imageColorSpace); +void readImage(const std::string& path, Image& image, EImageColorSpace imageColorSpace); + +/** + * @brief write an image with a given path and buffer + * @param[in] path The given path to the image + * @param[in] width The input image width + * @param[in] height The input image height + * @param[in] buffer The input image buffer + */ +void writeImage(const std::string& path, int width, int height, const std::vector& buffer, EImageQuality imageQuality, OutputFileColorSpace colorspace, const oiio::ParamValueList& metadata = oiio::ParamValueList()); +void writeImage(const std::string& path, int width, int height, const std::vector& buffer, EImageQuality imageQuality, OutputFileColorSpace colorspace, const oiio::ParamValueList& metadata = oiio::ParamValueList()); +void writeImage(const std::string& path, int width, int height, const std::vector& buffer, EImageQuality imageQuality, OutputFileColorSpace colorspace, const oiio::ParamValueList& metadata = oiio::ParamValueList()); +void writeImage(const std::string& path, int width, int height, const std::vector& buffer, EImageQuality imageQuality, OutputFileColorSpace colorspace, const oiio::ParamValueList& metadata = oiio::ParamValueList()); +void writeImage(const std::string& path, int width, int height, const std::vector& buffer, EImageQuality imageQuality, OutputFileColorSpace colorspace, const oiio::ParamValueList& metadata = oiio::ParamValueList()); +void writeImage(const std::string& path, Image& image, EImageQuality imageQuality, OutputFileColorSpace colorspace, const oiio::ParamValueList& metadata = oiio::ParamValueList()); + +/** + * @brief transpose a given image buffer + * @param[in] width The image buffer width + * @param[in] height The image buffer height + * @param[in,out] buffer The image buffer + */ +void transposeImage(int width, int height, std::vector& buffer); +void transposeImage(int width, int height, std::vector& buffer); +void transposeImage(int width, int height, std::vector& buffer); +void transposeImage(int width, int height, std::vector& buffer); +void transposeImage(Image& image); + +/** + * @brief resize a given image buffer + * @param[in] inWidth The input image buffer width + * @param[in] inHeight The input image buffer height + * @param[in] downscale The resize downscale + * @param[in] inBuffer The input image buffer + * @param[out] outBuffer The output image buffer + * @param[in] filter The name of a high-quality filter to use when resampling + * Default is bilinear resampling + * See openImageIO documentation "ImageBufAlgo filtername" + * @param[in] filterSize The resize filter size + */ +void resizeImage(int inWidth, int inHeight, int downscale, const std::vector& inBuffer, std::vector& outBuffer, const std::string& filter = "", float filterSize = 0); +void resizeImage(int inWidth, int inHeight, int downscale, const std::vector& inBuffer, std::vector& outBuffer, const std::string& filter = "", float filterSize = 0); +void resizeImage(int inWidth, int inHeight, int downscale, const std::vector& inBuffer, std::vector& outBuffer, const std::string& filter = "", float filterSize = 0); +void resizeImage(int inWidth, int inHeight, int downscale, const std::vector& inBuffer, std::vector& outBuffer, const std::string& filter = "", float filterSize = 0); +void resizeImage(int downscale, const Image& inImage, Image& outImage, const std::string& filter = "", float filterSize = 0); + +/** + * @brief convolve a given image buffer + * @param[in] inWidth The input image buffer width + * @param[in] inHeight The input image buffer heightt + * @param[in] inBuffer The input image buffer + * @param[out] outBuffer outBuffer The output image buffer + * @param[in] kernel The kernel name, can be "gaussian", "sharp-gaussian", "box", ... + * Default is gaussian kernel + * See openImageIO documentation "ImageBufAlgo.make_kernel" + * @param[in] kernelWidth The kernel width + * @param[in] kernelHeight The kernal height + */ +void convolveImage(int inWidth, int inHeight, const std::vector& inBuffer, std::vector& outBuffer, const std::string& kernel = "gaussian", float kernelWidth = 5.0f, float kernelHeight = 5.0f); +void convolveImage(int inWidth, int inHeight, const std::vector& inBuffer, std::vector& outBuffer, const std::string& kernel = "gaussian", float kernelWidth = 5.0f, float kernelHeight = 5.0f); +void convolveImage(int inWidth, int inHeight, const std::vector& inBuffer, std::vector& outBuffer, const std::string& kernel = "gaussian", float kernelWidth = 5.0f, float kernelHeight = 5.0f); +void convolveImage(int inWidth, int inHeight, const std::vector& inBuffer, std::vector& outBuffer, const std::string& kernel = "gaussian", float kernelWidth = 5.0f, float kernelHeight = 5.0f); +void convolveImage(const Image& inImage, Image& outImage, const std::string& kernel = "gaussian", float kernelWidth = 5.0f, float kernelHeight = 5.0f); + +/** + * @brief fill holes in a given image buffer with plausible values + * @param[in] inWidth The input image buffer width + * @param[in] inHeight The input image buffer height + * @param[in,out] colorBuffer The image buffer to fill + * @param[in] alphaBuffer The input alpha buffer containing 0.0/1.0 for empty/valid pixels + */ +void fillHoles(int inWidth, int inHeight, std::vector& colorBuffer, const std::vector& alphaBuffer); +void fillHoles(Image& image, const std::vector& alphaBuffer); + +} // namespace imageIO } // namespace aliceVision diff --git a/src/aliceVision/mvsUtils/CMakeLists.txt b/src/aliceVision/mvsUtils/CMakeLists.txt index 9ad0c58eca..9423decf8e 100644 --- a/src/aliceVision/mvsUtils/CMakeLists.txt +++ b/src/aliceVision/mvsUtils/CMakeLists.txt @@ -21,7 +21,6 @@ alicevision_add_library(aliceVision_mvsUtils aliceVision_multiview aliceVision_mvsData aliceVision_sfmData - aliceVision_imageIO PRIVATE_LINKS aliceVision_system ${Boost_FILESYSTEM_LIBRARY} diff --git a/src/aliceVision/mvsUtils/ImagesCache.hpp b/src/aliceVision/mvsUtils/ImagesCache.hpp index 4c4fea4205..1040265cb7 100644 --- a/src/aliceVision/mvsUtils/ImagesCache.hpp +++ b/src/aliceVision/mvsUtils/ImagesCache.hpp @@ -11,7 +11,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/aliceVision/mvsUtils/MultiViewParams.cpp b/src/aliceVision/mvsUtils/MultiViewParams.cpp index b4786bbb62..dbf0ad50c5 100644 --- a/src/aliceVision/mvsUtils/MultiViewParams.cpp +++ b/src/aliceVision/mvsUtils/MultiViewParams.cpp @@ -13,7 +13,7 @@ #include #include #include -#include +#include #include #include @@ -74,7 +74,7 @@ MultiViewParams::MultiViewParams(const sfmData::SfMData& sfmData, const auto findIt = std::find_if(fs::recursive_directory_iterator(_imagesFolder), end, [&view](const fs::directory_entry& e) { return (e.path().stem() == std::to_string(view.getViewId()) && - (isSupportedUndistortFormat(e.path().extension().string()))); + (imageIO::isSupportedUndistortFormat(e.path().extension().string()))); }); if(findIt == end) diff --git a/src/aliceVision/mvsUtils/fileIO.cpp b/src/aliceVision/mvsUtils/fileIO.cpp index 868424414c..fd9cc5fb56 100644 --- a/src/aliceVision/mvsUtils/fileIO.cpp +++ b/src/aliceVision/mvsUtils/fileIO.cpp @@ -8,7 +8,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/aliceVision/mvsUtils/fileIO.hpp b/src/aliceVision/mvsUtils/fileIO.hpp index 7494dfe006..970f8fe521 100644 --- a/src/aliceVision/mvsUtils/fileIO.hpp +++ b/src/aliceVision/mvsUtils/fileIO.hpp @@ -12,7 +12,7 @@ #include #include #include -#include +#include #include diff --git a/src/samples/multiBandBlending/CMakeLists.txt b/src/samples/multiBandBlending/CMakeLists.txt index e9c098187e..5aa3c7851b 100644 --- a/src/samples/multiBandBlending/CMakeLists.txt +++ b/src/samples/multiBandBlending/CMakeLists.txt @@ -4,7 +4,6 @@ alicevision_add_software(aliceVision_samples_multibandtest FOLDER ${FOLDER_SAMPLES} LINKS aliceVision_system aliceVision_mesh - aliceVision_imageIO aliceVision_mvsData ${Boost_LIBRARIES} -) \ No newline at end of file +) diff --git a/src/samples/multiBandBlending/main_multibandtest.cpp b/src/samples/multiBandBlending/main_multibandtest.cpp index 384ff73f85..578f47280e 100644 --- a/src/samples/multiBandBlending/main_multibandtest.cpp +++ b/src/samples/multiBandBlending/main_multibandtest.cpp @@ -6,7 +6,7 @@ #include #include -#include +#include #include #include #include diff --git a/src/software/pipeline/main_texturing.cpp b/src/software/pipeline/main_texturing.cpp index 7b134ff4d9..912eb3927d 100644 --- a/src/software/pipeline/main_texturing.cpp +++ b/src/software/pipeline/main_texturing.cpp @@ -43,7 +43,7 @@ int main(int argc, char* argv[]) std::string inputMeshFilepath; std::string outputFolder; std::string imagesFolder; - std::string outTextureFileTypeName = EImageFileType_enumToString(EImageFileType::PNG); + std::string outTextureFileTypeName = imageIO::EImageFileType_enumToString(imageIO::EImageFileType::PNG); bool flipNormals = false; mesh::TexturingParams texParams; @@ -67,7 +67,7 @@ int main(int argc, char* argv[]) "Use images from a specific folder instead of those specify in the SfMData file.\n" "Filename should be the image uid.") ("outputTextureFileType", po::value(&outTextureFileTypeName)->default_value(outTextureFileTypeName), - EImageFileType_informations().c_str()) + imageIO::EImageFileType_informations().c_str()) ("textureSide", po::value(&texParams.textureSide)->default_value(texParams.textureSide), "Output texture size") ("downscale", po::value(&texParams.downscale)->default_value(texParams.downscale), @@ -145,7 +145,7 @@ int main(int argc, char* argv[]) texParams.visibilityRemappingMethod = mesh::EVisibilityRemappingMethod_stringToEnum(visibilityRemappingMethod); // set output texture file type - const EImageFileType outputTextureFileType = EImageFileType_stringToEnum(outTextureFileTypeName); + const imageIO::EImageFileType outputTextureFileType = imageIO::EImageFileType_stringToEnum(outTextureFileTypeName); // read the input SfM scene sfmData::SfMData sfmData; From 973af01709b47768c37496ccd87c73e8b3575fb0 Mon Sep 17 00:00:00 2001 From: Clara Date: Wed, 5 Jun 2019 18:55:44 +0200 Subject: [PATCH 44/51] Merge mvsUtils::ImagesCache::Img class into mvsData::Image Simplify image management by having only one image class. [mvsData] Image: refactoring & add simple methods [mesh] Texturing: use Image class for AccuImage structure (instead of vector) + use imageIO methods taking an Image in argument [mvsUtils] fileIO: adapt the method for loading images to use mvsData::Image --- src/aliceVision/mesh/Texturing.cpp | 18 ++++----- src/aliceVision/mesh/Texturing.hpp | 14 +++---- src/aliceVision/mvsData/Image.cpp | 22 +++++------ src/aliceVision/mvsData/Image.hpp | 40 ++++++++++++-------- src/aliceVision/mvsUtils/ImagesCache.cpp | 9 ++--- src/aliceVision/mvsUtils/ImagesCache.hpp | 42 ++------------------- src/aliceVision/mvsUtils/fileIO.cpp | 47 ++++++++---------------- src/aliceVision/mvsUtils/fileIO.hpp | 2 +- 8 files changed, 73 insertions(+), 121 deletions(-) diff --git a/src/aliceVision/mesh/Texturing.cpp b/src/aliceVision/mesh/Texturing.cpp index 9e3a4709f2..682582692a 100644 --- a/src/aliceVision/mesh/Texturing.cpp +++ b/src/aliceVision/mesh/Texturing.cpp @@ -467,7 +467,7 @@ void Texturing::generateTexturesSubSet(const mvsUtils::MultiViewParams& mp, //pyramid of atlases frequency bands std::map accuPyramids; for(std::size_t atlasID: atlasIDs) - accuPyramids[atlasID].init(texParams.nbBand, textureSize); + accuPyramids[atlasID].init(texParams.nbBand, texParams.textureSide, texParams.textureSide); //for each camera, for each texture, iterate over triangles and fill the accuPyramids map for(int camId = 0; camId < contributionsPerCamera.size(); ++camId) @@ -481,11 +481,9 @@ void Texturing::generateTexturesSubSet(const mvsUtils::MultiViewParams& mp, } ALICEVISION_LOG_INFO("- camera " << camId + 1 << "/" << mp.ncams << " with contributions to " << cameraContributions.size() << " texture files:"); + //Load camera image from cache imageCache.refreshData(camId); - - //conversion ImagesCache::Img -> Image - mvsUtils::ImagesCache::ImgPtr imgPtr = imageCache.getImg_sync(camId); - Image camImg(imgPtr->data, imgPtr->getWidth(), imgPtr->getHeight()); + const Image& camImg = *(imageCache.getImg_sync(camId)); //Calculate laplacianPyramid std::vector pyramidL; //laplacian pyramid @@ -606,7 +604,7 @@ void Texturing::generateTexturesSubSet(const mvsUtils::MultiViewParams& mp, AccuImage& atlasLevelTexture = accuPyramid.pyramid[level]; //write the number of contributions for each texture - std::vector imgContrib(texParams.textureSide*texParams.textureSide); + std::vector imgContrib(textureSize); for(unsigned int yp = 0; yp < texParams.textureSide; ++yp) { @@ -795,18 +793,18 @@ void Texturing::writeTexture(AccuImage& atlasTexture, const std::size_t atlasID, alphaBuffer[xyoffset] = atlasTexture.imgCount[xyoffset] ? 1 : 0; } } - imageIO::fillHoles(texParams.textureSide, texParams.textureSide, atlasTexture.img, alphaBuffer); + imageIO::fillHoles(atlasTexture.img, alphaBuffer); alphaBuffer.clear(); } // downscale texture if required if(texParams.downscale > 1) { - std::vector resizedColorBuffer; + Image resizedColorBuffer; outTextureSide = texParams.textureSide / texParams.downscale; ALICEVISION_LOG_INFO(" - Downscaling texture (" << texParams.downscale << "x)."); - imageIO::resizeImage(texParams.textureSide, texParams.textureSide, texParams.downscale, atlasTexture.img, resizedColorBuffer); + imageIO::resizeImage(texParams.downscale, atlasTexture.img, resizedColorBuffer); std::swap(resizedColorBuffer, atlasTexture.img); } @@ -815,7 +813,7 @@ void Texturing::writeTexture(AccuImage& atlasTexture, const std::size_t atlasID, ALICEVISION_LOG_INFO(" - Writing texture file: " << texturePath.string()); using namespace imageIO; - imageIO::writeImage(texturePath.string(), outTextureSide, outTextureSide, atlasTexture.img, EImageQuality::OPTIMIZED, OutputFileColorSpace(EImageColorSpace::SRGB, EImageColorSpace::AUTO)); + imageIO::writeImage(texturePath.string(), atlasTexture.img, EImageQuality::OPTIMIZED, OutputFileColorSpace(EImageColorSpace::SRGB, EImageColorSpace::AUTO)); } diff --git a/src/aliceVision/mesh/Texturing.hpp b/src/aliceVision/mesh/Texturing.hpp index 3bd1131867..537fd80fde 100644 --- a/src/aliceVision/mesh/Texturing.hpp +++ b/src/aliceVision/mesh/Texturing.hpp @@ -152,24 +152,24 @@ struct Texturing // Create buffer for the set of output textures struct AccuImage { - std::vector img; + Image img; std::vector imgCount; - void resize(std::size_t s) + void resize(int width, int height) { - img.resize(s); - imgCount.resize(s); + img.resize(width, height); + imgCount.resize(width * height); } }; struct AccuPyramid { std::vector pyramid; - void init(std::size_t nbLevels, std::size_t imageSize) + void init(int nbLevels, int imgWidth, int imgHeight) { pyramid.resize(nbLevels); - for(auto& p : pyramid) - p.resize(imageSize); + for(auto& accuImage : pyramid) + accuImage.resize(imgWidth, imgHeight); } }; diff --git a/src/aliceVision/mvsData/Image.cpp b/src/aliceVision/mvsData/Image.cpp index 9e0b2ebf6b..f2cc00dd6c 100644 --- a/src/aliceVision/mvsData/Image.cpp +++ b/src/aliceVision/mvsData/Image.cpp @@ -12,7 +12,7 @@ namespace aliceVision{ -void Image::imageDiff(Image& inImgDownscaled, Image& outImg, unsigned int downscale) +void Image::imageDiff(const Image& inImgDownscaled, Image& outImg, unsigned int downscale) const { outImg.resize(_width, _height); for(int i = 0; i < _width*_height; ++i) @@ -20,12 +20,12 @@ void Image::imageDiff(Image& inImgDownscaled, Image& outImg, unsigned int downsc Point2d pix(i%_width, static_cast(i/_width)); Point2d pixd = pix/downscale; - outImg._img[i] = _img[i] - inImgDownscaled.getInterpolateColor(pixd); + outImg._data[i] = _data[i] - inImgDownscaled.getInterpolateColor(pixd); } } -void Image::laplacianPyramid(std::vector& out_pyramidL, int nbBand, unsigned int downscale) +void Image::laplacianPyramid(std::vector& out_pyramidL, int nbBand, unsigned int downscale) const { assert(nbBand >= 1); @@ -39,14 +39,14 @@ void Image::laplacianPyramid(std::vector& out_pyramidL, int nbBand, unsig //Create Laplacian pyramid for(int b = 0; b < nbBand-1; ++b) { - imageIO::resizeImage(img.width(), img.height(), static_cast(downscale), img.data(), imgDownscaled.data(), "gaussian"); + imageIO::resizeImage(static_cast(downscale), img, imgDownscaled, "gaussian"); img.imageDiff(imgDownscaled, out_pyramidL[b], downscale); img.swap(imgDownscaled); - +/* outW = static_cast(outW/downscale); outH = static_cast(outH/downscale); imgDownscaled.resize(outW, outH); - +*/ } out_pyramidL[nbBand-1] = img; @@ -63,10 +63,10 @@ Color Image::getInterpolateColor(const Point2d& pix) const const float ui = pix.x - static_cast(xp); const float vi = pix.y - static_cast(yp); - const Color lu = _img.at( yp * _width + xp ); - const Color ru = _img.at( yp * _width + (xp+1) ); - const Color rd = _img.at( (yp+1) * _width + (xp+1) ); - const Color ld = _img.at( (yp+1) * _width + xp ); + const Color lu = _data.at( yp * _width + xp ); + const Color ru = _data.at( yp * _width + (xp+1) ); + const Color rd = _data.at( (yp+1) * _width + (xp+1) ); + const Color ld = _data.at( (yp+1) * _width + xp ); // bilinear interpolation of the pixel intensity value const Color u = lu + (ru - lu) * ui; @@ -79,7 +79,7 @@ Color Image::getNearestPixelColor(const Point2d& pix) const { const int xp = std::min(static_cast(pix.x), _width-1); const int yp = std::min(static_cast(pix.y), _height-1); - const Color lu = _img.at( yp * _width + xp ); + const Color lu = _data.at( yp * _width + xp ); return lu; } diff --git a/src/aliceVision/mvsData/Image.hpp b/src/aliceVision/mvsData/Image.hpp index b9cda3df6b..d8dc75a3a5 100644 --- a/src/aliceVision/mvsData/Image.hpp +++ b/src/aliceVision/mvsData/Image.hpp @@ -17,7 +17,7 @@ namespace aliceVision { class Image { private: - std::vector _img; + std::vector _data; int _width{0}; int _height{0}; @@ -28,30 +28,32 @@ class Image : _width(width) , _height(height) { - _img.resize(width*height); + _data.resize(width*height); } Image(Color* data, int width, int height) : Image(width, height) { for(int i = 0; i < _width*_height; ++i) - _img[i] = data[i]; + _data[i] = data[i]; } Image(const Image& other) : _width(other._width) , _height(other._height) - , _img(other._img) + , _data(other._data) { } + int size() { return _width * _height; } + void resize(int width, int height) { _width = width; _height = height; - if(_width*_height != _img.size()) + if(_width*_height != _data.size()) { - _img.resize(0); // no need to copy image content - _img.resize(_width*_height); + _data.resize(0); // no need to copy image content + _data.resize(_width*_height); } } @@ -59,15 +61,15 @@ class Image { std::swap(_width, other._width); std::swap(_height, other._height); - _img.swap(other._img); + _data.swap(other._data); } Image& operator=(const Image& param) { _width = param._width; _height = param._height; - _img.resize(_width*_height); - _img = param._img; + _data.resize(_width*_height); + _data = param._data; return *this; } @@ -75,11 +77,17 @@ class Image int width() const { return _width; } int height() const { return _height; } - std::vector& data() { return _img; } - const std::vector& data() const { return _img; } + void setWidth(int width) { _width = width; } + void setHeight(int height) { _height = height; } + + std::vector& data() { return _data; } + const std::vector& data() const { return _data; } + + const Color& operator[](std::size_t index) const { return _data[index]; } + Color& operator[](std::size_t index) { return _data[index]; } - const Color& operator[](std::size_t index) const { return _img[index]; } - Color& operator[](std::size_t index) { return _img[index]; } + const Color& at(int x, int y) const { return _data[y * _width + x]; } + Color& at(int x, int y) { return _data[y * _width + x]; } Color getInterpolateColor(const Point2d& pix) const; Color getNearestPixelColor(const Point2d& pix) const; @@ -90,7 +98,7 @@ class Image * @param [outImg] the difference * @param [downscale] the downscale coefficient between image sizes */ - void imageDiff(Image& inImgDownscaled, Image& outImg, unsigned int downscale); + void imageDiff(const Image& inImgDownscaled, Image& outImg, unsigned int downscale) const; /** * @brief Calculate the laplacian pyramid of a given image, @@ -99,7 +107,7 @@ class Image * @param [nbBand] the number of frequency bands * @param [downscale] the downscale coefficient between floors of the pyramid */ - void laplacianPyramid(std::vector& out_pyramidL, int nbBand, unsigned int downscale); + void laplacianPyramid(std::vector& out_pyramidL, int nbBand, unsigned int downscale) const; }; diff --git a/src/aliceVision/mvsUtils/ImagesCache.cpp b/src/aliceVision/mvsUtils/ImagesCache.cpp index bf403db33d..4eb0728119 100644 --- a/src/aliceVision/mvsUtils/ImagesCache.cpp +++ b/src/aliceVision/mvsUtils/ImagesCache.cpp @@ -94,14 +94,13 @@ void ImagesCache::refreshData(int camId) long t1 = clock(); if (imgs[mapId] == nullptr) { - const std::size_t maxSize = mp->getMaxImageWidth() * mp->getMaxImageHeight(); - imgs[mapId] = std::make_shared( maxSize ); + const int maxWidth = mp->getMaxImageWidth(); + const int maxHeight = mp->getMaxImageHeight(); + imgs[mapId] = std::make_shared(maxWidth, maxHeight); } const std::string imagePath = imagesNames.at(camId); - memcpyRGBImageFromFileToArr(camId, imgs[mapId]->data, imagePath, mp, bandType, _colorspace); - imgs[mapId]->setWidth( mp->getWidth(camId) ); - imgs[mapId]->setHeight( mp->getHeight(camId) ); + loadImage(imagePath, mp, camId, *(imgs[mapId]), bandType, _colorspace); ALICEVISION_LOG_DEBUG("Add " << imagePath << " to image cache. " << formatElapsedTime(t1)); } diff --git a/src/aliceVision/mvsUtils/ImagesCache.hpp b/src/aliceVision/mvsUtils/ImagesCache.hpp index 1040265cb7..f915a7d183 100644 --- a/src/aliceVision/mvsUtils/ImagesCache.hpp +++ b/src/aliceVision/mvsUtils/ImagesCache.hpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -21,48 +22,11 @@ namespace mvsUtils { class ImagesCache { -public: - class Img - { - int _width; - int _height; - public: - Img( ) - : data(nullptr) - { } - Img( size_t sz ) - : data( new Color[sz] ) - { } - - ~Img( ) - { - delete [] data; - } - - inline void setWidth( int w ) { _width = w; } - inline void setHeight( int h ) { _height = h; } - - inline int getWidth() const { return _width; } - inline int getHeight() const { return _height; } - - inline Color& at( int x, int y ) - { - return data[y * _width + x]; - } - - inline const Color& at( int x, int y ) const - { - return data[y * _width + x]; - } - - Color* data; - }; - - typedef std::shared_ptr ImgPtr; - public: const MultiViewParams* mp; + typedef std::shared_ptr ImgPtr; + private: ImagesCache(const ImagesCache&) = delete; diff --git a/src/aliceVision/mvsUtils/fileIO.cpp b/src/aliceVision/mvsUtils/fileIO.cpp index fd9cc5fb56..4607597911 100644 --- a/src/aliceVision/mvsUtils/fileIO.cpp +++ b/src/aliceVision/mvsUtils/fileIO.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -322,20 +323,18 @@ Matrix3x4 load3x4MatrixFromFile(FILE* fi) return m; } -void memcpyRGBImageFromFileToArr(int camId, Color* imgArr, const std::string& fileNameOrigStr, const MultiViewParams* mp, int bandType, imageIO::EImageColorSpace colorspace) +void loadImage(const std::string& path, const MultiViewParams* mp, int camId, Image& img, int bandType, imageIO::EImageColorSpace colorspace) { - int origWidth, origHeight; - std::vector cimg; - imageIO::readImage(fileNameOrigStr, origWidth, origHeight, cimg, colorspace); + imageIO::readImage(path, img, colorspace); // check image size - if((mp->getOriginalWidth(camId) != origWidth) || (mp->getOriginalHeight(camId) != origHeight)) + if((mp->getOriginalWidth(camId) != img.width()) || (mp->getOriginalHeight(camId) != img.height())) { std::stringstream s; s << "Bad image dimension for camera : " << camId << "\n"; - s << "\t- image path : " << fileNameOrigStr << "\n"; + s << "\t- image path : " << path << "\n"; s << "\t- expected dimension : " << mp->getOriginalWidth(camId) << "x" << mp->getOriginalHeight(camId) << "\n"; - s << "\t- real dimension : " << origWidth << "x" << origHeight << "\n"; + s << "\t- real dimension : " << img.width() << "x" << img.height() << "\n"; throw std::runtime_error(s.str()); } @@ -347,47 +346,31 @@ void memcpyRGBImageFromFileToArr(int camId, Color* imgArr, const std::string& fi if(processScale > 1) { ALICEVISION_LOG_DEBUG("Downscale (x" << processScale << ") image: " << mp->getViewId(camId) << "."); - std::vector bmpr; - imageIO::resizeImage(origWidth, origHeight, processScale, cimg, bmpr); - cimg = bmpr; + Image bmpr; + imageIO::resizeImage(processScale, img, bmpr); + img.swap(bmpr); } if(bandType == 1) { - - // IplImage* cimg1=cvCreateImage(cvSize(cimg->width,cimg->height),IPL_DEPTH_8U,3); - // cvSmooth(cimg1,cimg,CV_BILATERAL,11,0,10.0,5.0); - // cvReleaseImage(&cimg); - // cimg=cimg1; - std::vector smooth; - imageIO::convolveImage(width, height, cimg, smooth, "gaussian", 11.0f, 11.0f); - cimg = smooth; + Image smooth; + imageIO::convolveImage(img, smooth, "gaussian", 11.0f, 11.0f); + img.swap(smooth); } if(bandType == 2) { - std::vector bmps; - imageIO::convolveImage(width, height, cimg, bmps, "gaussian", 11.0f, 11.0f); + Image bmps; + imageIO::convolveImage(img, bmps, "gaussian", 11.0f, 11.0f); for(int y = 0; y < height; y++) { for(int x = 0; x < width; x++) { - const std::size_t index = y * width + x; - Color& cimc = cimg.at(index); - cimc = cimc - bmps.at(index); //cimg(x, y) - bmps(x, y) + img.at(x, y) -= bmps.at(x, y); } } } - - for(int y = 0; y < height; y++) - { - for(int x = 0; x < width; x++) - { - const Color color = cimg.at(y * width + x); - imgArr[y * width + x] = color; - } - } } bool DeleteDirectory(const std::string& sPath) diff --git a/src/aliceVision/mvsUtils/fileIO.hpp b/src/aliceVision/mvsUtils/fileIO.hpp index 970f8fe521..9f84b610a7 100644 --- a/src/aliceVision/mvsUtils/fileIO.hpp +++ b/src/aliceVision/mvsUtils/fileIO.hpp @@ -26,7 +26,7 @@ std::string getFileNameFromViewId(const MultiViewParams* mp, int viewId, EFileTy std::string getFileNameFromIndex(const MultiViewParams* mp, int index, EFileType mv_file_type, int scale = 0); FILE* mv_openFile(const MultiViewParams* mp, int index, EFileType mv_file_type, const char* readWrite); Matrix3x4 load3x4MatrixFromFile(FILE* fi); -void memcpyRGBImageFromFileToArr(int camId, Color* imgArr, const std::string& fileNameOrigStr, const MultiViewParams* mp, +void loadImage(const std::string& path, const MultiViewParams* mp, int camId, Image& img, int bandType, imageIO::EImageColorSpace colorspace); bool DeleteDirectory(const std::string& sPath); From 17a5d5189384f935be46251469a6185379d6b55a Mon Sep 17 00:00:00 2001 From: Clara Date: Wed, 12 Jun 2019 16:37:42 +0200 Subject: [PATCH 45/51] [mesh][mvsUtils] check if mesh triangles reproject correctly When texturing: check if mesh triangles reproject into both source and undisto images. [mvsUtils] MultiViewParams: new method to project a triangle into source image (= image with distortion) [mesh] Mesh: adapt triangle reprojection functions --- src/aliceVision/mesh/Mesh.cpp | 29 ++++++-------------- src/aliceVision/mesh/Mesh.hpp | 4 +-- src/aliceVision/mesh/Texturing.cpp | 2 +- src/aliceVision/mesh/UVAtlas.cpp | 5 ++-- src/aliceVision/mvsUtils/MultiViewParams.cpp | 24 +++++++++++++--- src/aliceVision/mvsUtils/MultiViewParams.hpp | 4 ++- 6 files changed, 37 insertions(+), 31 deletions(-) diff --git a/src/aliceVision/mesh/Mesh.cpp b/src/aliceVision/mesh/Mesh.cpp index b93409fa3a..4bd2f5941e 100644 --- a/src/aliceVision/mesh/Mesh.cpp +++ b/src/aliceVision/mesh/Mesh.cpp @@ -227,28 +227,17 @@ Mesh::triangle_proj Mesh::getTriangleProjection(int triid, const mvsUtils::Multi return tp; } -bool Mesh::isTriangleProjectionInImage(const Mesh::triangle_proj& tp, int width, int height, int margin) const +bool Mesh::isTriangleProjectionInImage(const mvsUtils::MultiViewParams& mp, const Mesh::triangle_proj& tp, int camId, int margin) const { - int w = width - margin; - int h = height - margin; - for(int j = 0; j < 3; j++) - { - if(!((tp.tpixs[j].x > margin) && (tp.tpixs[j].x < w) && (tp.tpixs[j].y > margin) && (tp.tpixs[j].y < h))) - { - return false; - } - } - return true; + return (getTriangleNbVertexInImage(mp, tp, camId, margin) == 3); } -int Mesh::getTriangleNbVertexInImage(const Mesh::triangle_proj& tp, int width, int height, int margin) const +int Mesh::getTriangleNbVertexInImage(const mvsUtils::MultiViewParams& mp, const Mesh::triangle_proj& tp, int camId, int margin) const { int nbVertexInImage = 0; - int w = width - margin; - int h = height - margin; for (int j = 0; j < 3; j++) { - if ((tp.tpixs[j].x > margin) && (tp.tpixs[j].x < w) && (tp.tpixs[j].y > margin) && (tp.tpixs[j].y < h)) + if(mp.isPixelInImage(tp.tpixs[j], camId, margin) && mp.isPixelInSourceImage(tp.tpixs[j], camId, margin)) { ++nbVertexInImage; } @@ -665,7 +654,7 @@ StaticVector*>* Mesh::getTrisMap(const mvsUtils::MultiViewPara for(int i = 0; i < tris->size(); i++) { triangle_proj tp = getTriangleProjection(i, mp, rc, w, h); - if((isTriangleProjectionInImage(tp, w, h, 0))) + if((isTriangleProjectionInImage(*mp, tp, rc, 0))) { Pixel pix; for(pix.x = tp.lu.x; pix.x <= tp.rd.x; pix.x++) @@ -705,7 +694,7 @@ StaticVector*>* Mesh::getTrisMap(const mvsUtils::MultiViewPara for(int i = 0; i < tris->size(); i++) { triangle_proj tp = getTriangleProjection(i, mp, rc, w, h); - if((isTriangleProjectionInImage(tp, w, h, 0))) + if((isTriangleProjectionInImage(*mp, tp, rc, 0))) { Pixel pix; for(pix.x = tp.lu.x; pix.x <= tp.rd.x; pix.x++) @@ -744,7 +733,7 @@ StaticVector*>* Mesh::getTrisMap(StaticVector* visTris, c { int i = (*visTris)[m]; triangle_proj tp = getTriangleProjection(i, mp, rc, w, h); - if((isTriangleProjectionInImage(tp, w, h, 0))) + if((isTriangleProjectionInImage(*mp, tp, rc, 0))) { Pixel pix; for(pix.x = tp.lu.x; pix.x <= tp.rd.x; pix.x++) @@ -785,7 +774,7 @@ StaticVector*>* Mesh::getTrisMap(StaticVector* visTris, c { int i = (*visTris)[m]; triangle_proj tp = getTriangleProjection(i, mp, rc, w, h); - if((isTriangleProjectionInImage(tp, w, h, 0))) + if((isTriangleProjectionInImage(*mp, tp, rc, 0))) { Pixel pix; for(pix.x = tp.lu.x; pix.x <= tp.rd.x; pix.x++) @@ -989,7 +978,7 @@ StaticVector* Mesh::getVisibleTrianglesIndexes(StaticVector* depthMa Point3d cg = computeTriangleCenterOfGravity(i); Pixel pix; mp->getPixelFor3DPoint(&pix, cg, rc); - if(mp->isPixelInImage(pix, 1, rc)) + if(mp->isPixelInImage(pix, rc, 1)) { pix.x = (int)(((float)pix.x / (float)ow) * (float)w); pix.y = (int)(((float)pix.y / (float)oh) * (float)h); diff --git a/src/aliceVision/mesh/Mesh.hpp b/src/aliceVision/mesh/Mesh.hpp index b7aa1a4f09..7bedf09a04 100644 --- a/src/aliceVision/mesh/Mesh.hpp +++ b/src/aliceVision/mesh/Mesh.hpp @@ -169,8 +169,8 @@ class Mesh double computeTriangleProjectionArea(const triangle_proj& tp) const; double computeTriangleArea(int idTri) const; Mesh::triangle_proj getTriangleProjection(int triid, const mvsUtils::MultiViewParams* mp, int rc, int w, int h) const; - bool isTriangleProjectionInImage(const Mesh::triangle_proj& tp, int width, int height, int margin) const; - int getTriangleNbVertexInImage(const Mesh::triangle_proj& tp, int width, int height, int margin) const; + bool isTriangleProjectionInImage(const mvsUtils::MultiViewParams& mp, const Mesh::triangle_proj& tp, int camId, int margin) const; + int getTriangleNbVertexInImage(const mvsUtils::MultiViewParams& mp, const Mesh::triangle_proj& tp, int camId, int margin) const; bool doesTriangleIntersectsRectangle(Mesh::triangle_proj* tp, Mesh::rectangle* re); StaticVector* getTrianglePixelIntersectionsAndInternalPoints(Mesh::triangle_proj* tp, Mesh::rectangle* re); diff --git a/src/aliceVision/mesh/Texturing.cpp b/src/aliceVision/mesh/Texturing.cpp index 682582692a..d4c9a49ad4 100644 --- a/src/aliceVision/mesh/Texturing.cpp +++ b/src/aliceVision/mesh/Texturing.cpp @@ -409,7 +409,7 @@ void Texturing::generateTexturesSubSet(const mvsUtils::MultiViewParams& mp, const int h = mp.getHeight(camId); const Mesh::triangle_proj tProj = me->getTriangleProjection(triangleID, &mp, camId, w, h); - const int nbVertex = me->getTriangleNbVertexInImage(tProj, w, h, 20); + const int nbVertex = me->getTriangleNbVertexInImage(mp, tProj, camId, 20); if(nbVertex == 0) // No triangle vertex in the image continue; diff --git a/src/aliceVision/mesh/UVAtlas.cpp b/src/aliceVision/mesh/UVAtlas.cpp index 9a27ef53af..ef83fce565 100644 --- a/src/aliceVision/mesh/UVAtlas.cpp +++ b/src/aliceVision/mesh/UVAtlas.cpp @@ -57,9 +57,8 @@ void UVAtlas::createCharts(vector& charts, mvsUtils::MultiViewParams& mp, int cameraID = (*cameras)[c]; // project triangle Mesh::triangle_proj tProj = _mesh.getTriangleProjection(i, &mp, cameraID, mp.getWidth(cameraID), mp.getHeight(cameraID)); - if(!mp.isPixelInImage(Pixel(tProj.tp2ds[0]), 10, cameraID) - || !mp.isPixelInImage(Pixel(tProj.tp2ds[1]), 10, cameraID) - || !mp.isPixelInImage(Pixel(tProj.tp2ds[2]), 10, cameraID)) + + if(!_mesh.isTriangleProjectionInImage(mp, tProj, cameraID, 10)) continue; const float area = _mesh.computeTriangleProjectionArea(tProj); diff --git a/src/aliceVision/mvsUtils/MultiViewParams.cpp b/src/aliceVision/mvsUtils/MultiViewParams.cpp index dbf0ad50c5..9900f32003 100644 --- a/src/aliceVision/mvsUtils/MultiViewParams.cpp +++ b/src/aliceVision/mvsUtils/MultiViewParams.cpp @@ -511,14 +511,25 @@ double MultiViewParams::getCamsMinPixelSize(const Point3d& x0, StaticVector return minPixSize; } -bool MultiViewParams::isPixelInImage(const Pixel& pix, int d, int camId) const +bool MultiViewParams::isPixelInSourceImage(const Pixel& pixRC, int camId, int margin) const { - return ((pix.x >= d) && (pix.x < getWidth(camId) - d) && (pix.y >= d) && (pix.y < getHeight(camId) - d)); + const IndexT viewId = getViewId(camId); + const sfmData::View& view = *(_sfmData.getViews().at(viewId)); + const camera::IntrinsicBase* intrinsicPtr = _sfmData.getIntrinsicPtr(view.getIntrinsicId()); + + const double s = getDownscaleFactor(camId); + Vec2 pix_disto = intrinsicPtr->get_d_pixel({pixRC.x * s, pixRC.y * s}) / s; + return isPixelInImage(Pixel(pix_disto.x(), pix_disto.y()), camId, margin); +} + +bool MultiViewParams::isPixelInImage(const Pixel& pix, int camId, int margin) const +{ + return ((pix.x >= margin) && (pix.x < getWidth(camId) - margin) && + (pix.y >= margin) && (pix.y < getHeight(camId) - margin)); } bool MultiViewParams::isPixelInImage(const Pixel& pix, int camId) const { - return ((pix.x >= g_border) && (pix.x < getWidth(camId) - g_border) && - (pix.y >= g_border) && (pix.y < getHeight(camId) - g_border)); + return isPixelInImage(pix, camId, g_border); } bool MultiViewParams::isPixelInImage(const Point2d& pix, int camId) const @@ -526,6 +537,11 @@ bool MultiViewParams::isPixelInImage(const Point2d& pix, int camId) const return isPixelInImage(Pixel(pix), camId); } +bool MultiViewParams::isPixelInImage(const Point2d& pix, int camId, int margin) const +{ + return isPixelInImage(Pixel(pix), camId, margin); +} + void MultiViewParams::decomposeProjectionMatrix(Point3d& Co, Matrix3x3& Ro, Matrix3x3& iRo, Matrix3x3& Ko, Matrix3x3& iKo, Matrix3x3& iPo, const Matrix3x4& P) const { diff --git a/src/aliceVision/mvsUtils/MultiViewParams.hpp b/src/aliceVision/mvsUtils/MultiViewParams.hpp index 988ac285db..7d85bf3b22 100644 --- a/src/aliceVision/mvsUtils/MultiViewParams.hpp +++ b/src/aliceVision/mvsUtils/MultiViewParams.hpp @@ -251,9 +251,11 @@ class MultiViewParams double getCamsMinPixelSize(const Point3d& x0, std::vector* tcams) const; double getCamsMinPixelSize(const Point3d& x0, StaticVector& tcams) const; - bool isPixelInImage(const Pixel& pix, int d, int camId) const; + bool isPixelInSourceImage(const Pixel& pixRC, int camId, int margin) const; + bool isPixelInImage(const Pixel& pix, int camId, int margin) const; bool isPixelInImage(const Pixel& pix, int camId) const; bool isPixelInImage(const Point2d& pix, int camId) const; + bool isPixelInImage(const Point2d& pix, int camId, int margin) const; void decomposeProjectionMatrix(Point3d& Co, Matrix3x3& Ro, Matrix3x3& iRo, Matrix3x3& Ko, Matrix3x3& iKo, Matrix3x3& iPo, const Matrix3x4& P) const; /** From 15310521b5ee8606038f272adbf85816199f7329 Mon Sep 17 00:00:00 2001 From: Clara Date: Wed, 12 Jun 2019 17:17:15 +0200 Subject: [PATCH 46/51] [mesh] Texturing: fix nbContribMax calculus Update nbContribMax (= max number of contribution for a camera): number of contributions used to be cumulative but not anymore (more practical for the user) --- src/aliceVision/mesh/Texturing.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/aliceVision/mesh/Texturing.cpp b/src/aliceVision/mesh/Texturing.cpp index d4c9a49ad4..e257d6d1b7 100644 --- a/src/aliceVision/mesh/Texturing.cpp +++ b/src/aliceVision/mesh/Texturing.cpp @@ -275,6 +275,8 @@ void Texturing::generateTextures(const mvsUtils::MultiViewParams &mp, ALICEVISION_LOG_INFO(" - " << c); } + std::partial_sum(m.begin(), m.end(), m.begin()); + mvsUtils::ImagesCache imageCache(&mp, 0, imageIO::EImageColorSpace::SRGB); imageCache.setCacheSize(2); system::MemoryInfo memInfo = system::getMemoryInfo(); @@ -432,7 +434,6 @@ void Texturing::generateTexturesSubSet(const mvsUtils::MultiViewParams& mp, int nbContribMax = std::min(texParams.multiBandNbContrib.back(), static_cast(scorePerCamId.size())); int nbCumulatedVertices = 0; int band = 0; - int nbContribLevel = texParams.multiBandNbContrib[0]; for(int contrib = 0; nbCumulatedVertices < 3 * nbContribMax && contrib < nbContribMax; ++contrib) { nbCumulatedVertices += std::get<0>(scorePerCamId[contrib]); From 850ca7afc20e423e87fc7b0a4fa924a1f6030a33 Mon Sep 17 00:00:00 2001 From: Clara Date: Wed, 12 Jun 2019 17:20:09 +0200 Subject: [PATCH 47/51] [mesh] Texturing: bug fix on a shared_ptr Incorrect use of shared_ptr: if you remove the temporary variable and directly get the value from the shared_ptr, the shared_ptr ownership is removed. --- src/aliceVision/mesh/Texturing.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/aliceVision/mesh/Texturing.cpp b/src/aliceVision/mesh/Texturing.cpp index e257d6d1b7..a34d0a37cb 100644 --- a/src/aliceVision/mesh/Texturing.cpp +++ b/src/aliceVision/mesh/Texturing.cpp @@ -483,8 +483,9 @@ void Texturing::generateTexturesSubSet(const mvsUtils::MultiViewParams& mp, ALICEVISION_LOG_INFO("- camera " << camId + 1 << "/" << mp.ncams << " with contributions to " << cameraContributions.size() << " texture files:"); //Load camera image from cache - imageCache.refreshData(camId); - const Image& camImg = *(imageCache.getImg_sync(camId)); + imageCache.refreshData(camId); + mvsUtils::ImagesCache::ImgPtr imgPtr = imageCache.getImg_sync(camId); + const Image& camImg = *imgPtr; //Calculate laplacianPyramid std::vector pyramidL; //laplacian pyramid From 118ed549a3d545fee8c52a67433d6008c388613b Mon Sep 17 00:00:00 2001 From: Clara Date: Wed, 12 Jun 2019 17:21:19 +0200 Subject: [PATCH 48/51] [mesh] Texturing: code cleaning & setting param default value code cleaning + setting multiBandDownscale default value to 4 + renaming ImagesCache::ImgPtr to ImagesCache::ImgSharedPtr --- .../depthMap/cuda/PlaneSweepingCuda.cpp | 4 +-- src/aliceVision/mesh/Texturing.cpp | 25 +++++++++---------- src/aliceVision/mesh/Texturing.hpp | 2 +- src/aliceVision/mvsUtils/ImagesCache.cpp | 2 +- src/aliceVision/mvsUtils/ImagesCache.hpp | 6 ++--- 5 files changed, 19 insertions(+), 20 deletions(-) diff --git a/src/aliceVision/depthMap/cuda/PlaneSweepingCuda.cpp b/src/aliceVision/depthMap/cuda/PlaneSweepingCuda.cpp index f0f7c66f27..a91614714c 100644 --- a/src/aliceVision/depthMap/cuda/PlaneSweepingCuda.cpp +++ b/src/aliceVision/depthMap/cuda/PlaneSweepingCuda.cpp @@ -20,7 +20,7 @@ namespace aliceVision { namespace depthMap { -inline const uchar4 get( mvsUtils::ImagesCache::ImgPtr img, int x, int y ) +inline const uchar4 get( mvsUtils::ImagesCache::ImgSharedPtr img, int x, int y ) { const Color floatRGB = img->at(x,y) * 255.0f; @@ -137,7 +137,7 @@ void cps_fillCameraData(mvsUtils::ImagesCache* ic, cameraStruct* cam, int c, mvs // cam->tex_hmh_g->getBuffer(), // cam->tex_hmh_b->getBuffer(), mp->indexes[c], mp, true, 1, 0); - mvsUtils::ImagesCache::ImgPtr img = ic->getImg_sync(c); + mvsUtils::ImagesCache::ImgSharedPtr img = ic->getImg_sync(c); Pixel pix; { diff --git a/src/aliceVision/mesh/Texturing.cpp b/src/aliceVision/mesh/Texturing.cpp index a34d0a37cb..ce7a5d6a33 100644 --- a/src/aliceVision/mesh/Texturing.cpp +++ b/src/aliceVision/mesh/Texturing.cpp @@ -258,18 +258,17 @@ void Texturing::generateUVsBasicMethod(mvsUtils::MultiViewParams& mp) void Texturing::generateTextures(const mvsUtils::MultiViewParams &mp, const boost::filesystem::path &outPath, imageIO::EImageFileType textureFileType) { - ALICEVISION_LOG_INFO("Texturing: Use multiband blending with the following contributions per band:"); - - texParams.multiBandNbContrib.erase(std::remove(std::begin(texParams.multiBandNbContrib), std::end(texParams.multiBandNbContrib), 0), - std::end(texParams.multiBandNbContrib)); - texParams.nbBand = texParams.multiBandNbContrib.size(); + // Ensure that contribution levels do not contain 0 and are sorted (as each frequency band contributes to lower bands). + auto& m = texParams.multiBandNbContrib; + m.erase(std::remove(std::begin(m), std::end(m), 0), std::end(m)); + texParams.nbBand = m.size(); - if(!std::is_sorted(std::begin(texParams.multiBandNbContrib), std::end(texParams.multiBandNbContrib))) + if(!std::is_sorted(std::begin(m), std::end(m))) { ALICEVISION_LOG_INFO("Sorting contributions per band (necessary)."); - std::sort(std::begin(texParams.multiBandNbContrib), std::end(texParams.multiBandNbContrib)); - + std::sort(std::begin(m), std::end(m)); } + ALICEVISION_LOG_INFO("Texturing: Use multiband blending with the following contributions per band:"); for(int c: texParams.multiBandNbContrib) { ALICEVISION_LOG_INFO(" - " << c); @@ -454,10 +453,9 @@ void Texturing::generateTexturesSubSet(const mvsUtils::MultiViewParams& mp, camContribution[atlasID].resize(texParams.nbBand); camContribution.at(atlasID)[band].emplace_back(triangleID, triangleScore); - if(contrib + 1 == nbContribLevel) + if(contrib + 1 == texParams.multiBandNbContrib[band]) { ++band; - nbContribLevel = texParams.multiBandNbContrib[band]; } } } @@ -477,14 +475,14 @@ void Texturing::generateTexturesSubSet(const mvsUtils::MultiViewParams& mp, if(cameraContributions.empty()) { - ALICEVISION_LOG_INFO("- camera " << camId + 1 << "/" << mp.ncams << " unused."); + ALICEVISION_LOG_INFO("- camera " << mp.getViewId(camId) << " (" << camId + 1 << "/" << mp.ncams << ") unused."); continue; } - ALICEVISION_LOG_INFO("- camera " << camId + 1 << "/" << mp.ncams << " with contributions to " << cameraContributions.size() << " texture files:"); + ALICEVISION_LOG_INFO("- camera " << mp.getViewId(camId) << " (" << camId + 1 << "/" << mp.ncams << ") with contributions to " << cameraContributions.size() << " texture files:"); //Load camera image from cache imageCache.refreshData(camId); - mvsUtils::ImagesCache::ImgPtr imgPtr = imageCache.getImg_sync(camId); + mvsUtils::ImagesCache::ImgSharedPtr imgPtr = imageCache.getImg_sync(camId); const Image& camImg = *imgPtr; //Calculate laplacianPyramid @@ -601,6 +599,7 @@ void Texturing::generateTexturesSubSet(const mvsUtils::MultiViewParams& mp, #if TEXTURING_MBB_DEBUG { + // write the number of contribution per atlas frequency bands for(std::size_t level = 0; level < accuPyramid.pyramid.size(); ++level) { AccuImage& atlasLevelTexture = accuPyramid.pyramid[level]; diff --git a/src/aliceVision/mesh/Texturing.hpp b/src/aliceVision/mesh/Texturing.hpp index 537fd80fde..2c30d6e9af 100644 --- a/src/aliceVision/mesh/Texturing.hpp +++ b/src/aliceVision/mesh/Texturing.hpp @@ -66,7 +66,7 @@ struct TexturingParams { bool useScore = true; unsigned int nbBand = 4; - unsigned int multiBandDownscale = 2; + unsigned int multiBandDownscale = 4; std::vector multiBandNbContrib = {1, 5, 10, 0}; // number of contributions per frequency band for the multi-band blending double bestScoreThreshold = 0.0; //< 0.0 to disable filtering based on threshold to relative best score diff --git a/src/aliceVision/mvsUtils/ImagesCache.cpp b/src/aliceVision/mvsUtils/ImagesCache.cpp index 4eb0728119..03d196ea1a 100644 --- a/src/aliceVision/mvsUtils/ImagesCache.cpp +++ b/src/aliceVision/mvsUtils/ImagesCache.cpp @@ -124,7 +124,7 @@ Color ImagesCache::getPixelValueInterpolated(const Point2d* pix, int camId) { // get the image index in the memory const int i = camIdMapId[camId]; - const ImgPtr& img = imgs[i]; + const ImgSharedPtr& img = imgs[i]; const int xp = static_cast(pix->x); const int yp = static_cast(pix->y); diff --git a/src/aliceVision/mvsUtils/ImagesCache.hpp b/src/aliceVision/mvsUtils/ImagesCache.hpp index f915a7d183..3cb55349bc 100644 --- a/src/aliceVision/mvsUtils/ImagesCache.hpp +++ b/src/aliceVision/mvsUtils/ImagesCache.hpp @@ -25,13 +25,13 @@ class ImagesCache public: const MultiViewParams* mp; - typedef std::shared_ptr ImgPtr; + typedef std::shared_ptr ImgSharedPtr; private: ImagesCache(const ImagesCache&) = delete; int N_PRELOADED_IMAGES; - std::vector imgs; + std::vector imgs; std::vector camIdMapId; std::vector mapIdCamId; @@ -50,7 +50,7 @@ class ImagesCache void setCacheSize(int nbPreload); ~ImagesCache(); - inline ImgPtr getImg_sync( int camId ) + inline ImgSharedPtr getImg_sync( int camId ) { refreshData_sync(camId); const int imageId = camIdMapId[camId]; From 7fdc82b7616f0556f15f2b2b0297f1079de0ed31 Mon Sep 17 00:00:00 2001 From: Clara Date: Thu, 13 Jun 2019 15:53:45 +0200 Subject: [PATCH 49/51] [mesh] Texturing: set bestScoreThreshold default value to 0.1 only keep triangles with sufficient reprojection area --- src/aliceVision/mesh/Texturing.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/aliceVision/mesh/Texturing.hpp b/src/aliceVision/mesh/Texturing.hpp index 2c30d6e9af..820e4bf31f 100644 --- a/src/aliceVision/mesh/Texturing.hpp +++ b/src/aliceVision/mesh/Texturing.hpp @@ -69,7 +69,7 @@ struct TexturingParams unsigned int multiBandDownscale = 4; std::vector multiBandNbContrib = {1, 5, 10, 0}; // number of contributions per frequency band for the multi-band blending - double bestScoreThreshold = 0.0; //< 0.0 to disable filtering based on threshold to relative best score + double bestScoreThreshold = 0.1; //< 0.0 to disable filtering based on threshold to relative best score double angleHardThreshold = 90.0; //< 0.0 to disable angle hard threshold filtering bool forceVisibleByAllVertices = false; //< triangle visibility is based on the union of vertices visiblity EVisibilityRemappingMethod visibilityRemappingMethod = EVisibilityRemappingMethod::PullPush; From d794c42d1e19a218199aced286d787191e10f538 Mon Sep 17 00:00:00 2001 From: Clara Date: Fri, 14 Jun 2019 14:34:33 +0200 Subject: [PATCH 50/51] code cleaning on struct OutputFileColorSpace --- src/aliceVision/depthMap/DepthSimMap.cpp | 3 ++- src/aliceVision/mesh/Texturing.cpp | 2 +- src/aliceVision/mvsData/imageIO.cpp | 4 ++-- src/aliceVision/mvsData/imageIO.hpp | 8 +++----- src/aliceVision/mvsUtils/ImagesCache.cpp | 4 ---- src/aliceVision/mvsUtils/ImagesCache.hpp | 2 +- 6 files changed, 9 insertions(+), 14 deletions(-) diff --git a/src/aliceVision/depthMap/DepthSimMap.cpp b/src/aliceVision/depthMap/DepthSimMap.cpp index d446ef8539..3bbfb914d1 100644 --- a/src/aliceVision/depthMap/DepthSimMap.cpp +++ b/src/aliceVision/depthMap/DepthSimMap.cpp @@ -383,7 +383,8 @@ void DepthSimMap::saveToImage(std::string filename, float simThr) } } - imageIO::writeImage(filename, bufferWidth, h, colorBuffer, imageIO::EImageQuality::LOSSLESS, imageIO::EImageColorSpace::NO_CONVERSION); + using namespace imageIO; + writeImage(filename, bufferWidth, h, colorBuffer, EImageQuality::LOSSLESS, EImageColorSpace::NO_CONVERSION); } catch(...) { diff --git a/src/aliceVision/mesh/Texturing.cpp b/src/aliceVision/mesh/Texturing.cpp index ce7a5d6a33..bbfa52269b 100644 --- a/src/aliceVision/mesh/Texturing.cpp +++ b/src/aliceVision/mesh/Texturing.cpp @@ -814,7 +814,7 @@ void Texturing::writeTexture(AccuImage& atlasTexture, const std::size_t atlasID, ALICEVISION_LOG_INFO(" - Writing texture file: " << texturePath.string()); using namespace imageIO; - imageIO::writeImage(texturePath.string(), atlasTexture.img, EImageQuality::OPTIMIZED, OutputFileColorSpace(EImageColorSpace::SRGB, EImageColorSpace::AUTO)); + writeImage(texturePath.string(), atlasTexture.img, EImageQuality::OPTIMIZED, OutputFileColorSpace(EImageColorSpace::SRGB, EImageColorSpace::AUTO)); } diff --git a/src/aliceVision/mvsData/imageIO.cpp b/src/aliceVision/mvsData/imageIO.cpp index f1f9e9ae1d..898f64d7b6 100644 --- a/src/aliceVision/mvsData/imageIO.cpp +++ b/src/aliceVision/mvsData/imageIO.cpp @@ -384,7 +384,7 @@ void writeImage(const std::string& path, int width, int height, const std::vecto writeImage(path, oiio::TypeDesc::UCHAR, width, height, 1, buffer, imageQuality, colorspace, metadata); } -void writeImage(const std::string& path, int width, int height, const std::vector& buffer, EImageQuality imageQuality, OutputFileColorSpace colorspace, const oiio::ParamValueList& metadata) +void writeImage(const std::string& path, int width, int height, const std::vector& buffer, EImageQuality imageQuality, OutputFileColorSpace colorspace, const oiio::ParamValueList& metadata) { writeImage(path, oiio::TypeDesc::UINT16, width, height, 1, buffer, imageQuality, colorspace, metadata); } @@ -404,7 +404,7 @@ void writeImage(const std::string& path, int width, int height, const std::vecto writeImage(path, oiio::TypeDesc::FLOAT, width, height, 3, buffer, imageQuality, colorspace, metadata); } -void writeImage(const std::string &path, Image &image, EImageQuality imageQuality, OutputFileColorSpace colorspace, const OpenImageIO_v1_8::ParamValueList &metadata) +void writeImage(const std::string &path, Image &image, EImageQuality imageQuality, OutputFileColorSpace colorspace, const oiio::ParamValueList& metadata) { writeImage(path, oiio::TypeDesc::FLOAT, image.width(), image.height(), 3, image.data(), imageQuality, colorspace, metadata); } diff --git a/src/aliceVision/mvsData/imageIO.hpp b/src/aliceVision/mvsData/imageIO.hpp index 3dfef7aa43..a27193ab6e 100644 --- a/src/aliceVision/mvsData/imageIO.hpp +++ b/src/aliceVision/mvsData/imageIO.hpp @@ -45,8 +45,8 @@ enum class EImageColorSpace struct OutputFileColorSpace { - EImageColorSpace from = EImageColorSpace::LINEAR; - EImageColorSpace to = EImageColorSpace::AUTO; + EImageColorSpace from{EImageColorSpace::LINEAR}; + EImageColorSpace to{EImageColorSpace::AUTO}; OutputFileColorSpace(EImageColorSpace from_, EImageColorSpace to_) : from(from_) @@ -61,9 +61,7 @@ struct OutputFileColorSpace else to = to_; } - OutputFileColorSpace() - { - } + OutputFileColorSpace() = default; }; /** diff --git a/src/aliceVision/mvsUtils/ImagesCache.cpp b/src/aliceVision/mvsUtils/ImagesCache.cpp index 03d196ea1a..eb528b9f9e 100644 --- a/src/aliceVision/mvsUtils/ImagesCache.cpp +++ b/src/aliceVision/mvsUtils/ImagesCache.cpp @@ -68,10 +68,6 @@ void ImagesCache::setCacheSize(int nbPreload) mapIdClock.resize( N_PRELOADED_IMAGES, clock() ); } -ImagesCache::~ImagesCache() -{ -} - void ImagesCache::refreshData(int camId) { // printf("camId %i\n",camId); diff --git a/src/aliceVision/mvsUtils/ImagesCache.hpp b/src/aliceVision/mvsUtils/ImagesCache.hpp index 3cb55349bc..dbf9ecb19b 100644 --- a/src/aliceVision/mvsUtils/ImagesCache.hpp +++ b/src/aliceVision/mvsUtils/ImagesCache.hpp @@ -48,7 +48,7 @@ class ImagesCache ImagesCache( const MultiViewParams* _mp, int _bandType, imageIO::EImageColorSpace colorspace, std::vector& _imagesNames); void initIC( std::vector& _imagesNames ); void setCacheSize(int nbPreload); - ~ImagesCache(); + ~ImagesCache() = default; inline ImgSharedPtr getImg_sync( int camId ) { From 9f189bcfee675af1b1cc5be83249eb3a6761ddbd Mon Sep 17 00:00:00 2001 From: Clara Date: Thu, 20 Jun 2019 11:43:32 +0200 Subject: [PATCH 51/51] Explicit constructor for OutputFileColorSpace struct & missing reference --- src/aliceVision/depthMap/DepthSimMap.cpp | 15 ++++++++++----- src/aliceVision/depthMap/RefineRc.cpp | 4 +++- src/aliceVision/depthMap/SemiGlobalMatchingRc.cpp | 5 ++++- src/aliceVision/fuseCut/Fuser.cpp | 12 ++++++++---- src/aliceVision/mesh/Texturing.cpp | 4 ++-- src/aliceVision/mvsData/imageIO.cpp | 12 ++++++------ src/aliceVision/mvsData/imageIO.hpp | 14 +++++++------- 7 files changed, 40 insertions(+), 26 deletions(-) diff --git a/src/aliceVision/depthMap/DepthSimMap.cpp b/src/aliceVision/depthMap/DepthSimMap.cpp index 3bbfb914d1..9c79b817ab 100644 --- a/src/aliceVision/depthMap/DepthSimMap.cpp +++ b/src/aliceVision/depthMap/DepthSimMap.cpp @@ -384,7 +384,8 @@ void DepthSimMap::saveToImage(std::string filename, float simThr) } using namespace imageIO; - writeImage(filename, bufferWidth, h, colorBuffer, EImageQuality::LOSSLESS, EImageColorSpace::NO_CONVERSION); + OutputFileColorSpace colorspace(EImageColorSpace::NO_CONVERSION); + writeImage(filename, bufferWidth, h, colorBuffer, EImageQuality::LOSSLESS, colorspace); } catch(...) { @@ -416,8 +417,10 @@ void DepthSimMap::save(int rc, const StaticVector& tcams) metadata.push_back(oiio::ParamValue("AliceVision:P", oiio::TypeDesc(oiio::TypeDesc::DOUBLE, oiio::TypeDesc::MATRIX44), 1, matrixP.data())); } - imageIO::writeImage(getFileNameFromIndex(mp, rc, mvsUtils::EFileType::depthMap, scale), width, height, depthMap->getDataWritable(), imageIO::EImageQuality::LOSSLESS, imageIO::EImageColorSpace::NO_CONVERSION, metadata); - imageIO::writeImage(getFileNameFromIndex(mp, rc, mvsUtils::EFileType::simMap, scale), width, height, simMap->getDataWritable(), imageIO::EImageQuality::OPTIMIZED, imageIO::EImageColorSpace::NO_CONVERSION, metadata); + using namespace imageIO; + OutputFileColorSpace colorspace(EImageColorSpace::NO_CONVERSION); + writeImage(getFileNameFromIndex(mp, rc, mvsUtils::EFileType::depthMap, scale), width, height, depthMap->getDataWritable(), EImageQuality::LOSSLESS, colorspace, metadata); + writeImage(getFileNameFromIndex(mp, rc, mvsUtils::EFileType::simMap, scale), width, height, simMap->getDataWritable(), EImageQuality::OPTIMIZED, colorspace, metadata); } void DepthSimMap::load(int rc, int fromScale) @@ -467,8 +470,10 @@ void DepthSimMap::saveRefine(int rc, std::string depthMapFileName, std::string s metadata.push_back(oiio::ParamValue("AliceVision:P", oiio::TypeDesc(oiio::TypeDesc::DOUBLE, oiio::TypeDesc::MATRIX44), 1, matrixP.data())); } - imageIO::writeImage(depthMapFileName, width, height, depthMap, imageIO::EImageQuality::LOSSLESS, imageIO::EImageColorSpace::NO_CONVERSION, metadata); - imageIO::writeImage(simMapFileName, width, height, simMap, imageIO::EImageQuality::OPTIMIZED, imageIO::EImageColorSpace::NO_CONVERSION, metadata); + using namespace imageIO; + OutputFileColorSpace colorspace(EImageColorSpace::NO_CONVERSION); + writeImage(depthMapFileName, width, height, depthMap, EImageQuality::LOSSLESS, colorspace, metadata); + writeImage(simMapFileName, width, height, simMap, EImageQuality::OPTIMIZED, colorspace, metadata); } float DepthSimMap::getCellSmoothStep(int rc, const int cellId) diff --git a/src/aliceVision/depthMap/RefineRc.cpp b/src/aliceVision/depthMap/RefineRc.cpp index ec440c928b..0ccef4bd9b 100644 --- a/src/aliceVision/depthMap/RefineRc.cpp +++ b/src/aliceVision/depthMap/RefineRc.cpp @@ -407,7 +407,9 @@ void computeNormalMaps(int CUDADeviceNo, mvsUtils::MultiViewParams* mp, const St cps.computeNormalMap(&depthMap, &normalMap, rc, 1, igammaC, igammaP, wsh); - imageIO::writeImage(normalMapFilepath, mp->getWidth(rc), mp->getHeight(rc), normalMap.getDataWritable(), imageIO::EImageQuality::LOSSLESS, imageIO::EImageColorSpace::NO_CONVERSION); + using namespace imageIO; + OutputFileColorSpace colorspace(EImageColorSpace::NO_CONVERSION); + writeImage(normalMapFilepath, mp->getWidth(rc), mp->getHeight(rc), normalMap.getDataWritable(), EImageQuality::LOSSLESS, colorspace); } } } diff --git a/src/aliceVision/depthMap/SemiGlobalMatchingRc.cpp b/src/aliceVision/depthMap/SemiGlobalMatchingRc.cpp index f92ed9d1cc..f7b3f439c9 100644 --- a/src/aliceVision/depthMap/SemiGlobalMatchingRc.cpp +++ b/src/aliceVision/depthMap/SemiGlobalMatchingRc.cpp @@ -527,7 +527,10 @@ bool SemiGlobalMatchingRc::sgmrc(bool checkIfExists) std::vector volumeBestId(_volumeBestIdVal->size()); for(int i = 0; i < _volumeBestIdVal->size(); i++) volumeBestId.at(i) = std::max(0, (*_volumeBestIdVal)[i].id); - imageIO::writeImage(_sp->getSGM_idDepthMapFileName(_sp->mp->getViewId(_rc), _scale, _step), _width, _height, volumeBestId, imageIO::EImageQuality::LOSSLESS, imageIO::EImageColorSpace::NO_CONVERSION); + + using namespace imageIO; + OutputFileColorSpace colorspace(EImageColorSpace::NO_CONVERSION); + writeImage(_sp->getSGM_idDepthMapFileName(_sp->mp->getViewId(_rc), _scale, _step), _width, _height, volumeBestId, EImageQuality::LOSSLESS, colorspace); } return true; } diff --git a/src/aliceVision/fuseCut/Fuser.cpp b/src/aliceVision/fuseCut/Fuser.cpp index 3d43efac2e..f0b5fc923f 100644 --- a/src/aliceVision/fuseCut/Fuser.cpp +++ b/src/aliceVision/fuseCut/Fuser.cpp @@ -224,8 +224,10 @@ bool Fuser::filterGroupsRC(int rc, int pixSizeBall, int pixSizeBallWSP, int nNea } { - imageIO::transposeImage(h, w, numOfModalsMap); - imageIO::writeImage(getFileNameFromIndex(mp, rc, mvsUtils::EFileType::nmodMap), w, h, numOfModalsMap, imageIO::EImageQuality::LOSSLESS, imageIO::EImageColorSpace::NO_CONVERSION); + using namespace imageIO; + OutputFileColorSpace colorspace(EImageColorSpace::NO_CONVERSION); + transposeImage(h, w, numOfModalsMap); + writeImage(getFileNameFromIndex(mp, rc, mvsUtils::EFileType::nmodMap), w, h, numOfModalsMap, EImageQuality::LOSSLESS, colorspace); } delete numOfPtsMap; @@ -321,8 +323,10 @@ bool Fuser::filterDepthMapsRC(int rc, int minNumOfModals, int minNumOfModalsWSP2 metadata.push_back(oiio::ParamValue("AliceVision:P", oiio::TypeDesc(oiio::TypeDesc::DOUBLE, oiio::TypeDesc::MATRIX44), 1, matrixP.data())); } - imageIO::writeImage(getFileNameFromIndex(mp, rc, mvsUtils::EFileType::depthMap, 0), w, h, depthMap, imageIO::EImageQuality::LOSSLESS, imageIO::EImageColorSpace::NO_CONVERSION, metadata); - imageIO::writeImage(getFileNameFromIndex(mp, rc, mvsUtils::EFileType::simMap, 0), w, h, simMap, imageIO::EImageQuality::OPTIMIZED, imageIO::EImageColorSpace::NO_CONVERSION, metadata); + using namespace imageIO; + OutputFileColorSpace colorspace(EImageColorSpace::NO_CONVERSION); + writeImage(getFileNameFromIndex(mp, rc, mvsUtils::EFileType::depthMap, 0), w, h, depthMap, EImageQuality::LOSSLESS, colorspace, metadata); + writeImage(getFileNameFromIndex(mp, rc, mvsUtils::EFileType::simMap, 0), w, h, simMap, EImageQuality::OPTIMIZED, colorspace, metadata); if(mp->verbose) ALICEVISION_LOG_DEBUG(rc << " solved."); diff --git a/src/aliceVision/mesh/Texturing.cpp b/src/aliceVision/mesh/Texturing.cpp index bbfa52269b..f97998a42a 100644 --- a/src/aliceVision/mesh/Texturing.cpp +++ b/src/aliceVision/mesh/Texturing.cpp @@ -802,7 +802,6 @@ void Texturing::writeTexture(AccuImage& atlasTexture, const std::size_t atlasID, if(texParams.downscale > 1) { Image resizedColorBuffer; - outTextureSide = texParams.textureSide / texParams.downscale; ALICEVISION_LOG_INFO(" - Downscaling texture (" << texParams.downscale << "x)."); imageIO::resizeImage(texParams.downscale, atlasTexture.img, resizedColorBuffer); @@ -814,7 +813,8 @@ void Texturing::writeTexture(AccuImage& atlasTexture, const std::size_t atlasID, ALICEVISION_LOG_INFO(" - Writing texture file: " << texturePath.string()); using namespace imageIO; - writeImage(texturePath.string(), atlasTexture.img, EImageQuality::OPTIMIZED, OutputFileColorSpace(EImageColorSpace::SRGB, EImageColorSpace::AUTO)); + OutputFileColorSpace colorspace(EImageColorSpace::SRGB, EImageColorSpace::AUTO); + writeImage(texturePath.string(), atlasTexture.img, EImageQuality::OPTIMIZED, colorspace); } diff --git a/src/aliceVision/mvsData/imageIO.cpp b/src/aliceVision/mvsData/imageIO.cpp index 898f64d7b6..6c8990d9f2 100644 --- a/src/aliceVision/mvsData/imageIO.cpp +++ b/src/aliceVision/mvsData/imageIO.cpp @@ -379,32 +379,32 @@ void writeImage(const std::string& path, fs::rename(tmpPath, path); } -void writeImage(const std::string& path, int width, int height, const std::vector& buffer, EImageQuality imageQuality, OutputFileColorSpace colorspace, const oiio::ParamValueList& metadata) +void writeImage(const std::string& path, int width, int height, const std::vector& buffer, EImageQuality imageQuality, OutputFileColorSpace& colorspace, const oiio::ParamValueList& metadata) { writeImage(path, oiio::TypeDesc::UCHAR, width, height, 1, buffer, imageQuality, colorspace, metadata); } -void writeImage(const std::string& path, int width, int height, const std::vector& buffer, EImageQuality imageQuality, OutputFileColorSpace colorspace, const oiio::ParamValueList& metadata) +void writeImage(const std::string& path, int width, int height, const std::vector& buffer, EImageQuality imageQuality, OutputFileColorSpace& colorspace, const oiio::ParamValueList& metadata) { writeImage(path, oiio::TypeDesc::UINT16, width, height, 1, buffer, imageQuality, colorspace, metadata); } -void writeImage(const std::string& path, int width, int height, const std::vector& buffer, EImageQuality imageQuality, OutputFileColorSpace colorspace, const oiio::ParamValueList& metadata) +void writeImage(const std::string& path, int width, int height, const std::vector& buffer, EImageQuality imageQuality, OutputFileColorSpace& colorspace, const oiio::ParamValueList& metadata) { writeImage(path, oiio::TypeDesc::UCHAR, width, height, 3, buffer, imageQuality, colorspace, metadata); } -void writeImage(const std::string& path, int width, int height, const std::vector& buffer, EImageQuality imageQuality, OutputFileColorSpace colorspace, const oiio::ParamValueList& metadata) +void writeImage(const std::string& path, int width, int height, const std::vector& buffer, EImageQuality imageQuality, OutputFileColorSpace& colorspace, const oiio::ParamValueList& metadata) { writeImage(path, oiio::TypeDesc::FLOAT, width, height, 1, buffer, imageQuality, colorspace, metadata); } -void writeImage(const std::string& path, int width, int height, const std::vector& buffer, EImageQuality imageQuality, OutputFileColorSpace colorspace, const oiio::ParamValueList& metadata) +void writeImage(const std::string& path, int width, int height, const std::vector& buffer, EImageQuality imageQuality, OutputFileColorSpace& colorspace, const oiio::ParamValueList& metadata) { writeImage(path, oiio::TypeDesc::FLOAT, width, height, 3, buffer, imageQuality, colorspace, metadata); } -void writeImage(const std::string &path, Image &image, EImageQuality imageQuality, OutputFileColorSpace colorspace, const oiio::ParamValueList& metadata) +void writeImage(const std::string &path, Image &image, EImageQuality imageQuality, OutputFileColorSpace& colorspace, const oiio::ParamValueList& metadata) { writeImage(path, oiio::TypeDesc::FLOAT, image.width(), image.height(), 3, image.data(), imageQuality, colorspace, metadata); } diff --git a/src/aliceVision/mvsData/imageIO.hpp b/src/aliceVision/mvsData/imageIO.hpp index a27193ab6e..f03ae66fc3 100644 --- a/src/aliceVision/mvsData/imageIO.hpp +++ b/src/aliceVision/mvsData/imageIO.hpp @@ -54,7 +54,7 @@ struct OutputFileColorSpace { } /// @brief Assumes that @p from is LINEAR - OutputFileColorSpace(EImageColorSpace to_) + explicit OutputFileColorSpace(EImageColorSpace to_) { if(to_ == EImageColorSpace::NO_CONVERSION) to = from; @@ -199,12 +199,12 @@ void readImage(const std::string& path, Image& image, EImageColorSpace imageColo * @param[in] height The input image height * @param[in] buffer The input image buffer */ -void writeImage(const std::string& path, int width, int height, const std::vector& buffer, EImageQuality imageQuality, OutputFileColorSpace colorspace, const oiio::ParamValueList& metadata = oiio::ParamValueList()); -void writeImage(const std::string& path, int width, int height, const std::vector& buffer, EImageQuality imageQuality, OutputFileColorSpace colorspace, const oiio::ParamValueList& metadata = oiio::ParamValueList()); -void writeImage(const std::string& path, int width, int height, const std::vector& buffer, EImageQuality imageQuality, OutputFileColorSpace colorspace, const oiio::ParamValueList& metadata = oiio::ParamValueList()); -void writeImage(const std::string& path, int width, int height, const std::vector& buffer, EImageQuality imageQuality, OutputFileColorSpace colorspace, const oiio::ParamValueList& metadata = oiio::ParamValueList()); -void writeImage(const std::string& path, int width, int height, const std::vector& buffer, EImageQuality imageQuality, OutputFileColorSpace colorspace, const oiio::ParamValueList& metadata = oiio::ParamValueList()); -void writeImage(const std::string& path, Image& image, EImageQuality imageQuality, OutputFileColorSpace colorspace, const oiio::ParamValueList& metadata = oiio::ParamValueList()); +void writeImage(const std::string& path, int width, int height, const std::vector& buffer, EImageQuality imageQuality, OutputFileColorSpace& colorspace, const oiio::ParamValueList& metadata = oiio::ParamValueList()); +void writeImage(const std::string& path, int width, int height, const std::vector& buffer, EImageQuality imageQuality, OutputFileColorSpace& colorspace, const oiio::ParamValueList& metadata = oiio::ParamValueList()); +void writeImage(const std::string& path, int width, int height, const std::vector& buffer, EImageQuality imageQuality, OutputFileColorSpace& colorspace, const oiio::ParamValueList& metadata = oiio::ParamValueList()); +void writeImage(const std::string& path, int width, int height, const std::vector& buffer, EImageQuality imageQuality, OutputFileColorSpace& colorspace, const oiio::ParamValueList& metadata = oiio::ParamValueList()); +void writeImage(const std::string& path, int width, int height, const std::vector& buffer, EImageQuality imageQuality, OutputFileColorSpace& colorspace, const oiio::ParamValueList& metadata = oiio::ParamValueList()); +void writeImage(const std::string& path, Image& image, EImageQuality imageQuality, OutputFileColorSpace& colorspace, const oiio::ParamValueList& metadata = oiio::ParamValueList()); /** * @brief transpose a given image buffer