/*M/////////////////////////////////////////////////////////////////////////////////////// // // IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. // // By downloading, copying, installing or using the software you agree to this license. // If you do not agree to this license, do not download, install, // copy or use the software. // // // License Agreement // For Open Source Computer Vision Library // // Copyright (C) 2000-2008, Intel Corporation, all rights reserved. // Copyright (C) 2009, Willow Garage Inc., all rights reserved. // Third party copyrights are property of their respective owners. // // Redistribution and use in source and binary forms, with or without modification, // are permitted provided that the following conditions are met: // // * Redistribution's of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. // // * Redistribution's in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimer in the documentation // and/or other materials provided with the distribution. // // * The name of the copyright holders may not be used to endorse or promote products // derived from this software without specific prior written permission. // // This software is provided by the copyright holders and contributors "as is" and // any express or implied warranties, including, but not limited to, the implied // warranties of merchantability and fitness for a particular purpose are disclaimed. // In no event shall the Intel Corporation or contributors be liable for any direct, // indirect, incidental, special, exemplary, or consequential damages // (including, but not limited to, procurement of substitute goods or services; // loss of use, data, or profits; or business interruption) however caused // and on any theory of liability, whether in contract, strict liability, // or tort (including negligence or otherwise) arising in any way out of // the use of this software, even if advised of the possibility of such damage. // //M*/ // S. Farsiu , D. Robinson, M. Elad, P. Milanfar. Fast and robust multiframe super resolution. // Dennis Mitzel, Thomas Pock, Thomas Schoenemann, Daniel Cremers. Video Super Resolution using Duality Based TV-L1 Optical Flow. #include "precomp.hpp" using namespace std; using namespace cv; using namespace cv::superres; using namespace cv::superres::detail; namespace { void calcRelativeMotions(const vector& forwardMotions, const vector& backwardMotions, vector& relForwardMotions, vector& relBackwardMotions, int baseIdx, Size size) { const int count = static_cast(forwardMotions.size()); relForwardMotions.resize(count); relForwardMotions[baseIdx].create(size, CV_32FC2); relForwardMotions[baseIdx].setTo(Scalar::all(0)); relBackwardMotions.resize(count); relBackwardMotions[baseIdx].create(size, CV_32FC2); relBackwardMotions[baseIdx].setTo(Scalar::all(0)); for (int i = baseIdx - 1; i >= 0; --i) { add(relForwardMotions[i + 1], forwardMotions[i], relForwardMotions[i]); add(relBackwardMotions[i + 1], backwardMotions[i + 1], relBackwardMotions[i]); } for (int i = baseIdx + 1; i < count; ++i) { add(relForwardMotions[i - 1], backwardMotions[i], relForwardMotions[i]); add(relBackwardMotions[i - 1], forwardMotions[i - 1], relBackwardMotions[i]); } } void upscaleMotions(const vector& lowResMotions, vector& highResMotions, int scale) { highResMotions.resize(lowResMotions.size()); for (size_t i = 0; i < lowResMotions.size(); ++i) { resize(lowResMotions[i], highResMotions[i], Size(), scale, scale, INTER_CUBIC); multiply(highResMotions[i], Scalar::all(scale), highResMotions[i]); } } void buildMotionMaps(const Mat& forwardMotion, const Mat& backwardMotion, Mat& forwardMap, Mat& backwardMap) { forwardMap.create(forwardMotion.size(), CV_32FC2); backwardMap.create(forwardMotion.size(), CV_32FC2); for (int y = 0; y < forwardMotion.rows; ++y) { const Point2f* forwardMotionRow = forwardMotion.ptr(y); const Point2f* backwardMotionRow = backwardMotion.ptr(y); Point2f* forwardMapRow = forwardMap.ptr(y); Point2f* backwardMapRow = backwardMap.ptr(y); for (int x = 0; x < forwardMotion.cols; ++x) { Point2f base(static_cast(x), static_cast(y)); forwardMapRow[x] = base + backwardMotionRow[x]; backwardMapRow[x] = base + forwardMotionRow[x]; } } } template void upscaleImpl(const Mat& src, Mat& dst, int scale) { dst.create(src.rows * scale, src.cols * scale, src.type()); dst.setTo(Scalar::all(0)); for (int y = 0, Y = 0; y < src.rows; ++y, Y += scale) { const T* srcRow = src.ptr(y); T* dstRow = dst.ptr(Y); for (int x = 0, X = 0; x < src.cols; ++x, X += scale) dstRow[X] = srcRow[x]; } } void upscale(const Mat& src, Mat& dst, int scale) { typedef void (*func_t)(const Mat& src, Mat& dst, int scale); static const func_t funcs[] = { 0, upscaleImpl, 0, upscaleImpl }; CV_Assert( src.channels() == 1 || src.channels() == 3 || src.channels() == 4 ); const func_t func = funcs[src.channels()]; func(src, dst, scale); } float diffSign(float a, float b) { return a > b ? 1.0f : a < b ? -1.0f : 0.0f; } Point3f diffSign(Point3f a, Point3f b) { return Point3f( a.x > b.x ? 1.0f : a.x < b.x ? -1.0f : 0.0f, a.y > b.y ? 1.0f : a.y < b.y ? -1.0f : 0.0f, a.z > b.z ? 1.0f : a.z < b.z ? -1.0f : 0.0f ); } void diffSign(const Mat& src1, const Mat& src2, Mat& dst) { const int count = src1.cols * src1.channels(); dst.create(src1.size(), src1.type()); for (int y = 0; y < src1.rows; ++y) { const float* src1Ptr = src1.ptr(y); const float* src2Ptr = src2.ptr(y); float* dstPtr = dst.ptr(y); for (int x = 0; x < count; ++x) dstPtr[x] = diffSign(src1Ptr[x], src2Ptr[x]); } } void calcBtvWeights(int btvKernelSize, double alpha, vector& btvWeights) { const size_t size = btvKernelSize * btvKernelSize; btvWeights.resize(size); const int ksize = (btvKernelSize - 1) / 2; const float alpha_f = static_cast(alpha); for (int m = 0, ind = 0; m <= ksize; ++m) { for (int l = ksize; l + m >= 0; --l, ++ind) btvWeights[ind] = pow(alpha_f, std::abs(m) + std::abs(l)); } } template struct BtvRegularizationBody : ParallelLoopBody { void operator ()(const Range& range) const; Mat src; mutable Mat dst; int ksize; const float* btvWeights; }; template void BtvRegularizationBody::operator ()(const Range& range) const { for (int i = range.start; i < range.end; ++i) { const T* srcRow = src.ptr(i); T* dstRow = dst.ptr(i); for(int j = ksize; j < src.cols - ksize; ++j) { const T srcVal = srcRow[j]; for (int m = 0, ind = 0; m <= ksize; ++m) { const T* srcRow2 = src.ptr(i - m); const T* srcRow3 = src.ptr(i + m); for (int l = ksize; l + m >= 0; --l, ++ind) { dstRow[j] += btvWeights[ind] * (diffSign(srcVal, srcRow3[j + l]) - diffSign(srcRow2[j - l], srcVal)); } } } } } template void calcBtvRegularizationImpl(const Mat& src, Mat& dst, int btvKernelSize, const vector& btvWeights) { dst.create(src.size(), src.type()); dst.setTo(Scalar::all(0)); const int ksize = (btvKernelSize - 1) / 2; BtvRegularizationBody body; body.src = src; body.dst = dst; body.ksize = ksize; body.btvWeights = &btvWeights[0]; parallel_for_(Range(ksize, src.rows - ksize), body); } void calcBtvRegularization(const Mat& src, Mat& dst, int btvKernelSize, const vector& btvWeights) { typedef void (*func_t)(const Mat& src, Mat& dst, int btvKernelSize, const vector& btvWeights); static const func_t funcs[] = { 0, calcBtvRegularizationImpl, 0, calcBtvRegularizationImpl }; const func_t func = funcs[src.channels()]; func(src, dst, btvKernelSize, btvWeights); } class BTVL1_Base { public: BTVL1_Base(); void process(const vector& src, Mat& dst, const vector& forwardMotions, const vector& backwardMotions, int baseIdx); void collectGarbage(); protected: int scale_; int iterations_; double tau_; double lambda_; double alpha_; int btvKernelSize_; int blurKernelSize_; double blurSigma_; Ptr opticalFlow_; private: Ptr filter_; int curBlurKernelSize_; double curBlurSigma_; int curSrcType_; vector btvWeights_; int curBtvKernelSize_; double curAlpha_; vector lowResForwardMotions_; vector lowResBackwardMotions_; vector highResForwardMotions_; vector highResBackwardMotions_; vector forwardMaps_; vector backwardMaps_; Mat highRes_; Mat diffTerm_, regTerm_; Mat a_, b_, c_; }; BTVL1_Base::BTVL1_Base() { scale_ = 4; iterations_ = 180; lambda_ = 0.03; tau_ = 1.3; alpha_ = 0.7; btvKernelSize_ = 7; blurKernelSize_ = 5; blurSigma_ = 0.0; opticalFlow_ = createOptFlow_Farneback(); curBlurKernelSize_ = -1; curBlurSigma_ = -1.0; curSrcType_ = -1; curBtvKernelSize_ = -1; curAlpha_ = -1.0; } void BTVL1_Base::process(const vector& src, Mat& dst, const vector& forwardMotions, const vector& backwardMotions, int baseIdx) { CV_Assert( scale_ > 1 ); CV_Assert( iterations_ > 0 ); CV_Assert( tau_ > 0.0 ); CV_Assert( alpha_ > 0.0 ); CV_Assert( btvKernelSize_ > 0 ); CV_Assert( blurKernelSize_ > 0 ); CV_Assert( blurSigma_ >= 0.0 ); // update blur filter and btv weights if (filter_.empty() || blurKernelSize_ != curBlurKernelSize_ || blurSigma_ != curBlurSigma_ || src[0].type() != curSrcType_) { filter_ = createGaussianFilter(src[0].type(), Size(blurKernelSize_, blurKernelSize_), blurSigma_); curBlurKernelSize_ = blurKernelSize_; curBlurSigma_ = blurSigma_; curSrcType_ = src[0].type(); } if (btvWeights_.empty() || btvKernelSize_ != curBtvKernelSize_ || alpha_ != curAlpha_) { calcBtvWeights(btvKernelSize_, alpha_, btvWeights_); curBtvKernelSize_ = btvKernelSize_; curAlpha_ = alpha_; } // calc high res motions calcRelativeMotions(forwardMotions, backwardMotions, lowResForwardMotions_, lowResBackwardMotions_, baseIdx, src[0].size()); upscaleMotions(lowResForwardMotions_, highResForwardMotions_, scale_); upscaleMotions(lowResBackwardMotions_, highResBackwardMotions_, scale_); forwardMaps_.resize(highResForwardMotions_.size()); backwardMaps_.resize(highResForwardMotions_.size()); for (size_t i = 0; i < highResForwardMotions_.size(); ++i) buildMotionMaps(highResForwardMotions_[i], highResBackwardMotions_[i], forwardMaps_[i], backwardMaps_[i]); // initial estimation const Size lowResSize = src[0].size(); const Size highResSize(lowResSize.width * scale_, lowResSize.height * scale_); resize(src[baseIdx], highRes_, highResSize, 0, 0, INTER_CUBIC); // iterations diffTerm_.create(highResSize, highRes_.type()); a_.create(highResSize, highRes_.type()); b_.create(highResSize, highRes_.type()); c_.create(lowResSize, highRes_.type()); for (int i = 0; i < iterations_; ++i) { diffTerm_.setTo(Scalar::all(0)); for (size_t k = 0; k < src.size(); ++k) { // a = M * Ih remap(highRes_, a_, backwardMaps_[k], noArray(), INTER_NEAREST); // b = HM * Ih filter_->apply(a_, b_); // c = DHM * Ih resize(b_, c_, lowResSize, 0, 0, INTER_NEAREST); diffSign(src[k], c_, c_); // a = Dt * diff upscale(c_, a_, scale_); // b = HtDt * diff filter_->apply(a_, b_); // a = MtHtDt * diff remap(b_, a_, forwardMaps_[k], noArray(), INTER_NEAREST); add(diffTerm_, a_, diffTerm_); } if (lambda_ > 0) { calcBtvRegularization(highRes_, regTerm_, btvKernelSize_, btvWeights_); addWeighted(diffTerm_, 1.0, regTerm_, -lambda_, 0.0, diffTerm_); } addWeighted(highRes_, 1.0, diffTerm_, tau_, 0.0, highRes_); } Rect inner(btvKernelSize_, btvKernelSize_, highRes_.cols - 2 * btvKernelSize_, highRes_.rows - 2 * btvKernelSize_); highRes_(inner).copyTo(dst); } void BTVL1_Base::collectGarbage() { filter_.release(); lowResForwardMotions_.clear(); lowResBackwardMotions_.clear(); highResForwardMotions_.clear(); highResBackwardMotions_.clear(); forwardMaps_.clear(); backwardMaps_.clear(); highRes_.release(); diffTerm_.release(); regTerm_.release(); a_.release(); b_.release(); c_.release(); } //////////////////////////////////////////////////////////////////// class BTVL1 : public SuperResolution, private BTVL1_Base { public: AlgorithmInfo* info() const; BTVL1(); void collectGarbage(); protected: void initImpl(Ptr& frameSource); void processImpl(Ptr& frameSource, OutputArray output); private: int temporalAreaRadius_; void readNextFrame(Ptr& frameSource); void processFrame(int idx); Mat curFrame_; Mat prevFrame_; vector frames_; vector forwardMotions_; vector backwardMotions_; vector outputs_; int storePos_; int procPos_; int outPos_; vector srcFrames_; vector srcForwardMotions_; vector srcBackwardMotions_; Mat finalOutput_; }; CV_INIT_ALGORITHM(BTVL1, "SuperResolution.BTVL1", obj.info()->addParam(obj, "scale", obj.scale_, false, 0, 0, "Scale factor."); obj.info()->addParam(obj, "iterations", obj.iterations_, false, 0, 0, "Iteration count."); obj.info()->addParam(obj, "tau", obj.tau_, false, 0, 0, "Asymptotic value of steepest descent method."); obj.info()->addParam(obj, "lambda", obj.lambda_, false, 0, 0, "Weight parameter to balance data term and smoothness term."); obj.info()->addParam(obj, "alpha", obj.alpha_, false, 0, 0, "Parameter of spacial distribution in Bilateral-TV."); obj.info()->addParam(obj, "btvKernelSize", obj.btvKernelSize_, false, 0, 0, "Kernel size of Bilateral-TV filter."); obj.info()->addParam(obj, "blurKernelSize", obj.blurKernelSize_, false, 0, 0, "Gaussian blur kernel size."); obj.info()->addParam(obj, "blurSigma", obj.blurSigma_, false, 0, 0, "Gaussian blur sigma."); obj.info()->addParam(obj, "temporalAreaRadius", obj.temporalAreaRadius_, false, 0, 0, "Radius of the temporal search area."); obj.info()->addParam(obj, "opticalFlow", obj.opticalFlow_, false, 0, 0, "Dense optical flow algorithm.")); BTVL1::BTVL1() { temporalAreaRadius_ = 4; } void BTVL1::collectGarbage() { curFrame_.release(); prevFrame_.release(); frames_.clear(); forwardMotions_.clear(); backwardMotions_.clear(); outputs_.clear(); srcFrames_.clear(); srcForwardMotions_.clear(); srcBackwardMotions_.clear(); finalOutput_.release(); SuperResolution::collectGarbage(); BTVL1_Base::collectGarbage(); } void BTVL1::initImpl(Ptr& frameSource) { const int cacheSize = 2 * temporalAreaRadius_ + 1; frames_.resize(cacheSize); forwardMotions_.resize(cacheSize); backwardMotions_.resize(cacheSize); outputs_.resize(cacheSize); storePos_ = -1; for (int t = -temporalAreaRadius_; t <= temporalAreaRadius_; ++t) readNextFrame(frameSource); for (int i = 0; i <= temporalAreaRadius_; ++i) processFrame(i); procPos_ = temporalAreaRadius_; outPos_ = -1; } void BTVL1::processImpl(Ptr& frameSource, OutputArray _output) { if (outPos_ >= storePos_) { _output.release(); return; } readNextFrame(frameSource); if (procPos_ < storePos_) { ++procPos_; processFrame(procPos_); } ++outPos_; const Mat& curOutput = at(outPos_, outputs_); if (_output.kind() < _InputArray::OPENGL_BUFFER) curOutput.convertTo(_output, CV_8U); else { curOutput.convertTo(finalOutput_, CV_8U); arrCopy(finalOutput_, _output); } } void BTVL1::readNextFrame(Ptr& frameSource) { frameSource->nextFrame(curFrame_); if (curFrame_.empty()) return; ++storePos_; curFrame_.convertTo(at(storePos_, frames_), CV_32F); if (storePos_ > 0) { opticalFlow_->calc(prevFrame_, curFrame_, at(storePos_ - 1, forwardMotions_)); opticalFlow_->calc(curFrame_, prevFrame_, at(storePos_, backwardMotions_)); } curFrame_.copyTo(prevFrame_); } void BTVL1::processFrame(int idx) { const int startIdx = max(idx - temporalAreaRadius_, 0); const int procIdx = idx; const int endIdx = min(startIdx + 2 * temporalAreaRadius_, storePos_); const int count = endIdx - startIdx + 1; srcFrames_.resize(count); srcForwardMotions_.resize(count); srcBackwardMotions_.resize(count); int baseIdx = -1; for (int i = startIdx, k = 0; i <= endIdx; ++i, ++k) { if (i == procIdx) baseIdx = k; srcFrames_[k] = at(i, frames_); if (i < endIdx) srcForwardMotions_[k] = at(i, forwardMotions_); if (i > startIdx) srcBackwardMotions_[k] = at(i, backwardMotions_); } process(srcFrames_, at(idx, outputs_), srcForwardMotions_, srcBackwardMotions_, baseIdx); } } Ptr cv::superres::createSuperResolution_BTVL1() { return new BTVL1; }