// Copyright (c) 2012-2017 VideoStitch SAS // Copyright (c) 2018 stitchEm #include "voronoiKernel.hpp" // #define DEBUGMASKS #ifdef DEBUGMASKS #ifndef _MSC_VER static const std::string DEBUG_FOLDER = "/tmp/voronoi/"; #else static const std::string DEBUG_FOLDER = ""; #endif #ifdef NDEBUG #error "This is not supposed to be included in non-debug mode." #endif #include "util/debugUtils.hpp" #include <sstream> #endif namespace VideoStitch { namespace Core { namespace { /** * Returns the largest power of two smaller than @a v. */ unsigned largestPowerOfTwoLessThan(unsigned v) { unsigned res = 1; while (res < v) { res *= 2; } return res / 2; } } // namespace // returns pointer to destination of last step PotentialValue<GPU::Buffer<uint32_t>> distanceMapErect(GPU::Buffer<uint32_t> src, GPU::Buffer<uint32_t> work, PanoRegion region, bool hWrap, GPU::Stream stream, unsigned blockSize = 16) { GPU::Buffer<uint32_t> tmpSrc = src; GPU::Buffer<uint32_t> tmpDst = work; for (unsigned step = largestPowerOfTwoLessThan((unsigned)std::max(region.viewWidth, region.viewHeight)); step > 0; step /= 2) { FAIL_RETURN(voronoiComputeErect(tmpDst, tmpSrc, region, step, hWrap, blockSize, stream)); std::swap(tmpDst, tmpSrc); } return tmpSrc; } PotentialValue<GPU::Buffer<uint32_t>> distanceMap(GPU::Buffer<uint32_t> src, GPU::Buffer<uint32_t> work, std::size_t width, std::size_t height, bool hWrap, GPU::Stream stream, unsigned blockSize = 16) { GPU::Buffer<uint32_t> tmpSrc = src; GPU::Buffer<uint32_t> tmpDst = work; for (unsigned step = largestPowerOfTwoLessThan((unsigned)std::max(width, height)); step > 0; step /= 2) { FAIL_RETURN(voronoiComputeEuclidean(tmpDst, tmpSrc, width, height, step, hWrap, blockSize, stream)); std::swap(tmpDst, tmpSrc); } return tmpSrc; } Status voronoiCompute(GPU::Buffer<unsigned char> dst, GPU::Buffer<uint32_t> src, GPU::Buffer<uint32_t> work, std::size_t width, std::size_t height, uint32_t fromIdMask, uint32_t toIdMask, bool hWrap, unsigned blockSize, GPU::Stream stream) { FAIL_RETURN(voronoiInit(src, width, height, toIdMask, fromIdMask, blockSize, stream)); auto tmpDst = distanceMap(src, work, width, height, hWrap, stream, blockSize); FAIL_RETURN(tmpDst.status()); return voronoiMakeMask(dst, tmpDst.value(), width, height, blockSize, stream); } Status computeMask(GPU::Buffer<unsigned char> dst, GPU::Buffer<uint32_t> src, GPU::Buffer<uint32_t> workBuffer1, GPU::Buffer<uint32_t> workBuffer2, const PanoRegion& region, uint32_t fromIdMask, uint32_t toIdMask, bool hWrap, float maxTransitionDistance, float power, GPU::Stream stream) { const auto blackWork = workBuffer1; const auto whiteWork = workBuffer2; // Extract base distance maps. FAIL_RETURN( initForMaskComputation(blackWork, src, region.viewWidth, region.viewHeight, fromIdMask, toIdMask, stream)); FAIL_RETURN( initForMaskComputation(whiteWork, src, region.viewWidth, region.viewHeight, toIdMask, fromIdMask, stream)); #ifdef DEBUGMASKS { stream.synchronize(); const std::string prefix = DEBUG_FOLDER + "computeMask-" + std::to_string(fromIdMask) + "-" + std::to_string(toIdMask) + "-"; Debug::dumpRGBADeviceBuffer((prefix + "blackInit.png").c_str(), blackWork.as_const(), region.viewWidth, region.viewHeight); Debug::dumpRGBADeviceBuffer((prefix + "whiteInit.png").c_str(), whiteWork.as_const(), region.viewWidth, region.viewHeight); } #endif // DEBUGMASKS // Process black. const auto blackResult = distanceMapErect(blackWork, src, region, hWrap, stream); FAIL_RETURN(blackResult.status()); const auto workBuffer = (blackResult.value() == blackWork) ? src : blackWork; // Process white. const auto whiteResult = distanceMapErect(whiteWork, workBuffer, region, hWrap, stream); FAIL_RETURN(whiteResult.status()); #ifdef DEBUGMASKS { stream.synchronize(); const std::string prefix = DEBUG_FOLDER + "computeMask-" + std::to_string(fromIdMask) + "-" + std::to_string(toIdMask) + "-"; Debug::dumpRGBADeviceBuffer((prefix + "blackResult.png").c_str(), blackResult.value(), region.viewWidth, region.viewHeight); Debug::dumpRGBADeviceBuffer((prefix + "whiteResult.png").c_str(), whiteResult.value(), region.viewWidth, region.viewHeight); } #endif // DEBUGMASKS return makeMaskErect(dst, blackResult.value(), whiteResult.value(), region, hWrap, maxTransitionDistance, power, stream); } Status computeEuclideanDistanceMap(GPU::Buffer<unsigned char> dst, GPU::Buffer<const uint32_t> src, GPU::Buffer<uint32_t> work1, GPU::Buffer<uint32_t> work2, std::size_t width, std::size_t height, uint32_t fromIdMask, uint32_t toIdMask, bool hWrap, float maxTransitionDistance, float power, GPU::Stream stream) { auto whiteWork = work2; // Extract base distance maps. FAIL_RETURN(initForMaskComputation(whiteWork, src, width, height, toIdMask, fromIdMask, stream)); auto workBuffer = work1; // Process white. auto whiteResult = distanceMap(whiteWork, workBuffer, width, height, hWrap, stream); FAIL_RETURN(whiteResult.status()); return extractEuclideanDist(dst, whiteResult.value(), width, height, hWrap, maxTransitionDistance, power, stream); } Status computeEuclideanDistanceMap(GPU::Buffer<unsigned char> dst, GPU::Buffer<const uint32_t> src, GPU::Buffer<uint32_t> work1, GPU::Buffer<uint32_t> work2, std::size_t width, std::size_t height, uint32_t idMask, bool hWrap, float maxTransitionDistance, float power, GPU::Stream stream) { return computeEuclideanDistanceMap(dst, src, work1, work2, width, height, 0, idMask, hWrap, maxTransitionDistance, power, stream); } } // namespace Core } // namespace VideoStitch