Index: webrtc/modules/video_processing/video_denoiser.cc |
diff --git a/webrtc/modules/video_processing/video_denoiser.cc b/webrtc/modules/video_processing/video_denoiser.cc |
index 313a850b92bcea2c550153beabdb86303c70bed1..8c645f21cb7e834e353a8f219281d0f3acb44ad3 100644 |
--- a/webrtc/modules/video_processing/video_denoiser.cc |
+++ b/webrtc/modules/video_processing/video_denoiser.cc |
@@ -10,6 +10,7 @@ |
#include "webrtc/common_video/libyuv/include/webrtc_libyuv.h" |
#include "webrtc/modules/video_processing/video_denoiser.h" |
+#include "libyuv/planar_functions.h" |
namespace webrtc { |
@@ -30,37 +31,35 @@ static void ShowRect(const std::unique_ptr<DenoiserFilter>& filter, |
const std::unique_ptr<uint8_t[]>& moving_edge_red, |
const std::unique_ptr<uint8_t[]>& x_density, |
const std::unique_ptr<uint8_t[]>& y_density, |
- const uint8_t* u_src, |
- const uint8_t* v_src, |
- uint8_t* u_dst, |
- uint8_t* v_dst, |
+ const uint8_t* u_src, int stride_u_src, |
+ const uint8_t* v_src, int stride_v_src, |
+ uint8_t* u_dst, int stride_u_dst, |
+ uint8_t* v_dst, int stride_v_dst, |
int mb_rows_, |
- int mb_cols_, |
- int stride_u_, |
- int stride_v_) { |
+ int mb_cols_) { |
for (int mb_row = 0; mb_row < mb_rows_; ++mb_row) { |
for (int mb_col = 0; mb_col < mb_cols_; ++mb_col) { |
int mb_index = mb_row * mb_cols_ + mb_col; |
const uint8_t* mb_src_u = |
- u_src + (mb_row << 3) * stride_u_ + (mb_col << 3); |
+ u_src + (mb_row << 3) * stride_u_src + (mb_col << 3); |
const uint8_t* mb_src_v = |
- v_src + (mb_row << 3) * stride_v_ + (mb_col << 3); |
- uint8_t* mb_dst_u = u_dst + (mb_row << 3) * stride_u_ + (mb_col << 3); |
- uint8_t* mb_dst_v = v_dst + (mb_row << 3) * stride_v_ + (mb_col << 3); |
+ v_src + (mb_row << 3) * stride_v_src + (mb_col << 3); |
+ uint8_t* mb_dst_u = u_dst + (mb_row << 3) * stride_u_dst + (mb_col << 3); |
+ uint8_t* mb_dst_v = v_dst + (mb_row << 3) * stride_v_dst + (mb_col << 3); |
uint8_t uv_tmp[8 * 8]; |
memset(uv_tmp, 200, 8 * 8); |
if (d_status[mb_index] == 1) { |
// Paint to red. |
- CopyMem8x8(mb_src_u, stride_u_, mb_dst_u, stride_u_); |
- CopyMem8x8(uv_tmp, 8, mb_dst_v, stride_v_); |
+ CopyMem8x8(mb_src_u, stride_u_src, mb_dst_u, stride_u_dst); |
+ CopyMem8x8(uv_tmp, 8, mb_dst_v, stride_v_dst); |
} else if (moving_edge_red[mb_row * mb_cols_ + mb_col] && |
x_density[mb_col] * y_density[mb_row]) { |
// Paint to blue. |
- CopyMem8x8(uv_tmp, 8, mb_dst_u, stride_u_); |
- CopyMem8x8(mb_src_v, stride_v_, mb_dst_v, stride_v_); |
+ CopyMem8x8(uv_tmp, 8, mb_dst_u, stride_u_dst); |
+ CopyMem8x8(mb_src_v, stride_v_src, mb_dst_v, stride_v_dst); |
} else { |
- CopyMem8x8(mb_src_u, stride_u_, mb_dst_u, stride_u_); |
- CopyMem8x8(mb_src_v, stride_v_, mb_dst_v, stride_v_); |
+ CopyMem8x8(mb_src_u, stride_u_src, mb_dst_u, stride_u_dst); |
+ CopyMem8x8(mb_src_v, stride_v_src, mb_dst_v, stride_v_dst); |
} |
} |
} |
@@ -73,23 +72,11 @@ VideoDenoiser::VideoDenoiser(bool runtime_cpu_detection) |
filter_(DenoiserFilter::Create(runtime_cpu_detection, &cpu_type_)), |
ne_(new NoiseEstimation()) {} |
-void VideoDenoiser::DenoiserReset( |
- const rtc::scoped_refptr<VideoFrameBuffer>& frame, |
- rtc::scoped_refptr<I420Buffer>* denoised_frame, |
- rtc::scoped_refptr<I420Buffer>* denoised_frame_prev) { |
+void VideoDenoiser::DenoiserReset(rtc::scoped_refptr<VideoFrameBuffer> frame) { |
width_ = frame->width(); |
height_ = frame->height(); |
mb_cols_ = width_ >> 4; |
mb_rows_ = height_ >> 4; |
- stride_y_ = frame->StrideY(); |
- stride_u_ = frame->StrideU(); |
- stride_v_ = frame->StrideV(); |
- |
- // Allocate an empty buffer for denoised_frame_prev. |
- *denoised_frame_prev = I420Buffer::Create( |
- width_, height_, stride_y_, stride_u_, stride_v_); |
- // Allocate and initialize denoised_frame with key frame. |
- *denoised_frame = I420Buffer::CopyKeepStride(frame); |
// Init noise estimator and allocate buffers. |
ne_->Init(width_, height_, cpu_type_); |
@@ -176,14 +163,16 @@ bool VideoDenoiser::IsTrailingBlock(const std::unique_ptr<uint8_t[]>& d_status, |
return ret; |
} |
-void VideoDenoiser::CopySrcOnMOB(const uint8_t* y_src, uint8_t* y_dst) { |
+void VideoDenoiser::CopySrcOnMOB(const uint8_t* y_src, |
+ int stride_src, |
+ uint8_t* y_dst, |
+ int stride_dst) { |
// Loop over to copy src block if the block is marked as moving object block |
// or if the block may cause trailing artifacts. |
for (int mb_row = 0; mb_row < mb_rows_; ++mb_row) { |
const int mb_index_base = mb_row * mb_cols_; |
- const int offset_base = (mb_row << 4) * stride_y_; |
- const uint8_t* mb_src_base = y_src + offset_base; |
- uint8_t* mb_dst_base = y_dst + offset_base; |
+ const uint8_t* mb_src_base = y_src + (mb_row << 4) * stride_src; |
+ uint8_t* mb_dst_base = y_dst + (mb_row << 4) * stride_dst; |
for (int mb_col = 0; mb_col < mb_cols_; ++mb_col) { |
const int mb_index = mb_index_base + mb_col; |
const uint32_t offset_col = mb_col << 4; |
@@ -196,49 +185,55 @@ void VideoDenoiser::CopySrcOnMOB(const uint8_t* y_src, uint8_t* y_dst) { |
(x_density_[mb_col] * y_density_[mb_row] && |
moving_object_[mb_row * mb_cols_ + mb_col])) { |
// Copy y source. |
- filter_->CopyMem16x16(mb_src, stride_y_, mb_dst, stride_y_); |
+ filter_->CopyMem16x16(mb_src, stride_src, mb_dst, stride_dst); |
} |
} |
} |
} |
-void VideoDenoiser::CopyLumaOnMargin(const uint8_t* y_src, uint8_t* y_dst) { |
- if ((mb_rows_ << 4) != height_) { |
- const uint8_t* margin_y_src = y_src + (mb_rows_ << 4) * stride_y_; |
- uint8_t* margin_y_dst = y_dst + (mb_rows_ << 4) * stride_y_; |
- memcpy(margin_y_dst, margin_y_src, (height_ - (mb_rows_ << 4)) * stride_y_); |
+void VideoDenoiser::CopyLumaOnMargin(const uint8_t* y_src, |
+ int stride_src, |
+ uint8_t* y_dst, |
+ int stride_dst) { |
+ int height_margin = height_ - (mb_rows_ << 4); |
+ if (height_margin > 0) { |
+ const uint8_t* margin_y_src = y_src + (mb_rows_ << 4) * stride_src; |
+ uint8_t* margin_y_dst = y_dst + (mb_rows_ << 4) * stride_dst; |
+ libyuv::CopyPlane(margin_y_src, stride_src, margin_y_dst, stride_dst, |
+ width_, height_margin); |
} |
- if ((mb_cols_ << 4) != width_) { |
+ int width_margin = width_ - (mb_cols_ << 4); |
+ if (width_margin > 0) { |
const uint8_t* margin_y_src = y_src + (mb_cols_ << 4); |
uint8_t* margin_y_dst = y_dst + (mb_cols_ << 4); |
- for (int i = 0; i < height_; ++i) { |
- for (int j = mb_cols_ << 4; j < width_; ++j) { |
- margin_y_dst[i * stride_y_ + j] = margin_y_src[i * stride_y_ + j]; |
- } |
- } |
+ libyuv::CopyPlane(margin_y_src, stride_src, margin_y_dst, stride_dst, |
+ width_ - (mb_cols_ << 4), mb_rows_ << 4); |
} |
} |
-void VideoDenoiser::DenoiseFrame( |
- const rtc::scoped_refptr<VideoFrameBuffer>& frame, |
- rtc::scoped_refptr<I420Buffer>* denoised_frame, |
- rtc::scoped_refptr<I420Buffer>* denoised_frame_prev, |
+rtc::scoped_refptr<VideoFrameBuffer> VideoDenoiser::DenoiseFrame( |
+ rtc::scoped_refptr<VideoFrameBuffer> frame, |
bool noise_estimation_enabled) { |
// If previous width and height are different from current frame's, need to |
// reallocate the buffers and no denoising for the current frame. |
- if (width_ != frame->width() || height_ != frame->height()) { |
- DenoiserReset(frame, denoised_frame, denoised_frame_prev); |
- return; |
+ if (!prev_buffer_ || width_ != frame->width() || height_ != frame->height()) { |
+ DenoiserReset(frame); |
+ prev_buffer_ = frame; |
+ return frame; |
} |
// Set buffer pointers. |
const uint8_t* y_src = frame->DataY(); |
- const uint8_t* u_src = frame->DataU(); |
- const uint8_t* v_src = frame->DataV(); |
- uint8_t* y_dst = (*denoised_frame)->MutableDataY(); |
- uint8_t* u_dst = (*denoised_frame)->MutableDataU(); |
- uint8_t* v_dst = (*denoised_frame)->MutableDataV(); |
- uint8_t* y_dst_prev = (*denoised_frame_prev)->MutableDataY(); |
+ int stride_y_src = frame->StrideY(); |
+ rtc::scoped_refptr<I420Buffer> dst = |
+ buffer_pool_.CreateBuffer(width_, height_); |
+ |
+ uint8_t* y_dst = dst->MutableDataY(); |
+ int stride_y_dst = dst->StrideY(); |
+ |
+ const uint8_t* y_dst_prev = prev_buffer_->DataY(); |
+ int stride_prev = prev_buffer_->StrideY(); |
+ |
memset(x_density_.get(), 0, mb_cols_); |
memset(y_density_.get(), 0, mb_rows_); |
memset(moving_object_.get(), 1, mb_cols_ * mb_rows_); |
@@ -249,10 +244,9 @@ void VideoDenoiser::DenoiseFrame( |
// factors for moving object detection. |
for (int mb_row = 0; mb_row < mb_rows_; ++mb_row) { |
const int mb_index_base = mb_row * mb_cols_; |
- const int offset_base = (mb_row << 4) * stride_y_; |
- const uint8_t* mb_src_base = y_src + offset_base; |
- uint8_t* mb_dst_base = y_dst + offset_base; |
- uint8_t* mb_dst_prev_base = y_dst_prev + offset_base; |
+ const uint8_t* mb_src_base = y_src + (mb_row << 4) * stride_y_src; |
+ uint8_t* mb_dst_base = y_dst + (mb_row << 4) * stride_y_dst; |
+ const uint8_t* mb_dst_prev_base = y_dst_prev + (mb_row << 4) * stride_prev; |
for (int mb_col = 0; mb_col < mb_cols_; ++mb_col) { |
const int mb_index = mb_index_base + mb_col; |
const bool ne_enable = (mb_index % NOISE_SUBSAMPLE_INTERVAL == 0); |
@@ -261,22 +255,22 @@ void VideoDenoiser::DenoiseFrame( |
const uint32_t offset_col = mb_col << 4; |
const uint8_t* mb_src = mb_src_base + offset_col; |
uint8_t* mb_dst = mb_dst_base + offset_col; |
- uint8_t* mb_dst_prev = mb_dst_prev_base + offset_col; |
+ const uint8_t* mb_dst_prev = mb_dst_prev_base + offset_col; |
// TODO(jackychen): Need SSE2/NEON opt. |
int luma = 0; |
if (ne_enable) { |
for (int i = 4; i < 12; ++i) { |
for (int j = 4; j < 12; ++j) { |
- luma += mb_src[i * stride_y_ + j]; |
+ luma += mb_src[i * stride_y_src + j]; |
} |
} |
} |
// Get the filtered block and filter_decision. |
mb_filter_decision_[mb_index] = |
- filter_->MbDenoise(mb_dst_prev, stride_y_, mb_dst, stride_y_, mb_src, |
- stride_y_, 0, noise_level); |
+ filter_->MbDenoise(mb_dst_prev, stride_prev, mb_dst, stride_y_dst, |
+ mb_src, stride_y_src, 0, noise_level); |
// If filter decision is FILTER_BLOCK, no need to check moving edge. |
// It is unlikely for a moving edge block to be filtered in current |
@@ -286,8 +280,8 @@ void VideoDenoiser::DenoiseFrame( |
if (ne_enable) { |
// The variance used in noise estimation is based on the src block in |
// time t (mb_src) and filtered block in time t-1 (mb_dist_prev). |
- uint32_t noise_var = filter_->Variance16x8(mb_dst_prev, stride_y_, |
- mb_src, stride_y_, &sse_t); |
+ uint32_t noise_var = filter_->Variance16x8( |
+ mb_dst_prev, stride_y_dst, mb_src, stride_y_src, &sse_t); |
ne_->GetNoise(mb_index, noise_var, luma); |
} |
moving_edge_[mb_index] = 0; // Not a moving edge block. |
@@ -295,8 +289,8 @@ void VideoDenoiser::DenoiseFrame( |
uint32_t sse_t = 0; |
// The variance used in MOD is based on the filtered blocks in time |
// T (mb_dst) and T-1 (mb_dst_prev). |
- uint32_t noise_var = filter_->Variance16x8(mb_dst_prev, stride_y_, |
- mb_dst, stride_y_, &sse_t); |
+ uint32_t noise_var = filter_->Variance16x8( |
+ mb_dst_prev, stride_prev, mb_dst, stride_y_dst, &sse_t); |
if (noise_var > thr_var_adp) { // Moving edge checking. |
if (ne_enable) { |
ne_->ResetConsecLowVar(mb_index); |
@@ -310,7 +304,7 @@ void VideoDenoiser::DenoiseFrame( |
// The variance used in noise estimation is based on the src block |
// in time t (mb_src) and filtered block in time t-1 (mb_dist_prev). |
uint32_t noise_var = filter_->Variance16x8( |
- mb_dst_prev, stride_y_, mb_src, stride_y_, &sse_t); |
+ mb_dst_prev, stride_prev, mb_src, stride_y_src, &sse_t); |
ne_->GetNoise(mb_index, noise_var, luma); |
} |
} |
@@ -320,23 +314,31 @@ void VideoDenoiser::DenoiseFrame( |
ReduceFalseDetection(moving_edge_, &moving_object_, noise_level); |
- CopySrcOnMOB(y_src, y_dst); |
+ CopySrcOnMOB(y_src, stride_y_src, y_dst, stride_y_dst); |
// When frame width/height not divisible by 16, copy the margin to |
// denoised_frame. |
if ((mb_rows_ << 4) != height_ || (mb_cols_ << 4) != width_) |
- CopyLumaOnMargin(y_src, y_dst); |
+ CopyLumaOnMargin(y_src, stride_y_src, y_dst, stride_y_dst); |
- // TODO(jackychen): Need SSE2/NEON opt. |
// Copy u/v planes. |
- memcpy(u_dst, u_src, (height_ >> 1) * stride_u_); |
- memcpy(v_dst, v_src, (height_ >> 1) * stride_v_); |
+ libyuv::CopyPlane(frame->DataU(), frame->StrideU(), |
+ dst->MutableDataU(), dst->StrideU(), |
+ (width_ + 1) >> 1, (height_ + 1) >> 1); |
+ libyuv::CopyPlane(frame->DataV(), frame->StrideV(), |
+ dst->MutableDataV(), dst->StrideV(), |
+ (width_ + 1) >> 1, (height_ + 1) >> 1); |
#if DISPLAY || DISPLAYNEON |
// Show rectangular region |
- ShowRect(filter_, moving_edge_, moving_object_, x_density_, y_density_, u_src, |
- v_src, u_dst, v_dst, mb_rows_, mb_cols_, stride_u_, stride_v_); |
+ ShowRect(filter_, moving_edge_, moving_object_, x_density_, y_density_, |
+ frame->DataU(), frame->StrideU(), frame->DataV(), frame->StrideV(), |
+ dst->MutableDataU(), dst->StrideU(), |
+ dst->MutableDataV(), dst->StrideV(), |
+ mb_rows_, mb_cols_); |
#endif |
+ prev_buffer_ = dst; |
+ return dst; |
} |
} // namespace webrtc |