Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 /* | 1 /* |
| 2 * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. | 2 * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. |
| 3 * | 3 * |
| 4 * Use of this source code is governed by a BSD-style license | 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 | 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 | 6 * tree. An additional intellectual property rights grant can be found |
| 7 * in the file PATENTS. All contributing project authors may | 7 * in the file PATENTS. All contributing project authors may |
| 8 * be found in the AUTHORS file in the root of the source tree. | 8 * be found in the AUTHORS file in the root of the source tree. |
| 9 */ | 9 */ |
| 10 | |
| 10 #include "webrtc/common_video/libyuv/include/scaler.h" | 11 #include "webrtc/common_video/libyuv/include/scaler.h" |
| 11 #include "webrtc/common_video/libyuv/include/webrtc_libyuv.h" | 12 #include "webrtc/common_video/libyuv/include/webrtc_libyuv.h" |
| 12 #include "webrtc/modules/video_processing/video_denoiser.h" | 13 #include "webrtc/modules/video_processing/video_denoiser.h" |
| 13 | 14 |
| 14 namespace webrtc { | 15 namespace webrtc { |
| 15 | 16 |
| 16 VideoDenoiser::VideoDenoiser(bool runtime_cpu_detection) | 17 VideoDenoiser::VideoDenoiser(bool runtime_cpu_detection) |
| 17 : width_(0), | 18 : width_(0), |
| 18 height_(0), | 19 height_(0), |
| 19 filter_(DenoiserFilter::Create(runtime_cpu_detection, &cpu_type_)), | 20 filter_(DenoiserFilter::Create(runtime_cpu_detection, &cpu_type_)), |
| 20 ne_(new NoiseEstimation()) {} | 21 ne_(new NoiseEstimation()) {} |
| 21 | 22 |
| 22 #if EXPERIMENTAL | |
| 23 // Check the mb position(1: close to the center, 3: close to the border). | 23 // 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.
| |
| 24 static int PositionCheck(int mb_row, int mb_col, int mb_rows, int mb_cols) { | 24 static int PositionCheck(int mb_row, |
| 25 if ((mb_row >= (mb_rows >> 3)) && (mb_row <= (7 * mb_rows >> 3)) && | 25 int mb_col, |
| 26 (mb_col >= (mb_cols >> 3)) && (mb_col <= (7 * mb_cols >> 3))) | 26 int mb_rows, |
| 27 int mb_cols, | |
| 28 int noise_level) { | |
| 29 if (noise_level == 0) | |
| 27 return 1; | 30 return 1; |
| 28 else if ((mb_row >= (mb_rows >> 4)) && (mb_row <= (15 * mb_rows >> 4)) && | 31 if ((mb_row <= (mb_rows >> 4)) || (mb_col <= (mb_cols >> 4)) || |
| 29 (mb_col >= (mb_cols >> 4)) && (mb_col <= (15 * mb_cols >> 4))) | 32 (mb_col >= (15 * mb_cols >> 4))) |
| 33 return 3; | |
| 34 else if ((mb_row <= (mb_rows >> 3)) || (mb_col <= (mb_cols >> 3)) || | |
| 35 (mb_col >= (7 * mb_cols >> 3))) | |
| 30 return 2; | 36 return 2; |
| 31 else | 37 else |
| 32 return 3; | 38 return 1; |
| 33 } | 39 } |
| 34 | 40 |
| 41 // To reduce false detection in moving object detection (MOD). | |
| 35 static void ReduceFalseDetection(const std::unique_ptr<uint8_t[]>& d_status, | 42 static void ReduceFalseDetection(const std::unique_ptr<uint8_t[]>& d_status, |
| 36 std::unique_ptr<uint8_t[]>* d_status_tmp1, | 43 std::unique_ptr<uint8_t[]>* d_status_red, |
| 37 std::unique_ptr<uint8_t[]>* d_status_tmp2, | |
| 38 int noise_level, | 44 int noise_level, |
| 39 int mb_rows, | 45 int mb_rows, |
| 40 int mb_cols) { | 46 int mb_cols) { |
| 41 // Draft. This can be optimized. This code block is to reduce false detection | 47 // From up left corner. |
| 42 // in moving object detection. | 48 int mb_col_stop = mb_cols - 1; |
| 43 int mb_row_min = noise_level ? mb_rows >> 3 : 1; | 49 for (int mb_row = 0; mb_row <= mb_rows - 1; ++mb_row) { |
| 44 int mb_col_min = noise_level ? mb_cols >> 3 : 1; | 50 for (int mb_col = 0; mb_col <= mb_col_stop; ++mb_col) { |
| 45 int mb_row_max = noise_level ? (7 * mb_rows >> 3) : mb_rows - 2; | 51 if (d_status[mb_row * mb_cols + mb_col]) { |
| 46 int mb_col_max = noise_level ? (7 * mb_cols >> 3) : mb_cols - 2; | 52 mb_col_stop = mb_col - 1; |
| 47 memcpy((*d_status_tmp1).get(), d_status.get(), mb_rows * mb_cols); | 53 break; |
| 48 // Up left. | 54 } |
| 49 for (int mb_row = mb_row_min; mb_row <= mb_row_max; ++mb_row) { | 55 (*d_status_red)[mb_row * mb_cols + mb_col] = 0; |
| 50 for (int mb_col = mb_col_min; mb_col <= mb_col_max; ++mb_col) { | |
| 51 (*d_status_tmp1)[mb_row * mb_cols + mb_col] |= | |
| 52 ((*d_status_tmp1)[(mb_row - 1) * mb_cols + mb_col] | | |
| 53 (*d_status_tmp1)[mb_row * mb_cols + mb_col - 1]); | |
| 54 } | 56 } |
| 55 } | 57 } |
| 56 memcpy((*d_status_tmp2).get(), (*d_status_tmp1).get(), mb_rows * mb_cols); | 58 // From bottom left corner. |
| 57 memcpy((*d_status_tmp1).get(), d_status.get(), mb_rows * mb_cols); | 59 mb_col_stop = mb_cols - 1; |
| 58 // Bottom left. | 60 for (int mb_row = mb_rows - 1; mb_row >= 0; --mb_row) { |
| 59 for (int mb_row = mb_row_max; mb_row >= mb_row_min; --mb_row) { | 61 for (int mb_col = 0; mb_col <= mb_col_stop; ++mb_col) { |
| 60 for (int mb_col = mb_col_min; mb_col <= mb_col_max; ++mb_col) { | 62 if (d_status[mb_row * mb_cols + mb_col]) { |
| 61 (*d_status_tmp1)[mb_row * mb_cols + mb_col] |= | 63 mb_col_stop = mb_col - 1; |
| 62 ((*d_status_tmp1)[(mb_row + 1) * mb_cols + mb_col] | | 64 break; |
| 63 (*d_status_tmp1)[mb_row * mb_cols + mb_col - 1]); | 65 } |
| 64 (*d_status_tmp2)[mb_row * mb_cols + mb_col] &= | 66 (*d_status_red)[mb_row * mb_cols + mb_col] = 0; |
| 65 (*d_status_tmp1)[mb_row * mb_cols + mb_col]; | |
| 66 } | 67 } |
| 67 } | 68 } |
| 68 memcpy((*d_status_tmp1).get(), d_status.get(), mb_rows * mb_cols); | 69 // From up right corner. |
| 69 // Up right. | 70 mb_col_stop = 0; |
| 70 for (int mb_row = mb_row_min; mb_row <= mb_row_max; ++mb_row) { | 71 for (int mb_row = 0; mb_row <= mb_rows - 1; ++mb_row) { |
| 71 for (int mb_col = mb_col_max; mb_col >= mb_col_min; --mb_col) { | 72 for (int mb_col = mb_cols - 1; mb_col >= mb_col_stop; --mb_col) { |
| 72 (*d_status_tmp1)[mb_row * mb_cols + mb_col] |= | 73 if (d_status[mb_row * mb_cols + mb_col]) { |
| 73 ((*d_status_tmp1)[(mb_row - 1) * mb_cols + mb_col] | | 74 mb_col_stop = mb_col + 1; |
| 74 (*d_status_tmp1)[mb_row * mb_cols + mb_col + 1]); | 75 break; |
| 75 (*d_status_tmp2)[mb_row * mb_cols + mb_col] &= | 76 } |
| 76 (*d_status_tmp1)[mb_row * mb_cols + mb_col]; | 77 (*d_status_red)[mb_row * mb_cols + mb_col] = 0; |
| 77 } | 78 } |
| 78 } | 79 } |
| 79 memcpy((*d_status_tmp1).get(), d_status.get(), mb_rows * mb_cols); | 80 // From bottom right corner. |
| 80 // Bottom right. | 81 mb_col_stop = 0; |
| 81 for (int mb_row = mb_row_max; mb_row >= mb_row_min; --mb_row) { | 82 for (int mb_row = mb_rows - 1; mb_row >= 0; --mb_row) { |
| 82 for (int mb_col = mb_col_max; mb_col >= mb_col_min; --mb_col) { | 83 for (int mb_col = mb_cols - 1; mb_col >= mb_col_stop; --mb_col) { |
| 83 (*d_status_tmp1)[mb_row * mb_cols + mb_col] |= | 84 if (d_status[mb_row * mb_cols + mb_col]) { |
| 84 ((*d_status_tmp1)[(mb_row + 1) * mb_cols + mb_col] | | 85 mb_col_stop = mb_col + 1; |
| 85 (*d_status_tmp1)[mb_row * mb_cols + mb_col + 1]); | 86 break; |
| 86 (*d_status_tmp2)[mb_row * mb_cols + mb_col] &= | 87 } |
| 87 (*d_status_tmp1)[mb_row * mb_cols + mb_col]; | 88 (*d_status_red)[mb_row * mb_cols + mb_col] = 0; |
| 88 } | 89 } |
| 89 } | 90 } |
| 90 } | 91 } |
| 91 | 92 |
| 93 // Check if a neighbor block is a moving edge block. | |
| 92 static bool TrailingBlock(const std::unique_ptr<uint8_t[]>& d_status, | 94 static bool TrailingBlock(const std::unique_ptr<uint8_t[]>& d_status, |
| 93 int mb_row, | 95 int mb_row, |
| 94 int mb_col, | 96 int mb_col, |
| 95 int mb_rows, | 97 int mb_rows, |
| 96 int mb_cols) { | 98 int mb_cols) { |
| 99 bool ret = false; | |
| 97 int mb_index = mb_row * mb_cols + mb_col; | 100 int mb_index = mb_row * mb_cols + mb_col; |
| 98 if (!mb_row || !mb_col || mb_row == mb_rows - 1 || mb_col == mb_cols - 1) | 101 if (!mb_row || !mb_col || mb_row == mb_rows - 1 || mb_col == mb_cols - 1) |
| 99 return false; | 102 ret = false; |
| 100 return d_status[mb_index + 1] || d_status[mb_index - 1] || | 103 else |
| 101 d_status[mb_index + mb_cols] || d_status[mb_index - mb_cols]; | 104 ret = d_status[mb_index + 1] || d_status[mb_index - 1] || |
| 105 d_status[mb_index + mb_cols] || d_status[mb_index - mb_cols]; | |
| 106 return ret; | |
| 102 } | 107 } |
| 103 #endif | |
| 104 | 108 |
| 105 #if DISPLAY | 109 #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.
| |
| 106 void ShowRect(const std::unique_ptr<DenoiserFilter>& filter, | 110 static void CopyMem8x8(const uint8_t* src, |
| 107 const std::unique_ptr<uint8_t[]>& d_status, | 111 int src_stride, |
| 108 const std::unique_ptr<uint8_t[]>& d_status_tmp2, | 112 uint8_t* dst, |
| 109 const std::unique_ptr<uint8_t[]>& x_density, | 113 int dst_stride) { |
| 110 const std::unique_ptr<uint8_t[]>& y_density, | 114 for (int i = 0; i < 8; i++) { |
| 111 const uint8_t* u_src, | 115 memcpy(dst, src, 8); |
| 112 const uint8_t* v_src, | 116 src += src_stride; |
| 113 uint8_t* u_dst, | 117 dst += dst_stride; |
| 114 uint8_t* v_dst, | 118 } |
| 115 int mb_rows, | 119 } |
| 116 int mb_cols, | 120 |
| 117 int stride_u, | 121 static void ShowRect(const std::unique_ptr<DenoiserFilter>& filter, |
| 118 int stride_v) { | 122 const std::unique_ptr<uint8_t[]>& d_status, |
| 123 const std::unique_ptr<uint8_t[]>& d_status_red, | |
| 124 const std::unique_ptr<uint8_t[]>& x_density, | |
| 125 const std::unique_ptr<uint8_t[]>& y_density, | |
| 126 const uint8_t* u_src, | |
| 127 const uint8_t* v_src, | |
| 128 uint8_t* u_dst, | |
| 129 uint8_t* v_dst, | |
| 130 int mb_rows, | |
| 131 int mb_cols, | |
| 132 int stride_u, | |
| 133 int stride_v) { | |
| 119 for (int mb_row = 0; mb_row < mb_rows; ++mb_row) { | 134 for (int mb_row = 0; mb_row < mb_rows; ++mb_row) { |
| 120 for (int mb_col = 0; mb_col < mb_cols; ++mb_col) { | 135 for (int mb_col = 0; mb_col < mb_cols; ++mb_col) { |
| 121 int mb_index = mb_row * mb_cols + mb_col; | 136 int mb_index = mb_row * mb_cols + mb_col; |
| 122 const uint8_t* mb_src_u = | 137 const uint8_t* mb_src_u = |
| 123 u_src + (mb_row << 3) * stride_u + (mb_col << 3); | 138 u_src + (mb_row << 3) * stride_u + (mb_col << 3); |
| 124 const uint8_t* mb_src_v = | 139 const uint8_t* mb_src_v = |
| 125 v_src + (mb_row << 3) * stride_v + (mb_col << 3); | 140 v_src + (mb_row << 3) * stride_v + (mb_col << 3); |
| 126 uint8_t* mb_dst_u = u_dst + (mb_row << 3) * stride_u + (mb_col << 3); | 141 uint8_t* mb_dst_u = u_dst + (mb_row << 3) * stride_u + (mb_col << 3); |
| 127 uint8_t* mb_dst_v = v_dst + (mb_row << 3) * stride_v + (mb_col << 3); | 142 uint8_t* mb_dst_v = v_dst + (mb_row << 3) * stride_v + (mb_col << 3); |
| 128 uint8_t y_tmp_255[8 * 8]; | 143 uint8_t uv_tmp[8 * 8]; |
| 129 memset(y_tmp_255, 200, 8 * 8); | 144 memset(uv_tmp, 200, 8 * 8); |
| 130 // x_density_[mb_col] * y_density_[mb_row] | |
| 131 if (d_status[mb_index] == 1) { | 145 if (d_status[mb_index] == 1) { |
| 132 // Paint to red. | 146 // Paint to red. |
| 133 filter->CopyMem8x8(mb_src_u, stride_u, mb_dst_u, stride_u); | 147 CopyMem8x8(mb_src_u, stride_u, mb_dst_u, stride_u); |
| 134 filter->CopyMem8x8(y_tmp_255, 8, mb_dst_v, stride_v); | 148 CopyMem8x8(uv_tmp, 8, mb_dst_v, stride_v); |
| 135 #if EXPERIMENTAL | 149 } else if (d_status_red[mb_row * mb_cols + mb_col] && |
| 136 } else if (d_status_tmp2[mb_row * mb_cols + mb_col] && | |
| 137 x_density[mb_col] * y_density[mb_row]) { | 150 x_density[mb_col] * y_density[mb_row]) { |
| 138 #else | |
| 139 } else if (x_density[mb_col] * y_density[mb_row]) { | |
| 140 #endif | |
| 141 // Paint to blue. | 151 // Paint to blue. |
| 142 filter->CopyMem8x8(y_tmp_255, 8, mb_dst_u, stride_u); | 152 CopyMem8x8(uv_tmp, 8, mb_dst_u, stride_u); |
| 143 filter->CopyMem8x8(mb_src_v, stride_v, mb_dst_v, stride_v); | 153 CopyMem8x8(mb_src_v, stride_v, mb_dst_v, stride_v); |
| 144 } else { | 154 } else { |
| 145 filter->CopyMem8x8(mb_src_u, stride_u, mb_dst_u, stride_u); | 155 CopyMem8x8(mb_src_u, stride_u, mb_dst_u, stride_u); |
| 146 filter->CopyMem8x8(mb_src_v, stride_v, mb_dst_v, stride_v); | 156 CopyMem8x8(mb_src_v, stride_v, mb_dst_v, stride_v); |
| 147 } | 157 } |
| 148 } | 158 } |
| 149 } | 159 } |
| 150 } | 160 } |
| 151 #endif | 161 #endif |
| 152 | 162 |
| 153 void VideoDenoiser::DenoiseFrame(const VideoFrame& frame, | 163 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.
| |
| 154 VideoFrame* denoised_frame, | 164 VideoFrame* denoised_frame, |
| 155 VideoFrame* denoised_frame_prev, | 165 VideoFrame* denoised_frame_prev, |
| 156 int noise_level_prev) { | 166 bool noise_estimation_enabled) { |
| 157 int stride_y = frame.stride(kYPlane); | 167 int stride_y = frame.stride(kYPlane); |
| 158 int stride_u = frame.stride(kUPlane); | 168 int stride_u = frame.stride(kUPlane); |
| 159 int stride_v = frame.stride(kVPlane); | 169 int stride_v = frame.stride(kVPlane); |
| 160 // If previous width and height are different from current frame's, then no | 170 int mb_cols = width_ >> 4; |
| 161 // denoising for the current frame. | 171 int mb_rows = height_ >> 4; |
| 172 int mb_num = mb_cols * mb_rows; | |
| 173 // If previous width and height are different from current frame's, need to | |
| 174 // reallocate the buffers and no denoising for the current frame. | |
| 162 if (width_ != frame.width() || height_ != frame.height()) { | 175 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.
| |
| 163 width_ = frame.width(); | 176 width_ = frame.width(); |
| 164 height_ = frame.height(); | 177 height_ = frame.height(); |
| 178 mb_cols = width_ >> 4; | |
| 179 mb_rows = height_ >> 4; | |
| 180 mb_num = mb_cols * mb_rows; | |
| 181 // Allocate an empty buffer for denoised_frame_prev. | |
| 182 denoised_frame_prev->CreateEmptyFrame(width_, height_, stride_y, stride_u, | |
| 183 stride_v); | |
| 184 // Allocate and initialize denoised_frame with key frame. | |
| 165 denoised_frame->CreateFrame(frame.buffer(kYPlane), frame.buffer(kUPlane), | 185 denoised_frame->CreateFrame(frame.buffer(kYPlane), frame.buffer(kUPlane), |
| 166 frame.buffer(kVPlane), width_, height_, | 186 frame.buffer(kVPlane), width_, height_, |
| 167 stride_y, stride_u, stride_v, kVideoRotation_0); | 187 stride_y, stride_u, stride_v, kVideoRotation_0); |
| 168 denoised_frame_prev->CreateFrame( | 188 // Set time parameters to the output frame. |
| 169 frame.buffer(kYPlane), frame.buffer(kUPlane), frame.buffer(kVPlane), | |
| 170 width_, height_, stride_y, stride_u, stride_v, kVideoRotation_0); | |
| 171 // Setting time parameters to the output frame. | |
| 172 denoised_frame->set_timestamp(frame.timestamp()); | 189 denoised_frame->set_timestamp(frame.timestamp()); |
| 173 denoised_frame->set_render_time_ms(frame.render_time_ms()); | 190 denoised_frame->set_render_time_ms(frame.render_time_ms()); |
| 191 | |
| 192 // Init noise estimator and allocate buffers. | |
| 174 ne_->Init(width_, height_, cpu_type_); | 193 ne_->Init(width_, height_, cpu_type_); |
| 194 d_status_.reset(new uint8_t[mb_num]); | |
| 195 mb_filter_decision_.reset(new DenoiserDecision[mb_num]); | |
| 196 x_density_.reset(new uint8_t[mb_cols]); | |
| 197 y_density_.reset(new uint8_t[mb_rows]); | |
| 198 d_status_red_.reset(new uint8_t[mb_num]); | |
| 175 return; | 199 return; |
| 176 } | 200 } |
| 177 // For 16x16 block. | |
| 178 int mb_cols = width_ >> 4; | |
| 179 int mb_rows = height_ >> 4; | |
| 180 if (metrics_.get() == nullptr) | |
| 181 metrics_.reset(new DenoiseMetrics[mb_cols * mb_rows]()); | |
| 182 if (d_status_.get() == nullptr) { | |
| 183 d_status_.reset(new uint8_t[mb_cols * mb_rows]()); | |
| 184 #if EXPERIMENTAL | |
| 185 d_status_tmp1_.reset(new uint8_t[mb_cols * mb_rows]()); | |
| 186 d_status_tmp2_.reset(new uint8_t[mb_cols * mb_rows]()); | |
| 187 #endif | |
| 188 x_density_.reset(new uint8_t[mb_cols]()); | |
| 189 y_density_.reset(new uint8_t[mb_rows]()); | |
| 190 } | |
| 191 | 201 |
| 192 // Denoise on Y plane. | 202 const uint8_t* y_src = frame.buffer(kYPlane); |
| 203 const uint8_t* u_src = frame.buffer(kUPlane); | |
| 204 const uint8_t* v_src = frame.buffer(kVPlane); | |
| 193 uint8_t* y_dst = denoised_frame->buffer(kYPlane); | 205 uint8_t* y_dst = denoised_frame->buffer(kYPlane); |
| 194 uint8_t* u_dst = denoised_frame->buffer(kUPlane); | 206 uint8_t* u_dst = denoised_frame->buffer(kUPlane); |
| 195 uint8_t* v_dst = denoised_frame->buffer(kVPlane); | 207 uint8_t* v_dst = denoised_frame->buffer(kVPlane); |
| 196 uint8_t* y_dst_prev = denoised_frame_prev->buffer(kYPlane); | 208 uint8_t* y_dst_prev = denoised_frame_prev->buffer(kYPlane); |
| 197 const uint8_t* y_src = frame.buffer(kYPlane); | 209 uint8_t noise_level = noise_estimation_enabled ? ne_->GetNoiseLevel() : 0; |
| 198 const uint8_t* u_src = frame.buffer(kUPlane); | |
| 199 const uint8_t* v_src = frame.buffer(kVPlane); | |
| 200 uint8_t noise_level = noise_level_prev == -1 ? 0 : ne_->GetNoiseLevel(); | |
| 201 // Temporary buffer to store denoising result. | |
| 202 uint8_t y_tmp[16 * 16] = {0}; | |
| 203 memset(x_density_.get(), 0, mb_cols); | 210 memset(x_density_.get(), 0, mb_cols); |
| 204 memset(y_density_.get(), 0, mb_rows); | 211 memset(y_density_.get(), 0, mb_rows); |
| 212 memset(d_status_red_.get(), 1, mb_num); | |
| 205 | 213 |
| 214 int thr_var_base = 16 * 16 * 5; | |
| 206 // Loop over blocks to accumulate/extract noise level and update x/y_density | 215 // Loop over blocks to accumulate/extract noise level and update x/y_density |
| 207 // factors for moving object detection. | 216 // factors for moving object detection. |
| 208 for (int mb_row = 0; mb_row < mb_rows; ++mb_row) { | 217 for (int mb_row = 0; mb_row < mb_rows; ++mb_row) { |
| 218 const int mb_index_base = mb_row * mb_cols; | |
| 219 const int offset_base = (mb_row << 4) * stride_y; | |
| 220 const uint8_t* mb_src_base = y_src + offset_base; | |
| 221 uint8_t* mb_dst_base = y_dst + offset_base; | |
| 222 uint8_t* mb_dst_prev_base = y_dst_prev + offset_base; | |
| 209 for (int mb_col = 0; mb_col < mb_cols; ++mb_col) { | 223 for (int mb_col = 0; mb_col < mb_cols; ++mb_col) { |
| 210 const uint8_t* mb_src = y_src + (mb_row << 4) * stride_y + (mb_col << 4); | 224 const int mb_index = mb_index_base + mb_col; |
| 211 uint8_t* mb_dst_prev = | 225 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.
| |
| 212 y_dst_prev + (mb_row << 4) * stride_y + (mb_col << 4); | 226 const int pos_factor = |
| 213 int mb_index = mb_row * mb_cols + mb_col; | 227 PositionCheck(mb_row, mb_col, mb_rows, mb_cols, noise_level); |
| 214 #if EXPERIMENTAL | 228 const uint32_t thr_var_adp = thr_var_base * pos_factor; |
| 215 int pos_factor = PositionCheck(mb_row, mb_col, mb_rows, mb_cols); | 229 const uint32_t offset_col = mb_col << 4; |
| 216 uint32_t thr_var_adp = 16 * 16 * 5 * (noise_level ? pos_factor : 1); | 230 const uint8_t* mb_src = mb_src_base + offset_col; |
| 217 #else | 231 uint8_t* mb_dst = mb_dst_base + offset_col; |
| 218 uint32_t thr_var_adp = 16 * 16 * 5; | 232 uint8_t* mb_dst_prev = mb_dst_prev_base + offset_col; |
| 219 #endif | 233 |
| 220 int brightness = 0; | 234 // TODO(jackychen): Need SSE2/NEON opt. |
| 221 for (int i = 0; i < 16; ++i) { | 235 int luma = 0; |
| 222 for (int j = 0; j < 16; ++j) { | 236 if (ne_enable) { |
| 223 brightness += mb_src[i * stride_y + j]; | 237 for (int i = 4; i < 12; ++i) { |
| 238 for (int j = 4; j < 12; ++j) { | |
| 239 luma += mb_src[i * stride_y + j]; | |
| 240 } | |
| 224 } | 241 } |
| 225 } | 242 } |
| 226 | 243 |
| 227 // Get the denoised block. | 244 // Get the filtered block and filter_decision. |
| 228 filter_->MbDenoise(mb_dst_prev, stride_y, y_tmp, 16, mb_src, stride_y, 0, | 245 mb_filter_decision_[mb_index] = |
| 229 1, true); | 246 filter_->MbDenoise(mb_dst_prev, stride_y, mb_dst, stride_y, mb_src, |
| 230 // The variance is based on the denoised blocks in time T and T-1. | 247 stride_y, 0, noise_level); |
| 231 metrics_[mb_index].var = filter_->Variance16x8( | |
| 232 mb_dst_prev, stride_y, y_tmp, 16, &metrics_[mb_index].sad); | |
| 233 | 248 |
| 234 if (metrics_[mb_index].var > thr_var_adp) { | 249 // If filter decision is FILTER_BLOCK, no need to check moving edge, |
| 235 ne_->ResetConsecLowVar(mb_index); | 250 // 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.
| |
| 236 d_status_[mb_index] = 1; | 251 if (mb_filter_decision_[mb_index] == FILTER_BLOCK) { |
| 237 #if EXPERIMENTAL | 252 uint32_t sse_t = 0; |
| 238 if (noise_level == 0 || pos_factor < 3) { | 253 if (ne_enable) { |
| 239 x_density_[mb_col] += 1; | 254 // The variance is used in noise estimation, it is based on the src |
| 240 y_density_[mb_row] += 1; | 255 // block in time T and filtered block in time T-1. |
| 256 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.
| |
| 257 mb_src, stride_y, &sse_t); | |
| 258 ne_->GetNoise(mb_index, noise_var, luma); | |
| 241 } | 259 } |
| 242 #else | 260 d_status_[mb_index] = 0; // Not a moving edge block. |
| 243 x_density_[mb_col] += 1; | |
| 244 y_density_[mb_row] += 1; | |
| 245 #endif | |
| 246 } else { | 261 } 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.
| |
| 247 uint32_t sse_t = 0; | 262 uint32_t sse_t = 0; |
| 248 // The variance is based on the src blocks in time T and denoised block | 263 // 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.
| |
| 249 // in time T-1. | 264 // time T and T-1. |
| 250 uint32_t noise_var = filter_->Variance16x8(mb_dst_prev, stride_y, | 265 uint32_t noise_var = filter_->Variance16x8(mb_dst_prev, stride_y, |
| 251 mb_src, stride_y, &sse_t); | 266 mb_dst, stride_y, &sse_t); |
| 252 ne_->GetNoise(mb_index, noise_var, brightness); | 267 if (noise_var > thr_var_adp) { // Moving edge checking. |
| 253 d_status_[mb_index] = 0; | 268 if (ne_enable) { |
| 269 ne_->ResetConsecLowVar(mb_index); | |
| 270 } | |
| 271 d_status_[mb_index] = 1; // Mark as moving edge block. | |
| 272 x_density_[mb_col] += (pos_factor < 3); | |
| 273 y_density_[mb_row] += (pos_factor < 3); | |
| 274 } else { | |
| 275 d_status_[mb_index] = 0; | |
| 276 if (ne_enable) { | |
| 277 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.
| |
| 278 mb_dst_prev, stride_y, mb_src, stride_y, &sse_t); | |
| 279 ne_->GetNoise(mb_index, noise_var, luma); | |
| 280 } | |
| 281 } | |
| 254 } | 282 } |
| 255 // Track denoised frame. | 283 } // End of for loop |
| 256 filter_->CopyMem16x16(y_tmp, 16, mb_dst_prev, stride_y); | 284 } // End of for loop |
| 285 | |
| 286 ReduceFalseDetection(d_status_, &d_status_red_, noise_level, mb_rows, | |
| 287 mb_cols); | |
| 288 | |
| 289 // Loop over to copy src block if the block is marked as moving object block | |
| 290 // 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.
| |
| 291 for (int mb_row = 0; mb_row < mb_rows; ++mb_row) { | |
| 292 const int mb_index_base = mb_row * mb_cols; | |
| 293 const int offset_base = (mb_row << 4) * stride_y; | |
| 294 const uint8_t* mb_src_base = y_src + offset_base; | |
| 295 uint8_t* mb_dst_base = y_dst + offset_base; | |
| 296 for (int mb_col = 0; mb_col < mb_cols; ++mb_col) { | |
| 297 const int mb_index = mb_index_base + mb_col; | |
| 298 const uint32_t offset_col = mb_col << 4; | |
| 299 const uint8_t* mb_src = mb_src_base + offset_col; | |
| 300 uint8_t* mb_dst = mb_dst_base + offset_col; | |
| 301 // Check if the block is a moving object block or may cause a trailing | |
| 302 // artifacts. | |
| 303 if (mb_filter_decision_[mb_index] != FILTER_BLOCK || | |
| 304 TrailingBlock(d_status_, mb_row, mb_col, mb_rows, mb_cols) || | |
| 305 (x_density_[mb_col] * y_density_[mb_row] && | |
| 306 d_status_red_[mb_row * mb_cols + mb_col])) { | |
| 307 // Copy y source. | |
| 308 filter_->CopyMem16x16(mb_src, stride_y, mb_dst, stride_y); | |
| 309 } | |
| 257 } | 310 } |
| 258 } | 311 } |
| 259 | 312 |
| 260 #if EXPERIMENTAL | 313 // TODO(jackychen): Need SSE2/NEON opt. |
| 261 ReduceFalseDetection(d_status_, &d_status_tmp1_, &d_status_tmp2_, noise_level, | 314 // Copy u/v planes. |
| 262 mb_rows, mb_cols); | 315 memcpy(u_dst, u_src, (height_ >> 1) * stride_u); |
| 263 #endif | 316 memcpy(v_dst, v_src, (height_ >> 1) * stride_v); |
| 264 | |
| 265 // Denoise each MB based on the results of moving objects detection. | |
| 266 for (int mb_row = 0; mb_row < mb_rows; ++mb_row) { | |
| 267 for (int mb_col = 0; mb_col < mb_cols; ++mb_col) { | |
| 268 const uint8_t* mb_src = y_src + (mb_row << 4) * stride_y + (mb_col << 4); | |
| 269 uint8_t* mb_dst = y_dst + (mb_row << 4) * stride_y + (mb_col << 4); | |
| 270 const uint8_t* mb_src_u = | |
| 271 u_src + (mb_row << 3) * stride_u + (mb_col << 3); | |
| 272 const uint8_t* mb_src_v = | |
| 273 v_src + (mb_row << 3) * stride_v + (mb_col << 3); | |
| 274 uint8_t* mb_dst_u = u_dst + (mb_row << 3) * stride_u + (mb_col << 3); | |
| 275 uint8_t* mb_dst_v = v_dst + (mb_row << 3) * stride_v + (mb_col << 3); | |
| 276 #if EXPERIMENTAL | |
| 277 if ((!d_status_tmp2_[mb_row * mb_cols + mb_col] || | |
| 278 x_density_[mb_col] * y_density_[mb_row] == 0) && | |
| 279 !TrailingBlock(d_status_, mb_row, mb_col, mb_rows, mb_cols)) { | |
| 280 #else | |
| 281 if (x_density_[mb_col] * y_density_[mb_row] == 0) { | |
| 282 #endif | |
| 283 if (filter_->MbDenoise(mb_dst, stride_y, y_tmp, 16, mb_src, stride_y, 0, | |
| 284 noise_level, false) == FILTER_BLOCK) { | |
| 285 filter_->CopyMem16x16(y_tmp, 16, mb_dst, stride_y); | |
| 286 } else { | |
| 287 // Copy y source. | |
| 288 filter_->CopyMem16x16(mb_src, stride_y, mb_dst, stride_y); | |
| 289 } | |
| 290 } else { | |
| 291 // Copy y source. | |
| 292 filter_->CopyMem16x16(mb_src, stride_y, mb_dst, stride_y); | |
| 293 } | |
| 294 filter_->CopyMem8x8(mb_src_u, stride_u, mb_dst_u, stride_u); | |
| 295 filter_->CopyMem8x8(mb_src_v, stride_v, mb_dst_v, stride_v); | |
| 296 } | |
| 297 } | |
| 298 | 317 |
| 299 #if DISPLAY // Rectangle diagnostics | 318 #if DISPLAY // Rectangle diagnostics |
| 300 // Show rectangular region | 319 // Show rectangular region |
| 301 ShowRect(filter_, d_status_, d_status_tmp2_, x_density_, y_density_, u_src, | 320 ShowRect(filter_, d_status_, d_status_red_, x_density_, y_density_, u_src, |
| 302 v_src, u_dst, v_dst, mb_rows, mb_cols, stride_u, stride_v); | 321 v_src, u_dst, v_dst, mb_rows, mb_cols, stride_u, stride_v); |
| 303 #endif | 322 #endif |
| 304 | 323 |
| 305 // Setting time parameters to the output frame. | 324 // Set time parameters to the output frame. |
| 306 denoised_frame->set_timestamp(frame.timestamp()); | 325 denoised_frame->set_timestamp(frame.timestamp()); |
| 307 denoised_frame->set_render_time_ms(frame.render_time_ms()); | 326 denoised_frame->set_render_time_ms(frame.render_time_ms()); |
| 308 return; | 327 return; |
| 309 } | 328 } |
| 310 | 329 |
| 311 } // namespace webrtc | 330 } // namespace webrtc |
| OLD | NEW |