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