LCOV - code coverage report
Current view: top level - media/webrtc/trunk/webrtc/modules/video_processing - video_denoiser.cc (source / functions) Hit Total Coverage
Test: output.info Lines: 0 164 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 8 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :  *  Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
       3             :  *
       4             :  *  Use of this source code is governed by a BSD-style license
       5             :  *  that can be found in the LICENSE file in the root of the source
       6             :  *  tree. An additional intellectual property rights grant can be found
       7             :  *  in the file PATENTS.  All contributing project authors may
       8             :  *  be found in the AUTHORS file in the root of the source tree.
       9             :  */
      10             : 
      11             : #include "webrtc/common_video/libyuv/include/webrtc_libyuv.h"
      12             : #include "webrtc/modules/video_processing/video_denoiser.h"
      13             : #include "libyuv/planar_functions.h"
      14             : 
      15             : namespace webrtc {
      16             : 
      17             : #if DISPLAY || DISPLAYNEON
      18             : static void CopyMem8x8(const uint8_t* src,
      19             :                        int src_stride,
      20             :                        uint8_t* dst,
      21             :                        int dst_stride) {
      22             :   for (int i = 0; i < 8; i++) {
      23             :     memcpy(dst, src, 8);
      24             :     src += src_stride;
      25             :     dst += dst_stride;
      26             :   }
      27             : }
      28             : 
      29             : static void ShowRect(const std::unique_ptr<DenoiserFilter>& filter,
      30             :                      const std::unique_ptr<uint8_t[]>& d_status,
      31             :                      const std::unique_ptr<uint8_t[]>& moving_edge_red,
      32             :                      const std::unique_ptr<uint8_t[]>& x_density,
      33             :                      const std::unique_ptr<uint8_t[]>& y_density,
      34             :                      const uint8_t* u_src, int stride_u_src,
      35             :                      const uint8_t* v_src, int stride_v_src,
      36             :                      uint8_t* u_dst, int stride_u_dst,
      37             :                      uint8_t* v_dst, int stride_v_dst,
      38             :                      int mb_rows_,
      39             :                      int mb_cols_) {
      40             :   for (int mb_row = 0; mb_row < mb_rows_; ++mb_row) {
      41             :     for (int mb_col = 0; mb_col < mb_cols_; ++mb_col) {
      42             :       int mb_index = mb_row * mb_cols_ + mb_col;
      43             :       const uint8_t* mb_src_u =
      44             :           u_src + (mb_row << 3) * stride_u_src + (mb_col << 3);
      45             :       const uint8_t* mb_src_v =
      46             :           v_src + (mb_row << 3) * stride_v_src + (mb_col << 3);
      47             :       uint8_t* mb_dst_u = u_dst + (mb_row << 3) * stride_u_dst + (mb_col << 3);
      48             :       uint8_t* mb_dst_v = v_dst + (mb_row << 3) * stride_v_dst + (mb_col << 3);
      49             :       uint8_t uv_tmp[8 * 8];
      50             :       memset(uv_tmp, 200, 8 * 8);
      51             :       if (d_status[mb_index] == 1) {
      52             :         // Paint to red.
      53             :         CopyMem8x8(mb_src_u, stride_u_src, mb_dst_u, stride_u_dst);
      54             :         CopyMem8x8(uv_tmp, 8, mb_dst_v, stride_v_dst);
      55             :       } else if (moving_edge_red[mb_row * mb_cols_ + mb_col] &&
      56             :                  x_density[mb_col] * y_density[mb_row]) {
      57             :         // Paint to blue.
      58             :         CopyMem8x8(uv_tmp, 8, mb_dst_u, stride_u_dst);
      59             :         CopyMem8x8(mb_src_v, stride_v_src, mb_dst_v, stride_v_dst);
      60             :       } else {
      61             :         CopyMem8x8(mb_src_u, stride_u_src, mb_dst_u, stride_u_dst);
      62             :         CopyMem8x8(mb_src_v, stride_v_src, mb_dst_v, stride_v_dst);
      63             :       }
      64             :     }
      65             :   }
      66             : }
      67             : #endif
      68             : 
      69           0 : VideoDenoiser::VideoDenoiser(bool runtime_cpu_detection)
      70             :     : width_(0),
      71             :       height_(0),
      72             :       filter_(DenoiserFilter::Create(runtime_cpu_detection, &cpu_type_)),
      73           0 :       ne_(new NoiseEstimation()) {}
      74             : 
      75           0 : void VideoDenoiser::DenoiserReset(rtc::scoped_refptr<VideoFrameBuffer> frame) {
      76           0 :   width_ = frame->width();
      77           0 :   height_ = frame->height();
      78           0 :   mb_cols_ = width_ >> 4;
      79           0 :   mb_rows_ = height_ >> 4;
      80             : 
      81             :   // Init noise estimator and allocate buffers.
      82           0 :   ne_->Init(width_, height_, cpu_type_);
      83           0 :   moving_edge_.reset(new uint8_t[mb_cols_ * mb_rows_]);
      84           0 :   mb_filter_decision_.reset(new DenoiserDecision[mb_cols_ * mb_rows_]);
      85           0 :   x_density_.reset(new uint8_t[mb_cols_]);
      86           0 :   y_density_.reset(new uint8_t[mb_rows_]);
      87           0 :   moving_object_.reset(new uint8_t[mb_cols_ * mb_rows_]);
      88           0 : }
      89             : 
      90           0 : int VideoDenoiser::PositionCheck(int mb_row, int mb_col, int noise_level) {
      91           0 :   if (noise_level == 0)
      92           0 :     return 1;
      93           0 :   if ((mb_row <= (mb_rows_ >> 4)) || (mb_col <= (mb_cols_ >> 4)) ||
      94           0 :       (mb_col >= (15 * mb_cols_ >> 4)))
      95           0 :     return 3;
      96           0 :   else if ((mb_row <= (mb_rows_ >> 3)) || (mb_col <= (mb_cols_ >> 3)) ||
      97           0 :            (mb_col >= (7 * mb_cols_ >> 3)))
      98           0 :     return 2;
      99             :   else
     100           0 :     return 1;
     101             : }
     102             : 
     103           0 : void VideoDenoiser::ReduceFalseDetection(
     104             :     const std::unique_ptr<uint8_t[]>& d_status,
     105             :     std::unique_ptr<uint8_t[]>* moving_edge_red,
     106             :     int noise_level) {
     107             :   // From up left corner.
     108           0 :   int mb_col_stop = mb_cols_ - 1;
     109           0 :   for (int mb_row = 0; mb_row <= mb_rows_ - 1; ++mb_row) {
     110           0 :     for (int mb_col = 0; mb_col <= mb_col_stop; ++mb_col) {
     111           0 :       if (d_status[mb_row * mb_cols_ + mb_col]) {
     112           0 :         mb_col_stop = mb_col - 1;
     113           0 :         break;
     114             :       }
     115           0 :       (*moving_edge_red)[mb_row * mb_cols_ + mb_col] = 0;
     116             :     }
     117             :   }
     118             :   // From bottom left corner.
     119           0 :   mb_col_stop = mb_cols_ - 1;
     120           0 :   for (int mb_row = mb_rows_ - 1; mb_row >= 0; --mb_row) {
     121           0 :     for (int mb_col = 0; mb_col <= mb_col_stop; ++mb_col) {
     122           0 :       if (d_status[mb_row * mb_cols_ + mb_col]) {
     123           0 :         mb_col_stop = mb_col - 1;
     124           0 :         break;
     125             :       }
     126           0 :       (*moving_edge_red)[mb_row * mb_cols_ + mb_col] = 0;
     127             :     }
     128             :   }
     129             :   // From up right corner.
     130           0 :   mb_col_stop = 0;
     131           0 :   for (int mb_row = 0; mb_row <= mb_rows_ - 1; ++mb_row) {
     132           0 :     for (int mb_col = mb_cols_ - 1; mb_col >= mb_col_stop; --mb_col) {
     133           0 :       if (d_status[mb_row * mb_cols_ + mb_col]) {
     134           0 :         mb_col_stop = mb_col + 1;
     135           0 :         break;
     136             :       }
     137           0 :       (*moving_edge_red)[mb_row * mb_cols_ + mb_col] = 0;
     138             :     }
     139             :   }
     140             :   // From bottom right corner.
     141           0 :   mb_col_stop = 0;
     142           0 :   for (int mb_row = mb_rows_ - 1; mb_row >= 0; --mb_row) {
     143           0 :     for (int mb_col = mb_cols_ - 1; mb_col >= mb_col_stop; --mb_col) {
     144           0 :       if (d_status[mb_row * mb_cols_ + mb_col]) {
     145           0 :         mb_col_stop = mb_col + 1;
     146           0 :         break;
     147             :       }
     148           0 :       (*moving_edge_red)[mb_row * mb_cols_ + mb_col] = 0;
     149             :     }
     150             :   }
     151           0 : }
     152             : 
     153           0 : bool VideoDenoiser::IsTrailingBlock(const std::unique_ptr<uint8_t[]>& d_status,
     154             :                                     int mb_row,
     155             :                                     int mb_col) {
     156           0 :   bool ret = false;
     157           0 :   int mb_index = mb_row * mb_cols_ + mb_col;
     158           0 :   if (!mb_row || !mb_col || mb_row == mb_rows_ - 1 || mb_col == mb_cols_ - 1)
     159           0 :     ret = false;
     160             :   else
     161           0 :     ret = d_status[mb_index + 1] || d_status[mb_index - 1] ||
     162           0 :           d_status[mb_index + mb_cols_] || d_status[mb_index - mb_cols_];
     163           0 :   return ret;
     164             : }
     165             : 
     166           0 : void VideoDenoiser::CopySrcOnMOB(const uint8_t* y_src,
     167             :                                  int stride_src,
     168             :                                  uint8_t* y_dst,
     169             :                                  int stride_dst) {
     170             :   // Loop over to copy src block if the block is marked as moving object block
     171             :   // or if the block may cause trailing artifacts.
     172           0 :   for (int mb_row = 0; mb_row < mb_rows_; ++mb_row) {
     173           0 :     const int mb_index_base = mb_row * mb_cols_;
     174           0 :     const uint8_t* mb_src_base = y_src + (mb_row << 4) * stride_src;
     175           0 :     uint8_t* mb_dst_base = y_dst + (mb_row << 4) * stride_dst;
     176           0 :     for (int mb_col = 0; mb_col < mb_cols_; ++mb_col) {
     177           0 :       const int mb_index = mb_index_base + mb_col;
     178           0 :       const uint32_t offset_col = mb_col << 4;
     179           0 :       const uint8_t* mb_src = mb_src_base + offset_col;
     180           0 :       uint8_t* mb_dst = mb_dst_base + offset_col;
     181             :       // Check if the block is a moving object block or may cause a trailing
     182             :       // artifacts.
     183           0 :       if (mb_filter_decision_[mb_index] != FILTER_BLOCK ||
     184           0 :           IsTrailingBlock(moving_edge_, mb_row, mb_col) ||
     185           0 :           (x_density_[mb_col] * y_density_[mb_row] &&
     186           0 :            moving_object_[mb_row * mb_cols_ + mb_col])) {
     187             :         // Copy y source.
     188           0 :         filter_->CopyMem16x16(mb_src, stride_src, mb_dst, stride_dst);
     189             :       }
     190             :     }
     191             :   }
     192           0 : }
     193             : 
     194           0 : void VideoDenoiser::CopyLumaOnMargin(const uint8_t* y_src,
     195             :                                      int stride_src,
     196             :                                      uint8_t* y_dst,
     197             :                                      int stride_dst) {
     198           0 :   int height_margin = height_ - (mb_rows_ << 4);
     199           0 :   if (height_margin > 0) {
     200           0 :     const uint8_t* margin_y_src = y_src + (mb_rows_ << 4) * stride_src;
     201           0 :     uint8_t* margin_y_dst = y_dst + (mb_rows_ << 4) * stride_dst;
     202           0 :     libyuv::CopyPlane(margin_y_src, stride_src, margin_y_dst, stride_dst,
     203           0 :                       width_, height_margin);
     204             :   }
     205           0 :   int width_margin = width_ - (mb_cols_ << 4);
     206           0 :   if (width_margin > 0) {
     207           0 :     const uint8_t* margin_y_src = y_src + (mb_cols_ << 4);
     208           0 :     uint8_t* margin_y_dst = y_dst + (mb_cols_ << 4);
     209           0 :     libyuv::CopyPlane(margin_y_src, stride_src, margin_y_dst, stride_dst,
     210           0 :                       width_ - (mb_cols_ << 4), mb_rows_ << 4);
     211             :   }
     212           0 : }
     213             : 
     214           0 : rtc::scoped_refptr<VideoFrameBuffer> VideoDenoiser::DenoiseFrame(
     215             :     rtc::scoped_refptr<VideoFrameBuffer> frame,
     216             :     bool noise_estimation_enabled) {
     217             :   // If previous width and height are different from current frame's, need to
     218             :   // reallocate the buffers and no denoising for the current frame.
     219           0 :   if (!prev_buffer_ || width_ != frame->width() || height_ != frame->height()) {
     220           0 :     DenoiserReset(frame);
     221           0 :     prev_buffer_ = frame;
     222           0 :     return frame;
     223             :   }
     224             : 
     225             :   // Set buffer pointers.
     226           0 :   const uint8_t* y_src = frame->DataY();
     227           0 :   int stride_y_src = frame->StrideY();
     228             :   rtc::scoped_refptr<I420Buffer> dst =
     229           0 :       buffer_pool_.CreateBuffer(width_, height_);
     230             : 
     231           0 :   uint8_t* y_dst = dst->MutableDataY();
     232           0 :   int stride_y_dst = dst->StrideY();
     233             : 
     234           0 :   const uint8_t* y_dst_prev = prev_buffer_->DataY();
     235           0 :   int stride_prev = prev_buffer_->StrideY();
     236             : 
     237           0 :   memset(x_density_.get(), 0, mb_cols_);
     238           0 :   memset(y_density_.get(), 0, mb_rows_);
     239           0 :   memset(moving_object_.get(), 1, mb_cols_ * mb_rows_);
     240             : 
     241           0 :   uint8_t noise_level = noise_estimation_enabled ? ne_->GetNoiseLevel() : 0;
     242           0 :   int thr_var_base = 16 * 16 * 2;
     243             :   // Loop over blocks to accumulate/extract noise level and update x/y_density
     244             :   // factors for moving object detection.
     245           0 :   for (int mb_row = 0; mb_row < mb_rows_; ++mb_row) {
     246           0 :     const int mb_index_base = mb_row * mb_cols_;
     247           0 :     const uint8_t* mb_src_base = y_src + (mb_row << 4) * stride_y_src;
     248           0 :     uint8_t* mb_dst_base = y_dst + (mb_row << 4) * stride_y_dst;
     249           0 :     const uint8_t* mb_dst_prev_base = y_dst_prev + (mb_row << 4) * stride_prev;
     250           0 :     for (int mb_col = 0; mb_col < mb_cols_; ++mb_col) {
     251           0 :       const int mb_index = mb_index_base + mb_col;
     252           0 :       const bool ne_enable = (mb_index % NOISE_SUBSAMPLE_INTERVAL == 0);
     253           0 :       const int pos_factor = PositionCheck(mb_row, mb_col, noise_level);
     254           0 :       const uint32_t thr_var_adp = thr_var_base * pos_factor;
     255           0 :       const uint32_t offset_col = mb_col << 4;
     256           0 :       const uint8_t* mb_src = mb_src_base + offset_col;
     257           0 :       uint8_t* mb_dst = mb_dst_base + offset_col;
     258           0 :       const uint8_t* mb_dst_prev = mb_dst_prev_base + offset_col;
     259             : 
     260             :       // TODO(jackychen): Need SSE2/NEON opt.
     261           0 :       int luma = 0;
     262           0 :       if (ne_enable) {
     263           0 :         for (int i = 4; i < 12; ++i) {
     264           0 :           for (int j = 4; j < 12; ++j) {
     265           0 :             luma += mb_src[i * stride_y_src + j];
     266             :           }
     267             :         }
     268             :       }
     269             : 
     270             :       // Get the filtered block and filter_decision.
     271           0 :       mb_filter_decision_[mb_index] =
     272           0 :           filter_->MbDenoise(mb_dst_prev, stride_prev, mb_dst, stride_y_dst,
     273           0 :                              mb_src, stride_y_src, 0, noise_level);
     274             : 
     275             :       // If filter decision is FILTER_BLOCK, no need to check moving edge.
     276             :       // It is unlikely for a moving edge block to be filtered in current
     277             :       // setting.
     278           0 :       if (mb_filter_decision_[mb_index] == FILTER_BLOCK) {
     279           0 :         uint32_t sse_t = 0;
     280           0 :         if (ne_enable) {
     281             :           // The variance used in noise estimation is based on the src block in
     282             :           // time t (mb_src) and filtered block in time t-1 (mb_dist_prev).
     283           0 :           uint32_t noise_var = filter_->Variance16x8(
     284           0 :               mb_dst_prev, stride_y_dst, mb_src, stride_y_src, &sse_t);
     285           0 :           ne_->GetNoise(mb_index, noise_var, luma);
     286             :         }
     287           0 :         moving_edge_[mb_index] = 0;  // Not a moving edge block.
     288             :       } else {
     289           0 :         uint32_t sse_t = 0;
     290             :         // The variance used in MOD is based on the filtered blocks in time
     291             :         // T (mb_dst) and T-1 (mb_dst_prev).
     292           0 :         uint32_t noise_var = filter_->Variance16x8(
     293           0 :             mb_dst_prev, stride_prev, mb_dst, stride_y_dst, &sse_t);
     294           0 :         if (noise_var > thr_var_adp) {  // Moving edge checking.
     295           0 :           if (ne_enable) {
     296           0 :             ne_->ResetConsecLowVar(mb_index);
     297             :           }
     298           0 :           moving_edge_[mb_index] = 1;  // Mark as moving edge block.
     299           0 :           x_density_[mb_col] += (pos_factor < 3);
     300           0 :           y_density_[mb_row] += (pos_factor < 3);
     301             :         } else {
     302           0 :           moving_edge_[mb_index] = 0;
     303           0 :           if (ne_enable) {
     304             :             // The variance used in noise estimation is based on the src block
     305             :             // in time t (mb_src) and filtered block in time t-1 (mb_dist_prev).
     306           0 :             uint32_t noise_var = filter_->Variance16x8(
     307           0 :                 mb_dst_prev, stride_prev, mb_src, stride_y_src, &sse_t);
     308           0 :             ne_->GetNoise(mb_index, noise_var, luma);
     309             :           }
     310             :         }
     311             :       }
     312             :     }  // End of for loop
     313             :   }    // End of for loop
     314             : 
     315           0 :   ReduceFalseDetection(moving_edge_, &moving_object_, noise_level);
     316             : 
     317           0 :   CopySrcOnMOB(y_src, stride_y_src, y_dst, stride_y_dst);
     318             : 
     319             :   // When frame width/height not divisible by 16, copy the margin to
     320             :   // denoised_frame.
     321           0 :   if ((mb_rows_ << 4) != height_ || (mb_cols_ << 4) != width_)
     322           0 :     CopyLumaOnMargin(y_src, stride_y_src, y_dst, stride_y_dst);
     323             : 
     324             :   // Copy u/v planes.
     325           0 :   libyuv::CopyPlane(frame->DataU(), frame->StrideU(),
     326           0 :                     dst->MutableDataU(), dst->StrideU(),
     327           0 :                     (width_ + 1) >> 1, (height_ + 1) >> 1);
     328           0 :   libyuv::CopyPlane(frame->DataV(), frame->StrideV(),
     329           0 :                     dst->MutableDataV(), dst->StrideV(),
     330           0 :                     (width_ + 1) >> 1, (height_ + 1) >> 1);
     331             : 
     332             : #if DISPLAY || DISPLAYNEON
     333             :   // Show rectangular region
     334             :   ShowRect(filter_, moving_edge_, moving_object_, x_density_, y_density_,
     335             :            frame->DataU(), frame->StrideU(), frame->DataV(), frame->StrideV(),
     336             :            dst->MutableDataU(), dst->StrideU(),
     337             :            dst->MutableDataV(), dst->StrideV(),
     338             :            mb_rows_, mb_cols_);
     339             : #endif
     340           0 :   prev_buffer_ = dst;
     341           0 :   return dst;
     342             : }
     343             : 
     344             : }  // namespace webrtc

Generated by: LCOV version 1.13