Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[mesh] Texturing: Multi-band blending #629

Merged
merged 54 commits into from
Jun 20, 2019
Merged

Conversation

cvere
Copy link
Contributor

@cvere cvere commented Apr 26, 2019

Description

Largely improve the texturing quality by using multi-band blending technique.

Split each input camera into frequency bands, by calculating its laplacian pyramid. For each output texture file, it computes the texture fusion per frequency bands with different number of contributions. The color averaging is now weighted by a score defined by the triangle reprojection area.
The textures per frequency bands are then combined to create the final output texture files.

Features list

  • Split an image in frequency bands and reconstitute the image from its frequency bands
  • Add triangles contributions to a specific frequency band
  • Improve padding performance (2 image visit instead of 2*paddingSize)

Implementation remarks

  • Add colorspace option to ImageCache
  • Improve colorspace option of writeImage (allow change of input colorspace)

Fix #644

cvere added 30 commits March 22, 2019 11:12
Use an Image class instead of vector<Color> and width, height.
Does not change the results yet, but start to use the multiband.
Calculate the laplacian pyramid of an image with successives downscales (instead of successives blurs) -> memory saving
valid operators =, [] & resize without copying elements
calculate difference between an image of size s and an other of size s/2
For each camera, calculate the corresponding laplacian pyramid. For each texture triangle, fuse the contributions of frequencies bands from the best scoring cameras
save all frequency bands for all textures
Let openImageIO choose the size of the convolution kernel when downscaling by two & replace maxNbImagesForFusion by multiBandNbContrib
Create the texture padding with only two passes of the image instead of 2*padding
Add param to control the level of blur between each level of the laplacian pyramid (previously fixed to 2)
Add number of bands param, to control the number of level of the laplacian pyramid
Refine calculus of the number of atlases simultaneously computable
Texture padding computed in 2 passes instead of padding*2 passes
Triangles contribute to atlas textures according to their score (ie. the area of reprojection on the camera) -> less artefacts, especially on low frequencies band
Create new file to test multi-band blending on one image
Add colorspace attribute to ImagesCache and read images in that colorspace (LINEAR for depthMap, SRGB for texturing)
New colorspace type (AUTO_FROM_SRGB) -> calculate texture atlases in sRGB for multi-band blending accuracy
readImage kept to much space in memory
Bool to choose to use scores + scores in float type + simple rename
texParams.multibandNbContrib holds contributions to each frequency band, not the sum of contributions to all higher frequency bands as before
cvere added 3 commits June 4, 2019 15:36
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
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)
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<Color>) + use imageIO methods taking an Image in argument 
[mvsUtils] fileIO: adapt the method for loading images to use mvsData::Image
@fabiencastan fabiencastan added this to the 2019.2 milestone Jun 5, 2019
@@ -451,7 +451,7 @@ void Texturing::generateTexturesSubSet(const mvsUtils::MultiViewParams& mp,
if(contrib + 1 == nbContribLevel)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Simpler and avoid to access outside of the array.

if(contrib + 1 == texParams.multiBandNbContrib[band])
{
    ++band;
}

So we could remove the variable nbContribLevel.

@@ -451,7 +451,7 @@ void Texturing::generateTexturesSubSet(const mvsUtils::MultiViewParams& mp,
if(contrib + 1 == nbContribLevel)
{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Few lines above, we have:
int nbContribMax = std::min(texParams.multiBandNbContrib.back(), static_cast<int>(scorePerCamId.size()));

I think yes, but just to be sure: Does texParams.multiBandNbContrib still contains cumulative values in the latest version?

@@ -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:");

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

{
    // Ensure that contribution levels do not contain 0 and are sorted (as they represent cumulative values).
    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(m), std::end(m)))
    {
        ALICEVISION_LOG_INFO("Sorting contributions per band (necessary).");
        std::sort(std::begin(m), std::end(m));
    }
}

@@ -10,7 +10,6 @@ add_subdirectory(featuresRepeatability)
# add_subdirectory(imageData)
add_subdirectory(imageDescriberMatches)
add_subdirectory(kvldFilter)
add_subdirectory(multiBandBlending)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like a mistake as the sample itself is not removed...

@@ -38,7 +38,6 @@ endif()
# MVS modules

if(ALICEVISION_BUILD_MVS)
add_subdirectory(imageIO)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We tried to keep a split between IO and data structures as almost all modules have dependencies to data structures.
So if we don't split it, all modules will have dependencies to IO.

But maybe this commit is a needed intermediate solution before a new change? If not, we do not want to merge mvsData and imageIO.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have a several files related to image IO:

  • image/io.hpp // SfM: functions to read/writes images
  • imageIO/image.hpp // MVS: functions to read/write images, relies on mvsData for data structure types (rgb, Color and now Image)
  • mvsData/imageIO.hpp (image.hpp on develop) // MVS: containing EImageFileType enum

The idea was to start simplifying things. We will probably want to merge image types and IO at some point, but it already seems weird to have, for the MVS part, this split between imageIO/image.hpp and mvsData/imageIO.hpp. And everything that uses mvsData also uses imageIO, so the merge does not seem that problematic IMHO. But if we want to keep this imageIO module as the one that will stay, maybe it could be the other way around: move what's inside mvsData/imageIO.hpp into the imageIO module.

//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));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getImg_sync returns an ImgPtr which is a std::shared_ptr<Img>.
If you remove the temporary variable and directly get the value from the shared_ptr, the shared_ptr ownership is removed.

@@ -152,24 +152,24 @@ struct Texturing
// Create buffer for the set of output textures
struct AccuImage
{
std::vector<Color> img;
Image img;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1

@@ -21,48 +22,11 @@ namespace mvsUtils {

class ImagesCache
{
public:
class Img
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1

const camera::IntrinsicBase* intrinsicPtr = _sfmData.getIntrinsicPtr(view.getIntrinsicId());

Vec2 pix_disto = intrinsicPtr->get_d_pixel({pixRC.x, pixRC.y});
return isPixelInImage(Pixel(pix_disto.x(), pix_disto.y()), camId, margin);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All the MVS structures have a global downscale (backed in all matrices, etc.) but it's not the case for the sfmData which contains the original SfM.
So we need to compensate for that:

const double s = getDownscaleFactor(camId);
const Vec2 pix_disto = intrinsicPtr->get_d_pixel({pixRC.x * s, pixRC.y * s}) / s;

imageCache.refreshData(camId);
const Image& camImg = *(imageCache.getImg_sync(camId));
imageCache.refreshData(camId);
mvsUtils::ImagesCache::ImgPtr imgPtr = imageCache.getImg_sync(camId);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mvsUtils::ImagesCache::ImgPtr imgPtr = imageCache.getImg_sync(camId); // keep ownership

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be good to rename ImgPtr into ImgSharedPtr.

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
Update nbContribMax (= max number of contribution for a camera): number of contributions used to be cumulative but not anymore (more practical for the user)
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.
code cleaning + setting multiBandDownscale default value to 4 + renaming ImagesCache::ImgPtr to ImagesCache::ImgSharedPtr
only keep triangles with sufficient reprojection area
else
to = to_;
}
OutputFileColorSpace()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

= default;

@@ -294,29 +379,34 @@ void writeImage(const std::string& path,
fs::rename(tmpPath, path);
}

void writeImage(const std::string& path, int width, int height, const std::vector<unsigned char>& buffer, EImageQuality imageQuality, EImageColorSpace imageColorSpace, const oiio::ParamValueList& metadata)
void writeImage(const std::string& path, int width, int height, const std::vector<unsigned char>& buffer, EImageQuality imageQuality, OutputFileColorSpace colorspace, const oiio::ParamValueList& metadata)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

const OutputFileColorSpace& colorspace

@@ -238,7 +323,7 @@ void writeImage(const std::string& path,
int nchannels,
const std::vector<T>& buffer,
EImageQuality imageQuality,
EImageColorSpace imageColorSpace,
OutputFileColorSpace colorspace,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

const OutputFileColorSpace& colorspace


struct OutputFileColorSpace
{
EImageColorSpace from = EImageColorSpace::LINEAR;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

EImageColorSpace from{EImageColorSpace::LINEAR};
EImageColorSpace to{EImageColorSpace::AUTO};

mapIdCamId.resize( N_PRELOADED_IMAGES, -1 );
mapIdClock.resize( N_PRELOADED_IMAGES, clock() );
}

ImagesCache::~ImagesCache()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

= default

@fabiencastan
Copy link
Member

LGTM!

@fabiencastan fabiencastan merged commit c43f68c into develop Jun 20, 2019
@fabiencastan fabiencastan deleted the dev_multiBandBlending branch June 20, 2019 19:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
4 participants