// Copyright (c) 2012-2017 VideoStitch SAS // Copyright (c) 2018 stitchEm #include "gradientImageMerger.hpp" #include "maskMerger.hpp" #include "imageMapping.hpp" #include "inputsMap.hpp" #include "inputsMapCubemap.hpp" #include "parse/json.hpp" #include "gpu/image/blur.hpp" #include "gpu/image/imgExtract.hpp" #include "gpu/image/imgInsert.hpp" #include "gpu/buffer.hpp" #include "gpu/core1/voronoi.hpp" #include "libvideostitch/panoDef.hpp" #include "libvideostitch/profile.hpp" #include "libvideostitch/logging.hpp" #include "libvideostitch/parse.hpp" #include "libvideostitch/ptv.hpp" #include #include //#define DEBUGMASKS #ifdef DEBUGMASKS #include "util/pnm.hpp" #include "util/debugUtils.hpp" #endif namespace VideoStitch { namespace Core { GradientImageMerger::Factory::Factory(int feather, MaskMerger::MaskMergerType maskMergerType) : feather(feather), maskMergerType(maskMergerType) {} Potential GradientImageMerger::Factory::create(const PanoDefinition& pano, ImageMapping& fromIm, const ImageMerger* to, bool) const { return Potential(new GradientImageMerger(pano, fromIm, to, feather, maskMergerType)); } ImageMergerFactory* GradientImageMerger::Factory::clone() const { return new Factory(feather, maskMergerType); } std::string GradientImageMerger::Factory::hash() const { std::stringstream ss; ss << "v1_GradientImageMerger" << feather << " mask_merger" << (int)maskMergerType; return ss.str(); } Ptv::Value* GradientImageMerger::Factory::serialize() const { Ptv::Value* res = Ptv::Value::emptyObject(); res->push("type", new Parse::JsonValue("gradient")); res->push("feather", new Parse::JsonValue(feather)); res->push("mask_merger", new Parse::JsonValue((int)maskMergerType)); return res; } Potential GradientImageMerger::Factory::parse(const Ptv::Value& value) { int feather = DEFAULT_BLENDING_FEATHER; if (Parse::populateInt("GradientImageMergerFactory", value, "feather", feather, false) == Parse::PopulateResult_WrongType) { return {Origin::Stitcher, ErrType::InvalidConfiguration, "Invalid type for 'feather' configuration, expected integer"}; } MaskMerger::MaskMergerType maskMergerType; int maskType; switch (Parse::populateInt("GradientImageMergerFactory", value, "mask_merger", maskType, false)) { case Parse::PopulateResult_WrongType: return {Origin::Stitcher, ErrType::InvalidConfiguration, "Invalid type for 'mask_merger' configuration, expected integer"}; case Parse::PopulateResult_DoesNotExist: maskMergerType = MaskMerger::getDefaultMaskMerger(); break; default: maskMergerType = (MaskMerger::MaskMergerType)maskType; break; } feather = std::max(feather, 0); feather = std::min(feather, 100); return Potential(new GradientImageMerger::Factory(feather, maskMergerType)); } Status GradientImageMerger::mergeAsync(TextureTarget t, const PanoDefinition& pano, GPU::Buffer pbo, GPU::UniqueBuffer&, const ImageMapping& fromIm, bool /* isFirstMerger */, GPU::Stream stream) const { if (fromIm.getOutputRect(t).empty()) { return Status::OK(); } // insert into pano image size_t width, height; if (t == EQUIRECTANGULAR) { width = pano.getWidth(); height = pano.getHeight(); if (maskMerger->getAlpha(t).wasAllocated() && (GradientImageMerger::warpMergeType() == Format::Gradient)) { /* merge was already done at the same time as warp */ return Status::OK(); } } else { width = pano.getLength(); height = pano.getLength(); } return Image::imgInsertInto(pbo, width, height, fromIm.getDeviceOutputBuffer(t), fromIm.getOutputRect(t).getWidth(), fromIm.getOutputRect(t).getHeight(), fromIm.getOutputRect(t).left(), fromIm.getOutputRect(t).top(), maskMerger->getAlpha(t), fromIm.getOutputRect(t).right() >= (int64_t)pano.getLength(), false, stream); } GradientImageMerger::GradientImageMerger(const PanoDefinition& /*pano*/, ImageMapping& fromIm, const ImageMerger* to, int feather, MaskMerger::MaskMergerType maskMergerType) : ImageMerger(fromIm.getImId(), to) { maskMerger.reset(MaskMerger::factor(maskMergerType)); maskMerger->setParameters(std::vector{(double)(feather)}); } GradientImageMerger::~GradientImageMerger() {} Status GradientImageMerger::setup(const PanoDefinition& pano, InputsMap& inputsMap, const ImageMapping& fromIm, GPU::Stream stream) { SIMPLEPROFILE_MS("GradientImageMerger::setup() : Compute Mask"); if (fromIm.getOutputRect(EQUIRECTANGULAR).empty()) { return Status::OK(); } GPU::Buffer inputsMask = inputsMap.getMask(); FAIL_RETURN(maskMerger->setupMask(pano, inputsMask, fromIm, to, stream)); // make sure kernels are done before destroying the tmp buffers FAIL_RETURN(stream.synchronize()); #ifdef DEBUGMASKS if (!fromIm.getOutputRect(EQUIRECTANGULAR).empty()) { std::string outputFile = "gradient-mask-" + std::to_string(fromIm.getImId()) + "-" + std::to_string(EQUIRECTANGULAR) + ".png"; Debug::dumpMonochromeDeviceBuffer(outputFile, maskMerger->getAlpha(EQUIRECTANGULAR), fromIm.getOutputRect(EQUIRECTANGULAR).getWidth(), fromIm.getOutputRect(EQUIRECTANGULAR).getHeight()); } #endif return Status::OK(); } Status GradientImageMerger::setupCubemap(const PanoDefinition& pano, InputsMap& inputsMap, const ImageMapping& fromIm, GPU::Stream stream) { SIMPLEPROFILE_MS("GradientImageMerger::setup() : Compute Mask"); GPU::Buffer inputsMask = inputsMap.getMask(); FAIL_RETURN(maskMerger->setupMaskCubemap(pano, inputsMask, fromIm, to, stream)); // make sure kernels are done before destroying the tmp buffers FAIL_RETURN(stream.synchronize()); #ifdef DEBUGMASKS if (!fromIm.getOutputRect(target).empty()) { std::string outputFile = "gradient-mask-" + std::to_string(fromIm.getImId()) + "-" + std::to_string(target) + ".png"; Debug::dumpMonochromeDeviceBuffer(outputFile, maskMerger->getAlpha(target), fromIm.getOutputRect(target).getWidth(), fromIm.getOutputRect(target).getHeight()); } #endif return Status::OK(); } } // namespace Core } // namespace VideoStitch