Index: webrtc/modules/video_processing/deflickering.cc |
diff --git a/webrtc/modules/video_processing/deflickering.cc b/webrtc/modules/video_processing/deflickering.cc |
deleted file mode 100644 |
index 0e936ce9b77a5a203a1a5ae47e4c4362d1e31019..0000000000000000000000000000000000000000 |
--- a/webrtc/modules/video_processing/deflickering.cc |
+++ /dev/null |
@@ -1,402 +0,0 @@ |
-/* |
- * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. |
- * |
- * Use of this source code is governed by a BSD-style license |
- * that can be found in the LICENSE file in the root of the source |
- * tree. An additional intellectual property rights grant can be found |
- * in the file PATENTS. All contributing project authors may |
- * be found in the AUTHORS file in the root of the source tree. |
- */ |
- |
-#include "webrtc/modules/video_processing/deflickering.h" |
- |
-#include <math.h> |
-#include <stdlib.h> |
- |
-#include "webrtc/base/logging.h" |
-#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" |
-#include "webrtc/system_wrappers/include/sort.h" |
- |
-namespace webrtc { |
- |
-// Detection constants |
-// (Q4) Maximum allowed deviation for detection. |
-enum { kFrequencyDeviation = 39 }; |
-// (Q4) Minimum frequency that can be detected. |
-enum { kMinFrequencyToDetect = 32 }; |
-// Number of flickers before we accept detection |
-enum { kNumFlickerBeforeDetect = 2 }; |
-enum { kmean_valueScaling = 4 }; // (Q4) In power of 2 |
-// Dead-zone region in terms of pixel values |
-enum { kZeroCrossingDeadzone = 10 }; |
-// Deflickering constants. |
-// Compute the quantiles over 1 / DownsamplingFactor of the image. |
-enum { kDownsamplingFactor = 8 }; |
-enum { kLog2OfDownsamplingFactor = 3 }; |
- |
-// To generate in Matlab: |
-// >> probUW16 = round(2^11 * |
-// [0.05,0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,0.95,0.97]); |
-// >> fprintf('%d, ', probUW16) |
-// Resolution reduced to avoid overflow when multiplying with the |
-// (potentially) large number of pixels. |
-const uint16_t VPMDeflickering::prob_uw16_[kNumProbs] = { |
- 102, 205, 410, 614, 819, 1024, |
- 1229, 1434, 1638, 1843, 1946, 1987}; // <Q11> |
- |
-// To generate in Matlab: |
-// >> numQuants = 14; maxOnlyLength = 5; |
-// >> weightUW16 = round(2^15 * |
-// [linspace(0.5, 1.0, numQuants - maxOnlyLength)]); |
-// >> fprintf('%d, %d,\n ', weightUW16); |
-const uint16_t VPMDeflickering::weight_uw16_[kNumQuants - kMaxOnlyLength] = { |
- 16384, 18432, 20480, 22528, 24576, 26624, 28672, 30720, 32768}; // <Q15> |
- |
-VPMDeflickering::VPMDeflickering() { |
- Reset(); |
-} |
- |
-VPMDeflickering::~VPMDeflickering() {} |
- |
-void VPMDeflickering::Reset() { |
- mean_buffer_length_ = 0; |
- detection_state_ = 0; |
- frame_rate_ = 0; |
- |
- memset(mean_buffer_, 0, sizeof(int32_t) * kMeanBufferLength); |
- memset(timestamp_buffer_, 0, sizeof(int32_t) * kMeanBufferLength); |
- |
- // Initialize the history with a uniformly distributed histogram. |
- quant_hist_uw8_[0][0] = 0; |
- quant_hist_uw8_[0][kNumQuants - 1] = 255; |
- for (int32_t i = 0; i < kNumProbs; i++) { |
- // Unsigned round. <Q0> |
- quant_hist_uw8_[0][i + 1] = |
- static_cast<uint8_t>((prob_uw16_[i] * 255 + (1 << 10)) >> 11); |
- } |
- |
- for (int32_t i = 1; i < kFrameHistory_size; i++) { |
- memcpy(quant_hist_uw8_[i], quant_hist_uw8_[0], |
- sizeof(uint8_t) * kNumQuants); |
- } |
-} |
- |
-int32_t VPMDeflickering::ProcessFrame(VideoFrame* frame, |
- VideoProcessing::FrameStats* stats) { |
- assert(frame); |
- uint32_t frame_memory; |
- uint8_t quant_uw8[kNumQuants]; |
- uint8_t maxquant_uw8[kNumQuants]; |
- uint8_t minquant_uw8[kNumQuants]; |
- uint16_t target_quant_uw16[kNumQuants]; |
- uint16_t increment_uw16; |
- uint8_t map_uw8[256]; |
- |
- uint16_t tmp_uw16; |
- uint32_t tmp_uw32; |
- int width = frame->width(); |
- int height = frame->height(); |
- |
- if (frame->IsZeroSize()) { |
- return VPM_GENERAL_ERROR; |
- } |
- |
- // Stricter height check due to subsampling size calculation below. |
- if (height < 2) { |
- LOG(LS_ERROR) << "Invalid frame size."; |
- return VPM_GENERAL_ERROR; |
- } |
- |
- if (!VideoProcessing::ValidFrameStats(*stats)) { |
- return VPM_GENERAL_ERROR; |
- } |
- |
- if (PreDetection(frame->timestamp(), *stats) == -1) |
- return VPM_GENERAL_ERROR; |
- |
- // Flicker detection |
- int32_t det_flicker = DetectFlicker(); |
- if (det_flicker < 0) { |
- return VPM_GENERAL_ERROR; |
- } else if (det_flicker != 1) { |
- return 0; |
- } |
- |
- // Size of luminance component. |
- const uint32_t y_size = height * width; |
- |
- const uint32_t y_sub_size = |
- width * (((height - 1) >> kLog2OfDownsamplingFactor) + 1); |
- uint8_t* y_sorted = new uint8_t[y_sub_size]; |
- uint32_t sort_row_idx = 0; |
- for (int i = 0; i < height; i += kDownsamplingFactor) { |
- memcpy(y_sorted + sort_row_idx * width, frame->buffer(kYPlane) + i * width, |
- width); |
- sort_row_idx++; |
- } |
- |
- webrtc::Sort(y_sorted, y_sub_size, webrtc::TYPE_UWord8); |
- |
- uint32_t prob_idx_uw32 = 0; |
- quant_uw8[0] = 0; |
- quant_uw8[kNumQuants - 1] = 255; |
- |
- // Ensure we won't get an overflow below. |
- // In practice, the number of subsampled pixels will not become this large. |
- if (y_sub_size > (1 << 21) - 1) { |
- LOG(LS_ERROR) << "Subsampled number of pixels too large."; |
- return -1; |
- } |
- |
- for (int32_t i = 0; i < kNumProbs; i++) { |
- // <Q0>. |
- prob_idx_uw32 = WEBRTC_SPL_UMUL_32_16(y_sub_size, prob_uw16_[i]) >> 11; |
- quant_uw8[i + 1] = y_sorted[prob_idx_uw32]; |
- } |
- |
- delete[] y_sorted; |
- y_sorted = NULL; |
- |
- // Shift history for new frame. |
- memmove(quant_hist_uw8_[1], quant_hist_uw8_[0], |
- (kFrameHistory_size - 1) * kNumQuants * sizeof(uint8_t)); |
- // Store current frame in history. |
- memcpy(quant_hist_uw8_[0], quant_uw8, kNumQuants * sizeof(uint8_t)); |
- |
- // We use a frame memory equal to the ceiling of half the frame rate to |
- // ensure we capture an entire period of flicker. |
- frame_memory = (frame_rate_ + (1 << 5)) >> 5; // Unsigned ceiling. <Q0> |
- // frame_rate_ in Q4. |
- if (frame_memory > kFrameHistory_size) { |
- frame_memory = kFrameHistory_size; |
- } |
- |
- // Get maximum and minimum. |
- for (int32_t i = 0; i < kNumQuants; i++) { |
- maxquant_uw8[i] = 0; |
- minquant_uw8[i] = 255; |
- for (uint32_t j = 0; j < frame_memory; j++) { |
- if (quant_hist_uw8_[j][i] > maxquant_uw8[i]) { |
- maxquant_uw8[i] = quant_hist_uw8_[j][i]; |
- } |
- |
- if (quant_hist_uw8_[j][i] < minquant_uw8[i]) { |
- minquant_uw8[i] = quant_hist_uw8_[j][i]; |
- } |
- } |
- } |
- |
- // Get target quantiles. |
- for (int32_t i = 0; i < kNumQuants - kMaxOnlyLength; i++) { |
- // target = w * maxquant_uw8 + (1 - w) * minquant_uw8 |
- // Weights w = |weight_uw16_| are in Q15, hence the final output has to be |
- // right shifted by 8 to end up in Q7. |
- target_quant_uw16[i] = static_cast<uint16_t>( |
- (weight_uw16_[i] * maxquant_uw8[i] + |
- ((1 << 15) - weight_uw16_[i]) * minquant_uw8[i]) >> |
- 8); // <Q7> |
- } |
- |
- for (int32_t i = kNumQuants - kMaxOnlyLength; i < kNumQuants; i++) { |
- target_quant_uw16[i] = ((uint16_t)maxquant_uw8[i]) << 7; |
- } |
- |
- // Compute the map from input to output pixels. |
- uint16_t mapUW16; // <Q7> |
- for (int32_t i = 1; i < kNumQuants; i++) { |
- // As quant and targetQuant are limited to UWord8, it's safe to use Q7 here. |
- tmp_uw32 = |
- static_cast<uint32_t>(target_quant_uw16[i] - target_quant_uw16[i - 1]); |
- tmp_uw16 = static_cast<uint16_t>(quant_uw8[i] - quant_uw8[i - 1]); // <Q0> |
- |
- if (tmp_uw16 > 0) { |
- increment_uw16 = |
- static_cast<uint16_t>(WebRtcSpl_DivU32U16(tmp_uw32, |
- tmp_uw16)); // <Q7> |
- } else { |
- // The value is irrelevant; the loop below will only iterate once. |
- increment_uw16 = 0; |
- } |
- |
- mapUW16 = target_quant_uw16[i - 1]; |
- for (uint32_t j = quant_uw8[i - 1]; j < (uint32_t)(quant_uw8[i] + 1); j++) { |
- // Unsigned round. <Q0> |
- map_uw8[j] = (uint8_t)((mapUW16 + (1 << 6)) >> 7); |
- mapUW16 += increment_uw16; |
- } |
- } |
- |
- // Map to the output frame. |
- uint8_t* buffer = frame->buffer(kYPlane); |
- for (uint32_t i = 0; i < y_size; i++) { |
- buffer[i] = map_uw8[buffer[i]]; |
- } |
- |
- // Frame was altered, so reset stats. |
- VideoProcessing::ClearFrameStats(stats); |
- |
- return VPM_OK; |
-} |
- |
-/** |
- Performs some pre-detection operations. Must be called before |
- DetectFlicker(). |
- |
- \param[in] timestamp Timestamp of the current frame. |
- \param[in] stats Statistics of the current frame. |
- |
- \return 0: Success\n |
- 2: Detection not possible due to flickering frequency too close to |
- zero.\n |
- -1: Error |
-*/ |
-int32_t VPMDeflickering::PreDetection( |
- const uint32_t timestamp, |
- const VideoProcessing::FrameStats& stats) { |
- int32_t mean_val; // Mean value of frame (Q4) |
- uint32_t frame_rate = 0; |
- int32_t meanBufferLength; // Temp variable. |
- |
- mean_val = ((stats.sum << kmean_valueScaling) / stats.num_pixels); |
- // Update mean value buffer. |
- // This should be done even though we might end up in an unreliable detection. |
- memmove(mean_buffer_ + 1, mean_buffer_, |
- (kMeanBufferLength - 1) * sizeof(int32_t)); |
- mean_buffer_[0] = mean_val; |
- |
- // Update timestamp buffer. |
- // This should be done even though we might end up in an unreliable detection. |
- memmove(timestamp_buffer_ + 1, timestamp_buffer_, |
- (kMeanBufferLength - 1) * sizeof(uint32_t)); |
- timestamp_buffer_[0] = timestamp; |
- |
- /* Compute current frame rate (Q4) */ |
- if (timestamp_buffer_[kMeanBufferLength - 1] != 0) { |
- frame_rate = ((90000 << 4) * (kMeanBufferLength - 1)); |
- frame_rate /= |
- (timestamp_buffer_[0] - timestamp_buffer_[kMeanBufferLength - 1]); |
- } else if (timestamp_buffer_[1] != 0) { |
- frame_rate = (90000 << 4) / (timestamp_buffer_[0] - timestamp_buffer_[1]); |
- } |
- |
- /* Determine required size of mean value buffer (mean_buffer_length_) */ |
- if (frame_rate == 0) { |
- meanBufferLength = 1; |
- } else { |
- meanBufferLength = |
- (kNumFlickerBeforeDetect * frame_rate) / kMinFrequencyToDetect; |
- } |
- /* Sanity check of buffer length */ |
- if (meanBufferLength >= kMeanBufferLength) { |
- /* Too long buffer. The flickering frequency is too close to zero, which |
- * makes the estimation unreliable. |
- */ |
- mean_buffer_length_ = 0; |
- return 2; |
- } |
- mean_buffer_length_ = meanBufferLength; |
- |
- if ((timestamp_buffer_[mean_buffer_length_ - 1] != 0) && |
- (mean_buffer_length_ != 1)) { |
- frame_rate = ((90000 << 4) * (mean_buffer_length_ - 1)); |
- frame_rate /= |
- (timestamp_buffer_[0] - timestamp_buffer_[mean_buffer_length_ - 1]); |
- } else if (timestamp_buffer_[1] != 0) { |
- frame_rate = (90000 << 4) / (timestamp_buffer_[0] - timestamp_buffer_[1]); |
- } |
- frame_rate_ = frame_rate; |
- |
- return VPM_OK; |
-} |
- |
-/** |
- This function detects flicker in the video stream. As a side effect the |
- mean value buffer is updated with the new mean value. |
- |
- \return 0: No flickering detected\n |
- 1: Flickering detected\n |
- 2: Detection not possible due to unreliable frequency interval |
- -1: Error |
-*/ |
-int32_t VPMDeflickering::DetectFlicker() { |
- uint32_t i; |
- int32_t freqEst; // (Q4) Frequency estimate to base detection upon |
- int32_t ret_val = -1; |
- |
- /* Sanity check for mean_buffer_length_ */ |
- if (mean_buffer_length_ < 2) { |
- /* Not possible to estimate frequency */ |
- return 2; |
- } |
- // Count zero crossings with a dead zone to be robust against noise. If the |
- // noise std is 2 pixel this corresponds to about 95% confidence interval. |
- int32_t deadzone = (kZeroCrossingDeadzone << kmean_valueScaling); // Q4 |
- int32_t meanOfBuffer = 0; // Mean value of mean value buffer. |
- int32_t numZeros = 0; // Number of zeros that cross the dead-zone. |
- int32_t cntState = 0; // State variable for zero crossing regions. |
- int32_t cntStateOld = 0; // Previous state for zero crossing regions. |
- |
- for (i = 0; i < mean_buffer_length_; i++) { |
- meanOfBuffer += mean_buffer_[i]; |
- } |
- meanOfBuffer += (mean_buffer_length_ >> 1); // Rounding, not truncation. |
- meanOfBuffer /= mean_buffer_length_; |
- |
- // Count zero crossings. |
- cntStateOld = (mean_buffer_[0] >= (meanOfBuffer + deadzone)); |
- cntStateOld -= (mean_buffer_[0] <= (meanOfBuffer - deadzone)); |
- for (i = 1; i < mean_buffer_length_; i++) { |
- cntState = (mean_buffer_[i] >= (meanOfBuffer + deadzone)); |
- cntState -= (mean_buffer_[i] <= (meanOfBuffer - deadzone)); |
- if (cntStateOld == 0) { |
- cntStateOld = -cntState; |
- } |
- if (((cntState + cntStateOld) == 0) && (cntState != 0)) { |
- numZeros++; |
- cntStateOld = cntState; |
- } |
- } |
- // END count zero crossings. |
- |
- /* Frequency estimation according to: |
- * freqEst = numZeros * frame_rate / 2 / mean_buffer_length_; |
- * |
- * Resolution is set to Q4 |
- */ |
- freqEst = ((numZeros * 90000) << 3); |
- freqEst /= |
- (timestamp_buffer_[0] - timestamp_buffer_[mean_buffer_length_ - 1]); |
- |
- /* Translate frequency estimate to regions close to 100 and 120 Hz */ |
- uint8_t freqState = 0; // Current translation state; |
- // (0) Not in interval, |
- // (1) Within valid interval, |
- // (2) Out of range |
- int32_t freqAlias = freqEst; |
- if (freqEst > kMinFrequencyToDetect) { |
- uint8_t aliasState = 1; |
- while (freqState == 0) { |
- /* Increase frequency */ |
- freqAlias += (aliasState * frame_rate_); |
- freqAlias += ((freqEst << 1) * (1 - (aliasState << 1))); |
- /* Compute state */ |
- freqState = (abs(freqAlias - (100 << 4)) <= kFrequencyDeviation); |
- freqState += (abs(freqAlias - (120 << 4)) <= kFrequencyDeviation); |
- freqState += 2 * (freqAlias > ((120 << 4) + kFrequencyDeviation)); |
- /* Switch alias state */ |
- aliasState++; |
- aliasState &= 0x01; |
- } |
- } |
- /* Is frequency estimate within detection region? */ |
- if (freqState == 1) { |
- ret_val = 1; |
- } else if (freqState == 0) { |
- ret_val = 2; |
- } else { |
- ret_val = 0; |
- } |
- return ret_val; |
-} |
- |
-} // namespace webrtc |