Chromium Code Reviews| 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 b00da5c90a16c9808149d3c1b562165492a752c2..874c144e7704124b80877386337dc43084332701 100644 |
| --- a/webrtc/modules/video_processing/video_denoiser.cc |
| +++ b/webrtc/modules/video_processing/video_denoiser.cc |
| @@ -7,6 +7,7 @@ |
| * in the file PATENTS. All contributing project authors may |
| * be found in the AUTHORS file in the root of the source tree. |
| */ |
| + |
| #include "webrtc/common_video/libyuv/include/scaler.h" |
| #include "webrtc/common_video/libyuv/include/webrtc_libyuv.h" |
| #include "webrtc/modules/video_processing/video_denoiser.h" |
| @@ -19,103 +20,117 @@ VideoDenoiser::VideoDenoiser(bool runtime_cpu_detection) |
| filter_(DenoiserFilter::Create(runtime_cpu_detection, &cpu_type_)), |
| ne_(new NoiseEstimation()) {} |
| -#if EXPERIMENTAL |
| // Check the mb position(1: close to the center, 3: close to the border). |
|
marpan
2016/04/11 19:16:44
fix comment: return 1:..., 2:..., 3:...
and explai
jackychen_
2016/04/12 17:02:52
Done.
|
| -static int PositionCheck(int mb_row, int mb_col, int mb_rows, int mb_cols) { |
| - if ((mb_row >= (mb_rows >> 3)) && (mb_row <= (7 * mb_rows >> 3)) && |
| - (mb_col >= (mb_cols >> 3)) && (mb_col <= (7 * mb_cols >> 3))) |
| +static int PositionCheck(int mb_row, |
| + int mb_col, |
| + int mb_rows, |
| + int mb_cols, |
| + int noise_level) { |
| + if (noise_level == 0) |
| return 1; |
| - else if ((mb_row >= (mb_rows >> 4)) && (mb_row <= (15 * mb_rows >> 4)) && |
| - (mb_col >= (mb_cols >> 4)) && (mb_col <= (15 * mb_cols >> 4))) |
| + if ((mb_row <= (mb_rows >> 4)) || (mb_col <= (mb_cols >> 4)) || |
| + (mb_col >= (15 * mb_cols >> 4))) |
| + return 3; |
| + else if ((mb_row <= (mb_rows >> 3)) || (mb_col <= (mb_cols >> 3)) || |
| + (mb_col >= (7 * mb_cols >> 3))) |
| return 2; |
| else |
| - return 3; |
| + return 1; |
| } |
| +// To reduce false detection in moving object detection (MOD). |
| static void ReduceFalseDetection(const std::unique_ptr<uint8_t[]>& d_status, |
| - std::unique_ptr<uint8_t[]>* d_status_tmp1, |
| - std::unique_ptr<uint8_t[]>* d_status_tmp2, |
| + std::unique_ptr<uint8_t[]>* d_status_red, |
| int noise_level, |
| int mb_rows, |
| int mb_cols) { |
| - // Draft. This can be optimized. This code block is to reduce false detection |
| - // in moving object detection. |
| - int mb_row_min = noise_level ? mb_rows >> 3 : 1; |
| - int mb_col_min = noise_level ? mb_cols >> 3 : 1; |
| - int mb_row_max = noise_level ? (7 * mb_rows >> 3) : mb_rows - 2; |
| - int mb_col_max = noise_level ? (7 * mb_cols >> 3) : mb_cols - 2; |
| - memcpy((*d_status_tmp1).get(), d_status.get(), mb_rows * mb_cols); |
| - // Up left. |
| - for (int mb_row = mb_row_min; mb_row <= mb_row_max; ++mb_row) { |
| - for (int mb_col = mb_col_min; mb_col <= mb_col_max; ++mb_col) { |
| - (*d_status_tmp1)[mb_row * mb_cols + mb_col] |= |
| - ((*d_status_tmp1)[(mb_row - 1) * mb_cols + mb_col] | |
| - (*d_status_tmp1)[mb_row * mb_cols + mb_col - 1]); |
| + // From up left corner. |
| + int mb_col_stop = mb_cols - 1; |
| + for (int mb_row = 0; mb_row <= mb_rows - 1; ++mb_row) { |
| + for (int mb_col = 0; mb_col <= mb_col_stop; ++mb_col) { |
| + if (d_status[mb_row * mb_cols + mb_col]) { |
| + mb_col_stop = mb_col - 1; |
| + break; |
| + } |
| + (*d_status_red)[mb_row * mb_cols + mb_col] = 0; |
| } |
| } |
| - memcpy((*d_status_tmp2).get(), (*d_status_tmp1).get(), mb_rows * mb_cols); |
| - memcpy((*d_status_tmp1).get(), d_status.get(), mb_rows * mb_cols); |
| - // Bottom left. |
| - for (int mb_row = mb_row_max; mb_row >= mb_row_min; --mb_row) { |
| - for (int mb_col = mb_col_min; mb_col <= mb_col_max; ++mb_col) { |
| - (*d_status_tmp1)[mb_row * mb_cols + mb_col] |= |
| - ((*d_status_tmp1)[(mb_row + 1) * mb_cols + mb_col] | |
| - (*d_status_tmp1)[mb_row * mb_cols + mb_col - 1]); |
| - (*d_status_tmp2)[mb_row * mb_cols + mb_col] &= |
| - (*d_status_tmp1)[mb_row * mb_cols + mb_col]; |
| + // From bottom left corner. |
| + mb_col_stop = mb_cols - 1; |
| + for (int mb_row = mb_rows - 1; mb_row >= 0; --mb_row) { |
| + for (int mb_col = 0; mb_col <= mb_col_stop; ++mb_col) { |
| + if (d_status[mb_row * mb_cols + mb_col]) { |
| + mb_col_stop = mb_col - 1; |
| + break; |
| + } |
| + (*d_status_red)[mb_row * mb_cols + mb_col] = 0; |
| } |
| } |
| - memcpy((*d_status_tmp1).get(), d_status.get(), mb_rows * mb_cols); |
| - // Up right. |
| - for (int mb_row = mb_row_min; mb_row <= mb_row_max; ++mb_row) { |
| - for (int mb_col = mb_col_max; mb_col >= mb_col_min; --mb_col) { |
| - (*d_status_tmp1)[mb_row * mb_cols + mb_col] |= |
| - ((*d_status_tmp1)[(mb_row - 1) * mb_cols + mb_col] | |
| - (*d_status_tmp1)[mb_row * mb_cols + mb_col + 1]); |
| - (*d_status_tmp2)[mb_row * mb_cols + mb_col] &= |
| - (*d_status_tmp1)[mb_row * mb_cols + mb_col]; |
| + // From up right corner. |
| + mb_col_stop = 0; |
| + for (int mb_row = 0; mb_row <= mb_rows - 1; ++mb_row) { |
| + for (int mb_col = mb_cols - 1; mb_col >= mb_col_stop; --mb_col) { |
| + if (d_status[mb_row * mb_cols + mb_col]) { |
| + mb_col_stop = mb_col + 1; |
| + break; |
| + } |
| + (*d_status_red)[mb_row * mb_cols + mb_col] = 0; |
| } |
| } |
| - memcpy((*d_status_tmp1).get(), d_status.get(), mb_rows * mb_cols); |
| - // Bottom right. |
| - for (int mb_row = mb_row_max; mb_row >= mb_row_min; --mb_row) { |
| - for (int mb_col = mb_col_max; mb_col >= mb_col_min; --mb_col) { |
| - (*d_status_tmp1)[mb_row * mb_cols + mb_col] |= |
| - ((*d_status_tmp1)[(mb_row + 1) * mb_cols + mb_col] | |
| - (*d_status_tmp1)[mb_row * mb_cols + mb_col + 1]); |
| - (*d_status_tmp2)[mb_row * mb_cols + mb_col] &= |
| - (*d_status_tmp1)[mb_row * mb_cols + mb_col]; |
| + // From bottom right corner. |
| + mb_col_stop = 0; |
| + for (int mb_row = mb_rows - 1; mb_row >= 0; --mb_row) { |
| + for (int mb_col = mb_cols - 1; mb_col >= mb_col_stop; --mb_col) { |
| + if (d_status[mb_row * mb_cols + mb_col]) { |
| + mb_col_stop = mb_col + 1; |
| + break; |
| + } |
| + (*d_status_red)[mb_row * mb_cols + mb_col] = 0; |
| } |
| } |
| } |
| +// Check if a neighbor block is a moving edge block. |
| static bool TrailingBlock(const std::unique_ptr<uint8_t[]>& d_status, |
| int mb_row, |
| int mb_col, |
| int mb_rows, |
| int mb_cols) { |
| + bool ret = false; |
| int mb_index = mb_row * mb_cols + mb_col; |
| if (!mb_row || !mb_col || mb_row == mb_rows - 1 || mb_col == mb_cols - 1) |
| - return false; |
| - return d_status[mb_index + 1] || d_status[mb_index - 1] || |
| - d_status[mb_index + mb_cols] || d_status[mb_index - mb_cols]; |
| + ret = false; |
| + else |
| + ret = d_status[mb_index + 1] || d_status[mb_index - 1] || |
| + d_status[mb_index + mb_cols] || d_status[mb_index - mb_cols]; |
| + return ret; |
| } |
| -#endif |
| -#if DISPLAY |
| -void ShowRect(const std::unique_ptr<DenoiserFilter>& filter, |
| - const std::unique_ptr<uint8_t[]>& d_status, |
| - const std::unique_ptr<uint8_t[]>& d_status_tmp2, |
| - 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, |
| - int mb_rows, |
| - int mb_cols, |
| - int stride_u, |
| - int stride_v) { |
| +#if DISPLAY // Rectangle diagnostics |
|
marpan
2016/04/11 19:16:44
move this block of code #DISPLAY to say the top of
jackychen_
2016/04/12 17:02:52
Done.
|
| +static void CopyMem8x8(const uint8_t* src, |
| + int src_stride, |
| + uint8_t* dst, |
| + int dst_stride) { |
| + for (int i = 0; i < 8; i++) { |
| + memcpy(dst, src, 8); |
| + src += src_stride; |
| + dst += dst_stride; |
| + } |
| +} |
| + |
| +static void ShowRect(const std::unique_ptr<DenoiserFilter>& filter, |
| + const std::unique_ptr<uint8_t[]>& d_status, |
| + const std::unique_ptr<uint8_t[]>& d_status_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, |
| + int mb_rows, |
| + int mb_cols, |
| + int stride_u, |
| + int stride_v) { |
| 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; |
| @@ -125,25 +140,20 @@ void ShowRect(const std::unique_ptr<DenoiserFilter>& filter, |
| 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); |
| - uint8_t y_tmp_255[8 * 8]; |
| - memset(y_tmp_255, 200, 8 * 8); |
| - // x_density_[mb_col] * y_density_[mb_row] |
| + uint8_t uv_tmp[8 * 8]; |
| + memset(uv_tmp, 200, 8 * 8); |
| if (d_status[mb_index] == 1) { |
| // Paint to red. |
| - filter->CopyMem8x8(mb_src_u, stride_u, mb_dst_u, stride_u); |
| - filter->CopyMem8x8(y_tmp_255, 8, mb_dst_v, stride_v); |
| -#if EXPERIMENTAL |
| - } else if (d_status_tmp2[mb_row * mb_cols + mb_col] && |
| + CopyMem8x8(mb_src_u, stride_u, mb_dst_u, stride_u); |
| + CopyMem8x8(uv_tmp, 8, mb_dst_v, stride_v); |
| + } else if (d_status_red[mb_row * mb_cols + mb_col] && |
| x_density[mb_col] * y_density[mb_row]) { |
| -#else |
| - } else if (x_density[mb_col] * y_density[mb_row]) { |
| -#endif |
| // Paint to blue. |
| - filter->CopyMem8x8(y_tmp_255, 8, mb_dst_u, stride_u); |
| - filter->CopyMem8x8(mb_src_v, stride_v, mb_dst_v, stride_v); |
| + CopyMem8x8(uv_tmp, 8, mb_dst_u, stride_u); |
| + CopyMem8x8(mb_src_v, stride_v, mb_dst_v, stride_v); |
| } else { |
| - filter->CopyMem8x8(mb_src_u, stride_u, mb_dst_u, stride_u); |
| - filter->CopyMem8x8(mb_src_v, stride_v, mb_dst_v, stride_v); |
| + CopyMem8x8(mb_src_u, stride_u, mb_dst_u, stride_u); |
| + CopyMem8x8(mb_src_v, stride_v, mb_dst_v, stride_v); |
| } |
| } |
| } |
| @@ -153,156 +163,165 @@ void ShowRect(const std::unique_ptr<DenoiserFilter>& filter, |
| void VideoDenoiser::DenoiseFrame(const VideoFrame& frame, |
|
marpan
2016/04/11 19:16:44
optional: this function is still kind of long,
may
jackychen_
2016/04/12 17:02:53
Done.
|
| VideoFrame* denoised_frame, |
| VideoFrame* denoised_frame_prev, |
| - int noise_level_prev) { |
| + bool noise_estimation_enabled) { |
| int stride_y = frame.stride(kYPlane); |
| int stride_u = frame.stride(kUPlane); |
| int stride_v = frame.stride(kVPlane); |
| - // If previous width and height are different from current frame's, then no |
| - // denoising for the current frame. |
| + int mb_cols = width_ >> 4; |
| + int mb_rows = height_ >> 4; |
| + int mb_num = mb_cols * mb_rows; |
| + // 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()) { |
|
marpan
2016/04/11 19:16:44
maybe this block of code can be separate function:
jackychen_
2016/04/12 17:02:52
Done.
|
| width_ = frame.width(); |
| height_ = frame.height(); |
| + mb_cols = width_ >> 4; |
| + mb_rows = height_ >> 4; |
| + mb_num = mb_cols * mb_rows; |
| + // Allocate an empty buffer for denoised_frame_prev. |
| + denoised_frame_prev->CreateEmptyFrame(width_, height_, stride_y, stride_u, |
| + stride_v); |
| + // Allocate and initialize denoised_frame with key frame. |
| denoised_frame->CreateFrame(frame.buffer(kYPlane), frame.buffer(kUPlane), |
| frame.buffer(kVPlane), width_, height_, |
| stride_y, stride_u, stride_v, kVideoRotation_0); |
| - denoised_frame_prev->CreateFrame( |
| - frame.buffer(kYPlane), frame.buffer(kUPlane), frame.buffer(kVPlane), |
| - width_, height_, stride_y, stride_u, stride_v, kVideoRotation_0); |
| - // Setting time parameters to the output frame. |
| + // Set time parameters to the output frame. |
| denoised_frame->set_timestamp(frame.timestamp()); |
| denoised_frame->set_render_time_ms(frame.render_time_ms()); |
| + |
| + // Init noise estimator and allocate buffers. |
| ne_->Init(width_, height_, cpu_type_); |
| + d_status_.reset(new uint8_t[mb_num]); |
| + mb_filter_decision_.reset(new DenoiserDecision[mb_num]); |
| + x_density_.reset(new uint8_t[mb_cols]); |
| + y_density_.reset(new uint8_t[mb_rows]); |
| + d_status_red_.reset(new uint8_t[mb_num]); |
| return; |
| } |
| - // For 16x16 block. |
| - int mb_cols = width_ >> 4; |
| - int mb_rows = height_ >> 4; |
| - if (metrics_.get() == nullptr) |
| - metrics_.reset(new DenoiseMetrics[mb_cols * mb_rows]()); |
| - if (d_status_.get() == nullptr) { |
| - d_status_.reset(new uint8_t[mb_cols * mb_rows]()); |
| -#if EXPERIMENTAL |
| - d_status_tmp1_.reset(new uint8_t[mb_cols * mb_rows]()); |
| - d_status_tmp2_.reset(new uint8_t[mb_cols * mb_rows]()); |
| -#endif |
| - x_density_.reset(new uint8_t[mb_cols]()); |
| - y_density_.reset(new uint8_t[mb_rows]()); |
| - } |
| - // Denoise on Y plane. |
| + const uint8_t* y_src = frame.buffer(kYPlane); |
| + const uint8_t* u_src = frame.buffer(kUPlane); |
| + const uint8_t* v_src = frame.buffer(kVPlane); |
| uint8_t* y_dst = denoised_frame->buffer(kYPlane); |
| uint8_t* u_dst = denoised_frame->buffer(kUPlane); |
| uint8_t* v_dst = denoised_frame->buffer(kVPlane); |
| uint8_t* y_dst_prev = denoised_frame_prev->buffer(kYPlane); |
| - const uint8_t* y_src = frame.buffer(kYPlane); |
| - const uint8_t* u_src = frame.buffer(kUPlane); |
| - const uint8_t* v_src = frame.buffer(kVPlane); |
| - uint8_t noise_level = noise_level_prev == -1 ? 0 : ne_->GetNoiseLevel(); |
| - // Temporary buffer to store denoising result. |
| - uint8_t y_tmp[16 * 16] = {0}; |
| + uint8_t noise_level = noise_estimation_enabled ? ne_->GetNoiseLevel() : 0; |
| memset(x_density_.get(), 0, mb_cols); |
| memset(y_density_.get(), 0, mb_rows); |
| + memset(d_status_red_.get(), 1, mb_num); |
| + int thr_var_base = 16 * 16 * 5; |
| // Loop over blocks to accumulate/extract noise level and update x/y_density |
| // 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; |
| for (int mb_col = 0; mb_col < mb_cols; ++mb_col) { |
| - const uint8_t* mb_src = y_src + (mb_row << 4) * stride_y + (mb_col << 4); |
| - uint8_t* mb_dst_prev = |
| - y_dst_prev + (mb_row << 4) * stride_y + (mb_col << 4); |
| - int mb_index = mb_row * mb_cols + mb_col; |
| -#if EXPERIMENTAL |
| - int pos_factor = PositionCheck(mb_row, mb_col, mb_rows, mb_cols); |
| - uint32_t thr_var_adp = 16 * 16 * 5 * (noise_level ? pos_factor : 1); |
| -#else |
| - uint32_t thr_var_adp = 16 * 16 * 5; |
| -#endif |
| - int brightness = 0; |
| - for (int i = 0; i < 16; ++i) { |
| - for (int j = 0; j < 16; ++j) { |
| - brightness += mb_src[i * stride_y + j]; |
| + const int mb_index = mb_index_base + mb_col; |
| + const bool ne_enable = (mb_index % NOISE_SUBSAMPLE_INTERVAL == 0); |
|
marpan
2016/04/11 19:16:44
No need to change anything, just curious if its be
jackychen_
2016/04/12 17:02:52
Done.
|
| + const int pos_factor = |
| + PositionCheck(mb_row, mb_col, mb_rows, mb_cols, noise_level); |
| + const uint32_t thr_var_adp = thr_var_base * pos_factor; |
| + 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; |
| + |
| + // 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]; |
| + } |
| } |
| } |
| - // Get the denoised block. |
| - filter_->MbDenoise(mb_dst_prev, stride_y, y_tmp, 16, mb_src, stride_y, 0, |
| - 1, true); |
| - // The variance is based on the denoised blocks in time T and T-1. |
| - metrics_[mb_index].var = filter_->Variance16x8( |
| - mb_dst_prev, stride_y, y_tmp, 16, &metrics_[mb_index].sad); |
| + // 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); |
| - if (metrics_[mb_index].var > thr_var_adp) { |
| - ne_->ResetConsecLowVar(mb_index); |
| - d_status_[mb_index] = 1; |
| -#if EXPERIMENTAL |
| - if (noise_level == 0 || pos_factor < 3) { |
| - x_density_[mb_col] += 1; |
| - y_density_[mb_row] += 1; |
| + // If filter decision is FILTER_BLOCK, no need to check moving edge, |
| + // since no moving edge block will be filtered in MbDenoise. |
|
marpan
2016/04/11 19:16:44
is this always true? what it sumdiff_thresh is set
jackychen_
2016/04/12 17:02:52
Done.
|
| + if (mb_filter_decision_[mb_index] == FILTER_BLOCK) { |
| + uint32_t sse_t = 0; |
| + if (ne_enable) { |
| + // The variance is used in noise estimation, it is based on the src |
| + // block in time T and filtered block in time T-1. |
| + uint32_t noise_var = filter_->Variance16x8(mb_dst_prev, stride_y, |
|
marpan
2016/04/11 19:16:44
Change comment to?
The variance used in noise esti
jackychen_
2016/04/12 17:02:52
Done.
|
| + mb_src, stride_y, &sse_t); |
| + ne_->GetNoise(mb_index, noise_var, luma); |
| } |
| -#else |
| - x_density_[mb_col] += 1; |
| - y_density_[mb_row] += 1; |
| -#endif |
| + d_status_[mb_index] = 0; // Not a moving edge block. |
| } else { |
|
marpan
2016/04/11 19:16:44
is this else connected to if (mb_filter_decision_[
jackychen_
2016/04/12 17:02:52
It is filtered block.
|
| uint32_t sse_t = 0; |
| - // The variance is based on the src blocks in time T and denoised block |
| - // in time T-1. |
| + // The variance is used in MOD, it is based on the filtered blocks in |
|
marpan
2016/04/11 19:16:44
is this (mb_dst) now the filtered or source for ti
jackychen_
2016/04/12 17:02:53
Done.
|
| + // time T and T-1. |
| uint32_t noise_var = filter_->Variance16x8(mb_dst_prev, stride_y, |
| - mb_src, stride_y, &sse_t); |
| - ne_->GetNoise(mb_index, noise_var, brightness); |
| - d_status_[mb_index] = 0; |
| + mb_dst, stride_y, &sse_t); |
| + if (noise_var > thr_var_adp) { // Moving edge checking. |
| + if (ne_enable) { |
| + ne_->ResetConsecLowVar(mb_index); |
| + } |
| + d_status_[mb_index] = 1; // Mark as moving edge block. |
| + x_density_[mb_col] += (pos_factor < 3); |
| + y_density_[mb_row] += (pos_factor < 3); |
| + } else { |
| + d_status_[mb_index] = 0; |
| + if (ne_enable) { |
| + uint32_t noise_var = filter_->Variance16x8( |
|
marpan
2016/04/11 19:16:44
put comment here:
The variance used in noise estim
jackychen_
2016/04/12 17:02:52
Done.
|
| + mb_dst_prev, stride_y, mb_src, stride_y, &sse_t); |
| + ne_->GetNoise(mb_index, noise_var, luma); |
| + } |
| + } |
| } |
| - // Track denoised frame. |
| - filter_->CopyMem16x16(y_tmp, 16, mb_dst_prev, stride_y); |
| - } |
| - } |
| + } // End of for loop |
| + } // End of for loop |
| -#if EXPERIMENTAL |
| - ReduceFalseDetection(d_status_, &d_status_tmp1_, &d_status_tmp2_, noise_level, |
| - mb_rows, mb_cols); |
| -#endif |
| + ReduceFalseDetection(d_status_, &d_status_red_, noise_level, mb_rows, |
| + mb_cols); |
| - // Denoise each MB based on the results of moving objects detection. |
| + // Loop over to copy src block if the block is marked as moving object block |
| + // or may cause trailing artifacts. |
|
marpan
2016/04/11 19:16:44
"...if the block may cause...."
jackychen_
2016/04/12 17:02:52
Done.
|
| 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; |
| for (int mb_col = 0; mb_col < mb_cols; ++mb_col) { |
| - const uint8_t* mb_src = y_src + (mb_row << 4) * stride_y + (mb_col << 4); |
| - uint8_t* mb_dst = y_dst + (mb_row << 4) * stride_y + (mb_col << 4); |
| - const uint8_t* mb_src_u = |
| - u_src + (mb_row << 3) * stride_u + (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); |
| -#if EXPERIMENTAL |
| - if ((!d_status_tmp2_[mb_row * mb_cols + mb_col] || |
| - x_density_[mb_col] * y_density_[mb_row] == 0) && |
| - !TrailingBlock(d_status_, mb_row, mb_col, mb_rows, mb_cols)) { |
| -#else |
| - if (x_density_[mb_col] * y_density_[mb_row] == 0) { |
| -#endif |
| - if (filter_->MbDenoise(mb_dst, stride_y, y_tmp, 16, mb_src, stride_y, 0, |
| - noise_level, false) == FILTER_BLOCK) { |
| - filter_->CopyMem16x16(y_tmp, 16, mb_dst, stride_y); |
| - } else { |
| - // Copy y source. |
| - filter_->CopyMem16x16(mb_src, stride_y, mb_dst, stride_y); |
| - } |
| - } else { |
| + const int mb_index = mb_index_base + mb_col; |
| + 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; |
| + // Check if the block is a moving object block or may cause a trailing |
| + // artifacts. |
| + if (mb_filter_decision_[mb_index] != FILTER_BLOCK || |
| + TrailingBlock(d_status_, mb_row, mb_col, mb_rows, mb_cols) || |
| + (x_density_[mb_col] * y_density_[mb_row] && |
| + d_status_red_[mb_row * mb_cols + mb_col])) { |
| // Copy y source. |
| filter_->CopyMem16x16(mb_src, stride_y, mb_dst, stride_y); |
| } |
| - filter_->CopyMem8x8(mb_src_u, stride_u, mb_dst_u, stride_u); |
| - filter_->CopyMem8x8(mb_src_v, stride_v, mb_dst_v, stride_v); |
| } |
| } |
| + // 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); |
| + |
| #if DISPLAY // Rectangle diagnostics |
| // Show rectangular region |
| - ShowRect(filter_, d_status_, d_status_tmp2_, x_density_, y_density_, u_src, |
| + ShowRect(filter_, d_status_, d_status_red_, x_density_, y_density_, u_src, |
| v_src, u_dst, v_dst, mb_rows, mb_cols, stride_u, stride_v); |
| #endif |
| - // Setting time parameters to the output frame. |
| + // Set time parameters to the output frame. |
| denoised_frame->set_timestamp(frame.timestamp()); |
| denoised_frame->set_render_time_ms(frame.render_time_ms()); |
| return; |