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