| OLD | NEW |
| (Empty) |
| 1 /* | |
| 2 * Copyright (c) 2011 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 */ | |
| 10 | |
| 11 #include "webrtc/modules/video_processing/main/source/deflickering.h" | |
| 12 | |
| 13 #include <math.h> | |
| 14 #include <stdlib.h> | |
| 15 | |
| 16 #include "webrtc/base/logging.h" | |
| 17 #include "webrtc/common_audio/signal_processing/include/signal_processing_librar
y.h" | |
| 18 #include "webrtc/system_wrappers/include/sort.h" | |
| 19 | |
| 20 namespace webrtc { | |
| 21 | |
| 22 // Detection constants | |
| 23 // (Q4) Maximum allowed deviation for detection. | |
| 24 enum { kFrequencyDeviation = 39 }; | |
| 25 // (Q4) Minimum frequency that can be detected. | |
| 26 enum { kMinFrequencyToDetect = 32 }; | |
| 27 // Number of flickers before we accept detection | |
| 28 enum { kNumFlickerBeforeDetect = 2 }; | |
| 29 enum { kmean_valueScaling = 4 }; // (Q4) In power of 2 | |
| 30 // Dead-zone region in terms of pixel values | |
| 31 enum { kZeroCrossingDeadzone = 10 }; | |
| 32 // Deflickering constants. | |
| 33 // Compute the quantiles over 1 / DownsamplingFactor of the image. | |
| 34 enum { kDownsamplingFactor = 8 }; | |
| 35 enum { kLog2OfDownsamplingFactor = 3 }; | |
| 36 | |
| 37 // To generate in Matlab: | |
| 38 // >> probUW16 = round(2^11 * | |
| 39 // [0.05,0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,0.95,0.97]); | |
| 40 // >> fprintf('%d, ', probUW16) | |
| 41 // Resolution reduced to avoid overflow when multiplying with the | |
| 42 // (potentially) large number of pixels. | |
| 43 const uint16_t VPMDeflickering::prob_uw16_[kNumProbs] = {102, 205, 410, 614, | |
| 44 819, 1024, 1229, 1434, 1638, 1843, 1946, 1987}; // <Q11> | |
| 45 | |
| 46 // To generate in Matlab: | |
| 47 // >> numQuants = 14; maxOnlyLength = 5; | |
| 48 // >> weightUW16 = round(2^15 * | |
| 49 // [linspace(0.5, 1.0, numQuants - maxOnlyLength)]); | |
| 50 // >> fprintf('%d, %d,\n ', weightUW16); | |
| 51 const uint16_t VPMDeflickering::weight_uw16_[kNumQuants - kMaxOnlyLength] = | |
| 52 {16384, 18432, 20480, 22528, 24576, 26624, 28672, 30720, 32768}; // <Q15> | |
| 53 | |
| 54 VPMDeflickering::VPMDeflickering() { | |
| 55 Reset(); | |
| 56 } | |
| 57 | |
| 58 VPMDeflickering::~VPMDeflickering() {} | |
| 59 | |
| 60 void VPMDeflickering::Reset() { | |
| 61 mean_buffer_length_ = 0; | |
| 62 detection_state_ = 0; | |
| 63 frame_rate_ = 0; | |
| 64 | |
| 65 memset(mean_buffer_, 0, sizeof(int32_t) * kMeanBufferLength); | |
| 66 memset(timestamp_buffer_, 0, sizeof(int32_t) * kMeanBufferLength); | |
| 67 | |
| 68 // Initialize the history with a uniformly distributed histogram. | |
| 69 quant_hist_uw8_[0][0] = 0; | |
| 70 quant_hist_uw8_[0][kNumQuants - 1] = 255; | |
| 71 for (int32_t i = 0; i < kNumProbs; i++) { | |
| 72 // Unsigned round. <Q0> | |
| 73 quant_hist_uw8_[0][i + 1] = static_cast<uint8_t>( | |
| 74 (prob_uw16_[i] * 255 + (1 << 10)) >> 11); | |
| 75 } | |
| 76 | |
| 77 for (int32_t i = 1; i < kFrameHistory_size; i++) { | |
| 78 memcpy(quant_hist_uw8_[i], quant_hist_uw8_[0], | |
| 79 sizeof(uint8_t) * kNumQuants); | |
| 80 } | |
| 81 } | |
| 82 | |
| 83 int32_t VPMDeflickering::ProcessFrame( | |
| 84 VideoFrame* frame, | |
| 85 VideoProcessingModule::FrameStats* stats) { | |
| 86 assert(frame); | |
| 87 uint32_t frame_memory; | |
| 88 uint8_t quant_uw8[kNumQuants]; | |
| 89 uint8_t maxquant_uw8[kNumQuants]; | |
| 90 uint8_t minquant_uw8[kNumQuants]; | |
| 91 uint16_t target_quant_uw16[kNumQuants]; | |
| 92 uint16_t increment_uw16; | |
| 93 uint8_t map_uw8[256]; | |
| 94 | |
| 95 uint16_t tmp_uw16; | |
| 96 uint32_t tmp_uw32; | |
| 97 int width = frame->width(); | |
| 98 int height = frame->height(); | |
| 99 | |
| 100 if (frame->IsZeroSize()) { | |
| 101 return VPM_GENERAL_ERROR; | |
| 102 } | |
| 103 | |
| 104 // Stricter height check due to subsampling size calculation below. | |
| 105 if (height < 2) { | |
| 106 LOG(LS_ERROR) << "Invalid frame size."; | |
| 107 return VPM_GENERAL_ERROR; | |
| 108 } | |
| 109 | |
| 110 if (!VideoProcessingModule::ValidFrameStats(*stats)) { | |
| 111 return VPM_GENERAL_ERROR; | |
| 112 } | |
| 113 | |
| 114 if (PreDetection(frame->timestamp(), *stats) == -1) return VPM_GENERAL_ERROR; | |
| 115 | |
| 116 // Flicker detection | |
| 117 int32_t det_flicker = DetectFlicker(); | |
| 118 if (det_flicker < 0) { | |
| 119 return VPM_GENERAL_ERROR; | |
| 120 } else if (det_flicker != 1) { | |
| 121 return 0; | |
| 122 } | |
| 123 | |
| 124 // Size of luminance component. | |
| 125 const uint32_t y_size = height * width; | |
| 126 | |
| 127 const uint32_t y_sub_size = width * (((height - 1) >> | |
| 128 kLog2OfDownsamplingFactor) + 1); | |
| 129 uint8_t* y_sorted = new uint8_t[y_sub_size]; | |
| 130 uint32_t sort_row_idx = 0; | |
| 131 for (int i = 0; i < height; i += kDownsamplingFactor) { | |
| 132 memcpy(y_sorted + sort_row_idx * width, | |
| 133 frame->buffer(kYPlane) + i * width, width); | |
| 134 sort_row_idx++; | |
| 135 } | |
| 136 | |
| 137 webrtc::Sort(y_sorted, y_sub_size, webrtc::TYPE_UWord8); | |
| 138 | |
| 139 uint32_t prob_idx_uw32 = 0; | |
| 140 quant_uw8[0] = 0; | |
| 141 quant_uw8[kNumQuants - 1] = 255; | |
| 142 | |
| 143 // Ensure we won't get an overflow below. | |
| 144 // In practice, the number of subsampled pixels will not become this large. | |
| 145 if (y_sub_size > (1 << 21) - 1) { | |
| 146 LOG(LS_ERROR) << "Subsampled number of pixels too large."; | |
| 147 return -1; | |
| 148 } | |
| 149 | |
| 150 for (int32_t i = 0; i < kNumProbs; i++) { | |
| 151 // <Q0>. | |
| 152 prob_idx_uw32 = WEBRTC_SPL_UMUL_32_16(y_sub_size, prob_uw16_[i]) >> 11; | |
| 153 quant_uw8[i + 1] = y_sorted[prob_idx_uw32]; | |
| 154 } | |
| 155 | |
| 156 delete [] y_sorted; | |
| 157 y_sorted = NULL; | |
| 158 | |
| 159 // Shift history for new frame. | |
| 160 memmove(quant_hist_uw8_[1], quant_hist_uw8_[0], | |
| 161 (kFrameHistory_size - 1) * kNumQuants * sizeof(uint8_t)); | |
| 162 // Store current frame in history. | |
| 163 memcpy(quant_hist_uw8_[0], quant_uw8, kNumQuants * sizeof(uint8_t)); | |
| 164 | |
| 165 // We use a frame memory equal to the ceiling of half the frame rate to | |
| 166 // ensure we capture an entire period of flicker. | |
| 167 frame_memory = (frame_rate_ + (1 << 5)) >> 5; // Unsigned ceiling. <Q0> | |
| 168 // frame_rate_ in Q4. | |
| 169 if (frame_memory > kFrameHistory_size) { | |
| 170 frame_memory = kFrameHistory_size; | |
| 171 } | |
| 172 | |
| 173 // Get maximum and minimum. | |
| 174 for (int32_t i = 0; i < kNumQuants; i++) { | |
| 175 maxquant_uw8[i] = 0; | |
| 176 minquant_uw8[i] = 255; | |
| 177 for (uint32_t j = 0; j < frame_memory; j++) { | |
| 178 if (quant_hist_uw8_[j][i] > maxquant_uw8[i]) { | |
| 179 maxquant_uw8[i] = quant_hist_uw8_[j][i]; | |
| 180 } | |
| 181 | |
| 182 if (quant_hist_uw8_[j][i] < minquant_uw8[i]) { | |
| 183 minquant_uw8[i] = quant_hist_uw8_[j][i]; | |
| 184 } | |
| 185 } | |
| 186 } | |
| 187 | |
| 188 // Get target quantiles. | |
| 189 for (int32_t i = 0; i < kNumQuants - kMaxOnlyLength; i++) { | |
| 190 // target = w * maxquant_uw8 + (1 - w) * minquant_uw8 | |
| 191 // Weights w = |weight_uw16_| are in Q15, hence the final output has to be | |
| 192 // right shifted by 8 to end up in Q7. | |
| 193 target_quant_uw16[i] = static_cast<uint16_t>(( | |
| 194 weight_uw16_[i] * maxquant_uw8[i] + | |
| 195 ((1 << 15) - weight_uw16_[i]) * minquant_uw8[i]) >> 8); // <Q7> | |
| 196 } | |
| 197 | |
| 198 for (int32_t i = kNumQuants - kMaxOnlyLength; i < kNumQuants; i++) { | |
| 199 target_quant_uw16[i] = ((uint16_t)maxquant_uw8[i]) << 7; | |
| 200 } | |
| 201 | |
| 202 // Compute the map from input to output pixels. | |
| 203 uint16_t mapUW16; // <Q7> | |
| 204 for (int32_t i = 1; i < kNumQuants; i++) { | |
| 205 // As quant and targetQuant are limited to UWord8, it's safe to use Q7 here. | |
| 206 tmp_uw32 = static_cast<uint32_t>(target_quant_uw16[i] - | |
| 207 target_quant_uw16[i - 1]); | |
| 208 tmp_uw16 = static_cast<uint16_t>(quant_uw8[i] - quant_uw8[i - 1]); // <Q0> | |
| 209 | |
| 210 if (tmp_uw16 > 0) { | |
| 211 increment_uw16 = static_cast<uint16_t>(WebRtcSpl_DivU32U16(tmp_uw32, | |
| 212 tmp_uw16)); // <Q7> | |
| 213 } else { | |
| 214 // The value is irrelevant; the loop below will only iterate once. | |
| 215 increment_uw16 = 0; | |
| 216 } | |
| 217 | |
| 218 mapUW16 = target_quant_uw16[i - 1]; | |
| 219 for (uint32_t j = quant_uw8[i - 1]; j < (uint32_t)(quant_uw8[i] + 1); j++) { | |
| 220 // Unsigned round. <Q0> | |
| 221 map_uw8[j] = (uint8_t)((mapUW16 + (1 << 6)) >> 7); | |
| 222 mapUW16 += increment_uw16; | |
| 223 } | |
| 224 } | |
| 225 | |
| 226 // Map to the output frame. | |
| 227 uint8_t* buffer = frame->buffer(kYPlane); | |
| 228 for (uint32_t i = 0; i < y_size; i++) { | |
| 229 buffer[i] = map_uw8[buffer[i]]; | |
| 230 } | |
| 231 | |
| 232 // Frame was altered, so reset stats. | |
| 233 VideoProcessingModule::ClearFrameStats(stats); | |
| 234 | |
| 235 return VPM_OK; | |
| 236 } | |
| 237 | |
| 238 /** | |
| 239 Performs some pre-detection operations. Must be called before | |
| 240 DetectFlicker(). | |
| 241 | |
| 242 \param[in] timestamp Timestamp of the current frame. | |
| 243 \param[in] stats Statistics of the current frame. | |
| 244 | |
| 245 \return 0: Success\n | |
| 246 2: Detection not possible due to flickering frequency too close to | |
| 247 zero.\n | |
| 248 -1: Error | |
| 249 */ | |
| 250 int32_t VPMDeflickering::PreDetection(const uint32_t timestamp, | |
| 251 const VideoProcessingModule::FrameStats& stats) { | |
| 252 int32_t mean_val; // Mean value of frame (Q4) | |
| 253 uint32_t frame_rate = 0; | |
| 254 int32_t meanBufferLength; // Temp variable. | |
| 255 | |
| 256 mean_val = ((stats.sum << kmean_valueScaling) / stats.num_pixels); | |
| 257 // Update mean value buffer. | |
| 258 // This should be done even though we might end up in an unreliable detection. | |
| 259 memmove(mean_buffer_ + 1, mean_buffer_, | |
| 260 (kMeanBufferLength - 1) * sizeof(int32_t)); | |
| 261 mean_buffer_[0] = mean_val; | |
| 262 | |
| 263 // Update timestamp buffer. | |
| 264 // This should be done even though we might end up in an unreliable detection. | |
| 265 memmove(timestamp_buffer_ + 1, timestamp_buffer_, (kMeanBufferLength - 1) * | |
| 266 sizeof(uint32_t)); | |
| 267 timestamp_buffer_[0] = timestamp; | |
| 268 | |
| 269 /* Compute current frame rate (Q4) */ | |
| 270 if (timestamp_buffer_[kMeanBufferLength - 1] != 0) { | |
| 271 frame_rate = ((90000 << 4) * (kMeanBufferLength - 1)); | |
| 272 frame_rate /= | |
| 273 (timestamp_buffer_[0] - timestamp_buffer_[kMeanBufferLength - 1]); | |
| 274 } else if (timestamp_buffer_[1] != 0) { | |
| 275 frame_rate = (90000 << 4) / (timestamp_buffer_[0] - timestamp_buffer_[1]); | |
| 276 } | |
| 277 | |
| 278 /* Determine required size of mean value buffer (mean_buffer_length_) */ | |
| 279 if (frame_rate == 0) { | |
| 280 meanBufferLength = 1; | |
| 281 } else { | |
| 282 meanBufferLength = | |
| 283 (kNumFlickerBeforeDetect * frame_rate) / kMinFrequencyToDetect; | |
| 284 } | |
| 285 /* Sanity check of buffer length */ | |
| 286 if (meanBufferLength >= kMeanBufferLength) { | |
| 287 /* Too long buffer. The flickering frequency is too close to zero, which | |
| 288 * makes the estimation unreliable. | |
| 289 */ | |
| 290 mean_buffer_length_ = 0; | |
| 291 return 2; | |
| 292 } | |
| 293 mean_buffer_length_ = meanBufferLength; | |
| 294 | |
| 295 if ((timestamp_buffer_[mean_buffer_length_ - 1] != 0) && | |
| 296 (mean_buffer_length_ != 1)) { | |
| 297 frame_rate = ((90000 << 4) * (mean_buffer_length_ - 1)); | |
| 298 frame_rate /= | |
| 299 (timestamp_buffer_[0] - timestamp_buffer_[mean_buffer_length_ - 1]); | |
| 300 } else if (timestamp_buffer_[1] != 0) { | |
| 301 frame_rate = (90000 << 4) / (timestamp_buffer_[0] - timestamp_buffer_[1]); | |
| 302 } | |
| 303 frame_rate_ = frame_rate; | |
| 304 | |
| 305 return VPM_OK; | |
| 306 } | |
| 307 | |
| 308 /** | |
| 309 This function detects flicker in the video stream. As a side effect the | |
| 310 mean value buffer is updated with the new mean value. | |
| 311 | |
| 312 \return 0: No flickering detected\n | |
| 313 1: Flickering detected\n | |
| 314 2: Detection not possible due to unreliable frequency interval | |
| 315 -1: Error | |
| 316 */ | |
| 317 int32_t VPMDeflickering::DetectFlicker() { | |
| 318 uint32_t i; | |
| 319 int32_t freqEst; // (Q4) Frequency estimate to base detection upon | |
| 320 int32_t ret_val = -1; | |
| 321 | |
| 322 /* Sanity check for mean_buffer_length_ */ | |
| 323 if (mean_buffer_length_ < 2) { | |
| 324 /* Not possible to estimate frequency */ | |
| 325 return(2); | |
| 326 } | |
| 327 // Count zero crossings with a dead zone to be robust against noise. If the | |
| 328 // noise std is 2 pixel this corresponds to about 95% confidence interval. | |
| 329 int32_t deadzone = (kZeroCrossingDeadzone << kmean_valueScaling); // Q4 | |
| 330 int32_t meanOfBuffer = 0; // Mean value of mean value buffer. | |
| 331 int32_t numZeros = 0; // Number of zeros that cross the dead-zone. | |
| 332 int32_t cntState = 0; // State variable for zero crossing regions. | |
| 333 int32_t cntStateOld = 0; // Previous state for zero crossing regions. | |
| 334 | |
| 335 for (i = 0; i < mean_buffer_length_; i++) { | |
| 336 meanOfBuffer += mean_buffer_[i]; | |
| 337 } | |
| 338 meanOfBuffer += (mean_buffer_length_ >> 1); // Rounding, not truncation. | |
| 339 meanOfBuffer /= mean_buffer_length_; | |
| 340 | |
| 341 // Count zero crossings. | |
| 342 cntStateOld = (mean_buffer_[0] >= (meanOfBuffer + deadzone)); | |
| 343 cntStateOld -= (mean_buffer_[0] <= (meanOfBuffer - deadzone)); | |
| 344 for (i = 1; i < mean_buffer_length_; i++) { | |
| 345 cntState = (mean_buffer_[i] >= (meanOfBuffer + deadzone)); | |
| 346 cntState -= (mean_buffer_[i] <= (meanOfBuffer - deadzone)); | |
| 347 if (cntStateOld == 0) { | |
| 348 cntStateOld = -cntState; | |
| 349 } | |
| 350 if (((cntState + cntStateOld) == 0) && (cntState != 0)) { | |
| 351 numZeros++; | |
| 352 cntStateOld = cntState; | |
| 353 } | |
| 354 } | |
| 355 // END count zero crossings. | |
| 356 | |
| 357 /* Frequency estimation according to: | |
| 358 * freqEst = numZeros * frame_rate / 2 / mean_buffer_length_; | |
| 359 * | |
| 360 * Resolution is set to Q4 | |
| 361 */ | |
| 362 freqEst = ((numZeros * 90000) << 3); | |
| 363 freqEst /= | |
| 364 (timestamp_buffer_[0] - timestamp_buffer_[mean_buffer_length_ - 1]); | |
| 365 | |
| 366 /* Translate frequency estimate to regions close to 100 and 120 Hz */ | |
| 367 uint8_t freqState = 0; // Current translation state; | |
| 368 // (0) Not in interval, | |
| 369 // (1) Within valid interval, | |
| 370 // (2) Out of range | |
| 371 int32_t freqAlias = freqEst; | |
| 372 if (freqEst > kMinFrequencyToDetect) { | |
| 373 uint8_t aliasState = 1; | |
| 374 while(freqState == 0) { | |
| 375 /* Increase frequency */ | |
| 376 freqAlias += (aliasState * frame_rate_); | |
| 377 freqAlias += ((freqEst << 1) * (1 - (aliasState << 1))); | |
| 378 /* Compute state */ | |
| 379 freqState = (abs(freqAlias - (100 << 4)) <= kFrequencyDeviation); | |
| 380 freqState += (abs(freqAlias - (120 << 4)) <= kFrequencyDeviation); | |
| 381 freqState += 2 * (freqAlias > ((120 << 4) + kFrequencyDeviation)); | |
| 382 /* Switch alias state */ | |
| 383 aliasState++; | |
| 384 aliasState &= 0x01; | |
| 385 } | |
| 386 } | |
| 387 /* Is frequency estimate within detection region? */ | |
| 388 if (freqState == 1) { | |
| 389 ret_val = 1; | |
| 390 } else if (freqState == 0) { | |
| 391 ret_val = 2; | |
| 392 } else { | |
| 393 ret_val = 0; | |
| 394 } | |
| 395 return ret_val; | |
| 396 } | |
| 397 | |
| 398 } // namespace webrtc | |
| OLD | NEW |