blob: a926bf47f33966de458baffd0628c062882c14fc [file] [log] [blame]
jackychenf9b24212015-11-26 10:59:481/*
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 */
jackychenc42f2732016-04-13 06:02:5510
jackychenf9b24212015-11-26 10:59:4811#include "webrtc/common_video/libyuv/include/webrtc_libyuv.h"
12#include "webrtc/modules/video_processing/video_denoiser.h"
nissef10748d2016-11-07 09:34:5913#include "libyuv/planar_functions.h"
jackychenf9b24212015-11-26 10:59:4814
jackychenccbef032016-04-16 17:44:1615namespace webrtc {
16
jackychen6aefc972016-04-20 23:04:3117#if DISPLAY || DISPLAYNEON
jackychenc42f2732016-04-13 06:02:5518static 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
29static 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,
nissef10748d2016-11-07 09:34:5934 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,
jackychenc42f2732016-04-13 06:02:5538 int mb_rows_,
nissef10748d2016-11-07 09:34:5939 int mb_cols_) {
jackychenc42f2732016-04-13 06:02:5540 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 =
nissef10748d2016-11-07 09:34:5944 u_src + (mb_row << 3) * stride_u_src + (mb_col << 3);
jackychenc42f2732016-04-13 06:02:5545 const uint8_t* mb_src_v =
nissef10748d2016-11-07 09:34:5946 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);
jackychenc42f2732016-04-13 06:02:5549 uint8_t uv_tmp[8 * 8];
50 memset(uv_tmp, 200, 8 * 8);
51 if (d_status[mb_index] == 1) {
52 // Paint to red.
nissef10748d2016-11-07 09:34:5953 CopyMem8x8(mb_src_u, stride_u_src, mb_dst_u, stride_u_dst);
54 CopyMem8x8(uv_tmp, 8, mb_dst_v, stride_v_dst);
jackychenc42f2732016-04-13 06:02:5555 } else if (moving_edge_red[mb_row * mb_cols_ + mb_col] &&
56 x_density[mb_col] * y_density[mb_row]) {
57 // Paint to blue.
nissef10748d2016-11-07 09:34:5958 CopyMem8x8(uv_tmp, 8, mb_dst_u, stride_u_dst);
59 CopyMem8x8(mb_src_v, stride_v_src, mb_dst_v, stride_v_dst);
jackychenc42f2732016-04-13 06:02:5560 } else {
nissef10748d2016-11-07 09:34:5961 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);
jackychenc42f2732016-04-13 06:02:5563 }
64 }
65 }
66}
67#endif
68
jackychen32cf6e22016-01-12 05:34:0769VideoDenoiser::VideoDenoiser(bool runtime_cpu_detection)
70 : width_(0),
71 height_(0),
jackychen4f5eb5c2016-04-01 14:46:5872 filter_(DenoiserFilter::Create(runtime_cpu_detection, &cpu_type_)),
73 ne_(new NoiseEstimation()) {}
jackychenf9b24212015-11-26 10:59:4874
Magnus Jedvert5dc43932017-06-14 09:28:0875void VideoDenoiser::DenoiserReset(
76 rtc::scoped_refptr<I420BufferInterface> frame) {
Niels Möller383f4c92016-06-17 07:12:4477 width_ = frame->width();
78 height_ = frame->height();
jackychenc42f2732016-04-13 06:02:5579 mb_cols_ = width_ >> 4;
80 mb_rows_ = height_ >> 4;
jackychenc42f2732016-04-13 06:02:5581
82 // Init noise estimator and allocate buffers.
83 ne_->Init(width_, height_, cpu_type_);
84 moving_edge_.reset(new uint8_t[mb_cols_ * mb_rows_]);
85 mb_filter_decision_.reset(new DenoiserDecision[mb_cols_ * mb_rows_]);
86 x_density_.reset(new uint8_t[mb_cols_]);
87 y_density_.reset(new uint8_t[mb_rows_]);
88 moving_object_.reset(new uint8_t[mb_cols_ * mb_rows_]);
89}
90
91int VideoDenoiser::PositionCheck(int mb_row, int mb_col, int noise_level) {
92 if (noise_level == 0)
jackychen4f5eb5c2016-04-01 14:46:5893 return 1;
jackychenc42f2732016-04-13 06:02:5594 if ((mb_row <= (mb_rows_ >> 4)) || (mb_col <= (mb_cols_ >> 4)) ||
95 (mb_col >= (15 * mb_cols_ >> 4)))
96 return 3;
97 else if ((mb_row <= (mb_rows_ >> 3)) || (mb_col <= (mb_cols_ >> 3)) ||
98 (mb_col >= (7 * mb_cols_ >> 3)))
jackychen4f5eb5c2016-04-01 14:46:5899 return 2;
100 else
jackychenc42f2732016-04-13 06:02:55101 return 1;
jackychen4f5eb5c2016-04-01 14:46:58102}
103
jackychenc42f2732016-04-13 06:02:55104void VideoDenoiser::ReduceFalseDetection(
105 const std::unique_ptr<uint8_t[]>& d_status,
106 std::unique_ptr<uint8_t[]>* moving_edge_red,
107 int noise_level) {
108 // From up left corner.
109 int mb_col_stop = mb_cols_ - 1;
110 for (int mb_row = 0; mb_row <= mb_rows_ - 1; ++mb_row) {
111 for (int mb_col = 0; mb_col <= mb_col_stop; ++mb_col) {
112 if (d_status[mb_row * mb_cols_ + mb_col]) {
113 mb_col_stop = mb_col - 1;
114 break;
115 }
116 (*moving_edge_red)[mb_row * mb_cols_ + mb_col] = 0;
jackychen4f5eb5c2016-04-01 14:46:58117 }
118 }
jackychenc42f2732016-04-13 06:02:55119 // From bottom left corner.
120 mb_col_stop = mb_cols_ - 1;
121 for (int mb_row = mb_rows_ - 1; mb_row >= 0; --mb_row) {
122 for (int mb_col = 0; mb_col <= mb_col_stop; ++mb_col) {
123 if (d_status[mb_row * mb_cols_ + mb_col]) {
124 mb_col_stop = mb_col - 1;
125 break;
126 }
127 (*moving_edge_red)[mb_row * mb_cols_ + mb_col] = 0;
jackychen4f5eb5c2016-04-01 14:46:58128 }
129 }
jackychenc42f2732016-04-13 06:02:55130 // From up right corner.
131 mb_col_stop = 0;
132 for (int mb_row = 0; mb_row <= mb_rows_ - 1; ++mb_row) {
133 for (int mb_col = mb_cols_ - 1; mb_col >= mb_col_stop; --mb_col) {
134 if (d_status[mb_row * mb_cols_ + mb_col]) {
135 mb_col_stop = mb_col + 1;
136 break;
137 }
138 (*moving_edge_red)[mb_row * mb_cols_ + mb_col] = 0;
jackychen4f5eb5c2016-04-01 14:46:58139 }
140 }
jackychenc42f2732016-04-13 06:02:55141 // From bottom right corner.
142 mb_col_stop = 0;
143 for (int mb_row = mb_rows_ - 1; mb_row >= 0; --mb_row) {
144 for (int mb_col = mb_cols_ - 1; mb_col >= mb_col_stop; --mb_col) {
145 if (d_status[mb_row * mb_cols_ + mb_col]) {
146 mb_col_stop = mb_col + 1;
147 break;
148 }
149 (*moving_edge_red)[mb_row * mb_cols_ + mb_col] = 0;
jackychenf9b24212015-11-26 10:59:48150 }
151 }
152}
153
jackychenc42f2732016-04-13 06:02:55154bool VideoDenoiser::IsTrailingBlock(const std::unique_ptr<uint8_t[]>& d_status,
155 int mb_row,
156 int mb_col) {
157 bool ret = false;
158 int mb_index = mb_row * mb_cols_ + mb_col;
159 if (!mb_row || !mb_col || mb_row == mb_rows_ - 1 || mb_col == mb_cols_ - 1)
160 ret = false;
161 else
162 ret = d_status[mb_index + 1] || d_status[mb_index - 1] ||
163 d_status[mb_index + mb_cols_] || d_status[mb_index - mb_cols_];
164 return ret;
jackychen4f5eb5c2016-04-01 14:46:58165}
jackychen4f5eb5c2016-04-01 14:46:58166
nissef10748d2016-11-07 09:34:59167void VideoDenoiser::CopySrcOnMOB(const uint8_t* y_src,
168 int stride_src,
169 uint8_t* y_dst,
170 int stride_dst) {
jackychenc42f2732016-04-13 06:02:55171 // Loop over to copy src block if the block is marked as moving object block
172 // or if the block may cause trailing artifacts.
173 for (int mb_row = 0; mb_row < mb_rows_; ++mb_row) {
174 const int mb_index_base = mb_row * mb_cols_;
nissef10748d2016-11-07 09:34:59175 const uint8_t* mb_src_base = y_src + (mb_row << 4) * stride_src;
176 uint8_t* mb_dst_base = y_dst + (mb_row << 4) * stride_dst;
jackychenc42f2732016-04-13 06:02:55177 for (int mb_col = 0; mb_col < mb_cols_; ++mb_col) {
178 const int mb_index = mb_index_base + mb_col;
179 const uint32_t offset_col = mb_col << 4;
180 const uint8_t* mb_src = mb_src_base + offset_col;
181 uint8_t* mb_dst = mb_dst_base + offset_col;
182 // Check if the block is a moving object block or may cause a trailing
183 // artifacts.
184 if (mb_filter_decision_[mb_index] != FILTER_BLOCK ||
185 IsTrailingBlock(moving_edge_, mb_row, mb_col) ||
186 (x_density_[mb_col] * y_density_[mb_row] &&
187 moving_object_[mb_row * mb_cols_ + mb_col])) {
188 // Copy y source.
nissef10748d2016-11-07 09:34:59189 filter_->CopyMem16x16(mb_src, stride_src, mb_dst, stride_dst);
jackychen4f5eb5c2016-04-01 14:46:58190 }
191 }
192 }
193}
jackychen4f5eb5c2016-04-01 14:46:58194
nissef10748d2016-11-07 09:34:59195void VideoDenoiser::CopyLumaOnMargin(const uint8_t* y_src,
196 int stride_src,
197 uint8_t* y_dst,
198 int stride_dst) {
199 int height_margin = height_ - (mb_rows_ << 4);
200 if (height_margin > 0) {
201 const uint8_t* margin_y_src = y_src + (mb_rows_ << 4) * stride_src;
202 uint8_t* margin_y_dst = y_dst + (mb_rows_ << 4) * stride_dst;
203 libyuv::CopyPlane(margin_y_src, stride_src, margin_y_dst, stride_dst,
204 width_, height_margin);
jackychen90ee7c32016-04-25 23:53:59205 }
nissef10748d2016-11-07 09:34:59206 int width_margin = width_ - (mb_cols_ << 4);
207 if (width_margin > 0) {
jackychen90ee7c32016-04-25 23:53:59208 const uint8_t* margin_y_src = y_src + (mb_cols_ << 4);
209 uint8_t* margin_y_dst = y_dst + (mb_cols_ << 4);
nissef10748d2016-11-07 09:34:59210 libyuv::CopyPlane(margin_y_src, stride_src, margin_y_dst, stride_dst,
211 width_ - (mb_cols_ << 4), mb_rows_ << 4);
jackychen90ee7c32016-04-25 23:53:59212 }
213}
214
Magnus Jedvert5dc43932017-06-14 09:28:08215rtc::scoped_refptr<I420BufferInterface> VideoDenoiser::DenoiseFrame(
216 rtc::scoped_refptr<I420BufferInterface> frame,
Niels Möller383f4c92016-06-17 07:12:44217 bool noise_estimation_enabled) {
jackychenc42f2732016-04-13 06:02:55218 // If previous width and height are different from current frame's, need to
219 // reallocate the buffers and no denoising for the current frame.
nissef10748d2016-11-07 09:34:59220 if (!prev_buffer_ || width_ != frame->width() || height_ != frame->height()) {
221 DenoiserReset(frame);
222 prev_buffer_ = frame;
223 return frame;
jackychenf9b24212015-11-26 10:59:48224 }
jackychen4f5eb5c2016-04-01 14:46:58225
jackychenc42f2732016-04-13 06:02:55226 // Set buffer pointers.
Niels Möller383f4c92016-06-17 07:12:44227 const uint8_t* y_src = frame->DataY();
nissef10748d2016-11-07 09:34:59228 int stride_y_src = frame->StrideY();
229 rtc::scoped_refptr<I420Buffer> dst =
230 buffer_pool_.CreateBuffer(width_, height_);
231
232 uint8_t* y_dst = dst->MutableDataY();
233 int stride_y_dst = dst->StrideY();
234
235 const uint8_t* y_dst_prev = prev_buffer_->DataY();
236 int stride_prev = prev_buffer_->StrideY();
237
jackychenc42f2732016-04-13 06:02:55238 memset(x_density_.get(), 0, mb_cols_);
239 memset(y_density_.get(), 0, mb_rows_);
240 memset(moving_object_.get(), 1, mb_cols_ * mb_rows_);
jackychen4f5eb5c2016-04-01 14:46:58241
jackychenc42f2732016-04-13 06:02:55242 uint8_t noise_level = noise_estimation_enabled ? ne_->GetNoiseLevel() : 0;
jackychen39645922016-05-03 18:21:26243 int thr_var_base = 16 * 16 * 2;
jackychen4f5eb5c2016-04-01 14:46:58244 // Loop over blocks to accumulate/extract noise level and update x/y_density
245 // factors for moving object detection.
jackychenc42f2732016-04-13 06:02:55246 for (int mb_row = 0; mb_row < mb_rows_; ++mb_row) {
247 const int mb_index_base = mb_row * mb_cols_;
nissef10748d2016-11-07 09:34:59248 const uint8_t* mb_src_base = y_src + (mb_row << 4) * stride_y_src;
249 uint8_t* mb_dst_base = y_dst + (mb_row << 4) * stride_y_dst;
250 const uint8_t* mb_dst_prev_base = y_dst_prev + (mb_row << 4) * stride_prev;
jackychenc42f2732016-04-13 06:02:55251 for (int mb_col = 0; mb_col < mb_cols_; ++mb_col) {
252 const int mb_index = mb_index_base + mb_col;
253 const bool ne_enable = (mb_index % NOISE_SUBSAMPLE_INTERVAL == 0);
254 const int pos_factor = PositionCheck(mb_row, mb_col, noise_level);
255 const uint32_t thr_var_adp = thr_var_base * pos_factor;
256 const uint32_t offset_col = mb_col << 4;
257 const uint8_t* mb_src = mb_src_base + offset_col;
258 uint8_t* mb_dst = mb_dst_base + offset_col;
nissef10748d2016-11-07 09:34:59259 const uint8_t* mb_dst_prev = mb_dst_prev_base + offset_col;
jackychenc42f2732016-04-13 06:02:55260
261 // TODO(jackychen): Need SSE2/NEON opt.
262 int luma = 0;
263 if (ne_enable) {
264 for (int i = 4; i < 12; ++i) {
265 for (int j = 4; j < 12; ++j) {
nissef10748d2016-11-07 09:34:59266 luma += mb_src[i * stride_y_src + j];
jackychenc42f2732016-04-13 06:02:55267 }
jackychen4f5eb5c2016-04-01 14:46:58268 }
269 }
270
jackychenc42f2732016-04-13 06:02:55271 // Get the filtered block and filter_decision.
272 mb_filter_decision_[mb_index] =
nissef10748d2016-11-07 09:34:59273 filter_->MbDenoise(mb_dst_prev, stride_prev, mb_dst, stride_y_dst,
274 mb_src, stride_y_src, 0, noise_level);
jackychen4f5eb5c2016-04-01 14:46:58275
jackychenc42f2732016-04-13 06:02:55276 // If filter decision is FILTER_BLOCK, no need to check moving edge.
277 // It is unlikely for a moving edge block to be filtered in current
278 // setting.
279 if (mb_filter_decision_[mb_index] == FILTER_BLOCK) {
280 uint32_t sse_t = 0;
281 if (ne_enable) {
282 // The variance used in noise estimation is based on the src block in
283 // time t (mb_src) and filtered block in time t-1 (mb_dist_prev).
nissef10748d2016-11-07 09:34:59284 uint32_t noise_var = filter_->Variance16x8(
285 mb_dst_prev, stride_y_dst, mb_src, stride_y_src, &sse_t);
jackychenc42f2732016-04-13 06:02:55286 ne_->GetNoise(mb_index, noise_var, luma);
jackychen4f5eb5c2016-04-01 14:46:58287 }
jackychenc42f2732016-04-13 06:02:55288 moving_edge_[mb_index] = 0; // Not a moving edge block.
jackychen4f5eb5c2016-04-01 14:46:58289 } else {
290 uint32_t sse_t = 0;
jackychenc42f2732016-04-13 06:02:55291 // The variance used in MOD is based on the filtered blocks in time
292 // T (mb_dst) and T-1 (mb_dst_prev).
nissef10748d2016-11-07 09:34:59293 uint32_t noise_var = filter_->Variance16x8(
294 mb_dst_prev, stride_prev, mb_dst, stride_y_dst, &sse_t);
jackychenc42f2732016-04-13 06:02:55295 if (noise_var > thr_var_adp) { // Moving edge checking.
296 if (ne_enable) {
297 ne_->ResetConsecLowVar(mb_index);
298 }
299 moving_edge_[mb_index] = 1; // Mark as moving edge block.
300 x_density_[mb_col] += (pos_factor < 3);
301 y_density_[mb_row] += (pos_factor < 3);
jackychen4f5eb5c2016-04-01 14:46:58302 } else {
jackychenc42f2732016-04-13 06:02:55303 moving_edge_[mb_index] = 0;
304 if (ne_enable) {
305 // The variance used in noise estimation is based on the src block
306 // in time t (mb_src) and filtered block in time t-1 (mb_dist_prev).
307 uint32_t noise_var = filter_->Variance16x8(
nissef10748d2016-11-07 09:34:59308 mb_dst_prev, stride_prev, mb_src, stride_y_src, &sse_t);
jackychenc42f2732016-04-13 06:02:55309 ne_->GetNoise(mb_index, noise_var, luma);
310 }
jackychen4f5eb5c2016-04-01 14:46:58311 }
jackychen4f5eb5c2016-04-01 14:46:58312 }
jackychenc42f2732016-04-13 06:02:55313 } // End of for loop
314 } // End of for loop
315
316 ReduceFalseDetection(moving_edge_, &moving_object_, noise_level);
317
nissef10748d2016-11-07 09:34:59318 CopySrcOnMOB(y_src, stride_y_src, y_dst, stride_y_dst);
jackychenc42f2732016-04-13 06:02:55319
jackychen90ee7c32016-04-25 23:53:59320 // When frame width/height not divisible by 16, copy the margin to
321 // denoised_frame.
322 if ((mb_rows_ << 4) != height_ || (mb_cols_ << 4) != width_)
nissef10748d2016-11-07 09:34:59323 CopyLumaOnMargin(y_src, stride_y_src, y_dst, stride_y_dst);
jackychen90ee7c32016-04-25 23:53:59324
jackychenc42f2732016-04-13 06:02:55325 // Copy u/v planes.
nissef10748d2016-11-07 09:34:59326 libyuv::CopyPlane(frame->DataU(), frame->StrideU(),
327 dst->MutableDataU(), dst->StrideU(),
328 (width_ + 1) >> 1, (height_ + 1) >> 1);
329 libyuv::CopyPlane(frame->DataV(), frame->StrideV(),
330 dst->MutableDataV(), dst->StrideV(),
331 (width_ + 1) >> 1, (height_ + 1) >> 1);
jackychenc42f2732016-04-13 06:02:55332
jackychen6aefc972016-04-20 23:04:31333#if DISPLAY || DISPLAYNEON
jackychen4f5eb5c2016-04-01 14:46:58334 // Show rectangular region
nissef10748d2016-11-07 09:34:59335 ShowRect(filter_, moving_edge_, moving_object_, x_density_, y_density_,
336 frame->DataU(), frame->StrideU(), frame->DataV(), frame->StrideV(),
337 dst->MutableDataU(), dst->StrideU(),
338 dst->MutableDataV(), dst->StrideV(),
339 mb_rows_, mb_cols_);
jackychen4f5eb5c2016-04-01 14:46:58340#endif
nissef10748d2016-11-07 09:34:59341 prev_buffer_ = dst;
342 return dst;
jackychenf9b24212015-11-26 10:59:48343}
344
345} // namespace webrtc