1 /*M/////////////////////////////////////////////////////////////////////////////////////// 2 // 3 // IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. 4 // 5 // By downloading, copying, installing or using the software you agree to this license. 6 // If you do not agree to this license, do not download, install, 7 // copy or use the software. 8 // 9 // 10 // License Agreement 11 // For Open Source Computer Vision Library 12 // 13 // Copyright (C) 2000-2008, Intel Corporation, all rights reserved. 14 // Copyright (C) 2009, Willow Garage Inc., all rights reserved. 15 // Third party copyrights are property of their respective owners. 16 // 17 // Redistribution and use in source and binary forms, with or without modification, 18 // are permitted provided that the following conditions are met: 19 // 20 // * Redistribution's of source code must retain the above copyright notice, 21 // this list of conditions and the following disclaimer. 22 // 23 // * Redistribution's in binary form must reproduce the above copyright notice, 24 // this list of conditions and the following disclaimer in the documentation 25 // and/or other materials provided with the distribution. 26 // 27 // * The name of the copyright holders may not be used to endorse or promote products 28 // derived from this software without specific prior written permission. 29 // 30 // This software is provided by the copyright holders and contributors "as is" and 31 // any express or implied warranties, including, but not limited to, the implied 32 // warranties of merchantability and fitness for a particular purpose are disclaimed. 33 // In no event shall the Intel Corporation or contributors be liable for any direct, 34 // indirect, incidental, special, exemplary, or consequential damages 35 // (including, but not limited to, procurement of substitute goods or services; 36 // loss of use, data, or profits; or business interruption) however caused 37 // and on any theory of liability, whether in contract, strict liability, 38 // or tort (including negligence or otherwise) arising in any way out of 39 // the use of this software, even if advised of the possibility of such damage. 40 // 41 //M*/ 42 43 // S. Farsiu , D. Robinson, M. Elad, P. Milanfar. Fast and robust multiframe super resolution. 44 // Dennis Mitzel, Thomas Pock, Thomas Schoenemann, Daniel Cremers. Video Super Resolution using Duality Based TV-L1 Optical Flow. 45 46 #include "precomp.hpp" 47 48 using namespace cv; 49 using namespace cv::cuda; 50 using namespace cv::superres; 51 using namespace cv::superres::detail; 52 53 #if !defined(HAVE_CUDA) || !defined(HAVE_OPENCV_CUDAARITHM) || !defined(HAVE_OPENCV_CUDAWARPING) || !defined(HAVE_OPENCV_CUDAFILTERS) 54 55 Ptr<SuperResolution> cv::superres::createSuperResolution_BTVL1_CUDA() 56 { 57 CV_Error(Error::StsNotImplemented, "The called functionality is disabled for current build or platform"); 58 return Ptr<SuperResolution>(); 59 } 60 61 #else // HAVE_CUDA 62 63 namespace btv_l1_cudev 64 { 65 void buildMotionMaps(PtrStepSzf forwardMotionX, PtrStepSzf forwardMotionY, 66 PtrStepSzf backwardMotionX, PtrStepSzf bacwardMotionY, 67 PtrStepSzf forwardMapX, PtrStepSzf forwardMapY, 68 PtrStepSzf backwardMapX, PtrStepSzf backwardMapY); 69 70 template <int cn> 71 void upscale(const PtrStepSzb src, PtrStepSzb dst, int scale, cudaStream_t stream); 72 73 void diffSign(PtrStepSzf src1, PtrStepSzf src2, PtrStepSzf dst, cudaStream_t stream); 74 75 void loadBtvWeights(const float* weights, size_t count); 76 template <int cn> void calcBtvRegularization(PtrStepSzb src, PtrStepSzb dst, int ksize); 77 } 78 79 namespace 80 { 81 void calcRelativeMotions(const std::vector<std::pair<GpuMat, GpuMat> >& forwardMotions, const std::vector<std::pair<GpuMat, GpuMat> >& backwardMotions, 82 std::vector<std::pair<GpuMat, GpuMat> >& relForwardMotions, std::vector<std::pair<GpuMat, GpuMat> >& relBackwardMotions, 83 int baseIdx, Size size) 84 { 85 const int count = static_cast<int>(forwardMotions.size()); 86 87 relForwardMotions.resize(count); 88 relForwardMotions[baseIdx].first.create(size, CV_32FC1); 89 relForwardMotions[baseIdx].first.setTo(Scalar::all(0)); 90 relForwardMotions[baseIdx].second.create(size, CV_32FC1); 91 relForwardMotions[baseIdx].second.setTo(Scalar::all(0)); 92 93 relBackwardMotions.resize(count); 94 relBackwardMotions[baseIdx].first.create(size, CV_32FC1); 95 relBackwardMotions[baseIdx].first.setTo(Scalar::all(0)); 96 relBackwardMotions[baseIdx].second.create(size, CV_32FC1); 97 relBackwardMotions[baseIdx].second.setTo(Scalar::all(0)); 98 99 for (int i = baseIdx - 1; i >= 0; --i) 100 { 101 cuda::add(relForwardMotions[i + 1].first, forwardMotions[i].first, relForwardMotions[i].first); 102 cuda::add(relForwardMotions[i + 1].second, forwardMotions[i].second, relForwardMotions[i].second); 103 104 cuda::add(relBackwardMotions[i + 1].first, backwardMotions[i + 1].first, relBackwardMotions[i].first); 105 cuda::add(relBackwardMotions[i + 1].second, backwardMotions[i + 1].second, relBackwardMotions[i].second); 106 } 107 108 for (int i = baseIdx + 1; i < count; ++i) 109 { 110 cuda::add(relForwardMotions[i - 1].first, backwardMotions[i].first, relForwardMotions[i].first); 111 cuda::add(relForwardMotions[i - 1].second, backwardMotions[i].second, relForwardMotions[i].second); 112 113 cuda::add(relBackwardMotions[i - 1].first, forwardMotions[i - 1].first, relBackwardMotions[i].first); 114 cuda::add(relBackwardMotions[i - 1].second, forwardMotions[i - 1].second, relBackwardMotions[i].second); 115 } 116 } 117 118 void upscaleMotions(const std::vector<std::pair<GpuMat, GpuMat> >& lowResMotions, std::vector<std::pair<GpuMat, GpuMat> >& highResMotions, int scale) 119 { 120 highResMotions.resize(lowResMotions.size()); 121 122 for (size_t i = 0; i < lowResMotions.size(); ++i) 123 { 124 cuda::resize(lowResMotions[i].first, highResMotions[i].first, Size(), scale, scale, INTER_CUBIC); 125 cuda::resize(lowResMotions[i].second, highResMotions[i].second, Size(), scale, scale, INTER_CUBIC); 126 127 cuda::multiply(highResMotions[i].first, Scalar::all(scale), highResMotions[i].first); 128 cuda::multiply(highResMotions[i].second, Scalar::all(scale), highResMotions[i].second); 129 } 130 } 131 132 void buildMotionMaps(const std::pair<GpuMat, GpuMat>& forwardMotion, const std::pair<GpuMat, GpuMat>& backwardMotion, 133 std::pair<GpuMat, GpuMat>& forwardMap, std::pair<GpuMat, GpuMat>& backwardMap) 134 { 135 forwardMap.first.create(forwardMotion.first.size(), CV_32FC1); 136 forwardMap.second.create(forwardMotion.first.size(), CV_32FC1); 137 138 backwardMap.first.create(forwardMotion.first.size(), CV_32FC1); 139 backwardMap.second.create(forwardMotion.first.size(), CV_32FC1); 140 141 btv_l1_cudev::buildMotionMaps(forwardMotion.first, forwardMotion.second, 142 backwardMotion.first, backwardMotion.second, 143 forwardMap.first, forwardMap.second, 144 backwardMap.first, backwardMap.second); 145 } 146 147 void upscale(const GpuMat& src, GpuMat& dst, int scale, Stream& stream) 148 { 149 typedef void (*func_t)(const PtrStepSzb src, PtrStepSzb dst, int scale, cudaStream_t stream); 150 static const func_t funcs[] = 151 { 152 0, btv_l1_cudev::upscale<1>, 0, btv_l1_cudev::upscale<3>, btv_l1_cudev::upscale<4> 153 }; 154 155 CV_Assert( src.channels() == 1 || src.channels() == 3 || src.channels() == 4 ); 156 157 dst.create(src.rows * scale, src.cols * scale, src.type()); 158 dst.setTo(Scalar::all(0)); 159 160 const func_t func = funcs[src.channels()]; 161 162 func(src, dst, scale, StreamAccessor::getStream(stream)); 163 } 164 165 void diffSign(const GpuMat& src1, const GpuMat& src2, GpuMat& dst, Stream& stream) 166 { 167 dst.create(src1.size(), src1.type()); 168 169 btv_l1_cudev::diffSign(src1.reshape(1), src2.reshape(1), dst.reshape(1), StreamAccessor::getStream(stream)); 170 } 171 172 void calcBtvWeights(int btvKernelSize, double alpha, std::vector<float>& btvWeights) 173 { 174 const size_t size = btvKernelSize * btvKernelSize; 175 176 btvWeights.resize(size); 177 178 const int ksize = (btvKernelSize - 1) / 2; 179 const float alpha_f = static_cast<float>(alpha); 180 181 for (int m = 0, ind = 0; m <= ksize; ++m) 182 { 183 for (int l = ksize; l + m >= 0; --l, ++ind) 184 btvWeights[ind] = pow(alpha_f, std::abs(m) + std::abs(l)); 185 } 186 187 btv_l1_cudev::loadBtvWeights(&btvWeights[0], size); 188 } 189 190 void calcBtvRegularization(const GpuMat& src, GpuMat& dst, int btvKernelSize) 191 { 192 typedef void (*func_t)(PtrStepSzb src, PtrStepSzb dst, int ksize); 193 static const func_t funcs[] = 194 { 195 0, 196 btv_l1_cudev::calcBtvRegularization<1>, 197 0, 198 btv_l1_cudev::calcBtvRegularization<3>, 199 btv_l1_cudev::calcBtvRegularization<4> 200 }; 201 202 dst.create(src.size(), src.type()); 203 dst.setTo(Scalar::all(0)); 204 205 const int ksize = (btvKernelSize - 1) / 2; 206 207 funcs[src.channels()](src, dst, ksize); 208 } 209 210 class BTVL1_CUDA_Base : public cv::superres::SuperResolution 211 { 212 public: 213 BTVL1_CUDA_Base(); 214 215 void process(const std::vector<GpuMat>& src, GpuMat& dst, 216 const std::vector<std::pair<GpuMat, GpuMat> >& forwardMotions, const std::vector<std::pair<GpuMat, GpuMat> >& backwardMotions, 217 int baseIdx); 218 219 void collectGarbage(); 220 221 CV_IMPL_PROPERTY(int, Scale, scale_) 222 CV_IMPL_PROPERTY(int, Iterations, iterations_) 223 CV_IMPL_PROPERTY(double, Tau, tau_) 224 CV_IMPL_PROPERTY(double, Labmda, lambda_) 225 CV_IMPL_PROPERTY(double, Alpha, alpha_) 226 CV_IMPL_PROPERTY(int, KernelSize, btvKernelSize_) 227 CV_IMPL_PROPERTY(int, BlurKernelSize, blurKernelSize_) 228 CV_IMPL_PROPERTY(double, BlurSigma, blurSigma_) 229 CV_IMPL_PROPERTY(int, TemporalAreaRadius, temporalAreaRadius_) 230 CV_IMPL_PROPERTY_S(Ptr<cv::superres::DenseOpticalFlowExt>, OpticalFlow, opticalFlow_) 231 232 protected: 233 int scale_; 234 int iterations_; 235 double lambda_; 236 double tau_; 237 double alpha_; 238 int btvKernelSize_; 239 int blurKernelSize_; 240 double blurSigma_; 241 int temporalAreaRadius_; 242 Ptr<cv::superres::DenseOpticalFlowExt> opticalFlow_; 243 244 private: 245 std::vector<Ptr<cuda::Filter> > filters_; 246 int curBlurKernelSize_; 247 double curBlurSigma_; 248 int curSrcType_; 249 250 std::vector<float> btvWeights_; 251 int curBtvKernelSize_; 252 double curAlpha_; 253 254 std::vector<std::pair<GpuMat, GpuMat> > lowResForwardMotions_; 255 std::vector<std::pair<GpuMat, GpuMat> > lowResBackwardMotions_; 256 257 std::vector<std::pair<GpuMat, GpuMat> > highResForwardMotions_; 258 std::vector<std::pair<GpuMat, GpuMat> > highResBackwardMotions_; 259 260 std::vector<std::pair<GpuMat, GpuMat> > forwardMaps_; 261 std::vector<std::pair<GpuMat, GpuMat> > backwardMaps_; 262 263 GpuMat highRes_; 264 265 std::vector<Stream> streams_; 266 std::vector<GpuMat> diffTerms_; 267 std::vector<GpuMat> a_, b_, c_; 268 GpuMat regTerm_; 269 }; 270 271 BTVL1_CUDA_Base::BTVL1_CUDA_Base() 272 { 273 scale_ = 4; 274 iterations_ = 180; 275 lambda_ = 0.03; 276 tau_ = 1.3; 277 alpha_ = 0.7; 278 btvKernelSize_ = 7; 279 blurKernelSize_ = 5; 280 blurSigma_ = 0.0; 281 282 #ifdef HAVE_OPENCV_CUDAOPTFLOW 283 opticalFlow_ = createOptFlow_Farneback_CUDA(); 284 #else 285 opticalFlow_ = createOptFlow_Farneback(); 286 #endif 287 temporalAreaRadius_ = 0; 288 289 curBlurKernelSize_ = -1; 290 curBlurSigma_ = -1.0; 291 curSrcType_ = -1; 292 293 curBtvKernelSize_ = -1; 294 curAlpha_ = -1.0; 295 } 296 297 void BTVL1_CUDA_Base::process(const std::vector<GpuMat>& src, GpuMat& dst, 298 const std::vector<std::pair<GpuMat, GpuMat> >& forwardMotions, const std::vector<std::pair<GpuMat, GpuMat> >& backwardMotions, 299 int baseIdx) 300 { 301 CV_Assert( scale_ > 1 ); 302 CV_Assert( iterations_ > 0 ); 303 CV_Assert( tau_ > 0.0 ); 304 CV_Assert( alpha_ > 0.0 ); 305 CV_Assert( btvKernelSize_ > 0 && btvKernelSize_ <= 16 ); 306 CV_Assert( blurKernelSize_ > 0 ); 307 CV_Assert( blurSigma_ >= 0.0 ); 308 309 // update blur filter and btv weights 310 311 if (filters_.size() != src.size() || blurKernelSize_ != curBlurKernelSize_ || blurSigma_ != curBlurSigma_ || src[0].type() != curSrcType_) 312 { 313 filters_.resize(src.size()); 314 for (size_t i = 0; i < src.size(); ++i) 315 filters_[i] = cuda::createGaussianFilter(src[0].type(), -1, Size(blurKernelSize_, blurKernelSize_), blurSigma_); 316 curBlurKernelSize_ = blurKernelSize_; 317 curBlurSigma_ = blurSigma_; 318 curSrcType_ = src[0].type(); 319 } 320 321 if (btvWeights_.empty() || btvKernelSize_ != curBtvKernelSize_ || alpha_ != curAlpha_) 322 { 323 calcBtvWeights(btvKernelSize_, alpha_, btvWeights_); 324 curBtvKernelSize_ = btvKernelSize_; 325 curAlpha_ = alpha_; 326 } 327 328 // calc motions between input frames 329 330 calcRelativeMotions(forwardMotions, backwardMotions, lowResForwardMotions_, lowResBackwardMotions_, baseIdx, src[0].size()); 331 332 upscaleMotions(lowResForwardMotions_, highResForwardMotions_, scale_); 333 upscaleMotions(lowResBackwardMotions_, highResBackwardMotions_, scale_); 334 335 forwardMaps_.resize(highResForwardMotions_.size()); 336 backwardMaps_.resize(highResForwardMotions_.size()); 337 for (size_t i = 0; i < highResForwardMotions_.size(); ++i) 338 buildMotionMaps(highResForwardMotions_[i], highResBackwardMotions_[i], forwardMaps_[i], backwardMaps_[i]); 339 340 // initial estimation 341 342 const Size lowResSize = src[0].size(); 343 const Size highResSize(lowResSize.width * scale_, lowResSize.height * scale_); 344 345 cuda::resize(src[baseIdx], highRes_, highResSize, 0, 0, INTER_CUBIC); 346 347 // iterations 348 349 streams_.resize(src.size()); 350 diffTerms_.resize(src.size()); 351 a_.resize(src.size()); 352 b_.resize(src.size()); 353 c_.resize(src.size()); 354 355 for (int i = 0; i < iterations_; ++i) 356 { 357 for (size_t k = 0; k < src.size(); ++k) 358 { 359 // a = M * Ih 360 cuda::remap(highRes_, a_[k], backwardMaps_[k].first, backwardMaps_[k].second, INTER_NEAREST, BORDER_REPLICATE, Scalar(), streams_[k]); 361 // b = HM * Ih 362 filters_[k]->apply(a_[k], b_[k], streams_[k]); 363 // c = DHF * Ih 364 cuda::resize(b_[k], c_[k], lowResSize, 0, 0, INTER_NEAREST, streams_[k]); 365 366 diffSign(src[k], c_[k], c_[k], streams_[k]); 367 368 // a = Dt * diff 369 upscale(c_[k], a_[k], scale_, streams_[k]); 370 // b = HtDt * diff 371 filters_[k]->apply(a_[k], b_[k], streams_[k]); 372 // diffTerm = MtHtDt * diff 373 cuda::remap(b_[k], diffTerms_[k], forwardMaps_[k].first, forwardMaps_[k].second, INTER_NEAREST, BORDER_REPLICATE, Scalar(), streams_[k]); 374 } 375 376 if (lambda_ > 0) 377 { 378 calcBtvRegularization(highRes_, regTerm_, btvKernelSize_); 379 cuda::addWeighted(highRes_, 1.0, regTerm_, -tau_ * lambda_, 0.0, highRes_); 380 } 381 382 for (size_t k = 0; k < src.size(); ++k) 383 { 384 streams_[k].waitForCompletion(); 385 cuda::addWeighted(highRes_, 1.0, diffTerms_[k], tau_, 0.0, highRes_); 386 } 387 } 388 389 Rect inner(btvKernelSize_, btvKernelSize_, highRes_.cols - 2 * btvKernelSize_, highRes_.rows - 2 * btvKernelSize_); 390 highRes_(inner).copyTo(dst); 391 } 392 393 void BTVL1_CUDA_Base::collectGarbage() 394 { 395 filters_.clear(); 396 397 lowResForwardMotions_.clear(); 398 lowResBackwardMotions_.clear(); 399 400 highResForwardMotions_.clear(); 401 highResBackwardMotions_.clear(); 402 403 forwardMaps_.clear(); 404 backwardMaps_.clear(); 405 406 highRes_.release(); 407 408 diffTerms_.clear(); 409 a_.clear(); 410 b_.clear(); 411 c_.clear(); 412 regTerm_.release(); 413 } 414 415 //////////////////////////////////////////////////////////// 416 417 class BTVL1_CUDA : public BTVL1_CUDA_Base 418 { 419 public: 420 BTVL1_CUDA(); 421 422 void collectGarbage(); 423 424 protected: 425 void initImpl(Ptr<FrameSource>& frameSource); 426 void processImpl(Ptr<FrameSource>& frameSource, OutputArray output); 427 428 private: 429 void readNextFrame(Ptr<FrameSource>& frameSource); 430 void processFrame(int idx); 431 432 GpuMat curFrame_; 433 GpuMat prevFrame_; 434 435 std::vector<GpuMat> frames_; 436 std::vector<std::pair<GpuMat, GpuMat> > forwardMotions_; 437 std::vector<std::pair<GpuMat, GpuMat> > backwardMotions_; 438 std::vector<GpuMat> outputs_; 439 440 int storePos_; 441 int procPos_; 442 int outPos_; 443 444 std::vector<GpuMat> srcFrames_; 445 std::vector<std::pair<GpuMat, GpuMat> > srcForwardMotions_; 446 std::vector<std::pair<GpuMat, GpuMat> > srcBackwardMotions_; 447 GpuMat finalOutput_; 448 }; 449 450 BTVL1_CUDA::BTVL1_CUDA() 451 { 452 temporalAreaRadius_ = 4; 453 } 454 455 void BTVL1_CUDA::collectGarbage() 456 { 457 curFrame_.release(); 458 prevFrame_.release(); 459 460 frames_.clear(); 461 forwardMotions_.clear(); 462 backwardMotions_.clear(); 463 outputs_.clear(); 464 465 srcFrames_.clear(); 466 srcForwardMotions_.clear(); 467 srcBackwardMotions_.clear(); 468 finalOutput_.release(); 469 470 SuperResolution::collectGarbage(); 471 BTVL1_CUDA_Base::collectGarbage(); 472 } 473 474 void BTVL1_CUDA::initImpl(Ptr<FrameSource>& frameSource) 475 { 476 const int cacheSize = 2 * temporalAreaRadius_ + 1; 477 478 frames_.resize(cacheSize); 479 forwardMotions_.resize(cacheSize); 480 backwardMotions_.resize(cacheSize); 481 outputs_.resize(cacheSize); 482 483 storePos_ = -1; 484 485 for (int t = -temporalAreaRadius_; t <= temporalAreaRadius_; ++t) 486 readNextFrame(frameSource); 487 488 for (int i = 0; i <= temporalAreaRadius_; ++i) 489 processFrame(i); 490 491 procPos_ = temporalAreaRadius_; 492 outPos_ = -1; 493 } 494 495 void BTVL1_CUDA::processImpl(Ptr<FrameSource>& frameSource, OutputArray _output) 496 { 497 if (outPos_ >= storePos_) 498 { 499 _output.release(); 500 return; 501 } 502 503 readNextFrame(frameSource); 504 505 if (procPos_ < storePos_) 506 { 507 ++procPos_; 508 processFrame(procPos_); 509 } 510 511 ++outPos_; 512 const GpuMat& curOutput = at(outPos_, outputs_); 513 514 if (_output.kind() == _InputArray::CUDA_GPU_MAT) 515 curOutput.convertTo(_output.getGpuMatRef(), CV_8U); 516 else 517 { 518 curOutput.convertTo(finalOutput_, CV_8U); 519 arrCopy(finalOutput_, _output); 520 } 521 } 522 523 void BTVL1_CUDA::readNextFrame(Ptr<FrameSource>& frameSource) 524 { 525 frameSource->nextFrame(curFrame_); 526 527 if (curFrame_.empty()) 528 return; 529 530 ++storePos_; 531 curFrame_.convertTo(at(storePos_, frames_), CV_32F); 532 533 if (storePos_ > 0) 534 { 535 std::pair<GpuMat, GpuMat>& forwardMotion = at(storePos_ - 1, forwardMotions_); 536 std::pair<GpuMat, GpuMat>& backwardMotion = at(storePos_, backwardMotions_); 537 538 opticalFlow_->calc(prevFrame_, curFrame_, forwardMotion.first, forwardMotion.second); 539 opticalFlow_->calc(curFrame_, prevFrame_, backwardMotion.first, backwardMotion.second); 540 } 541 542 curFrame_.copyTo(prevFrame_); 543 } 544 545 void BTVL1_CUDA::processFrame(int idx) 546 { 547 const int startIdx = std::max(idx - temporalAreaRadius_, 0); 548 const int procIdx = idx; 549 const int endIdx = std::min(startIdx + 2 * temporalAreaRadius_, storePos_); 550 551 const int count = endIdx - startIdx + 1; 552 553 srcFrames_.resize(count); 554 srcForwardMotions_.resize(count); 555 srcBackwardMotions_.resize(count); 556 557 int baseIdx = -1; 558 559 for (int i = startIdx, k = 0; i <= endIdx; ++i, ++k) 560 { 561 if (i == procIdx) 562 baseIdx = k; 563 564 srcFrames_[k] = at(i, frames_); 565 566 if (i < endIdx) 567 srcForwardMotions_[k] = at(i, forwardMotions_); 568 if (i > startIdx) 569 srcBackwardMotions_[k] = at(i, backwardMotions_); 570 } 571 572 process(srcFrames_, at(idx, outputs_), srcForwardMotions_, srcBackwardMotions_, baseIdx); 573 } 574 } 575 576 Ptr<SuperResolution> cv::superres::createSuperResolution_BTVL1_CUDA() 577 { 578 return makePtr<BTVL1_CUDA>(); 579 } 580 581 #endif // HAVE_CUDA 582