Reformatting VPM: First step - No functional changes.

R=marpan@google.com, marpan@webrtc.org

Review URL: https://webrtc-codereview.appspot.com/2333004

git-svn-id: http://webrtc.googlecode.com/svn/trunk@4912 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
mikhal@webrtc.org 2013-10-03 16:42:41 +00:00
parent 26f78f7ecb
commit b43d8078a1
31 changed files with 2335 additions and 2809 deletions

View File

@ -249,7 +249,7 @@ QualityModesTest::Perform(const CmdArgs& args)
VideoContentMetrics* contentMetrics = NULL;
// setting user frame rate
_vpm->SetMaxFrameRate((uint32_t)(_nativeFrameRate+ 0.5f));
_vpm->SetMaxFramerate((uint32_t)(_nativeFrameRate+ 0.5f));
// for starters: keeping native values:
_vpm->SetTargetResolution(_width, _height,
(uint32_t)(_frameRate+ 0.5f));

View File

@ -24,281 +24,274 @@
#include "webrtc/modules/video_processing/main/interface/video_processing_defines.h"
/**
The module is largely intended to process video streams, except functionality
The module is largely intended to process video streams, except functionality
provided by static functions which operate independent of previous frames. It
is recommended, but not required that a unique instance be used for each
is recommended, but not required that a unique instance be used for each
concurrently processed stream. Similarly, it is recommended to call Reset()
before switching to a new stream, but this is not absolutely required.
The module provides basic thread safety by permitting only a single function
to execute concurrently.
*/
namespace webrtc {
class VideoProcessingModule : public Module
{
public:
/**
Structure to hold frame statistics. Populate it with GetFrameStats().
*/
struct FrameStats
{
FrameStats() :
mean(0),
sum(0),
numPixels(0),
subSamplWidth(0),
subSamplHeight(0)
{
memset(hist, 0, sizeof(hist));
}
uint32_t hist[256]; /**< Histogram of frame */
uint32_t mean; /**< Mean value of frame */
uint32_t sum; /**< Sum of frame */
uint32_t numPixels; /**< Number of pixels */
uint8_t subSamplWidth; /**< Subsampling rate of width in powers
of 2 */
uint8_t subSamplHeight; /**< Subsampling rate of height in powers
of 2 */
};
/**
Specifies the warning types returned by BrightnessDetection().
*/
enum BrightnessWarning
{
kNoWarning, /**< Frame has acceptable brightness */
kDarkWarning, /**< Frame is too dark */
kBrightWarning /**< Frame is too bright */
};
/*
Creates a VPM object.
\param[in] id
Unique identifier of this object.
\return Pointer to a VPM object.
*/
static VideoProcessingModule* Create(int32_t id);
/**
Destroys a VPM object.
\param[in] module
Pointer to the VPM object to destroy.
*/
static void Destroy(VideoProcessingModule* module);
/**
Not supported.
*/
virtual int32_t TimeUntilNextProcess() { return -1; }
/**
Not supported.
*/
virtual int32_t Process() { return -1; }
/**
Resets all processing components to their initial states. This should be
called whenever a new video stream is started.
*/
virtual void Reset() = 0;
/**
Retrieves statistics for the input frame. This function must be used to
prepare a FrameStats struct for use in certain VPM functions.
\param[out] stats
The frame statistics will be stored here on return.
\param[in] frame
Reference to the video frame.
\return 0 on success, -1 on failure.
*/
static int32_t GetFrameStats(FrameStats* stats,
const I420VideoFrame& frame);
/**
Checks the validity of a FrameStats struct. Currently, valid implies only
that is had changed from its initialized state.
\param[in] stats
Frame statistics.
\return True on valid stats, false on invalid stats.
*/
static bool ValidFrameStats(const FrameStats& stats);
/**
Returns a FrameStats struct to its intialized state.
\param[in,out] stats
Frame statistics.
*/
static void ClearFrameStats(FrameStats* stats);
/**
Enhances the color of an image through a constant mapping. Only the
chrominance is altered. Has a fixed-point implementation.
\param[in,out] frame
Pointer to the video frame.
*/
static int32_t ColorEnhancement(I420VideoFrame* frame);
/**
Increases/decreases the luminance value.
\param[in,out] frame
Pointer to the video frame.
\param[in] delta
The amount to change the chrominance value of every single pixel.
Can be < 0 also.
\return 0 on success, -1 on failure.
*/
static int32_t Brighten(I420VideoFrame* frame, int delta);
/**
Detects and removes camera flicker from a video stream. Every frame from
the stream must be passed in. A frame will only be altered if flicker has
been detected. Has a fixed-point implementation.
\param[in,out] frame
Pointer to the video frame.
\param[in,out] stats
Frame statistics provided by GetFrameStats(). On return the stats will
be reset to zero if the frame was altered. Call GetFrameStats() again
if the statistics for the altered frame are required.
\return 0 on success, -1 on failure.
*/
virtual int32_t Deflickering(I420VideoFrame* frame, FrameStats* stats) = 0;
/**
Denoises a video frame. Every frame from the stream should be passed in.
Has a fixed-point implementation.
\param[in,out] frame
Pointer to the video frame.
\return The number of modified pixels on success, -1 on failure.
*/
virtual int32_t Denoising(I420VideoFrame* frame) = 0;
/**
Detects if a video frame is excessively bright or dark. Returns a
warning if this is the case. Multiple frames should be passed in before
expecting a warning. Has a floating-point implementation.
\param[in] frame
Pointer to the video frame.
\param[in] stats
Frame statistics provided by GetFrameStats().
\return A member of BrightnessWarning on success, -1 on error
*/
virtual int32_t BrightnessDetection(const I420VideoFrame& frame,
const FrameStats& stats) = 0;
/**
The following functions refer to the pre-processor unit within VPM. The
pre-processor perfoms spatial/temporal decimation and content analysis on
the frames prior to encoding.
*/
/**
Enable/disable temporal decimation
\param[in] enable when true, temporal decimation is enabled
*/
virtual void EnableTemporalDecimation(bool enable) = 0;
/**
Set target resolution
\param[in] width
Target width
\param[in] height
Target height
\param[in] frameRate
Target frameRate
\return VPM_OK on success, a negative value on error (see error codes)
*/
virtual int32_t SetTargetResolution(uint32_t width,
uint32_t height,
uint32_t frameRate) = 0;
/**
Set max frame rate
\param[in] maxFrameRate: maximum frame rate (limited to native frame rate)
\return VPM_OK on success, a negative value on error (see error codes)
*/
virtual int32_t SetMaxFrameRate(uint32_t maxFrameRate) = 0;
/**
Get decimated(target) frame rate
*/
virtual uint32_t DecimatedFrameRate() = 0;
/**
Get decimated(target) frame width
*/
virtual uint32_t DecimatedWidth() const = 0;
/**
Get decimated(target) frame height
*/
virtual uint32_t DecimatedHeight() const = 0 ;
/**
Set the spatial resampling settings of the VPM: The resampler may either be
disabled or one of the following:
scaling to a close to target dimension followed by crop/pad
\param[in] resamplingMode
Set resampling mode (a member of VideoFrameResampling)
*/
virtual void SetInputFrameResampleMode(VideoFrameResampling
resamplingMode) = 0;
/**
Get Processed (decimated) frame
\param[in] frame pointer to the video frame.
\param[in] processedFrame pointer (double) to the processed frame. If no
processing is required, processedFrame will be NULL.
\return VPM_OK on success, a negative value on error (see error codes)
*/
virtual int32_t PreprocessFrame(const I420VideoFrame& frame,
I420VideoFrame** processedFrame) = 0;
/**
Return content metrics for the last processed frame
*/
virtual VideoContentMetrics* ContentMetrics() const = 0 ;
/**
Enable content analysis
*/
virtual void EnableContentAnalysis(bool enable) = 0;
class VideoProcessingModule : public Module {
public:
/**
Structure to hold frame statistics. Populate it with GetFrameStats().
*/
struct FrameStats {
FrameStats() :
mean(0),
sum(0),
num_pixels(0),
subSamplWidth(0),
subSamplHeight(0) {
memset(hist, 0, sizeof(hist));
}
uint32_t hist[256]; // FRame histogram.
uint32_t mean; // Frame Mean value.
uint32_t sum; // Sum of frame.
uint32_t num_pixels; // Number of pixels.
uint8_t subSamplWidth; // Subsampling rate of width in powers of 2.
uint8_t subSamplHeight; // Subsampling rate of height in powers of 2.
};
} // namespace
/**
Specifies the warning types returned by BrightnessDetection().
*/
enum BrightnessWarning {
kNoWarning, // Frame has acceptable brightness.
kDarkWarning, // Frame is too dark.
kBrightWarning // Frame is too bright.
};
#endif
/*
Creates a VPM object.
\param[in] id
Unique identifier of this object.
\return Pointer to a VPM object.
*/
static VideoProcessingModule* Create(int32_t id);
/**
Destroys a VPM object.
\param[in] module
Pointer to the VPM object to destroy.
*/
static void Destroy(VideoProcessingModule* module);
/**
Not supported.
*/
virtual int32_t TimeUntilNextProcess() { return -1; }
/**
Not supported.
*/
virtual int32_t Process() { return -1; }
/**
Resets all processing components to their initial states. This should be
called whenever a new video stream is started.
*/
virtual void Reset() = 0;
/**
Retrieves statistics for the input frame. This function must be used to
prepare a FrameStats struct for use in certain VPM functions.
\param[out] stats
The frame statistics will be stored here on return.
\param[in] frame
Reference to the video frame.
\return 0 on success, -1 on failure.
*/
static int32_t GetFrameStats(FrameStats* stats,
const I420VideoFrame& frame);
/**
Checks the validity of a FrameStats struct. Currently, valid implies only
that is had changed from its initialized state.
\param[in] stats
Frame statistics.
\return True on valid stats, false on invalid stats.
*/
static bool ValidFrameStats(const FrameStats& stats);
/**
Returns a FrameStats struct to its intialized state.
\param[in,out] stats
Frame statistics.
*/
static void ClearFrameStats(FrameStats* stats);
/**
Enhances the color of an image through a constant mapping. Only the
chrominance is altered. Has a fixed-point implementation.
\param[in,out] frame
Pointer to the video frame.
*/
static int32_t ColorEnhancement(I420VideoFrame* frame);
/**
Increases/decreases the luminance value.
\param[in,out] frame
Pointer to the video frame.
\param[in] delta
The amount to change the chrominance value of every single pixel.
Can be < 0 also.
\return 0 on success, -1 on failure.
*/
static int32_t Brighten(I420VideoFrame* frame, int delta);
/**
Detects and removes camera flicker from a video stream. Every frame from
the stream must be passed in. A frame will only be altered if flicker has
been detected. Has a fixed-point implementation.
\param[in,out] frame
Pointer to the video frame.
\param[in,out] stats
Frame statistics provided by GetFrameStats(). On return the stats will
be reset to zero if the frame was altered. Call GetFrameStats() again
if the statistics for the altered frame are required.
\return 0 on success, -1 on failure.
*/
virtual int32_t Deflickering(I420VideoFrame* frame, FrameStats* stats) = 0;
/**
Denoises a video frame. Every frame from the stream should be passed in.
Has a fixed-point implementation.
\param[in,out] frame
Pointer to the video frame.
\return The number of modified pixels on success, -1 on failure.
*/
virtual int32_t Denoising(I420VideoFrame* frame) = 0;
/**
Detects if a video frame is excessively bright or dark. Returns a
warning if this is the case. Multiple frames should be passed in before
expecting a warning. Has a floating-point implementation.
\param[in] frame
Pointer to the video frame.
\param[in] stats
Frame statistics provided by GetFrameStats().
\return A member of BrightnessWarning on success, -1 on error
*/
virtual int32_t BrightnessDetection(const I420VideoFrame& frame,
const FrameStats& stats) = 0;
/**
The following functions refer to the pre-processor unit within VPM. The
pre-processor perfoms spatial/temporal decimation and content analysis on
the frames prior to encoding.
*/
/**
Enable/disable temporal decimation
\param[in] enable when true, temporal decimation is enabled
*/
virtual void EnableTemporalDecimation(bool enable) = 0;
/**
Set target resolution
\param[in] width
Target width
\param[in] height
Target height
\param[in] frame_rate
Target frame_rate
\return VPM_OK on success, a negative value on error (see error codes)
*/
virtual int32_t SetTargetResolution(uint32_t width,
uint32_t height,
uint32_t frame_rate) = 0;
/**
Set max frame rate
\param[in] max_frame_rate: maximum frame rate (limited to native frame rate)
\return VPM_OK on success, a negative value on error (see error codes)
*/
virtual int32_t SetMaxFramerate(uint32_t max_frame_rate) = 0;
/**
Get decimated(target) frame rate
*/
virtual uint32_t Decimatedframe_rate() = 0;
/**
Get decimated(target) frame width
*/
virtual uint32_t DecimatedWidth() const = 0;
/**
Get decimated(target) frame height
*/
virtual uint32_t DecimatedHeight() const = 0 ;
/**
Set the spatial resampling settings of the VPM: The resampler may either be
disabled or one of the following:
scaling to a close to target dimension followed by crop/pad
\param[in] resampling_mode
Set resampling mode (a member of VideoFrameResampling)
*/
virtual void SetInputFrameResampleMode(VideoFrameResampling
resampling_mode) = 0;
/**
Get Processed (decimated) frame
\param[in] frame pointer to the video frame.
\param[in] processed_frame pointer (double) to the processed frame. If no
processing is required, processed_frame will be NULL.
\return VPM_OK on success, a negative value on error (see error codes)
*/
virtual int32_t PreprocessFrame(const I420VideoFrame& frame,
I420VideoFrame** processed_frame) = 0;
/**
Return content metrics for the last processed frame
*/
virtual VideoContentMetrics* ContentMetrics() const = 0 ;
/**
Enable content analysis
*/
virtual void EnableContentAnalysis(bool enable) = 0;
};
} // namespace webrtc
#endif // WEBRTC_MODULES_INTERFACE_VIDEO_PROCESSING_H

View File

@ -29,15 +29,13 @@ namespace webrtc {
#define VPM_UNINITIALIZED -5
#define VPM_UNIMPLEMENTED -6
enum VideoFrameResampling
{
// TODO: Do we still need crop/pad?
kNoRescaling, // disables rescaling
kFastRescaling, // point
kBiLinear, // bi-linear interpolation
kBox, // Box inteprolation
enum VideoFrameResampling {
kNoRescaling, // Disables rescaling.
kFastRescaling, // Point filter.
kBiLinear, // Bi-linear interpolation.
kBox, // Box inteprolation.
};
} // namespace
} // namespace webrtc
#endif
#endif // WEBRTC_MODULES_INTERFACE_VIDEO_PROCESSING_DEFINES_H

View File

@ -31,19 +31,19 @@ int32_t Brighten(I420VideoFrame* frame, int delta) {
return VPM_PARAMETER_ERROR;
}
int numPixels = frame->width() * frame->height();
int num_pixels = frame->width() * frame->height();
int lookUp[256];
int look_up[256];
for (int i = 0; i < 256; i++) {
int val = i + delta;
lookUp[i] = ((((val < 0) ? 0 : val) > 255) ? 255 : val);
look_up[i] = ((((val < 0) ? 0 : val) > 255) ? 255 : val);
}
uint8_t* tempPtr = frame->buffer(kYPlane);
uint8_t* temp_ptr = frame->buffer(kYPlane);
for (int i = 0; i < numPixels; i++) {
*tempPtr = static_cast<uint8_t>(lookUp[*tempPtr]);
tempPtr++;
for (int i = 0; i < num_pixels; i++) {
*temp_ptr = static_cast<uint8_t>(look_up[*temp_ptr]);
temp_ptr++;
}
return VPM_OK;
}

View File

@ -17,180 +17,128 @@
namespace webrtc {
VPMBrightnessDetection::VPMBrightnessDetection() :
_id(0)
{
Reset();
id_(0) {
Reset();
}
VPMBrightnessDetection::~VPMBrightnessDetection()
{
VPMBrightnessDetection::~VPMBrightnessDetection() {}
int32_t VPMBrightnessDetection::ChangeUniqueId(const int32_t id) {
id_ = id;
return VPM_OK;
}
int32_t
VPMBrightnessDetection::ChangeUniqueId(const int32_t id)
{
_id = id;
return VPM_OK;
void VPMBrightnessDetection::Reset() {
frame_cnt_bright_ = 0;
frame_cnt_dark_ = 0;
}
void
VPMBrightnessDetection::Reset()
{
_frameCntBright = 0;
_frameCntDark = 0;
}
int32_t VPMBrightnessDetection::ProcessFrame(
const I420VideoFrame& frame,
const VideoProcessingModule::FrameStats& stats) {
if (frame.IsZeroSize()) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoPreocessing, id_,
"Null frame pointer");
return VPM_PARAMETER_ERROR;
}
int width = frame.width();
int height = frame.height();
int32_t
VPMBrightnessDetection::ProcessFrame(const I420VideoFrame& frame,
const VideoProcessingModule::FrameStats&
stats)
{
if (frame.IsZeroSize())
{
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoPreocessing, _id,
"Null frame pointer");
return VPM_PARAMETER_ERROR;
}
int width = frame.width();
int height = frame.height();
if (!VideoProcessingModule::ValidFrameStats(stats)) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoPreocessing, id_,
"Invalid frame stats");
return VPM_PARAMETER_ERROR;
}
if (!VideoProcessingModule::ValidFrameStats(stats))
{
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoPreocessing, _id,
"Invalid frame stats");
return VPM_PARAMETER_ERROR;
}
const uint8_t frame_cnt_alarm = 2;
const uint8_t frameCntAlarm = 2;
// Get proportion in lowest bins.
uint8_t low_th = 20;
float prop_low = 0;
for (uint32_t i = 0; i < low_th; i++) {
prop_low += stats.hist[i];
}
prop_low /= stats.num_pixels;
// Get proportion in lowest bins
uint8_t lowTh = 20;
float propLow = 0;
for (uint32_t i = 0; i < lowTh; i++)
{
propLow += stats.hist[i];
}
propLow /= stats.numPixels;
// Get proportion in highest bins.
unsigned char high_th = 230;
float prop_high = 0;
for (uint32_t i = high_th; i < 256; i++) {
prop_high += stats.hist[i];
}
prop_high /= stats.num_pixels;
// Get proportion in highest bins
unsigned char highTh = 230;
float propHigh = 0;
for (uint32_t i = highTh; i < 256; i++)
{
propHigh += stats.hist[i];
}
propHigh /= stats.numPixels;
if (prop_high < 0.4) {
if (stats.mean < 90 || stats.mean > 170) {
// Standard deviation of Y
const uint8_t* buffer = frame.buffer(kYPlane);
float std_y = 0;
for (int h = 0; h < height; h += (1 << stats.subSamplHeight)) {
int row = h*width;
for (int w = 0; w < width; w += (1 << stats.subSamplWidth)) {
std_y += (buffer[w + row] - stats.mean) * (buffer[w + row] -
stats.mean);
}
}
std_y = sqrt(std_y / stats.num_pixels);
if(propHigh < 0.4)
{
if (stats.mean < 90 || stats.mean > 170)
{
// Standard deviation of Y
const uint8_t* buffer = frame.buffer(kYPlane);
float stdY = 0;
for (int h = 0; h < height; h += (1 << stats.subSamplHeight))
{
int row = h*width;
for (int w = 0; w < width; w += (1 << stats.subSamplWidth))
{
stdY += (buffer[w + row] - stats.mean) * (buffer[w + row] -
stats.mean);
}
}
stdY = sqrt(stdY / stats.numPixels);
// Get percentiles
uint32_t sum = 0;
uint32_t medianY = 140;
uint32_t perc05 = 0;
uint32_t perc95 = 255;
float posPerc05 = stats.numPixels * 0.05f;
float posMedian = stats.numPixels * 0.5f;
float posPerc95 = stats.numPixels * 0.95f;
for (uint32_t i = 0; i < 256; i++)
{
sum += stats.hist[i];
if (sum < posPerc05)
{
perc05 = i; // 5th perc
}
if (sum < posMedian)
{
medianY = i; // 50th perc
}
if (sum < posPerc95)
{
perc95 = i; // 95th perc
}
else
{
break;
}
}
// Check if image is too dark
if ((stdY < 55) && (perc05 < 50))
{
if (medianY < 60 || stats.mean < 80 || perc95 < 130 ||
propLow > 0.20)
{
_frameCntDark++;
}
else
{
_frameCntDark = 0;
}
}
else
{
_frameCntDark = 0;
}
// Check if image is too bright
if ((stdY < 52) && (perc95 > 200) && (medianY > 160))
{
if (medianY > 185 || stats.mean > 185 || perc05 > 140 ||
propHigh > 0.25)
{
_frameCntBright++;
}
else
{
_frameCntBright = 0;
}
}
else
{
_frameCntBright = 0;
}
}
// Get percentiles.
uint32_t sum = 0;
uint32_t median_y = 140;
uint32_t perc05 = 0;
uint32_t perc95 = 255;
float pos_perc05 = stats.num_pixels * 0.05f;
float pos_median = stats.num_pixels * 0.5f;
float posPerc95 = stats.num_pixels * 0.95f;
for (uint32_t i = 0; i < 256; i++) {
sum += stats.hist[i];
if (sum < pos_perc05) perc05 = i; // 5th perc.
if (sum < pos_median) median_y = i; // 50th perc.
if (sum < posPerc95)
perc95 = i; // 95th perc.
else
{
_frameCntDark = 0;
_frameCntBright = 0;
break;
}
// Check if image is too dark
if ((std_y < 55) && (perc05 < 50)) {
if (median_y < 60 || stats.mean < 80 || perc95 < 130 ||
prop_low > 0.20) {
frame_cnt_dark_++;
} else {
frame_cnt_dark_ = 0;
}
} else {
frame_cnt_dark_ = 0;
}
}
else
{
_frameCntBright++;
_frameCntDark = 0;
}
if (_frameCntDark > frameCntAlarm)
{
return VideoProcessingModule::kDarkWarning;
}
else if (_frameCntBright > frameCntAlarm)
{
return VideoProcessingModule::kBrightWarning;
}
else
{
return VideoProcessingModule::kNoWarning;
// Check if image is too bright
if ((std_y < 52) && (perc95 > 200) && (median_y > 160)) {
if (median_y > 185 || stats.mean > 185 || perc05 > 140 ||
prop_high > 0.25) {
frame_cnt_bright_++;
} else {
frame_cnt_bright_ = 0;
}
} else {
frame_cnt_bright_ = 0;
}
} else {
frame_cnt_dark_ = 0;
frame_cnt_bright_ = 0;
}
} else {
frame_cnt_bright_++;
frame_cnt_dark_ = 0;
}
if (frame_cnt_dark_ > frame_cnt_alarm) {
return VideoProcessingModule::kDarkWarning;
} else if (frame_cnt_bright_ > frame_cnt_alarm) {
return VideoProcessingModule::kBrightWarning;
} else {
return VideoProcessingModule::kNoWarning;
}
}
} // namespace
} // namespace webrtc

View File

@ -11,34 +11,30 @@
/*
* brightness_detection.h
*/
#ifndef VPM_BRIGHTNESS_DETECTION_H
#define VPM_BRIGHTNESS_DETECTION_H
#ifndef MODULES_VIDEO_PROCESSING_MAIN_SOURCE_BRIGHTNESS_DETECTION_H
#define MODULES_VIDEO_PROCESSING_MAIN_SOURCE_BRIGHTNESS_DETECTION_H
#include "webrtc/modules/video_processing/main/interface/video_processing.h"
#include "webrtc/typedefs.h"
namespace webrtc {
class VPMBrightnessDetection
{
public:
VPMBrightnessDetection();
~VPMBrightnessDetection();
class VPMBrightnessDetection {
public:
VPMBrightnessDetection();
~VPMBrightnessDetection();
int32_t ChangeUniqueId(int32_t id);
int32_t ChangeUniqueId(int32_t id);
void Reset();
int32_t ProcessFrame(const I420VideoFrame& frame,
const VideoProcessingModule::FrameStats& stats);
void Reset();
private:
int32_t id_;
int32_t ProcessFrame(const I420VideoFrame& frame,
const VideoProcessingModule::FrameStats& stats);
private:
int32_t _id;
uint32_t _frameCntBright;
uint32_t _frameCntDark;
uint32_t frame_cnt_bright_;
uint32_t frame_cnt_dark_;
};
} // namespace
} // namespace webrtc
#endif // VPM_BRIGHTNESS_DETECTION_H
#endif // MODULES_VIDEO_PROCESSING_MAIN_SOURCE_BRIGHTNESS_DETECTION_H

View File

@ -16,50 +16,42 @@
namespace webrtc {
namespace VideoProcessing
{
int32_t
ColorEnhancement(I420VideoFrame* frame)
{
assert(frame);
// pointers to U and V color pixels
uint8_t* ptrU;
uint8_t* ptrV;
uint8_t tempChroma;
namespace VideoProcessing {
int32_t ColorEnhancement(I420VideoFrame* frame) {
assert(frame);
// Pointers to U and V color pixels.
uint8_t* ptr_u;
uint8_t* ptr_v;
uint8_t temp_chroma;
if (frame->IsZeroSize()) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoPreocessing,
-1, "Null frame pointer");
return VPM_GENERAL_ERROR;
}
if (frame->IsZeroSize())
{
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoPreocessing,
-1, "Null frame pointer");
return VPM_GENERAL_ERROR;
}
if (frame->width() == 0 || frame->height() == 0) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoPreocessing,
-1, "Invalid frame size");
return VPM_GENERAL_ERROR;
}
if (frame->width() == 0 || frame->height() == 0)
{
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoPreocessing,
-1, "Invalid frame size");
return VPM_GENERAL_ERROR;
}
// set pointers to first U and V pixels (skip luminance)
ptr_u = frame->buffer(kUPlane);
ptr_v = frame->buffer(kVPlane);
int size_uv = ((frame->width() + 1) / 2) * ((frame->height() + 1) / 2);
// set pointers to first U and V pixels (skip luminance)
ptrU = frame->buffer(kUPlane);
ptrV = frame->buffer(kVPlane);
int size_uv = ((frame->width() + 1) / 2) * ((frame->height() + 1) / 2);
// Loop through all chrominance pixels and modify color
for (int ix = 0; ix < size_uv; ix++) {
temp_chroma = colorTable[*ptr_u][*ptr_v];
*ptr_v = colorTable[*ptr_v][*ptr_u];
*ptr_u = temp_chroma;
// loop through all chrominance pixels and modify color
for (int ix = 0; ix < size_uv; ix++)
{
tempChroma = colorTable[*ptrU][*ptrV];
*ptrV = colorTable[*ptrV][*ptrU];
*ptrU = tempChroma;
// increment pointers
ptrU++;
ptrV++;
}
return VPM_OK;
}
ptr_u++;
ptr_v++;
}
return VPM_OK;
}
} // namespace
} // namespace VideoProcessing
} // namespace webrtc

View File

@ -11,19 +11,18 @@
/*
* color_enhancement.h
*/
#ifndef VPM_COLOR_ENHANCEMENT_H
#define VPM_COLOR_ENHANCEMENT_H
#ifndef WEBRTC_MODULES_VIDEO_PROCESSING_COLOR_ENHANCEMENT_H
#define WEBRTC_MODULES_VIDEO_PROCESSING_COLOR_ENHANCEMENT_H
#include "webrtc/modules/video_processing/main/interface/video_processing.h"
#include "webrtc/typedefs.h"
namespace webrtc {
namespace VideoProcessing
{
int32_t ColorEnhancement(I420VideoFrame* frame);
namespace VideoProcessing {
int32_t ColorEnhancement(I420VideoFrame* frame);
}
} // namespace
} // namespace webrtc
#endif // VPM_COLOR_ENHANCEMENT_H
#endif // WEBRTC_MODULES_VIDEO_PROCESSING_COLOR_ENHANCEMENT_H

View File

@ -1,3 +1,13 @@
/*
* 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.
*/
#ifndef VPM_COLOR_ENHANCEMENT_PRIVATE_H
#define VPM_COLOR_ENHANCEMENT_PRIVATE_H
@ -270,4 +280,4 @@ static const uint8_t colorTable[256][256] = {
} // namespace
#endif // VPM_COLOR_ENHANCEMENT_PRIVATE_H
#endif // VPM_COLOR_ENHANCEMENT_PRIVATE_H

View File

@ -17,229 +17,185 @@
namespace webrtc {
VPMContentAnalysis::VPMContentAnalysis(bool runtime_cpu_detection):
_origFrame(NULL),
_prevFrame(NULL),
_width(0),
_height(0),
_skipNum(1),
_border(8),
_motionMagnitude(0.0f),
_spatialPredErr(0.0f),
_spatialPredErrH(0.0f),
_spatialPredErrV(0.0f),
_firstFrame(true),
_CAInit(false),
_cMetrics(NULL)
{
ComputeSpatialMetrics = &VPMContentAnalysis::ComputeSpatialMetrics_C;
TemporalDiffMetric = &VPMContentAnalysis::TemporalDiffMetric_C;
VPMContentAnalysis::VPMContentAnalysis(bool runtime_cpu_detection)
: orig_frame_(NULL),
prev_frame_(NULL),
width_(0),
height_(0),
skip_num_(1),
border_(8),
motion_magnitude_(0.0f),
spatial_pred_err_(0.0f),
spatial_pred_err_h_(0.0f),
spatial_pred_err_v_(0.0f),
first_frame_(true),
ca_Init_(false),
content_metrics_(NULL) {
ComputeSpatialMetrics = &VPMContentAnalysis::ComputeSpatialMetrics_C;
TemporalDiffMetric = &VPMContentAnalysis::TemporalDiffMetric_C;
if (runtime_cpu_detection)
{
if (runtime_cpu_detection) {
#if defined(WEBRTC_ARCH_X86_FAMILY)
if (WebRtc_GetCPUInfo(kSSE2))
{
ComputeSpatialMetrics =
&VPMContentAnalysis::ComputeSpatialMetrics_SSE2;
TemporalDiffMetric = &VPMContentAnalysis::TemporalDiffMetric_SSE2;
}
if (WebRtc_GetCPUInfo(kSSE2)) {
ComputeSpatialMetrics = &VPMContentAnalysis::ComputeSpatialMetrics_SSE2;
TemporalDiffMetric = &VPMContentAnalysis::TemporalDiffMetric_SSE2;
}
#endif
}
Release();
}
Release();
}
VPMContentAnalysis::~VPMContentAnalysis()
{
Release();
VPMContentAnalysis::~VPMContentAnalysis() {
Release();
}
VideoContentMetrics*
VPMContentAnalysis::ComputeContentMetrics(const I420VideoFrame& inputFrame)
{
if (inputFrame.IsZeroSize())
{
return NULL;
}
VideoContentMetrics* VPMContentAnalysis::ComputeContentMetrics(
const I420VideoFrame& inputFrame) {
if (inputFrame.IsZeroSize())
return NULL;
// Init if needed (native dimension change)
if (_width != inputFrame.width() || _height != inputFrame.height())
{
if (VPM_OK != Initialize(inputFrame.width(), inputFrame.height()))
{
return NULL;
}
}
// Only interested in the Y plane.
_origFrame = inputFrame.buffer(kYPlane);
// Init if needed (native dimension change).
if (width_ != inputFrame.width() || height_ != inputFrame.height()) {
if (VPM_OK != Initialize(inputFrame.width(), inputFrame.height()))
return NULL;
}
// Only interested in the Y plane.
orig_frame_ = inputFrame.buffer(kYPlane);
// compute spatial metrics: 3 spatial prediction errors
(this->*ComputeSpatialMetrics)();
// Compute spatial metrics: 3 spatial prediction errors.
(this->*ComputeSpatialMetrics)();
// compute motion metrics
if (_firstFrame == false)
ComputeMotionMetrics();
// Compute motion metrics
if (first_frame_ == false)
ComputeMotionMetrics();
// saving current frame as previous one: Y only
memcpy(_prevFrame, _origFrame, _width * _height);
// Saving current frame as previous one: Y only.
memcpy(prev_frame_, orig_frame_, width_ * height_);
_firstFrame = false;
_CAInit = true;
first_frame_ = false;
ca_Init_ = true;
return ContentMetrics();
return ContentMetrics();
}
int32_t
VPMContentAnalysis::Release()
{
if (_cMetrics != NULL)
{
delete _cMetrics;
_cMetrics = NULL;
}
int32_t VPMContentAnalysis::Release() {
if (content_metrics_ != NULL) {
delete content_metrics_;
content_metrics_ = NULL;
}
if (_prevFrame != NULL)
{
delete [] _prevFrame;
_prevFrame = NULL;
}
if (prev_frame_ != NULL) {
delete [] prev_frame_;
prev_frame_ = NULL;
}
_width = 0;
_height = 0;
_firstFrame = true;
width_ = 0;
height_ = 0;
first_frame_ = true;
return VPM_OK;
return VPM_OK;
}
int32_t
VPMContentAnalysis::Initialize(int width, int height)
{
_width = width;
_height = height;
_firstFrame = true;
int32_t VPMContentAnalysis::Initialize(int width, int height) {
width_ = width;
height_ = height;
first_frame_ = true;
// skip parameter: # of skipped rows: for complexity reduction
// temporal also currently uses it for column reduction.
_skipNum = 1;
// skip parameter: # of skipped rows: for complexity reduction
// temporal also currently uses it for column reduction.
skip_num_ = 1;
// use skipNum = 2 for 4CIF, WHD
if ( (_height >= 576) && (_width >= 704) )
{
_skipNum = 2;
}
// use skipNum = 4 for FULLL_HD images
if ( (_height >= 1080) && (_width >= 1920) )
{
_skipNum = 4;
}
// use skipNum = 2 for 4CIF, WHD
if ( (height_ >= 576) && (width_ >= 704) ) {
skip_num_ = 2;
}
// use skipNum = 4 for FULLL_HD images
if ( (height_ >= 1080) && (width_ >= 1920) ) {
skip_num_ = 4;
}
if (_cMetrics != NULL)
{
delete _cMetrics;
}
if (content_metrics_ != NULL) {
delete content_metrics_;
}
if (_prevFrame != NULL)
{
delete [] _prevFrame;
}
if (prev_frame_ != NULL) {
delete [] prev_frame_;
}
// Spatial Metrics don't work on a border of 8. Minimum processing
// block size is 16 pixels. So make sure the width and height support this.
if (_width <= 32 || _height <= 32)
{
_CAInit = false;
return VPM_PARAMETER_ERROR;
}
// Spatial Metrics don't work on a border of 8. Minimum processing
// block size is 16 pixels. So make sure the width and height support this.
if (width_ <= 32 || height_ <= 32) {
ca_Init_ = false;
return VPM_PARAMETER_ERROR;
}
_cMetrics = new VideoContentMetrics();
if (_cMetrics == NULL)
{
return VPM_MEMORY;
}
content_metrics_ = new VideoContentMetrics();
if (content_metrics_ == NULL) {
return VPM_MEMORY;
}
_prevFrame = new uint8_t[_width * _height] ; // Y only
if (_prevFrame == NULL)
{
return VPM_MEMORY;
}
prev_frame_ = new uint8_t[width_ * height_]; // Y only.
if (prev_frame_ == NULL) return VPM_MEMORY;
return VPM_OK;
return VPM_OK;
}
// Compute motion metrics: magnitude over non-zero motion vectors,
// and size of zero cluster
int32_t
VPMContentAnalysis::ComputeMotionMetrics()
{
// Motion metrics: only one is derived from normalized
// (MAD) temporal difference
(this->*TemporalDiffMetric)();
return VPM_OK;
int32_t VPMContentAnalysis::ComputeMotionMetrics() {
// Motion metrics: only one is derived from normalized
// (MAD) temporal difference
(this->*TemporalDiffMetric)();
return VPM_OK;
}
// Normalized temporal difference (MAD): used as a motion level metric
// Normalize MAD by spatial contrast: images with more contrast
// (pixel variance) likely have larger temporal difference
// To reduce complexity, we compute the metric for a reduced set of points.
int32_t
VPMContentAnalysis::TemporalDiffMetric_C()
{
// size of original frame
int sizei = _height;
int sizej = _width;
int32_t VPMContentAnalysis::TemporalDiffMetric_C() {
// size of original frame
int sizei = height_;
int sizej = width_;
uint32_t tempDiffSum = 0;
uint32_t pixelSum = 0;
uint64_t pixelSqSum = 0;
uint32_t tempDiffSum = 0;
uint32_t pixelSum = 0;
uint64_t pixelSqSum = 0;
uint32_t num_pixels = 0; // Counter for # of pixels.
const int width_end = ((width_ - 2*border_) & -16) + border_;
uint32_t numPixels = 0; // counter for # of pixels
for (int i = border_; i < sizei - border_; i += skip_num_) {
for (int j = border_; j < width_end; j++) {
num_pixels += 1;
int ssn = i * sizej + j;
const int width_end = ((_width - 2*_border) & -16) + _border;
uint8_t currPixel = orig_frame_[ssn];
uint8_t prevPixel = prev_frame_[ssn];
for(int i = _border; i < sizei - _border; i += _skipNum)
{
for(int j = _border; j < width_end; j++)
{
numPixels += 1;
int ssn = i * sizej + j;
uint8_t currPixel = _origFrame[ssn];
uint8_t prevPixel = _prevFrame[ssn];
tempDiffSum += (uint32_t)
abs((int16_t)(currPixel - prevPixel));
pixelSum += (uint32_t) currPixel;
pixelSqSum += (uint64_t) (currPixel * currPixel);
}
tempDiffSum += (uint32_t)abs((int16_t)(currPixel - prevPixel));
pixelSum += (uint32_t) currPixel;
pixelSqSum += (uint64_t) (currPixel * currPixel);
}
}
// default
_motionMagnitude = 0.0f;
// Default.
motion_magnitude_ = 0.0f;
if (tempDiffSum == 0)
{
return VPM_OK;
}
if (tempDiffSum == 0) return VPM_OK;
// normalize over all pixels
float const tempDiffAvg = (float)tempDiffSum / (float)(numPixels);
float const pixelSumAvg = (float)pixelSum / (float)(numPixels);
float const pixelSqSumAvg = (float)pixelSqSum / (float)(numPixels);
float contrast = pixelSqSumAvg - (pixelSumAvg * pixelSumAvg);
if (contrast > 0.0)
{
contrast = sqrt(contrast);
_motionMagnitude = tempDiffAvg/contrast;
}
return VPM_OK;
// Normalize over all pixels.
float const tempDiffAvg = (float)tempDiffSum / (float)(num_pixels);
float const pixelSumAvg = (float)pixelSum / (float)(num_pixels);
float const pixelSqSumAvg = (float)pixelSqSum / (float)(num_pixels);
float contrast = pixelSqSumAvg - (pixelSumAvg * pixelSumAvg);
if (contrast > 0.0) {
contrast = sqrt(contrast);
motion_magnitude_ = tempDiffAvg/contrast;
}
return VPM_OK;
}
// Compute spatial metrics:
@ -249,88 +205,71 @@ VPMContentAnalysis::TemporalDiffMetric_C()
// The metrics are a simple estimate of the up-sampling prediction error,
// estimated assuming sub-sampling for decimation (no filtering),
// and up-sampling back up with simple bilinear interpolation.
int32_t
VPMContentAnalysis::ComputeSpatialMetrics_C()
{
//size of original frame
const int sizei = _height;
const int sizej = _width;
int32_t VPMContentAnalysis::ComputeSpatialMetrics_C() {
const int sizei = height_;
const int sizej = width_;
// pixel mean square average: used to normalize the spatial metrics
uint32_t pixelMSA = 0;
// Pixel mean square average: used to normalize the spatial metrics.
uint32_t pixelMSA = 0;
uint32_t spatialErrSum = 0;
uint32_t spatialErrVSum = 0;
uint32_t spatialErrHSum = 0;
uint32_t spatialErrSum = 0;
uint32_t spatialErrVSum = 0;
uint32_t spatialErrHSum = 0;
// make sure work section is a multiple of 16
const int width_end = ((sizej - 2*_border) & -16) + _border;
// make sure work section is a multiple of 16
const int width_end = ((sizej - 2*border_) & -16) + border_;
for(int i = _border; i < sizei - _border; i += _skipNum)
{
for(int j = _border; j < width_end; j++)
{
for (int i = border_; i < sizei - border_; i += skip_num_) {
for (int j = border_; j < width_end; j++) {
int ssn1= i * sizej + j;
int ssn2 = (i + 1) * sizej + j; // bottom
int ssn3 = (i - 1) * sizej + j; // top
int ssn4 = i * sizej + j + 1; // right
int ssn5 = i * sizej + j - 1; // left
int ssn1= i * sizej + j;
int ssn2 = (i + 1) * sizej + j; // bottom
int ssn3 = (i - 1) * sizej + j; // top
int ssn4 = i * sizej + j + 1; // right
int ssn5 = i * sizej + j - 1; // left
uint16_t refPixel1 = orig_frame_[ssn1] << 1;
uint16_t refPixel2 = orig_frame_[ssn1] << 2;
uint16_t refPixel1 = _origFrame[ssn1] << 1;
uint16_t refPixel2 = _origFrame[ssn1] << 2;
uint8_t bottPixel = orig_frame_[ssn2];
uint8_t topPixel = orig_frame_[ssn3];
uint8_t rightPixel = orig_frame_[ssn4];
uint8_t leftPixel = orig_frame_[ssn5];
uint8_t bottPixel = _origFrame[ssn2];
uint8_t topPixel = _origFrame[ssn3];
uint8_t rightPixel = _origFrame[ssn4];
uint8_t leftPixel = _origFrame[ssn5];
spatialErrSum += (uint32_t) abs((int16_t)(refPixel2
- (uint16_t)(bottPixel + topPixel
+ leftPixel + rightPixel)));
spatialErrVSum += (uint32_t) abs((int16_t)(refPixel1
- (uint16_t)(bottPixel + topPixel)));
spatialErrHSum += (uint32_t) abs((int16_t)(refPixel1
- (uint16_t)(leftPixel + rightPixel)));
pixelMSA += _origFrame[ssn1];
}
spatialErrSum += (uint32_t) abs((int16_t)(refPixel2
- (uint16_t)(bottPixel + topPixel + leftPixel + rightPixel)));
spatialErrVSum += (uint32_t) abs((int16_t)(refPixel1
- (uint16_t)(bottPixel + topPixel)));
spatialErrHSum += (uint32_t) abs((int16_t)(refPixel1
- (uint16_t)(leftPixel + rightPixel)));
pixelMSA += orig_frame_[ssn1];
}
}
// normalize over all pixels
const float spatialErr = (float)(spatialErrSum >> 2);
const float spatialErrH = (float)(spatialErrHSum >> 1);
const float spatialErrV = (float)(spatialErrVSum >> 1);
const float norm = (float)pixelMSA;
// Normalize over all pixels.
const float spatialErr = (float)(spatialErrSum >> 2);
const float spatialErrH = (float)(spatialErrHSum >> 1);
const float spatialErrV = (float)(spatialErrVSum >> 1);
const float norm = (float)pixelMSA;
// 2X2:
_spatialPredErr = spatialErr / norm;
// 1X2:
_spatialPredErrH = spatialErrH / norm;
// 2X1:
_spatialPredErrV = spatialErrV / norm;
return VPM_OK;
// 2X2:
spatial_pred_err_ = spatialErr / norm;
// 1X2:
spatial_pred_err_h_ = spatialErrH / norm;
// 2X1:
spatial_pred_err_v_ = spatialErrV / norm;
return VPM_OK;
}
VideoContentMetrics*
VPMContentAnalysis::ContentMetrics()
{
if (_CAInit == false)
{
return NULL;
}
VideoContentMetrics* VPMContentAnalysis::ContentMetrics() {
if (ca_Init_ == false) return NULL;
_cMetrics->spatial_pred_err = _spatialPredErr;
_cMetrics->spatial_pred_err_h = _spatialPredErrH;
_cMetrics->spatial_pred_err_v = _spatialPredErrV;
// Motion metric: normalized temporal difference (MAD)
_cMetrics->motion_magnitude = _motionMagnitude;
return _cMetrics;
content_metrics_->spatial_pred_err = spatial_pred_err_;
content_metrics_->spatial_pred_err_h = spatial_pred_err_h_;
content_metrics_->spatial_pred_err_v = spatial_pred_err_v_;
// Motion metric: normalized temporal difference (MAD).
content_metrics_->motion_magnitude = motion_magnitude_;
return content_metrics_;
}
} // namespace
} // namespace webrtc

View File

@ -8,8 +8,8 @@
* be found in the AUTHORS file in the root of the source tree.
*/
#ifndef VPM_CONTENT_ANALYSIS_H
#define VPM_CONTENT_ANALYSIS_H
#ifndef WEBRTC_MODULES_VIDEO_PROCESSING_MAIN_SOURCE_CONTENT_ANALYSIS_H
#define WEBRTC_MODULES_VIDEO_PROCESSING_MAIN_SOURCE_CONTENT_ANALYSIS_H
#include "webrtc/common_video/interface/i420_video_frame.h"
#include "webrtc/modules/interface/module_common_types.h"
@ -18,75 +18,71 @@
namespace webrtc {
class VPMContentAnalysis
{
public:
// When |runtime_cpu_detection| is true, runtime selection of an optimized
// code path is allowed.
VPMContentAnalysis(bool runtime_cpu_detection);
~VPMContentAnalysis();
class VPMContentAnalysis {
public:
// When |runtime_cpu_detection| is true, runtime selection of an optimized
// code path is allowed.
explicit VPMContentAnalysis(bool runtime_cpu_detection);
~VPMContentAnalysis();
// Initialize ContentAnalysis - should be called prior to
// extractContentFeature
// Inputs: width, height
// Return value: 0 if OK, negative value upon error
int32_t Initialize(int width, int height);
// Initialize ContentAnalysis - should be called prior to
// extractContentFeature
// Inputs: width, height
// Return value: 0 if OK, negative value upon error
int32_t Initialize(int width, int height);
// Extract content Feature - main function of ContentAnalysis
// Input: new frame
// Return value: pointer to structure containing content Analysis
// metrics or NULL value upon error
VideoContentMetrics* ComputeContentMetrics(const I420VideoFrame&
inputFrame);
// Extract content Feature - main function of ContentAnalysis
// Input: new frame
// Return value: pointer to structure containing content Analysis
// metrics or NULL value upon error
VideoContentMetrics* ComputeContentMetrics(const I420VideoFrame&
inputFrame);
// Release all allocated memory
// Output: 0 if OK, negative value upon error
int32_t Release();
// Release all allocated memory
// Output: 0 if OK, negative value upon error
int32_t Release();
private:
private:
// return motion metrics
VideoContentMetrics* ContentMetrics();
// return motion metrics
VideoContentMetrics* ContentMetrics();
// Normalized temporal difference metric: for motion magnitude
typedef int32_t (VPMContentAnalysis::*TemporalDiffMetricFunc)();
TemporalDiffMetricFunc TemporalDiffMetric;
int32_t TemporalDiffMetric_C();
// Normalized temporal difference metric: for motion magnitude
typedef int32_t (VPMContentAnalysis::*TemporalDiffMetricFunc)();
TemporalDiffMetricFunc TemporalDiffMetric;
int32_t TemporalDiffMetric_C();
// Motion metric method: call 2 metrics (magnitude and size)
int32_t ComputeMotionMetrics();
// Motion metric method: call 2 metrics (magnitude and size)
int32_t ComputeMotionMetrics();
// Spatial metric method: computes the 3 frame-average spatial
// prediction errors (1x2,2x1,2x2)
typedef int32_t (VPMContentAnalysis::*ComputeSpatialMetricsFunc)();
ComputeSpatialMetricsFunc ComputeSpatialMetrics;
int32_t ComputeSpatialMetrics_C();
// Spatial metric method: computes the 3 frame-average spatial
// prediction errors (1x2,2x1,2x2)
typedef int32_t (VPMContentAnalysis::*ComputeSpatialMetricsFunc)();
ComputeSpatialMetricsFunc ComputeSpatialMetrics;
int32_t ComputeSpatialMetrics_C();
#if defined(WEBRTC_ARCH_X86_FAMILY)
int32_t ComputeSpatialMetrics_SSE2();
int32_t TemporalDiffMetric_SSE2();
int32_t ComputeSpatialMetrics_SSE2();
int32_t TemporalDiffMetric_SSE2();
#endif
const uint8_t* _origFrame;
uint8_t* _prevFrame;
int _width;
int _height;
int _skipNum;
int _border;
const uint8_t* orig_frame_;
uint8_t* prev_frame_;
int width_;
int height_;
int skip_num_;
int border_;
// Content Metrics:
// stores the local average of the metrics
float _motionMagnitude; // motion class
float _spatialPredErr; // spatial class
float _spatialPredErrH; // spatial class
float _spatialPredErrV; // spatial class
bool _firstFrame;
bool _CAInit;
// Content Metrics: Stores the local average of the metrics.
float motion_magnitude_; // motion class
float spatial_pred_err_; // spatial class
float spatial_pred_err_h_; // spatial class
float spatial_pred_err_v_; // spatial class
bool first_frame_;
bool ca_Init_;
VideoContentMetrics* _cMetrics;
VideoContentMetrics* content_metrics_;
};
}; // end of VPMContentAnalysis class definition
} // namespace webrtc
} // namespace
#endif
#endif // WEBRTC_MODULES_VIDEO_PROCESSING_MAIN_SOURCE_CONTENT_ANALYSIS_H

View File

@ -15,284 +15,248 @@
namespace webrtc {
int32_t
VPMContentAnalysis::TemporalDiffMetric_SSE2()
{
uint32_t numPixels = 0; // counter for # of pixels
int32_t VPMContentAnalysis::TemporalDiffMetric_SSE2() {
uint32_t num_pixels = 0; // counter for # of pixels
const uint8_t* imgBufO = orig_frame_ + border_*width_ + border_;
const uint8_t* imgBufP = prev_frame_ + border_*width_ + border_;
const uint8_t* imgBufO = _origFrame + _border*_width + _border;
const uint8_t* imgBufP = _prevFrame + _border*_width + _border;
const int32_t width_end = ((width_ - 2*border_) & -16) + border_;
const int32_t width_end = ((_width - 2*_border) & -16) + _border;
__m128i sad_64 = _mm_setzero_si128();
__m128i sum_64 = _mm_setzero_si128();
__m128i sqsum_64 = _mm_setzero_si128();
const __m128i z = _mm_setzero_si128();
__m128i sad_64 = _mm_setzero_si128();
__m128i sum_64 = _mm_setzero_si128();
__m128i sqsum_64 = _mm_setzero_si128();
const __m128i z = _mm_setzero_si128();
for (uint16_t i = 0; i < (height_ - 2*border_); i += skip_num_) {
__m128i sqsum_32 = _mm_setzero_si128();
for(uint16_t i = 0; i < (_height - 2*_border); i += _skipNum)
{
__m128i sqsum_32 = _mm_setzero_si128();
const uint8_t *lineO = imgBufO;
const uint8_t *lineP = imgBufP;
const uint8_t *lineO = imgBufO;
const uint8_t *lineP = imgBufP;
// Work on 16 pixels at a time. For HD content with a width of 1920
// this loop will run ~67 times (depending on border). Maximum for
// abs(o-p) and sum(o) will be 255. _mm_sad_epu8 produces 2 64 bit
// results which are then accumulated. There is no chance of
// rollover for these two accumulators.
// o*o will have a maximum of 255*255 = 65025. This will roll over
// a 16 bit accumulator as 67*65025 > 65535, but will fit in a
// 32 bit accumulator.
for (uint16_t j = 0; j < width_end - border_; j += 16) {
const __m128i o = _mm_loadu_si128((__m128i*)(lineO));
const __m128i p = _mm_loadu_si128((__m128i*)(lineP));
// Work on 16 pixels at a time. For HD content with a width of 1920
// this loop will run ~67 times (depending on border). Maximum for
// abs(o-p) and sum(o) will be 255. _mm_sad_epu8 produces 2 64 bit
// results which are then accumulated. There is no chance of
// rollover for these two accumulators.
// o*o will have a maximum of 255*255 = 65025. This will roll over
// a 16 bit accumulator as 67*65025 > 65535, but will fit in a
// 32 bit accumulator.
for(uint16_t j = 0; j < width_end - _border; j += 16)
{
const __m128i o = _mm_loadu_si128((__m128i*)(lineO));
const __m128i p = _mm_loadu_si128((__m128i*)(lineP));
lineO += 16;
lineP += 16;
lineO += 16;
lineP += 16;
// Abs pixel difference between frames.
sad_64 = _mm_add_epi64 (sad_64, _mm_sad_epu8(o, p));
// abs pixel difference between frames
sad_64 = _mm_add_epi64 (sad_64, _mm_sad_epu8(o, p));
// sum of all pixels in frame
sum_64 = _mm_add_epi64 (sum_64, _mm_sad_epu8(o, z));
// sum of all pixels in frame
sum_64 = _mm_add_epi64 (sum_64, _mm_sad_epu8(o, z));
// Squared sum of all pixels in frame.
const __m128i olo = _mm_unpacklo_epi8(o,z);
const __m128i ohi = _mm_unpackhi_epi8(o,z);
// squared sum of all pixels in frame
const __m128i olo = _mm_unpacklo_epi8(o,z);
const __m128i ohi = _mm_unpackhi_epi8(o,z);
const __m128i sqsum_32_lo = _mm_madd_epi16(olo, olo);
const __m128i sqsum_32_hi = _mm_madd_epi16(ohi, ohi);
const __m128i sqsum_32_lo = _mm_madd_epi16(olo, olo);
const __m128i sqsum_32_hi = _mm_madd_epi16(ohi, ohi);
sqsum_32 = _mm_add_epi32(sqsum_32, sqsum_32_lo);
sqsum_32 = _mm_add_epi32(sqsum_32, sqsum_32_hi);
}
// Add to 64 bit running sum as to not roll over.
sqsum_64 = _mm_add_epi64(sqsum_64,
_mm_add_epi64(_mm_unpackhi_epi32(sqsum_32,z),
_mm_unpacklo_epi32(sqsum_32,z)));
imgBufO += _width * _skipNum;
imgBufP += _width * _skipNum;
numPixels += (width_end - _border);
sqsum_32 = _mm_add_epi32(sqsum_32, sqsum_32_lo);
sqsum_32 = _mm_add_epi32(sqsum_32, sqsum_32_hi);
}
__m128i sad_final_128;
__m128i sum_final_128;
__m128i sqsum_final_128;
// Add to 64 bit running sum as to not roll over.
sqsum_64 = _mm_add_epi64(sqsum_64,
_mm_add_epi64(_mm_unpackhi_epi32(sqsum_32,z),
_mm_unpacklo_epi32(sqsum_32,z)));
// bring sums out of vector registers and into integer register
// domain, summing them along the way
_mm_store_si128 (&sad_final_128, sad_64);
_mm_store_si128 (&sum_final_128, sum_64);
_mm_store_si128 (&sqsum_final_128, sqsum_64);
imgBufO += width_ * skip_num_;
imgBufP += width_ * skip_num_;
num_pixels += (width_end - border_);
}
uint64_t *sad_final_64 =
reinterpret_cast<uint64_t*>(&sad_final_128);
uint64_t *sum_final_64 =
reinterpret_cast<uint64_t*>(&sum_final_128);
uint64_t *sqsum_final_64 =
reinterpret_cast<uint64_t*>(&sqsum_final_128);
__m128i sad_final_128;
__m128i sum_final_128;
__m128i sqsum_final_128;
const uint32_t pixelSum = sum_final_64[0] + sum_final_64[1];
const uint64_t pixelSqSum = sqsum_final_64[0] + sqsum_final_64[1];
const uint32_t tempDiffSum = sad_final_64[0] + sad_final_64[1];
// Bring sums out of vector registers and into integer register
// domain, summing them along the way.
_mm_store_si128 (&sad_final_128, sad_64);
_mm_store_si128 (&sum_final_128, sum_64);
_mm_store_si128 (&sqsum_final_128, sqsum_64);
// default
_motionMagnitude = 0.0f;
uint64_t *sad_final_64 = reinterpret_cast<uint64_t*>(&sad_final_128);
uint64_t *sum_final_64 = reinterpret_cast<uint64_t*>(&sum_final_128);
uint64_t *sqsum_final_64 = reinterpret_cast<uint64_t*>(&sqsum_final_128);
if (tempDiffSum == 0)
{
return VPM_OK;
}
const uint32_t pixelSum = sum_final_64[0] + sum_final_64[1];
const uint64_t pixelSqSum = sqsum_final_64[0] + sqsum_final_64[1];
const uint32_t tempDiffSum = sad_final_64[0] + sad_final_64[1];
// normalize over all pixels
const float tempDiffAvg = (float)tempDiffSum / (float)(numPixels);
const float pixelSumAvg = (float)pixelSum / (float)(numPixels);
const float pixelSqSumAvg = (float)pixelSqSum / (float)(numPixels);
float contrast = pixelSqSumAvg - (pixelSumAvg * pixelSumAvg);
// Default.
motion_magnitude_ = 0.0f;
if (contrast > 0.0)
{
contrast = sqrt(contrast);
_motionMagnitude = tempDiffAvg/contrast;
}
if (tempDiffSum == 0) return VPM_OK;
return VPM_OK;
// Normalize over all pixels.
const float tempDiffAvg = (float)tempDiffSum / (float)(num_pixels);
const float pixelSumAvg = (float)pixelSum / (float)(num_pixels);
const float pixelSqSumAvg = (float)pixelSqSum / (float)(num_pixels);
float contrast = pixelSqSumAvg - (pixelSumAvg * pixelSumAvg);
if (contrast > 0.0) {
contrast = sqrt(contrast);
motion_magnitude_ = tempDiffAvg/contrast;
}
return VPM_OK;
}
int32_t
VPMContentAnalysis::ComputeSpatialMetrics_SSE2()
{
const uint8_t* imgBuf = _origFrame + _border*_width;
const int32_t width_end = ((_width - 2*_border) & -16) + _border;
int32_t VPMContentAnalysis::ComputeSpatialMetrics_SSE2() {
const uint8_t* imgBuf = orig_frame_ + border_*width_;
const int32_t width_end = ((width_ - 2 * border_) & -16) + border_;
__m128i se_32 = _mm_setzero_si128();
__m128i sev_32 = _mm_setzero_si128();
__m128i seh_32 = _mm_setzero_si128();
__m128i msa_32 = _mm_setzero_si128();
const __m128i z = _mm_setzero_si128();
__m128i se_32 = _mm_setzero_si128();
__m128i sev_32 = _mm_setzero_si128();
__m128i seh_32 = _mm_setzero_si128();
__m128i msa_32 = _mm_setzero_si128();
const __m128i z = _mm_setzero_si128();
// Error is accumulated as a 32 bit value. Looking at HD content with a
// height of 1080 lines, or about 67 macro blocks. If the 16 bit row
// value is maxed out at 65529 for every row, 65529*1080 = 70777800, which
// will not roll over a 32 bit accumulator.
// _skipNum is also used to reduce the number of rows
for(int32_t i = 0; i < (_height - 2*_border); i += _skipNum)
{
__m128i se_16 = _mm_setzero_si128();
__m128i sev_16 = _mm_setzero_si128();
__m128i seh_16 = _mm_setzero_si128();
__m128i msa_16 = _mm_setzero_si128();
// Error is accumulated as a 32 bit value. Looking at HD content with a
// height of 1080 lines, or about 67 macro blocks. If the 16 bit row
// value is maxed out at 65529 for every row, 65529*1080 = 70777800, which
// will not roll over a 32 bit accumulator.
// skip_num_ is also used to reduce the number of rows
for (int32_t i = 0; i < (height_ - 2*border_); i += skip_num_) {
__m128i se_16 = _mm_setzero_si128();
__m128i sev_16 = _mm_setzero_si128();
__m128i seh_16 = _mm_setzero_si128();
__m128i msa_16 = _mm_setzero_si128();
// Row error is accumulated as a 16 bit value. There are 8
// accumulators. Max value of a 16 bit number is 65529. Looking
// at HD content, 1080p, has a width of 1920, 120 macro blocks.
// A mb at a time is processed at a time. Absolute max error at
// a point would be abs(0-255+255+255+255) which equals 1020.
// 120*1020 = 122400. The probability of hitting this is quite low
// on well behaved content. A specially crafted image could roll over.
// _border could also be adjusted to concentrate on just the center of
// the images for an HD capture in order to reduce the possiblity of
// rollover.
const uint8_t *lineTop = imgBuf - _width + _border;
const uint8_t *lineCen = imgBuf + _border;
const uint8_t *lineBot = imgBuf + _width + _border;
// Row error is accumulated as a 16 bit value. There are 8
// accumulators. Max value of a 16 bit number is 65529. Looking
// at HD content, 1080p, has a width of 1920, 120 macro blocks.
// A mb at a time is processed at a time. Absolute max error at
// a point would be abs(0-255+255+255+255) which equals 1020.
// 120*1020 = 122400. The probability of hitting this is quite low
// on well behaved content. A specially crafted image could roll over.
// border_ could also be adjusted to concentrate on just the center of
// the images for an HD capture in order to reduce the possiblity of
// rollover.
const uint8_t *lineTop = imgBuf - width_ + border_;
const uint8_t *lineCen = imgBuf + border_;
const uint8_t *lineBot = imgBuf + width_ + border_;
for(int32_t j = 0; j < width_end - _border; j += 16)
{
const __m128i t = _mm_loadu_si128((__m128i*)(lineTop));
const __m128i l = _mm_loadu_si128((__m128i*)(lineCen - 1));
const __m128i c = _mm_loadu_si128((__m128i*)(lineCen));
const __m128i r = _mm_loadu_si128((__m128i*)(lineCen + 1));
const __m128i b = _mm_loadu_si128((__m128i*)(lineBot));
for (int32_t j = 0; j < width_end - border_; j += 16) {
const __m128i t = _mm_loadu_si128((__m128i*)(lineTop));
const __m128i l = _mm_loadu_si128((__m128i*)(lineCen - 1));
const __m128i c = _mm_loadu_si128((__m128i*)(lineCen));
const __m128i r = _mm_loadu_si128((__m128i*)(lineCen + 1));
const __m128i b = _mm_loadu_si128((__m128i*)(lineBot));
lineTop += 16;
lineCen += 16;
lineBot += 16;
lineTop += 16;
lineCen += 16;
lineBot += 16;
// center pixel unpacked
__m128i clo = _mm_unpacklo_epi8(c,z);
__m128i chi = _mm_unpackhi_epi8(c,z);
// center pixel unpacked
__m128i clo = _mm_unpacklo_epi8(c,z);
__m128i chi = _mm_unpackhi_epi8(c,z);
// left right pixels unpacked and added together
const __m128i lrlo = _mm_add_epi16(_mm_unpacklo_epi8(l,z),
_mm_unpacklo_epi8(r,z));
const __m128i lrhi = _mm_add_epi16(_mm_unpackhi_epi8(l,z),
_mm_unpackhi_epi8(r,z));
// left right pixels unpacked and added together
const __m128i lrlo = _mm_add_epi16(_mm_unpacklo_epi8(l,z),
_mm_unpacklo_epi8(r,z));
const __m128i lrhi = _mm_add_epi16(_mm_unpackhi_epi8(l,z),
_mm_unpackhi_epi8(r,z));
// top & bottom pixels unpacked and added together
const __m128i tblo = _mm_add_epi16(_mm_unpacklo_epi8(t,z),
_mm_unpacklo_epi8(b,z));
const __m128i tbhi = _mm_add_epi16(_mm_unpackhi_epi8(t,z),
_mm_unpackhi_epi8(b,z));
// top & bottom pixels unpacked and added together
const __m128i tblo = _mm_add_epi16(_mm_unpacklo_epi8(t,z),
_mm_unpacklo_epi8(b,z));
const __m128i tbhi = _mm_add_epi16(_mm_unpackhi_epi8(t,z),
_mm_unpackhi_epi8(b,z));
// running sum of all pixels
msa_16 = _mm_add_epi16(msa_16, _mm_add_epi16(chi, clo));
// running sum of all pixels
msa_16 = _mm_add_epi16(msa_16, _mm_add_epi16(chi, clo));
clo = _mm_slli_epi16(clo, 1);
chi = _mm_slli_epi16(chi, 1);
const __m128i sevtlo = _mm_subs_epi16(clo, tblo);
const __m128i sevthi = _mm_subs_epi16(chi, tbhi);
const __m128i sehtlo = _mm_subs_epi16(clo, lrlo);
const __m128i sehthi = _mm_subs_epi16(chi, lrhi);
clo = _mm_slli_epi16(clo, 1);
chi = _mm_slli_epi16(chi, 1);
const __m128i sevtlo = _mm_subs_epi16(clo, tblo);
const __m128i sevthi = _mm_subs_epi16(chi, tbhi);
const __m128i sehtlo = _mm_subs_epi16(clo, lrlo);
const __m128i sehthi = _mm_subs_epi16(chi, lrhi);
clo = _mm_slli_epi16(clo, 1);
chi = _mm_slli_epi16(chi, 1);
const __m128i setlo = _mm_subs_epi16(clo,
_mm_add_epi16(lrlo, tblo));
const __m128i sethi = _mm_subs_epi16(chi,
_mm_add_epi16(lrhi, tbhi));
clo = _mm_slli_epi16(clo, 1);
chi = _mm_slli_epi16(chi, 1);
const __m128i setlo = _mm_subs_epi16(clo, _mm_add_epi16(lrlo, tblo));
const __m128i sethi = _mm_subs_epi16(chi, _mm_add_epi16(lrhi, tbhi));
// Add to 16 bit running sum
se_16 = _mm_add_epi16(se_16,
_mm_max_epi16(setlo,
_mm_subs_epi16(z, setlo)));
se_16 = _mm_add_epi16(se_16,
_mm_max_epi16(sethi,
_mm_subs_epi16(z, sethi)));
sev_16 = _mm_add_epi16(sev_16,
_mm_max_epi16(sevtlo,
_mm_subs_epi16(z, sevtlo)));
sev_16 = _mm_add_epi16(sev_16,
_mm_max_epi16(sevthi,
_mm_subs_epi16(z, sevthi)));
seh_16 = _mm_add_epi16(seh_16,
_mm_max_epi16(sehtlo,
_mm_subs_epi16(z, sehtlo)));
seh_16 = _mm_add_epi16(seh_16,
_mm_max_epi16(sehthi,
_mm_subs_epi16(z, sehthi)));
}
// Add to 32 bit running sum as to not roll over.
se_32 = _mm_add_epi32(se_32,
_mm_add_epi32(_mm_unpackhi_epi16(se_16,z),
_mm_unpacklo_epi16(se_16,z)));
sev_32 = _mm_add_epi32(sev_32,
_mm_add_epi32(_mm_unpackhi_epi16(sev_16,z),
_mm_unpacklo_epi16(sev_16,z)));
seh_32 = _mm_add_epi32(seh_32,
_mm_add_epi32(_mm_unpackhi_epi16(seh_16,z),
_mm_unpacklo_epi16(seh_16,z)));
msa_32 = _mm_add_epi32(msa_32,
_mm_add_epi32(_mm_unpackhi_epi16(msa_16,z),
_mm_unpacklo_epi16(msa_16,z)));
imgBuf += _width * _skipNum;
// Add to 16 bit running sum
se_16 = _mm_add_epi16(se_16, _mm_max_epi16(setlo,
_mm_subs_epi16(z, setlo)));
se_16 = _mm_add_epi16(se_16, _mm_max_epi16(sethi,
_mm_subs_epi16(z, sethi)));
sev_16 = _mm_add_epi16(sev_16, _mm_max_epi16(sevtlo,
_mm_subs_epi16(z, sevtlo)));
sev_16 = _mm_add_epi16(sev_16, _mm_max_epi16(sevthi,
_mm_subs_epi16(z, sevthi)));
seh_16 = _mm_add_epi16(seh_16, _mm_max_epi16(sehtlo,
_mm_subs_epi16(z, sehtlo)));
seh_16 = _mm_add_epi16(seh_16, _mm_max_epi16(sehthi,
_mm_subs_epi16(z, sehthi)));
}
__m128i se_128;
__m128i sev_128;
__m128i seh_128;
__m128i msa_128;
// Add to 32 bit running sum as to not roll over.
se_32 = _mm_add_epi32(se_32, _mm_add_epi32(_mm_unpackhi_epi16(se_16,z),
_mm_unpacklo_epi16(se_16,z)));
sev_32 = _mm_add_epi32(sev_32, _mm_add_epi32(_mm_unpackhi_epi16(sev_16,z),
_mm_unpacklo_epi16(sev_16,z)));
seh_32 = _mm_add_epi32(seh_32, _mm_add_epi32(_mm_unpackhi_epi16(seh_16,z),
_mm_unpacklo_epi16(seh_16,z)));
msa_32 = _mm_add_epi32(msa_32, _mm_add_epi32(_mm_unpackhi_epi16(msa_16,z),
_mm_unpacklo_epi16(msa_16,z)));
// bring sums out of vector registers and into integer register
// domain, summing them along the way
_mm_store_si128 (&se_128,
_mm_add_epi64(_mm_unpackhi_epi32(se_32,z),
_mm_unpacklo_epi32(se_32,z)));
_mm_store_si128 (&sev_128,
_mm_add_epi64(_mm_unpackhi_epi32(sev_32,z),
_mm_unpacklo_epi32(sev_32,z)));
_mm_store_si128 (&seh_128,
_mm_add_epi64(_mm_unpackhi_epi32(seh_32,z),
_mm_unpacklo_epi32(seh_32,z)));
_mm_store_si128 (&msa_128,
_mm_add_epi64(_mm_unpackhi_epi32(msa_32,z),
_mm_unpacklo_epi32(msa_32,z)));
imgBuf += width_ * skip_num_;
}
uint64_t *se_64 =
reinterpret_cast<uint64_t*>(&se_128);
uint64_t *sev_64 =
reinterpret_cast<uint64_t*>(&sev_128);
uint64_t *seh_64 =
reinterpret_cast<uint64_t*>(&seh_128);
uint64_t *msa_64 =
reinterpret_cast<uint64_t*>(&msa_128);
__m128i se_128;
__m128i sev_128;
__m128i seh_128;
__m128i msa_128;
const uint32_t spatialErrSum = se_64[0] + se_64[1];
const uint32_t spatialErrVSum = sev_64[0] + sev_64[1];
const uint32_t spatialErrHSum = seh_64[0] + seh_64[1];
const uint32_t pixelMSA = msa_64[0] + msa_64[1];
// Bring sums out of vector registers and into integer register
// domain, summing them along the way.
_mm_store_si128 (&se_128, _mm_add_epi64(_mm_unpackhi_epi32(se_32,z),
_mm_unpacklo_epi32(se_32,z)));
_mm_store_si128 (&sev_128, _mm_add_epi64(_mm_unpackhi_epi32(sev_32,z),
_mm_unpacklo_epi32(sev_32,z)));
_mm_store_si128 (&seh_128, _mm_add_epi64(_mm_unpackhi_epi32(seh_32,z),
_mm_unpacklo_epi32(seh_32,z)));
_mm_store_si128 (&msa_128, _mm_add_epi64(_mm_unpackhi_epi32(msa_32,z),
_mm_unpacklo_epi32(msa_32,z)));
// normalize over all pixels
const float spatialErr = (float)(spatialErrSum >> 2);
const float spatialErrH = (float)(spatialErrHSum >> 1);
const float spatialErrV = (float)(spatialErrVSum >> 1);
const float norm = (float)pixelMSA;
uint64_t *se_64 = reinterpret_cast<uint64_t*>(&se_128);
uint64_t *sev_64 = reinterpret_cast<uint64_t*>(&sev_128);
uint64_t *seh_64 = reinterpret_cast<uint64_t*>(&seh_128);
uint64_t *msa_64 = reinterpret_cast<uint64_t*>(&msa_128);
// 2X2:
_spatialPredErr = spatialErr / norm;
const uint32_t spatialErrSum = se_64[0] + se_64[1];
const uint32_t spatialErrVSum = sev_64[0] + sev_64[1];
const uint32_t spatialErrHSum = seh_64[0] + seh_64[1];
const uint32_t pixelMSA = msa_64[0] + msa_64[1];
// 1X2:
_spatialPredErrH = spatialErrH / norm;
// Normalize over all pixels.
const float spatialErr = (float)(spatialErrSum >> 2);
const float spatialErrH = (float)(spatialErrHSum >> 1);
const float spatialErrV = (float)(spatialErrVSum >> 1);
const float norm = (float)pixelMSA;
// 2X1:
_spatialPredErrV = spatialErrV / norm;
// 2X2:
spatial_pred_err_ = spatialErr / norm;
// 1X2:
spatial_pred_err_h_ = spatialErrH / norm;
// 2X1:
spatial_pred_err_v_ = spatialErrV / norm;
return VPM_OK;
}

View File

@ -20,431 +20,386 @@
namespace webrtc {
// Detection constants
enum { kFrequencyDeviation = 39 }; // (Q4) Maximum allowed deviation for detection
enum { kMinFrequencyToDetect = 32 }; // (Q4) Minimum frequency that can be detected
enum { kNumFlickerBeforeDetect = 2 }; // Number of flickers before we accept detection
enum { kMeanValueScaling = 4 }; // (Q4) In power of 2
enum { kZeroCrossingDeadzone = 10 }; // Deadzone region in terms of pixel values
// Deflickering 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]);
// >> 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::_probUW16[kNumProbs] =
{102, 205, 410, 614, 819, 1024, 1229, 1434, 1638, 1843, 1946, 1987}; // <Q11>
// 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)]);
// >> weightUW16 = round(2^15 *
// [linspace(0.5, 1.0, numQuants - maxOnlyLength)]);
// >> fprintf('%d, %d,\n ', weightUW16);
const uint16_t VPMDeflickering::_weightUW16[kNumQuants - kMaxOnlyLength] =
const uint16_t VPMDeflickering::weight_uw16_[kNumQuants - kMaxOnlyLength] =
{16384, 18432, 20480, 22528, 24576, 26624, 28672, 30720, 32768}; // <Q15>
VPMDeflickering::VPMDeflickering() :
_id(0)
{
Reset();
VPMDeflickering::VPMDeflickering()
: id_(0) {
Reset();
}
VPMDeflickering::~VPMDeflickering()
{
VPMDeflickering::~VPMDeflickering() {}
int32_t VPMDeflickering::ChangeUniqueId(const int32_t id) {
id_ = id;
return 0;
}
int32_t
VPMDeflickering::ChangeUniqueId(const int32_t id)
{
_id = id;
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++) {
quant_hist_uw8_[0][i + 1] = static_cast<uint8_t>((WEBRTC_SPL_UMUL_16_16(
prob_uw16_[i], 255) + (1 << 10)) >> 11); // Unsigned round. <Q0>
}
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(I420VideoFrame* frame,
VideoProcessingModule::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()) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoPreocessing, id_,
"Null frame pointer");
return VPM_GENERAL_ERROR;
}
// Stricter height check due to subsampling size calculation below.
if (height < 2) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoPreocessing, id_,
"Invalid frame size");
return VPM_GENERAL_ERROR;
}
if (!VideoProcessingModule::ValidFrameStats(*stats)) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoPreocessing, id_,
"Invalid frame 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;
}
}
void
VPMDeflickering::Reset()
{
_meanBufferLength = 0;
_detectionState = 0;
_frameRate = 0;
// Size of luminance component.
const uint32_t y_size = height * width;
memset(_meanBuffer, 0, sizeof(int32_t) * kMeanBufferLength);
memset(_timestampBuffer, 0, sizeof(int32_t) * kMeanBufferLength);
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++;
}
// Initialize the history with a uniformly distributed histogram
_quantHistUW8[0][0] = 0;
_quantHistUW8[0][kNumQuants - 1] = 255;
for (int32_t i = 0; i < kNumProbs; i++)
{
_quantHistUW8[0][i + 1] = static_cast<uint8_t>((WEBRTC_SPL_UMUL_16_16(
_probUW16[i], 255) + (1 << 10)) >> 11); // Unsigned round. <Q0>
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) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoPreocessing, id_,
"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];
}
}
for (int32_t i = 1; i < kFrameHistorySize; i++)
{
memcpy(_quantHistUW8[i], _quantHistUW8[0], sizeof(uint8_t) * kNumQuants);
}
}
}
int32_t
VPMDeflickering::ProcessFrame(I420VideoFrame* frame,
VideoProcessingModule::FrameStats* stats)
{
assert(frame);
uint32_t frameMemory;
uint8_t quantUW8[kNumQuants];
uint8_t maxQuantUW8[kNumQuants];
uint8_t minQuantUW8[kNumQuants];
uint16_t targetQuantUW16[kNumQuants];
uint16_t incrementUW16;
uint8_t mapUW8[256];
// Get target quantiles.
for (int32_t i = 0; i < kNumQuants - kMaxOnlyLength; i++) {
target_quant_uw16[i] = static_cast<uint16_t>((WEBRTC_SPL_UMUL_16_16(
weight_uw16_[i], maxquant_uw8[i]) + WEBRTC_SPL_UMUL_16_16((1 << 15) -
weight_uw16_[i], minquant_uw8[i])) >> 8); // <Q7>
}
uint16_t tmpUW16;
uint32_t tmpUW32;
int width = frame->width();
int height = frame->height();
for (int32_t i = kNumQuants - kMaxOnlyLength; i < kNumQuants; i++) {
target_quant_uw16[i] = ((uint16_t)maxquant_uw8[i]) << 7;
}
if (frame->IsZeroSize())
{
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoPreocessing, _id,
"Null frame pointer");
return VPM_GENERAL_ERROR;
// 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;
}
// Stricter height check due to subsampling size calculation below.
if (height < 2)
{
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoPreocessing, _id,
"Invalid frame size");
return VPM_GENERAL_ERROR;
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;
}
}
if (!VideoProcessingModule::ValidFrameStats(*stats))
{
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoPreocessing, _id,
"Invalid frame stats");
return VPM_GENERAL_ERROR;
}
// 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]];
}
if (PreDetection(frame->timestamp(), *stats) == -1)
{
return VPM_GENERAL_ERROR;
}
// Frame was altered, so reset stats.
VideoProcessingModule::ClearFrameStats(stats);
// Flicker detection
int32_t detFlicker = DetectFlicker();
if (detFlicker < 0)
{ // Error
return VPM_GENERAL_ERROR;
}
else if (detFlicker != 1)
{
return 0;
}
// Size of luminance component
const uint32_t ySize = height * width;
const uint32_t ySubSize = width * (((height - 1) >>
kLog2OfDownsamplingFactor) + 1);
uint8_t* ySorted = new uint8_t[ySubSize];
uint32_t sortRowIdx = 0;
for (int i = 0; i < height; i += kDownsamplingFactor)
{
memcpy(ySorted + sortRowIdx * width,
frame->buffer(kYPlane) + i * width, width);
sortRowIdx++;
}
webrtc::Sort(ySorted, ySubSize, webrtc::TYPE_UWord8);
uint32_t probIdxUW32 = 0;
quantUW8[0] = 0;
quantUW8[kNumQuants - 1] = 255;
// Ensure we won't get an overflow below.
// In practice, the number of subsampled pixels will not become this large.
if (ySubSize > (1 << 21) - 1)
{
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoPreocessing, _id,
"Subsampled number of pixels too large");
return -1;
}
for (int32_t i = 0; i < kNumProbs; i++)
{
probIdxUW32 = WEBRTC_SPL_UMUL_32_16(ySubSize, _probUW16[i]) >> 11; // <Q0>
quantUW8[i + 1] = ySorted[probIdxUW32];
}
delete [] ySorted;
ySorted = NULL;
// Shift history for new frame.
memmove(_quantHistUW8[1], _quantHistUW8[0], (kFrameHistorySize - 1) * kNumQuants *
sizeof(uint8_t));
// Store current frame in history.
memcpy(_quantHistUW8[0], quantUW8, 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.
frameMemory = (_frameRate + (1 << 5)) >> 5; // Unsigned ceiling. <Q0>
// _frameRate in Q4.
if (frameMemory > kFrameHistorySize)
{
frameMemory = kFrameHistorySize;
}
// Get maximum and minimum.
for (int32_t i = 0; i < kNumQuants; i++)
{
maxQuantUW8[i] = 0;
minQuantUW8[i] = 255;
for (uint32_t j = 0; j < frameMemory; j++)
{
if (_quantHistUW8[j][i] > maxQuantUW8[i])
{
maxQuantUW8[i] = _quantHistUW8[j][i];
}
if (_quantHistUW8[j][i] < minQuantUW8[i])
{
minQuantUW8[i] = _quantHistUW8[j][i];
}
}
}
// Get target quantiles.
for (int32_t i = 0; i < kNumQuants - kMaxOnlyLength; i++)
{
targetQuantUW16[i] = static_cast<uint16_t>((WEBRTC_SPL_UMUL_16_16(
_weightUW16[i], maxQuantUW8[i]) + WEBRTC_SPL_UMUL_16_16((1 << 15) -
_weightUW16[i], minQuantUW8[i])) >> 8); // <Q7>
}
for (int32_t i = kNumQuants - kMaxOnlyLength; i < kNumQuants; i++)
{
targetQuantUW16[i] = ((uint16_t)maxQuantUW8[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, we're safe to use Q7 here.
tmpUW32 = static_cast<uint32_t>(targetQuantUW16[i] -
targetQuantUW16[i - 1]); // <Q7>
tmpUW16 = static_cast<uint16_t>(quantUW8[i] - quantUW8[i - 1]); // <Q0>
if (tmpUW16 > 0)
{
incrementUW16 = static_cast<uint16_t>(WebRtcSpl_DivU32U16(tmpUW32,
tmpUW16)); // <Q7>
}
else
{
// The value is irrelevant; the loop below will only iterate once.
incrementUW16 = 0;
}
mapUW16 = targetQuantUW16[i - 1];
for (uint32_t j = quantUW8[i - 1]; j < (uint32_t)(quantUW8[i] + 1); j++)
{
mapUW8[j] = (uint8_t)((mapUW16 + (1 << 6)) >> 7); // Unsigned round. <Q0>
mapUW16 += incrementUW16;
}
}
// Map to the output frame.
uint8_t* buffer = frame->buffer(kYPlane);
for (uint32_t i = 0; i < ySize; i++)
{
buffer[i] = mapUW8[buffer[i]];
}
// Frame was altered, so reset stats.
VideoProcessingModule::ClearFrameStats(stats);
return 0;
return VPM_OK;
}
/**
Performs some pre-detection operations. Must be called before
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 VideoProcessingModule::FrameStats& stats)
{
int32_t meanVal; // Mean value of frame (Q4)
uint32_t frameRate = 0;
int32_t meanBufferLength; // Temp variable
int32_t VPMDeflickering::PreDetection(const uint32_t timestamp,
const VideoProcessingModule::FrameStats& stats) {
int32_t mean_val; // Mean value of frame (Q4)
uint32_t frame_rate = 0;
int32_t meanBufferLength; // Temp variable.
meanVal = ((stats.sum << kMeanValueScaling) / stats.numPixels);
/* Update mean value buffer.
* This should be done even though we might end up in an unreliable detection.
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.
*/
memmove(_meanBuffer + 1, _meanBuffer, (kMeanBufferLength - 1) * sizeof(int32_t));
_meanBuffer[0] = meanVal;
mean_buffer_length_ = 0;
return 2;
}
mean_buffer_length_ = meanBufferLength;
/* Update timestamp buffer.
* This should be done even though we might end up in an unreliable detection.
*/
memmove(_timestampBuffer + 1, _timestampBuffer, (kMeanBufferLength - 1) *
sizeof(uint32_t));
_timestampBuffer[0] = timestamp;
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;
/* Compute current frame rate (Q4) */
if (_timestampBuffer[kMeanBufferLength - 1] != 0)
{
frameRate = ((90000 << 4) * (kMeanBufferLength - 1));
frameRate /= (_timestampBuffer[0] - _timestampBuffer[kMeanBufferLength - 1]);
}else if (_timestampBuffer[1] != 0)
{
frameRate = (90000 << 4) / (_timestampBuffer[0] - _timestampBuffer[1]);
}
/* Determine required size of mean value buffer (_meanBufferLength) */
if (frameRate == 0) {
meanBufferLength = 1;
}
else {
meanBufferLength = (kNumFlickerBeforeDetect * frameRate) / 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.
*/
_meanBufferLength = 0;
return 2;
}
_meanBufferLength = meanBufferLength;
if ((_timestampBuffer[_meanBufferLength - 1] != 0) && (_meanBufferLength != 1))
{
frameRate = ((90000 << 4) * (_meanBufferLength - 1));
frameRate /= (_timestampBuffer[0] - _timestampBuffer[_meanBufferLength - 1]);
}else if (_timestampBuffer[1] != 0)
{
frameRate = (90000 << 4) / (_timestampBuffer[0] - _timestampBuffer[1]);
}
_frameRate = frameRate;
return 0;
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.
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()
{
/* Local variables */
uint32_t i;
int32_t freqEst; // (Q4) Frequency estimate to base detection upon
int32_t retVal = -1;
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 _meanBufferLength */
if (_meanBufferLength < 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 << kMeanValueScaling); // Q4
int32_t meanOfBuffer = 0; // Mean value of mean value buffer
int32_t numZeros = 0; // Number of zeros that cross the deadzone
int32_t cntState = 0; // State variable for zero crossing regions
int32_t cntStateOld = 0; // Previous state variable for zero crossing regions
/* 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 < _meanBufferLength; i++)
{
meanOfBuffer += _meanBuffer[i];
}
meanOfBuffer += (_meanBufferLength >> 1); // Rounding, not truncation
meanOfBuffer /= _meanBufferLength;
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 = (_meanBuffer[0] >= (meanOfBuffer + deadzone));
cntStateOld -= (_meanBuffer[0] <= (meanOfBuffer - deadzone));
for (i = 1; i < _meanBufferLength; i++)
{
cntState = (_meanBuffer[i] >= (meanOfBuffer + deadzone));
cntState -= (_meanBuffer[i] <= (meanOfBuffer - deadzone));
if (cntStateOld == 0)
{
cntStateOld = -cntState;
}
if (((cntState + cntStateOld) == 0) && (cntState != 0))
{
numZeros++;
cntStateOld = cntState;
}
// 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;
}
/* END count zero crossings */
if (((cntState + cntStateOld) == 0) && (cntState != 0)) {
numZeros++;
cntStateOld = cntState;
}
}
// END count zero crossings.
/* Frequency estimation according to:
* freqEst = numZeros * frameRate / 2 / _meanBufferLength;
*
* Resolution is set to Q4
*/
freqEst = ((numZeros * 90000) << 3);
freqEst /= (_timestampBuffer[0] - _timestampBuffer[_meanBufferLength - 1]);
/* 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 * _frameRate);
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;
}
/* 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)
{
retVal = 1;
}else if (freqState == 0)
{
retVal = 2;
}else
{
retVal = 0;
}
return retVal;
}
/* 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
} // namespace webrtc

View File

@ -8,12 +8,8 @@
* be found in the AUTHORS file in the root of the source tree.
*/
/*
* deflickering.h
*/
#ifndef VPM_DEFLICKERING_H
#define VPM_DEFLICKERING_H
#ifndef WEBRTC_MODULES_VIDEO_PROCESSING_MAIN_SOURCEdeflickering__H
#define WEBRTC_MODULES_VIDEO_PROCESSING_MAIN_SOURCEdeflickering__H
#include <string.h> // NULL
@ -22,44 +18,43 @@
namespace webrtc {
class VPMDeflickering
{
public:
VPMDeflickering();
~VPMDeflickering();
class VPMDeflickering {
public:
VPMDeflickering();
~VPMDeflickering();
int32_t ChangeUniqueId(int32_t id);
int32_t ChangeUniqueId(int32_t id);
void Reset();
void Reset();
int32_t ProcessFrame(I420VideoFrame* frame,
VideoProcessingModule::FrameStats* stats);
int32_t ProcessFrame(I420VideoFrame* frame,
VideoProcessingModule::FrameStats* stats);
private:
int32_t PreDetection(uint32_t timestamp,
const VideoProcessingModule::FrameStats& stats);
private:
int32_t PreDetection(uint32_t timestamp,
const VideoProcessingModule::FrameStats& stats);
int32_t DetectFlicker();
int32_t DetectFlicker();
enum { kMeanBufferLength = 32 };
enum { kFrameHistorySize = 15 };
enum { kNumProbs = 12 };
enum { kNumQuants = kNumProbs + 2 };
enum { kMaxOnlyLength = 5 };
enum { kMeanBufferLength = 32 };
enum { kFrameHistory_size = 15 };
enum { kNumProbs = 12 };
enum { kNumQuants = kNumProbs + 2 };
enum { kMaxOnlyLength = 5 };
int32_t _id;
int32_t id_;
uint32_t _meanBufferLength;
uint8_t _detectionState; // 0: No flickering
// 1: Flickering detected
// 2: In flickering
int32_t _meanBuffer[kMeanBufferLength];
uint32_t _timestampBuffer[kMeanBufferLength];
uint32_t _frameRate;
static const uint16_t _probUW16[kNumProbs];
static const uint16_t _weightUW16[kNumQuants - kMaxOnlyLength];
uint8_t _quantHistUW8[kFrameHistorySize][kNumQuants];
uint32_t mean_buffer_length_;
uint8_t detection_state_; // 0: No flickering
// 1: Flickering detected
// 2: In flickering
int32_t mean_buffer_[kMeanBufferLength];
uint32_t timestamp_buffer_[kMeanBufferLength];
uint32_t frame_rate_;
static const uint16_t prob_uw16_[kNumProbs];
static const uint16_t weight_uw16_[kNumQuants - kMaxOnlyLength];
uint8_t quant_hist_uw8_[kFrameHistory_size][kNumQuants];
};
} // namespace
} // namespace webrtc
#endif // VPM_DEFLICKERING_H
#endif // WEBRTC_MODULES_VIDEO_PROCESSING_MAIN_SOURCEdeflickering__H

View File

@ -14,167 +14,146 @@
#include <string.h>
namespace webrtc {
// Down-sampling in time (unit: number of frames)
enum { kSubsamplingTime = 0 };
// Sub-sampling in width (unit: power of 2.
enum { kSubsamplingWidth = 0 };
// Sub-sampling in height (unit: power of 2)
enum { kSubsamplingHeight = 0 };
// (Q8) De-noising filter parameter
enum { kDenoiseFiltParam = 179 };
// (Q8) 1 - filter parameter
enum { kDenoiseFiltParamRec = 77 };
// (Q8) De-noising threshold level
enum { kDenoiseThreshold = 19200 };
enum { kSubsamplingTime = 0 }; // Down-sampling in time (unit: number of frames)
enum { kSubsamplingWidth = 0 }; // Sub-sampling in width (unit: power of 2)
enum { kSubsamplingHeight = 0 }; // Sub-sampling in height (unit: power of 2)
enum { kDenoiseFiltParam = 179 }; // (Q8) De-noising filter parameter
enum { kDenoiseFiltParamRec = 77 }; // (Q8) 1 - filter parameter
enum { kDenoiseThreshold = 19200 }; // (Q8) De-noising threshold level
VPMDenoising::VPMDenoising() :
_id(0),
_moment1(NULL),
_moment2(NULL)
{
Reset();
VPMDenoising::VPMDenoising()
: id_(0),
moment1_(NULL),
moment2_(NULL) {
Reset();
}
VPMDenoising::~VPMDenoising()
{
if (_moment1)
{
delete [] _moment1;
_moment1 = NULL;
}
if (_moment2)
{
delete [] _moment2;
_moment2 = NULL;
}
VPMDenoising::~VPMDenoising() {
if (moment1_) {
delete [] moment1_;
moment1_ = NULL;
}
int32_t
VPMDenoising::ChangeUniqueId(const int32_t id)
{
_id = id;
return VPM_OK;
if (moment2_) {
delete [] moment2_;
moment2_ = NULL;
}
}
void
VPMDenoising::Reset()
{
_frameSize = 0;
_denoiseFrameCnt = 0;
if (_moment1)
{
delete [] _moment1;
_moment1 = NULL;
}
if (_moment2)
{
delete [] _moment2;
_moment2 = NULL;
}
int32_t VPMDenoising::ChangeUniqueId(const int32_t id) {
id_ = id;
return VPM_OK;
}
int32_t
VPMDenoising::ProcessFrame(I420VideoFrame* frame)
{
assert(frame);
int32_t thevar;
int k;
int jsub, ksub;
int32_t diff0;
uint32_t tmpMoment1;
uint32_t tmpMoment2;
uint32_t tmp;
int32_t numPixelsChanged = 0;
void VPMDenoising::Reset() {
frame_size_ = 0;
denoise_frame_cnt_ = 0;
if (frame->IsZeroSize())
{
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoPreocessing, _id,
"zero size frame");
return VPM_GENERAL_ERROR;
if (moment1_) {
delete [] moment1_;
moment1_ = NULL;
}
if (moment2_) {
delete [] moment2_;
moment2_ = NULL;
}
}
int32_t VPMDenoising::ProcessFrame(I420VideoFrame* frame) {
assert(frame);
int32_t thevar;
int k;
int jsub, ksub;
int32_t diff0;
uint32_t tmp_moment1;
uint32_t tmp_moment2;
uint32_t tmp;
int32_t num_pixels_changed = 0;
if (frame->IsZeroSize()) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoPreocessing, id_,
"zero size frame");
return VPM_GENERAL_ERROR;
}
int width = frame->width();
int height = frame->height();
/* Size of luminance component */
const uint32_t y_size = height * width;
/* Initialization */
if (y_size != frame_size_) {
delete [] moment1_;
moment1_ = NULL;
delete [] moment2_;
moment2_ = NULL;
}
frame_size_ = y_size;
if (!moment1_) {
moment1_ = new uint32_t[y_size];
memset(moment1_, 0, sizeof(uint32_t)*y_size);
}
if (!moment2_) {
moment2_ = new uint32_t[y_size];
memset(moment2_, 0, sizeof(uint32_t)*y_size);
}
/* Apply de-noising on each pixel, but update variance sub-sampled */
uint8_t* buffer = frame->buffer(kYPlane);
for (int i = 0; i < height; i++) { // Collect over height
k = i * width;
ksub = ((i >> kSubsamplingHeight) << kSubsamplingHeight) * width;
for (int j = 0; j < width; j++) { // Collect over width
jsub = ((j >> kSubsamplingWidth) << kSubsamplingWidth);
/* Update mean value for every pixel and every frame */
tmp_moment1 = moment1_[k + j];
tmp_moment1 *= kDenoiseFiltParam; // Q16
tmp_moment1 += ((kDenoiseFiltParamRec * ((uint32_t)buffer[k + j])) << 8);
tmp_moment1 >>= 8; // Q8
moment1_[k + j] = tmp_moment1;
tmp_moment2 = moment2_[ksub + jsub];
if ((ksub == k) && (jsub == j) && (denoise_frame_cnt_ == 0)) {
tmp = ((uint32_t)buffer[k + j] *
(uint32_t)buffer[k + j]);
tmp_moment2 *= kDenoiseFiltParam; // Q16
tmp_moment2 += ((kDenoiseFiltParamRec * tmp) << 8);
tmp_moment2 >>= 8; // Q8
}
moment2_[k + j] = tmp_moment2;
/* Current event = deviation from mean value */
diff0 = ((int32_t)buffer[k + j] << 8) - moment1_[k + j];
/* Recent events = variance (variations over time) */
thevar = moment2_[k + j];
thevar -= ((moment1_[k + j] * moment1_[k + j]) >> 8);
// De-noising criteria, i.e., when should we replace a pixel by its mean.
// 1) recent events are minor.
// 2) current events are minor.
if ((thevar < kDenoiseThreshold)
&& ((diff0 * diff0 >> 8) < kDenoiseThreshold)) {
// Replace with mean.
buffer[k + j] = (uint8_t)(moment1_[k + j] >> 8);
num_pixels_changed++;
}
}
}
int width = frame->width();
int height = frame->height();
denoise_frame_cnt_++;
if (denoise_frame_cnt_ > kSubsamplingTime)
denoise_frame_cnt_ = 0;
/* Size of luminance component */
const uint32_t ysize = height * width;
/* Initialization */
if (ysize != _frameSize)
{
delete [] _moment1;
_moment1 = NULL;
delete [] _moment2;
_moment2 = NULL;
}
_frameSize = ysize;
if (!_moment1)
{
_moment1 = new uint32_t[ysize];
memset(_moment1, 0, sizeof(uint32_t)*ysize);
}
if (!_moment2)
{
_moment2 = new uint32_t[ysize];
memset(_moment2, 0, sizeof(uint32_t)*ysize);
}
/* Apply de-noising on each pixel, but update variance sub-sampled */
uint8_t* buffer = frame->buffer(kYPlane);
for (int i = 0; i < height; i++)
{ // Collect over height
k = i * width;
ksub = ((i >> kSubsamplingHeight) << kSubsamplingHeight) * width;
for (int j = 0; j < width; j++)
{ // Collect over width
jsub = ((j >> kSubsamplingWidth) << kSubsamplingWidth);
/* Update mean value for every pixel and every frame */
tmpMoment1 = _moment1[k + j];
tmpMoment1 *= kDenoiseFiltParam; // Q16
tmpMoment1 += ((kDenoiseFiltParamRec *
((uint32_t)buffer[k + j])) << 8);
tmpMoment1 >>= 8; // Q8
_moment1[k + j] = tmpMoment1;
tmpMoment2 = _moment2[ksub + jsub];
if ((ksub == k) && (jsub == j) && (_denoiseFrameCnt == 0))
{
tmp = ((uint32_t)buffer[k + j] *
(uint32_t)buffer[k + j]);
tmpMoment2 *= kDenoiseFiltParam; // Q16
tmpMoment2 += ((kDenoiseFiltParamRec * tmp)<<8);
tmpMoment2 >>= 8; // Q8
}
_moment2[k + j] = tmpMoment2;
/* Current event = deviation from mean value */
diff0 = ((int32_t)buffer[k + j] << 8) - _moment1[k + j];
/* Recent events = variance (variations over time) */
thevar = _moment2[k + j];
thevar -= ((_moment1[k + j] * _moment1[k + j]) >> 8);
/***************************************************************************
* De-noising criteria, i.e., when should we replace a pixel by its mean
*
* 1) recent events are minor
* 2) current events are minor
***************************************************************************/
if ((thevar < kDenoiseThreshold)
&& ((diff0 * diff0 >> 8) < kDenoiseThreshold))
{ // Replace with mean
buffer[k + j] = (uint8_t)(_moment1[k + j] >> 8);
numPixelsChanged++;
}
}
}
/* Update frame counter */
_denoiseFrameCnt++;
if (_denoiseFrameCnt > kSubsamplingTime)
{
_denoiseFrameCnt = 0;
}
return numPixelsChanged;
return num_pixels_changed;
}
} // namespace

View File

@ -8,39 +8,35 @@
* be found in the AUTHORS file in the root of the source tree.
*/
/*
* denoising.h
*/
#ifndef VPM_DENOISING_H
#define VPM_DENOISING_H
#ifndef WEBRTC_MODULES_VIDEO_PROCESSING_MAIN_SOURCE_DENOISING_H_
#define WEBRTC_MODULES_VIDEO_PROCESSING_MAIN_SOURCE_DENOISING_H_
#include "webrtc/modules/video_processing/main/interface/video_processing.h"
#include "webrtc/typedefs.h"
namespace webrtc {
class VPMDenoising
{
public:
VPMDenoising();
~VPMDenoising();
class VPMDenoising {
public:
VPMDenoising();
~VPMDenoising();
int32_t ChangeUniqueId(int32_t id);
int32_t ChangeUniqueId(int32_t id);
void Reset();
void Reset();
int32_t ProcessFrame(I420VideoFrame* frame);
int32_t ProcessFrame(I420VideoFrame* frame);
private:
int32_t _id;
private:
int32_t id_;
uint32_t* _moment1; // (Q8) First order moment (mean)
uint32_t* _moment2; // (Q8) Second order moment
uint32_t _frameSize; // Size (# of pixels) of frame
int _denoiseFrameCnt; // Counter for subsampling in time
uint32_t* moment1_; // (Q8) First order moment (mean).
uint32_t* moment2_; // (Q8) Second order moment.
uint32_t frame_size_; // Size (# of pixels) of frame.
int denoise_frame_cnt_; // Counter for subsampling in time.
};
} // namespace
} // namespace webrtc
#endif // WEBRTC_MODULES_VIDEO_PROCESSING_MAIN_SOURCE_DENOISING_H_
#endif // VPM_DENOISING_H

View File

@ -13,177 +13,137 @@
namespace webrtc {
VPMFramePreprocessor::VPMFramePreprocessor():
_id(0),
_contentMetrics(NULL),
_maxFrameRate(0),
_resampledFrame(),
_enableCA(false),
_frameCnt(0)
{
_spatialResampler = new VPMSimpleSpatialResampler();
_ca = new VPMContentAnalysis(true);
_vd = new VPMVideoDecimator();
VPMFramePreprocessor::VPMFramePreprocessor()
: id_(0),
content_metrics_(NULL),
max_frame_rate_(0),
resampled_frame_(),
enable_ca_(false),
frame_cnt_(0) {
spatial_resampler_ = new VPMSimpleSpatialResampler();
ca_ = new VPMContentAnalysis(true);
vd_ = new VPMVideoDecimator();
}
VPMFramePreprocessor::~VPMFramePreprocessor()
{
Reset();
delete _spatialResampler;
delete _ca;
delete _vd;
VPMFramePreprocessor::~VPMFramePreprocessor() {
Reset();
delete spatial_resampler_;
delete ca_;
delete vd_;
}
int32_t
VPMFramePreprocessor::ChangeUniqueId(const int32_t id)
{
_id = id;
return VPM_OK;
int32_t VPMFramePreprocessor::ChangeUniqueId(const int32_t id) {
id_ = id;
return VPM_OK;
}
void
VPMFramePreprocessor::Reset()
{
_ca->Release();
_vd->Reset();
_contentMetrics = NULL;
_spatialResampler->Reset();
_enableCA = false;
_frameCnt = 0;
}
void
VPMFramePreprocessor::EnableTemporalDecimation(bool enable)
{
_vd->EnableTemporalDecimation(enable);
}
void
VPMFramePreprocessor::EnableContentAnalysis(bool enable)
{
_enableCA = enable;
void VPMFramePreprocessor::Reset() {
ca_->Release();
vd_->Reset();
content_metrics_ = NULL;
spatial_resampler_->Reset();
enable_ca_ = false;
frame_cnt_ = 0;
}
void
VPMFramePreprocessor::SetInputFrameResampleMode(VideoFrameResampling resamplingMode)
{
_spatialResampler->SetInputFrameResampleMode(resamplingMode);
void VPMFramePreprocessor::EnableTemporalDecimation(bool enable) {
vd_->EnableTemporalDecimation(enable);
}
int32_t
VPMFramePreprocessor::SetMaxFrameRate(uint32_t maxFrameRate)
{
if (maxFrameRate == 0)
{
return VPM_PARAMETER_ERROR;
void VPMFramePreprocessor::EnableContentAnalysis(bool enable) {
enable_ca_ = enable;
}
void VPMFramePreprocessor::SetInputFrameResampleMode(
VideoFrameResampling resampling_mode) {
spatial_resampler_->SetInputFrameResampleMode(resampling_mode);
}
int32_t VPMFramePreprocessor::SetMaxFramerate(uint32_t max_frame_rate) {
if (max_frame_rate == 0) return VPM_PARAMETER_ERROR;
// Max allowed frame_rate.
max_frame_rate_ = max_frame_rate;
return vd_->SetMaxFramerate(max_frame_rate);
}
int32_t VPMFramePreprocessor::SetTargetResolution(
uint32_t width, uint32_t height, uint32_t frame_rate) {
if ( (width == 0) || (height == 0) || (frame_rate == 0)) {
return VPM_PARAMETER_ERROR;
}
int32_t ret_val = 0;
ret_val = spatial_resampler_->SetTargetFrameSize(width, height);
if (ret_val < 0) return ret_val;
ret_val = vd_->SetTargetframe_rate(frame_rate);
if (ret_val < 0) return ret_val;
return VPM_OK;
}
void VPMFramePreprocessor::UpdateIncomingframe_rate() {
vd_->UpdateIncomingframe_rate();
}
uint32_t VPMFramePreprocessor::Decimatedframe_rate() {
return vd_->Decimatedframe_rate();
}
uint32_t VPMFramePreprocessor::DecimatedWidth() const {
return spatial_resampler_->TargetWidth();
}
uint32_t VPMFramePreprocessor::DecimatedHeight() const {
return spatial_resampler_->TargetHeight();
}
int32_t VPMFramePreprocessor::PreprocessFrame(const I420VideoFrame& frame,
I420VideoFrame** processed_frame) {
if (frame.IsZeroSize()) {
return VPM_PARAMETER_ERROR;
}
vd_->UpdateIncomingframe_rate();
if (vd_->DropFrame()) {
WEBRTC_TRACE(webrtc::kTraceStream, webrtc::kTraceVideo, id_,
"Drop frame due to frame rate");
return 1; // drop 1 frame
}
// Resizing incoming frame if needed. Otherwise, remains NULL.
// We are not allowed to resample the input frame (must make a copy of it).
*processed_frame = NULL;
if (spatial_resampler_->ApplyResample(frame.width(), frame.height())) {
int32_t ret = spatial_resampler_->ResampleFrame(frame, &resampled_frame_);
if (ret != VPM_OK) return ret;
*processed_frame = &resampled_frame_;
}
// Perform content analysis on the frame to be encoded.
if (enable_ca_) {
// Compute new metrics every |kSkipFramesCA| frames, starting with
// the first frame.
if (frame_cnt_ % kSkipFrameCA == 0) {
if (*processed_frame == NULL) {
content_metrics_ = ca_->ComputeContentMetrics(frame);
} else {
content_metrics_ = ca_->ComputeContentMetrics(resampled_frame_);
}
}
//Max allowed frame rate
_maxFrameRate = maxFrameRate;
return _vd->SetMaxFrameRate(maxFrameRate);
}
int32_t
VPMFramePreprocessor::SetTargetResolution(uint32_t width, uint32_t height, uint32_t frameRate)
{
if ( (width == 0) || (height == 0) || (frameRate == 0))
{
return VPM_PARAMETER_ERROR;
}
int32_t retVal = 0;
retVal = _spatialResampler->SetTargetFrameSize(width, height);
if (retVal < 0)
{
return retVal;
}
retVal = _vd->SetTargetFrameRate(frameRate);
if (retVal < 0)
{
return retVal;
}
return VPM_OK;
++frame_cnt_;
}
return VPM_OK;
}
void
VPMFramePreprocessor::UpdateIncomingFrameRate()
{
_vd->UpdateIncomingFrameRate();
}
uint32_t
VPMFramePreprocessor::DecimatedFrameRate()
{
return _vd->DecimatedFrameRate();
}
uint32_t
VPMFramePreprocessor::DecimatedWidth() const
{
return _spatialResampler->TargetWidth();
}
uint32_t
VPMFramePreprocessor::DecimatedHeight() const
{
return _spatialResampler->TargetHeight();
}
int32_t
VPMFramePreprocessor::PreprocessFrame(const I420VideoFrame& frame,
I420VideoFrame** processedFrame)
{
if (frame.IsZeroSize())
{
return VPM_PARAMETER_ERROR;
}
_vd->UpdateIncomingFrameRate();
if (_vd->DropFrame())
{
WEBRTC_TRACE(webrtc::kTraceStream, webrtc::kTraceVideo, _id,
"Drop frame due to frame rate");
return 1; // drop 1 frame
}
// Resizing incoming frame if needed. Otherwise, remains NULL.
// We are not allowed to resample the input frame (must make a copy of it).
*processedFrame = NULL;
if (_spatialResampler->ApplyResample(frame.width(), frame.height())) {
int32_t ret = _spatialResampler->ResampleFrame(frame, &_resampledFrame);
if (ret != VPM_OK)
return ret;
*processedFrame = &_resampledFrame;
}
// Perform content analysis on the frame to be encoded.
if (_enableCA)
{
// Compute new metrics every |kSkipFramesCA| frames, starting with
// the first frame.
if (_frameCnt % kSkipFrameCA == 0) {
if (*processedFrame == NULL) {
_contentMetrics = _ca->ComputeContentMetrics(frame);
} else {
_contentMetrics = _ca->ComputeContentMetrics(_resampledFrame);
}
}
++_frameCnt;
}
return VPM_OK;
}
VideoContentMetrics*
VPMFramePreprocessor::ContentMetrics() const
{
return _contentMetrics;
VideoContentMetrics* VPMFramePreprocessor::ContentMetrics() const {
return content_metrics_;
}
} // namespace

View File

@ -11,8 +11,8 @@
/*
* frame_preprocessor.h
*/
#ifndef VPM_FRAME_PREPROCESSOR_H
#define VPM_FRAME_PREPROCESSOR_H
#ifndef WEBRTC_MODULES_VIDEO_PROCESSING_MAIN_SOURCE_FRAME_PREPROCESSOR_H
#define WEBRTC_MODULES_VIDEO_PROCESSING_MAIN_SOURCE_FRAME_PREPROCESSOR_H
#include "webrtc/modules/video_processing/main/interface/video_processing.h"
#include "webrtc/modules/video_processing/main/source/content_analysis.h"
@ -22,65 +22,62 @@
namespace webrtc {
class VPMFramePreprocessor {
public:
VPMFramePreprocessor();
~VPMFramePreprocessor();
class VPMFramePreprocessor
{
public:
int32_t ChangeUniqueId(const int32_t id);
VPMFramePreprocessor();
~VPMFramePreprocessor();
void Reset();
int32_t ChangeUniqueId(const int32_t id);
// Enable temporal decimation.
void EnableTemporalDecimation(bool enable);
void Reset();
void SetInputFrameResampleMode(VideoFrameResampling resampling_mode);
// Enable temporal decimation
void EnableTemporalDecimation(bool enable);
// Enable content analysis.
void EnableContentAnalysis(bool enable);
void SetInputFrameResampleMode(VideoFrameResampling resamplingMode);
// Set max frame rate.
int32_t SetMaxFramerate(uint32_t max_frame_rate);
//Enable content analysis
void EnableContentAnalysis(bool enable);
// Set target resolution: frame rate and dimension.
int32_t SetTargetResolution(uint32_t width, uint32_t height,
uint32_t frame_rate);
//Set max frame rate
int32_t SetMaxFrameRate(uint32_t maxFrameRate);
// Update incoming frame rate/dimension.
void UpdateIncomingframe_rate();
//Set target resolution: frame rate and dimension
int32_t SetTargetResolution(uint32_t width, uint32_t height,
uint32_t frameRate);
int32_t updateIncomingFrameSize(uint32_t width, uint32_t height);
//Update incoming frame rate/dimension
void UpdateIncomingFrameRate();
// Set decimated values: frame rate/dimension.
uint32_t Decimatedframe_rate();
uint32_t DecimatedWidth() const;
uint32_t DecimatedHeight() const;
int32_t updateIncomingFrameSize(uint32_t width, uint32_t height);
// Preprocess output:
int32_t PreprocessFrame(const I420VideoFrame& frame,
I420VideoFrame** processed_frame);
VideoContentMetrics* ContentMetrics() const;
//Set decimated values: frame rate/dimension
uint32_t DecimatedFrameRate();
uint32_t DecimatedWidth() const;
uint32_t DecimatedHeight() const;
private:
// The content does not change so much every frame, so to reduce complexity
// we can compute new content metrics every |kSkipFrameCA| frames.
enum { kSkipFrameCA = 2 };
//Preprocess output:
int32_t PreprocessFrame(const I420VideoFrame& frame,
I420VideoFrame** processedFrame);
VideoContentMetrics* ContentMetrics() const;
int32_t id_;
VideoContentMetrics* content_metrics_;
uint32_t max_frame_rate_;
I420VideoFrame resampled_frame_;
VPMSpatialResampler* spatial_resampler_;
VPMContentAnalysis* ca_;
VPMVideoDecimator* vd_;
bool enable_ca_;
int frame_cnt_;
private:
// The content does not change so much every frame, so to reduce complexity
// we can compute new content metrics every |kSkipFrameCA| frames.
enum { kSkipFrameCA = 2 };
};
int32_t _id;
VideoContentMetrics* _contentMetrics;
uint32_t _maxFrameRate;
I420VideoFrame _resampledFrame;
VPMSpatialResampler* _spatialResampler;
VPMContentAnalysis* _ca;
VPMVideoDecimator* _vd;
bool _enableCA;
int _frameCnt;
}; // end of VPMFramePreprocessor class definition
} // namespace webrtc
} // namespace
#endif // VPM_FRAME_PREPROCESS_H
#endif // WEBRTC_MODULES_VIDEO_PROCESSING_MAIN_SOURCE_FRAME_PREPROCESSOR_H

View File

@ -14,109 +14,85 @@
namespace webrtc {
VPMSimpleSpatialResampler::VPMSimpleSpatialResampler()
:
_resamplingMode(kFastRescaling),
_targetWidth(0),
_targetHeight(0),
_scaler()
{
}
: resampling_mode_(kFastRescaling),
target_width_(0),
target_height_(0),
scaler_() {}
VPMSimpleSpatialResampler::~VPMSimpleSpatialResampler()
{
//
}
VPMSimpleSpatialResampler::~VPMSimpleSpatialResampler() {}
int32_t
VPMSimpleSpatialResampler::SetTargetFrameSize(int32_t width,
int32_t height)
{
if (_resamplingMode == kNoRescaling) {
return VPM_OK;
}
int32_t VPMSimpleSpatialResampler::SetTargetFrameSize(int32_t width,
int32_t height) {
if (resampling_mode_ == kNoRescaling) return VPM_OK;
if (width < 1 || height < 1) {
return VPM_PARAMETER_ERROR;
}
if (width < 1 || height < 1) return VPM_PARAMETER_ERROR;
_targetWidth = width;
_targetHeight = height;
target_width_ = width;
target_height_ = height;
return VPM_OK;
}
void
VPMSimpleSpatialResampler::SetInputFrameResampleMode(VideoFrameResampling
resamplingMode)
{
_resamplingMode = resamplingMode;
void VPMSimpleSpatialResampler::SetInputFrameResampleMode(
VideoFrameResampling resampling_mode) {
resampling_mode_ = resampling_mode;
}
void
VPMSimpleSpatialResampler::Reset()
{
_resamplingMode = kFastRescaling;
_targetWidth = 0;
_targetHeight = 0;
void VPMSimpleSpatialResampler::Reset() {
resampling_mode_ = kFastRescaling;
target_width_ = 0;
target_height_ = 0;
}
int32_t
VPMSimpleSpatialResampler::ResampleFrame(const I420VideoFrame& inFrame,
I420VideoFrame* outFrame)
{
int32_t VPMSimpleSpatialResampler::ResampleFrame(const I420VideoFrame& inFrame,
I420VideoFrame* outFrame) {
// Don't copy if frame remains as is.
if (_resamplingMode == kNoRescaling)
if (resampling_mode_ == kNoRescaling)
return VPM_OK;
// Check if re-sampling is needed
else if ((inFrame.width() == _targetWidth) &&
(inFrame.height() == _targetHeight)) {
else if ((inFrame.width() == target_width_) &&
(inFrame.height() == target_height_)) {
return VPM_OK;
}
// Setting scaler
// TODO(mikhal/marpan): Should we allow for setting the filter mode in
// _scale.Set() with |_resamplingMode|?
int retVal = 0;
retVal = _scaler.Set(inFrame.width(), inFrame.height(),
_targetWidth, _targetHeight, kI420, kI420, kScaleBox);
if (retVal < 0)
return retVal;
// _scale.Set() with |resampling_mode_|?
int ret_val = 0;
ret_val = scaler_.Set(inFrame.width(), inFrame.height(),
target_width_, target_height_, kI420, kI420, kScaleBox);
if (ret_val < 0)
return ret_val;
retVal = _scaler.Scale(inFrame, outFrame);
ret_val = scaler_.Scale(inFrame, outFrame);
// Setting time parameters to the output frame.
// Timestamp will be reset in Scale call above, so we should set it after.
outFrame->set_timestamp(inFrame.timestamp());
outFrame->set_render_time_ms(inFrame.render_time_ms());
if (retVal == 0)
if (ret_val == 0)
return VPM_OK;
else
return VPM_SCALE_ERROR;
}
int32_t
VPMSimpleSpatialResampler::TargetHeight()
{
return _targetHeight;
int32_t VPMSimpleSpatialResampler::TargetHeight() {
return target_height_;
}
int32_t
VPMSimpleSpatialResampler::TargetWidth()
{
return _targetWidth;
int32_t VPMSimpleSpatialResampler::TargetWidth() {
return target_width_;
}
bool
VPMSimpleSpatialResampler::ApplyResample(int32_t width,
int32_t height)
{
if ((width == _targetWidth && height == _targetHeight) ||
_resamplingMode == kNoRescaling)
bool VPMSimpleSpatialResampler::ApplyResample(int32_t width,
int32_t height) {
if ((width == target_width_ && height == target_height_) ||
resampling_mode_ == kNoRescaling)
return false;
else
return true;
}
} // namespace
} // namespace webrtc

View File

@ -8,12 +8,8 @@
* be found in the AUTHORS file in the root of the source tree.
*/
/*
* spatial_resampler.h
*/
#ifndef VPM_SPATIAL_RESAMPLER_H
#define VPM_SPATIAL_RESAMPLER_H
#ifndef WEBRTC_MODULES_VIDEO_PROCESSING_MAIN_SOURCE_SPATIAL_RESAMPLER_H
#define WEBRTC_MODULES_VIDEO_PROCESSING_MAIN_SOURCE_SPATIAL_RESAMPLER_H
#include "webrtc/typedefs.h"
@ -25,13 +21,12 @@
namespace webrtc {
class VPMSpatialResampler
{
public:
class VPMSpatialResampler {
public:
virtual ~VPMSpatialResampler() {};
virtual int32_t SetTargetFrameSize(int32_t width, int32_t height) = 0;
virtual void SetInputFrameResampleMode(VideoFrameResampling
resamplingMode) = 0;
resampling_mode) = 0;
virtual void Reset() = 0;
virtual int32_t ResampleFrame(const I420VideoFrame& inFrame,
I420VideoFrame* outFrame) = 0;
@ -40,13 +35,12 @@ public:
virtual bool ApplyResample(int32_t width, int32_t height) = 0;
};
class VPMSimpleSpatialResampler : public VPMSpatialResampler
{
public:
class VPMSimpleSpatialResampler : public VPMSpatialResampler {
public:
VPMSimpleSpatialResampler();
~VPMSimpleSpatialResampler();
virtual int32_t SetTargetFrameSize(int32_t width, int32_t height);
virtual void SetInputFrameResampleMode(VideoFrameResampling resamplingMode);
virtual void SetInputFrameResampleMode(VideoFrameResampling resampling_mode);
virtual void Reset();
virtual int32_t ResampleFrame(const I420VideoFrame& inFrame,
I420VideoFrame* outFrame);
@ -54,14 +48,14 @@ public:
virtual int32_t TargetHeight();
virtual bool ApplyResample(int32_t width, int32_t height);
private:
private:
VideoFrameResampling _resamplingMode;
int32_t _targetWidth;
int32_t _targetHeight;
Scaler _scaler;
VideoFrameResampling resampling_mode_;
int32_t target_width_;
int32_t target_height_;
Scaler scaler_;
};
} // namespace
} // namespace webrtc
#endif
#endif // WEBRTC_MODULES_VIDEO_PROCESSING_MAIN_SOURCE_SPATIAL_RESAMPLER_H

View File

@ -17,219 +17,156 @@
namespace webrtc {
VPMVideoDecimator::VPMVideoDecimator()
:
_overShootModifier(0),
_dropCount(0),
_keepCount(0),
_targetFrameRate(30),
_incomingFrameRate(0.0f),
_maxFrameRate(30),
_incomingFrameTimes(),
_enableTemporalDecimation(true)
{
Reset();
: overshoot_modifier_(0),
drop_count_(0),
keep_count_(0),
target_frame_rate_(30),
incoming_frame_rate_(0.0f),
max_frame_rate_(30),
incoming_frame_times_(),
enable_temporal_decimation_(true) {
Reset();
}
VPMVideoDecimator::~VPMVideoDecimator()
{
//
VPMVideoDecimator::~VPMVideoDecimator() {}
void VPMVideoDecimator::Reset() {
overshoot_modifier_ = 0;
drop_count_ = 0;
keep_count_ = 0;
target_frame_rate_ = 30;
incoming_frame_rate_ = 0.0f;
max_frame_rate_ = 30;
memset(incoming_frame_times_, 0, sizeof(incoming_frame_times_));
enable_temporal_decimation_ = true;
}
void
VPMVideoDecimator::Reset()
{
_overShootModifier = 0;
_dropCount = 0;
_keepCount = 0;
_targetFrameRate = 30;
_incomingFrameRate = 0.0f;
_maxFrameRate = 30;
memset(_incomingFrameTimes, 0, sizeof(_incomingFrameTimes));
_enableTemporalDecimation = true;
void VPMVideoDecimator::EnableTemporalDecimation(bool enable) {
enable_temporal_decimation_ = enable;
}
void
VPMVideoDecimator::EnableTemporalDecimation(bool enable)
{
_enableTemporalDecimation = enable;
}
int32_t
VPMVideoDecimator::SetMaxFrameRate(uint32_t maxFrameRate)
{
if (maxFrameRate == 0)
{
return VPM_PARAMETER_ERROR;
}
int32_t VPMVideoDecimator::SetMaxFramerate(uint32_t max_frame_rate) {
if (max_frame_rate == 0) return VPM_PARAMETER_ERROR;
_maxFrameRate = maxFrameRate;
if (_targetFrameRate > _maxFrameRate)
{
_targetFrameRate = _maxFrameRate;
max_frame_rate_ = max_frame_rate;
}
return VPM_OK;
if (target_frame_rate_ > max_frame_rate_)
target_frame_rate_ = max_frame_rate_;
return VPM_OK;
}
int32_t
VPMVideoDecimator::SetTargetFrameRate(uint32_t frameRate)
{
if (frameRate == 0)
{
return VPM_PARAMETER_ERROR;
}
if (frameRate > _maxFrameRate)
{
//override
_targetFrameRate = _maxFrameRate;
}
else
{
_targetFrameRate = frameRate;
}
return VPM_OK;
int32_t VPMVideoDecimator::SetTargetframe_rate(uint32_t frame_rate) {
if (frame_rate == 0) return VPM_PARAMETER_ERROR;
if (frame_rate > max_frame_rate_) {
// Override.
target_frame_rate_ = max_frame_rate_;
} else {
target_frame_rate_ = frame_rate;
}
return VPM_OK;
}
bool
VPMVideoDecimator::DropFrame()
{
if (!_enableTemporalDecimation)
{
return false;
bool VPMVideoDecimator::DropFrame() {
if (!enable_temporal_decimation_) return false;
if (incoming_frame_rate_ <= 0) return false;
const uint32_t incomingframe_rate =
static_cast<uint32_t>(incoming_frame_rate_ + 0.5f);
if (target_frame_rate_ == 0) return true;
bool drop = false;
if (incomingframe_rate > target_frame_rate_) {
int32_t overshoot =
overshoot_modifier_ + (incomingframe_rate - target_frame_rate_);
if (overshoot < 0) {
overshoot = 0;
overshoot_modifier_ = 0;
}
if (_incomingFrameRate <= 0)
{
return false;
if (overshoot && 2 * overshoot < (int32_t) incomingframe_rate) {
if (drop_count_) { // Just got here so drop to be sure.
drop_count_ = 0;
return true;
}
const uint32_t dropVar = incomingframe_rate / overshoot;
if (keep_count_ >= dropVar) {
drop = true;
overshoot_modifier_ = -((int32_t) incomingframe_rate % overshoot) / 3;
keep_count_ = 1;
} else {
keep_count_++;
}
} else {
keep_count_ = 0;
const uint32_t dropVar = overshoot / target_frame_rate_;
if (drop_count_ < dropVar) {
drop = true;
drop_count_++;
} else {
overshoot_modifier_ = overshoot % target_frame_rate_;
drop = false;
drop_count_ = 0;
}
}
const uint32_t incomingFrameRate = static_cast<uint32_t>(_incomingFrameRate + 0.5f);
if (_targetFrameRate == 0)
{
return true;
}
bool drop = false;
if (incomingFrameRate > _targetFrameRate)
{
int32_t overshoot = _overShootModifier + (incomingFrameRate - _targetFrameRate);
if(overshoot < 0)
{
overshoot = 0;
_overShootModifier = 0;
}
if (overshoot && 2 * overshoot < (int32_t) incomingFrameRate)
{
if (_dropCount) // Just got here so drop to be sure.
{
_dropCount = 0;
return true;
}
const uint32_t dropVar = incomingFrameRate / overshoot;
if (_keepCount >= dropVar)
{
drop = true;
_overShootModifier = -((int32_t) incomingFrameRate % overshoot) / 3;
_keepCount = 1;
}
else
{
_keepCount++;
}
}
else
{
_keepCount = 0;
const uint32_t dropVar = overshoot / _targetFrameRate;
if (_dropCount < dropVar)
{
drop = true;
_dropCount++;
}
else
{
_overShootModifier = overshoot % _targetFrameRate;
drop = false;
_dropCount = 0;
}
}
}
return drop;
}
return drop;
}
uint32_t
VPMVideoDecimator::DecimatedFrameRate()
{
ProcessIncomingFrameRate(TickTime::MillisecondTimestamp());
if (!_enableTemporalDecimation)
{
return static_cast<uint32_t>(_incomingFrameRate + 0.5f);
}
return VD_MIN(_targetFrameRate, static_cast<uint32_t>(_incomingFrameRate + 0.5f));
uint32_t VPMVideoDecimator::Decimatedframe_rate() {
ProcessIncomingframe_rate(TickTime::MillisecondTimestamp());
if (!enable_temporal_decimation_) {
return static_cast<uint32_t>(incoming_frame_rate_ + 0.5f);
}
return VD_MIN(target_frame_rate_,
static_cast<uint32_t>(incoming_frame_rate_ + 0.5f));
}
uint32_t
VPMVideoDecimator::InputFrameRate()
{
ProcessIncomingFrameRate(TickTime::MillisecondTimestamp());
return static_cast<uint32_t>(_incomingFrameRate + 0.5f);
uint32_t VPMVideoDecimator::Inputframe_rate() {
ProcessIncomingframe_rate(TickTime::MillisecondTimestamp());
return static_cast<uint32_t>(incoming_frame_rate_ + 0.5f);
}
void
VPMVideoDecimator::UpdateIncomingFrameRate()
{
int64_t now = TickTime::MillisecondTimestamp();
if(_incomingFrameTimes[0] == 0)
{
// first no shift
} else
{
// shift
for(int i = (kFrameCountHistorySize - 2); i >= 0 ; i--)
{
_incomingFrameTimes[i+1] = _incomingFrameTimes[i];
}
void VPMVideoDecimator::UpdateIncomingframe_rate() {
int64_t now = TickTime::MillisecondTimestamp();
if (incoming_frame_times_[0] == 0) {
// First no shift.
} else {
// Shift.
for (int i = kFrameCountHistory_size - 2; i >= 0; i--) {
incoming_frame_times_[i+1] = incoming_frame_times_[i];
}
_incomingFrameTimes[0] = now;
ProcessIncomingFrameRate(now);
}
incoming_frame_times_[0] = now;
ProcessIncomingframe_rate(now);
}
void
VPMVideoDecimator::ProcessIncomingFrameRate(int64_t now)
{
int32_t num = 0;
int32_t nrOfFrames = 0;
for(num = 1; num < (kFrameCountHistorySize - 1); num++)
{
if (_incomingFrameTimes[num] <= 0 ||
now - _incomingFrameTimes[num] > kFrameHistoryWindowMs) // don't use data older than 2sec
{
break;
} else
{
nrOfFrames++;
}
void VPMVideoDecimator::ProcessIncomingframe_rate(int64_t now) {
int32_t num = 0;
int32_t nrOfFrames = 0;
for (num = 1; num < (kFrameCountHistory_size - 1); num++) {
// Don't use data older than 2sec.
if (incoming_frame_times_[num] <= 0 ||
now - incoming_frame_times_[num] > kFrameHistoryWindowMs) {
break;
} else {
nrOfFrames++;
}
if (num > 1)
{
int64_t diff = now - _incomingFrameTimes[num-1];
_incomingFrameRate = 1.0;
if(diff >0)
{
_incomingFrameRate = nrOfFrames * 1000.0f / static_cast<float>(diff);
}
}
else
{
_incomingFrameRate = static_cast<float>(nrOfFrames);
}
if (num > 1) {
int64_t diff = now - incoming_frame_times_[num-1];
incoming_frame_rate_ = 1.0;
if (diff > 0) {
incoming_frame_rate_ = nrOfFrames * 1000.0f / static_cast<float>(diff);
}
} else {
incoming_frame_rate_ = static_cast<float>(nrOfFrames);
}
}
} // namespace
} // namespace webrtc

View File

@ -8,58 +8,53 @@
* be found in the AUTHORS file in the root of the source tree.
*/
/*
* video_decimator.h
*/
#ifndef VPM_VIDEO_DECIMATOR_H
#define VPM_VIDEO_DECIMATOR_H
#ifndef WEBRTC_MODULES_VIDEO_PROCESSING_MAIN_SOURCE_VIDEO_DECIMATOR_H
#define WEBRTC_MODULES_VIDEO_PROCESSING_MAIN_SOURCE_VIDEO_DECIMATOR_H
#include "webrtc/modules/interface/module_common_types.h"
#include "webrtc/typedefs.h"
namespace webrtc {
class VPMVideoDecimator
{
public:
VPMVideoDecimator();
~VPMVideoDecimator();
void Reset();
void EnableTemporalDecimation(bool enable);
int32_t SetMaxFrameRate(uint32_t maxFrameRate);
int32_t SetTargetFrameRate(uint32_t frameRate);
class VPMVideoDecimator {
public:
VPMVideoDecimator();
~VPMVideoDecimator();
bool DropFrame();
void UpdateIncomingFrameRate();
void Reset();
// Get Decimated Frame Rate/Dimensions
uint32_t DecimatedFrameRate();
void EnableTemporalDecimation(bool enable);
//Get input frame rate
uint32_t InputFrameRate();
int32_t SetMaxFramerate(uint32_t max_frame_rate);
int32_t SetTargetframe_rate(uint32_t frame_rate);
private:
void ProcessIncomingFrameRate(int64_t now);
bool DropFrame();
enum { kFrameCountHistorySize = 90};
enum { kFrameHistoryWindowMs = 2000};
void UpdateIncomingframe_rate();
// Temporal decimation
int32_t _overShootModifier;
uint32_t _dropCount;
uint32_t _keepCount;
uint32_t _targetFrameRate;
float _incomingFrameRate;
uint32_t _maxFrameRate;
int64_t _incomingFrameTimes[kFrameCountHistorySize];
bool _enableTemporalDecimation;
// Get Decimated Frame Rate/Dimensions.
uint32_t Decimatedframe_rate();
// Get input frame rate.
uint32_t Inputframe_rate();
private:
void ProcessIncomingframe_rate(int64_t now);
enum { kFrameCountHistory_size = 90};
enum { kFrameHistoryWindowMs = 2000};
// Temporal decimation.
int32_t overshoot_modifier_;
uint32_t drop_count_;
uint32_t keep_count_;
uint32_t target_frame_rate_;
float incoming_frame_rate_;
uint32_t max_frame_rate_;
int64_t incoming_frame_times_[kFrameCountHistory_size];
bool enable_temporal_decimation_;
};
} // namespace
} // namespace webrtc
#endif
#endif // WEBRTC_MODULES_VIDEO_PROCESSING_MAIN_SOURCE_VIDEO_DECIMATOR_H

View File

@ -17,277 +17,204 @@
namespace webrtc {
namespace
{
void
SetSubSampling(VideoProcessingModule::FrameStats* stats,
const int32_t width,
const int32_t height)
{
if (width * height >= 640 * 480)
{
stats->subSamplWidth = 3;
stats->subSamplHeight = 3;
}
else if (width * height >= 352 * 288)
{
stats->subSamplWidth = 2;
stats->subSamplHeight = 2;
}
else if (width * height >= 176 * 144)
{
stats->subSamplWidth = 1;
stats->subSamplHeight = 1;
}
else
{
stats->subSamplWidth = 0;
stats->subSamplHeight = 0;
}
}
}
VideoProcessingModule*
VideoProcessingModule::Create(const int32_t id)
{
return new VideoProcessingModuleImpl(id);
}
void
VideoProcessingModule::Destroy(VideoProcessingModule* module)
{
if (module)
{
delete static_cast<VideoProcessingModuleImpl*>(module);
}
}
int32_t
VideoProcessingModuleImpl::ChangeUniqueId(const int32_t id)
{
CriticalSectionScoped mutex(&_mutex);
_id = id;
_brightnessDetection.ChangeUniqueId(id);
_deflickering.ChangeUniqueId(id);
_denoising.ChangeUniqueId(id);
_framePreProcessor.ChangeUniqueId(id);
return VPM_OK;
}
int32_t
VideoProcessingModuleImpl::Id() const
{
CriticalSectionScoped mutex(&_mutex);
return _id;
}
VideoProcessingModuleImpl::VideoProcessingModuleImpl(const int32_t id) :
_id(id),
_mutex(*CriticalSectionWrapper::CreateCriticalSection())
{
_brightnessDetection.ChangeUniqueId(id);
_deflickering.ChangeUniqueId(id);
_denoising.ChangeUniqueId(id);
_framePreProcessor.ChangeUniqueId(id);
WEBRTC_TRACE(webrtc::kTraceMemory, webrtc::kTraceVideoPreocessing, _id,
"Created");
}
VideoProcessingModuleImpl::~VideoProcessingModuleImpl()
{
WEBRTC_TRACE(webrtc::kTraceMemory, webrtc::kTraceVideoPreocessing, _id,
"Destroyed");
delete &_mutex;
}
void
VideoProcessingModuleImpl::Reset()
{
CriticalSectionScoped mutex(&_mutex);
_deflickering.Reset();
_denoising.Reset();
_brightnessDetection.Reset();
_framePreProcessor.Reset();
}
int32_t
VideoProcessingModule::GetFrameStats(FrameStats* stats,
const I420VideoFrame& frame)
{
if (frame.IsZeroSize())
{
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoPreocessing, -1,
"zero size frame");
return VPM_PARAMETER_ERROR;
}
int width = frame.width();
int height = frame.height();
ClearFrameStats(stats); // The histogram needs to be zeroed out.
SetSubSampling(stats, width, height);
const uint8_t* buffer = frame.buffer(kYPlane);
// Compute histogram and sum of frame
for (int i = 0; i < height; i += (1 << stats->subSamplHeight))
{
int k = i * width;
for (int j = 0; j < width; j += (1 << stats->subSamplWidth))
{
stats->hist[buffer[k + j]]++;
stats->sum += buffer[k + j];
}
}
stats->numPixels = (width * height) / ((1 << stats->subSamplWidth) *
(1 << stats->subSamplHeight));
assert(stats->numPixels > 0);
// Compute mean value of frame
stats->mean = stats->sum / stats->numPixels;
return VPM_OK;
}
bool
VideoProcessingModule::ValidFrameStats(const FrameStats& stats)
{
if (stats.numPixels == 0)
{
return false;
}
return true;
}
void
VideoProcessingModule::ClearFrameStats(FrameStats* stats)
{
stats->mean = 0;
stats->sum = 0;
stats->numPixels = 0;
namespace {
void SetSubSampling(VideoProcessingModule::FrameStats* stats,
const int32_t width,
const int32_t height) {
if (width * height >= 640 * 480) {
stats->subSamplWidth = 3;
stats->subSamplHeight = 3;
} else if (width * height >= 352 * 288) {
stats->subSamplWidth = 2;
stats->subSamplHeight = 2;
} else if (width * height >= 176 * 144) {
stats->subSamplWidth = 1;
stats->subSamplHeight = 1;
} else {
stats->subSamplWidth = 0;
stats->subSamplHeight = 0;
memset(stats->hist, 0, sizeof(stats->hist));
}
}
int32_t
VideoProcessingModule::ColorEnhancement(I420VideoFrame* frame)
{
return VideoProcessing::ColorEnhancement(frame);
}
int32_t
VideoProcessingModule::Brighten(I420VideoFrame* frame, int delta)
{
return VideoProcessing::Brighten(frame, delta);
}
int32_t
VideoProcessingModuleImpl::Deflickering(I420VideoFrame* frame,
FrameStats* stats)
{
CriticalSectionScoped mutex(&_mutex);
return _deflickering.ProcessFrame(frame, stats);
}
int32_t
VideoProcessingModuleImpl::Denoising(I420VideoFrame* frame)
{
CriticalSectionScoped mutex(&_mutex);
return _denoising.ProcessFrame(frame);
}
int32_t
VideoProcessingModuleImpl::BrightnessDetection(const I420VideoFrame& frame,
const FrameStats& stats)
{
CriticalSectionScoped mutex(&_mutex);
return _brightnessDetection.ProcessFrame(frame, stats);
}
void
VideoProcessingModuleImpl::EnableTemporalDecimation(bool enable)
{
CriticalSectionScoped mutex(&_mutex);
_framePreProcessor.EnableTemporalDecimation(enable);
}
void
VideoProcessingModuleImpl::SetInputFrameResampleMode(VideoFrameResampling
resamplingMode)
{
CriticalSectionScoped cs(&_mutex);
_framePreProcessor.SetInputFrameResampleMode(resamplingMode);
}
int32_t
VideoProcessingModuleImpl::SetMaxFrameRate(uint32_t maxFrameRate)
{
CriticalSectionScoped cs(&_mutex);
return _framePreProcessor.SetMaxFrameRate(maxFrameRate);
}
int32_t
VideoProcessingModuleImpl::SetTargetResolution(uint32_t width,
uint32_t height,
uint32_t frameRate)
{
CriticalSectionScoped cs(&_mutex);
return _framePreProcessor.SetTargetResolution(width, height, frameRate);
}
uint32_t
VideoProcessingModuleImpl::DecimatedFrameRate()
{
CriticalSectionScoped cs(&_mutex);
return _framePreProcessor.DecimatedFrameRate();
}
uint32_t
VideoProcessingModuleImpl::DecimatedWidth() const
{
CriticalSectionScoped cs(&_mutex);
return _framePreProcessor.DecimatedWidth();
}
uint32_t
VideoProcessingModuleImpl::DecimatedHeight() const
{
CriticalSectionScoped cs(&_mutex);
return _framePreProcessor.DecimatedHeight();
}
int32_t
VideoProcessingModuleImpl::PreprocessFrame(const I420VideoFrame& frame,
I420VideoFrame **processedFrame)
{
CriticalSectionScoped mutex(&_mutex);
return _framePreProcessor.PreprocessFrame(frame, processedFrame);
}
VideoContentMetrics*
VideoProcessingModuleImpl::ContentMetrics() const
{
CriticalSectionScoped mutex(&_mutex);
return _framePreProcessor.ContentMetrics();
}
void
VideoProcessingModuleImpl::EnableContentAnalysis(bool enable)
{
CriticalSectionScoped mutex(&_mutex);
_framePreProcessor.EnableContentAnalysis(enable);
}
} // namespace
VideoProcessingModule* VideoProcessingModule::Create(const int32_t id) {
return new VideoProcessingModuleImpl(id);
}
void VideoProcessingModule::Destroy(VideoProcessingModule* module) {
if (module)
delete static_cast<VideoProcessingModuleImpl*>(module);
}
int32_t VideoProcessingModuleImpl::ChangeUniqueId(const int32_t id) {
CriticalSectionScoped mutex(&mutex_);
id_ = id;
brightness_detection_.ChangeUniqueId(id);
deflickering_.ChangeUniqueId(id);
denoising_.ChangeUniqueId(id);
frame_pre_processor_.ChangeUniqueId(id);
return VPM_OK;
}
int32_t VideoProcessingModuleImpl::Id() const {
CriticalSectionScoped mutex(&mutex_);
return id_;
}
VideoProcessingModuleImpl::VideoProcessingModuleImpl(const int32_t id)
: id_(id),
mutex_(*CriticalSectionWrapper::CreateCriticalSection()) {
brightness_detection_.ChangeUniqueId(id);
deflickering_.ChangeUniqueId(id);
denoising_.ChangeUniqueId(id);
frame_pre_processor_.ChangeUniqueId(id);
WEBRTC_TRACE(webrtc::kTraceMemory, webrtc::kTraceVideoPreocessing, id_,
"Created");
}
VideoProcessingModuleImpl::~VideoProcessingModuleImpl() {
WEBRTC_TRACE(webrtc::kTraceMemory, webrtc::kTraceVideoPreocessing, id_,
"Destroyed");
delete &mutex_;
}
void VideoProcessingModuleImpl::Reset() {
CriticalSectionScoped mutex(&mutex_);
deflickering_.Reset();
denoising_.Reset();
brightness_detection_.Reset();
frame_pre_processor_.Reset();
}
int32_t VideoProcessingModule::GetFrameStats(FrameStats* stats,
const I420VideoFrame& frame) {
if (frame.IsZeroSize()) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoPreocessing, -1,
"zero size frame");
return VPM_PARAMETER_ERROR;
}
int width = frame.width();
int height = frame.height();
ClearFrameStats(stats); // The histogram needs to be zeroed out.
SetSubSampling(stats, width, height);
const uint8_t* buffer = frame.buffer(kYPlane);
// Compute histogram and sum of frame
for (int i = 0; i < height; i += (1 << stats->subSamplHeight)) {
int k = i * width;
for (int j = 0; j < width; j += (1 << stats->subSamplWidth)) {
stats->hist[buffer[k + j]]++;
stats->sum += buffer[k + j];
}
}
stats->num_pixels = (width * height) / ((1 << stats->subSamplWidth) *
(1 << stats->subSamplHeight));
assert(stats->num_pixels > 0);
// Compute mean value of frame
stats->mean = stats->sum / stats->num_pixels;
return VPM_OK;
}
bool VideoProcessingModule::ValidFrameStats(const FrameStats& stats) {
if (stats.num_pixels == 0) return false;
return true;
}
void VideoProcessingModule::ClearFrameStats(FrameStats* stats) {
stats->mean = 0;
stats->sum = 0;
stats->num_pixels = 0;
stats->subSamplWidth = 0;
stats->subSamplHeight = 0;
memset(stats->hist, 0, sizeof(stats->hist));
}
int32_t VideoProcessingModule::ColorEnhancement(I420VideoFrame* frame) {
return VideoProcessing::ColorEnhancement(frame);
}
int32_t VideoProcessingModule::Brighten(I420VideoFrame* frame, int delta) {
return VideoProcessing::Brighten(frame, delta);
}
int32_t VideoProcessingModuleImpl::Deflickering(I420VideoFrame* frame,
FrameStats* stats) {
CriticalSectionScoped mutex(&mutex_);
return deflickering_.ProcessFrame(frame, stats);
}
int32_t VideoProcessingModuleImpl::Denoising(I420VideoFrame* frame) {
CriticalSectionScoped mutex(&mutex_);
return denoising_.ProcessFrame(frame);
}
int32_t VideoProcessingModuleImpl::BrightnessDetection(
const I420VideoFrame& frame,
const FrameStats& stats) {
CriticalSectionScoped mutex(&mutex_);
return brightness_detection_.ProcessFrame(frame, stats);
}
void VideoProcessingModuleImpl::EnableTemporalDecimation(bool enable) {
CriticalSectionScoped mutex(&mutex_);
frame_pre_processor_.EnableTemporalDecimation(enable);
}
void VideoProcessingModuleImpl::SetInputFrameResampleMode(VideoFrameResampling
resampling_mode) {
CriticalSectionScoped cs(&mutex_);
frame_pre_processor_.SetInputFrameResampleMode(resampling_mode);
}
int32_t VideoProcessingModuleImpl::SetMaxFramerate(uint32_t max_frame_rate) {
CriticalSectionScoped cs(&mutex_);
return frame_pre_processor_.SetMaxFramerate(max_frame_rate);
}
int32_t VideoProcessingModuleImpl::SetTargetResolution(uint32_t width,
uint32_t height,
uint32_t frame_rate) {
CriticalSectionScoped cs(&mutex_);
return frame_pre_processor_.SetTargetResolution(width, height, frame_rate);
}
uint32_t VideoProcessingModuleImpl::Decimatedframe_rate() {
CriticalSectionScoped cs(&mutex_);
return frame_pre_processor_.Decimatedframe_rate();
}
uint32_t VideoProcessingModuleImpl::DecimatedWidth() const {
CriticalSectionScoped cs(&mutex_);
return frame_pre_processor_.DecimatedWidth();
}
uint32_t VideoProcessingModuleImpl::DecimatedHeight() const {
CriticalSectionScoped cs(&mutex_);
return frame_pre_processor_.DecimatedHeight();
}
int32_t VideoProcessingModuleImpl::PreprocessFrame(
const I420VideoFrame& frame,
I420VideoFrame **processed_frame) {
CriticalSectionScoped mutex(&mutex_);
return frame_pre_processor_.PreprocessFrame(frame, processed_frame);
}
VideoContentMetrics* VideoProcessingModuleImpl::ContentMetrics() const {
CriticalSectionScoped mutex(&mutex_);
return frame_pre_processor_.ContentMetrics();
}
void VideoProcessingModuleImpl::EnableContentAnalysis(bool enable) {
CriticalSectionScoped mutex(&mutex_);
frame_pre_processor_.EnableContentAnalysis(enable);
}
} // namespace webrtc

View File

@ -22,67 +22,64 @@
namespace webrtc {
class CriticalSectionWrapper;
class VideoProcessingModuleImpl : public VideoProcessingModule
{
public:
class VideoProcessingModuleImpl : public VideoProcessingModule {
public:
VideoProcessingModuleImpl(int32_t id);
VideoProcessingModuleImpl(int32_t id);
virtual ~VideoProcessingModuleImpl();
virtual ~VideoProcessingModuleImpl();
int32_t Id() const;
int32_t Id() const;
virtual int32_t ChangeUniqueId(const int32_t id);
virtual int32_t ChangeUniqueId(const int32_t id);
virtual void Reset();
virtual void Reset();
virtual int32_t Deflickering(I420VideoFrame* frame, FrameStats* stats);
virtual int32_t Deflickering(I420VideoFrame* frame, FrameStats* stats);
virtual int32_t Denoising(I420VideoFrame* frame);
virtual int32_t Denoising(I420VideoFrame* frame);
virtual int32_t BrightnessDetection(const I420VideoFrame& frame,
const FrameStats& stats);
virtual int32_t BrightnessDetection(const I420VideoFrame& frame,
const FrameStats& stats);
// Frame pre-processor functions
//Frame pre-processor functions
// Enable temporal decimation
virtual void EnableTemporalDecimation(bool enable);
//Enable temporal decimation
virtual void EnableTemporalDecimation(bool enable);
virtual void SetInputFrameResampleMode(VideoFrameResampling resampling_mode);
virtual void SetInputFrameResampleMode(VideoFrameResampling resamplingMode);
// Enable content analysis
virtual void EnableContentAnalysis(bool enable);
//Enable content analysis
virtual void EnableContentAnalysis(bool enable);
// Set max frame rate
virtual int32_t SetMaxFramerate(uint32_t max_frame_rate);
//Set max frame rate
virtual int32_t SetMaxFrameRate(uint32_t maxFrameRate);
// Set Target Resolution: frame rate and dimension
virtual int32_t SetTargetResolution(uint32_t width,
uint32_t height,
uint32_t frameRate);
// Set Target Resolution: frame rate and dimension
virtual int32_t SetTargetResolution(uint32_t width,
uint32_t height,
uint32_t frame_rate);
// Get decimated values: frame rate/dimension
virtual uint32_t DecimatedFrameRate();
virtual uint32_t DecimatedWidth() const;
virtual uint32_t DecimatedHeight() const;
// Get decimated values: frame rate/dimension
virtual uint32_t Decimatedframe_rate();
virtual uint32_t DecimatedWidth() const;
virtual uint32_t DecimatedHeight() const;
// Preprocess:
// Pre-process incoming frame: Sample when needed and compute content
// metrics when enabled.
// If no resampling takes place - processedFrame is set to NULL.
virtual int32_t PreprocessFrame(const I420VideoFrame& frame,
I420VideoFrame** processedFrame);
virtual VideoContentMetrics* ContentMetrics() const;
// Preprocess:
// Pre-process incoming frame: Sample when needed and compute content
// metrics when enabled.
// If no resampling takes place - processed_frame is set to NULL.
virtual int32_t PreprocessFrame(const I420VideoFrame& frame,
I420VideoFrame** processed_frame);
virtual VideoContentMetrics* ContentMetrics() const;
private:
int32_t _id;
CriticalSectionWrapper& _mutex;
VPMDeflickering _deflickering;
VPMDenoising _denoising;
VPMBrightnessDetection _brightnessDetection;
VPMFramePreprocessor _framePreProcessor;
private:
int32_t id_;
CriticalSectionWrapper& mutex_;
VPMDeflickering deflickering_;
VPMDenoising denoising_;
VPMBrightnessDetection brightness_detection_;
VPMFramePreprocessor frame_pre_processor_;
};
} // namespace

View File

@ -19,24 +19,24 @@ TEST_F(VideoProcessingModuleTest, BrightnessDetection)
uint32_t frameNum = 0;
int32_t brightnessWarning = 0;
uint32_t warningCount = 0;
scoped_array<uint8_t> video_buffer(new uint8_t[_frame_length]);
while (fread(video_buffer.get(), 1, _frame_length, _sourceFile) ==
_frame_length)
scoped_array<uint8_t> video_buffer(new uint8_t[frame_length_]);
while (fread(video_buffer.get(), 1, frame_length_, source_file_) ==
frame_length_)
{
EXPECT_EQ(0, ConvertToI420(kI420, video_buffer.get(), 0, 0,
_width, _height,
0, kRotateNone, &_videoFrame));
width_, height_,
0, kRotateNone, &video_frame_));
frameNum++;
VideoProcessingModule::FrameStats stats;
ASSERT_EQ(0, _vpm->GetFrameStats(&stats, _videoFrame));
ASSERT_GE(brightnessWarning = _vpm->BrightnessDetection(_videoFrame,
ASSERT_EQ(0, vpm_->GetFrameStats(&stats, video_frame_));
ASSERT_GE(brightnessWarning = vpm_->BrightnessDetection(video_frame_,
stats), 0);
if (brightnessWarning != VideoProcessingModule::kNoWarning)
{
warningCount++;
}
}
ASSERT_NE(0, feof(_sourceFile)) << "Error reading source file";
ASSERT_NE(0, feof(source_file_)) << "Error reading source file";
// Expect few warnings
float warningProportion = static_cast<float>(warningCount) / frameNum * 100;
@ -44,21 +44,21 @@ TEST_F(VideoProcessingModuleTest, BrightnessDetection)
printf("Stock foreman: %.1f %%\n", warningProportion);
EXPECT_LT(warningProportion, 10);
rewind(_sourceFile);
rewind(source_file_);
frameNum = 0;
warningCount = 0;
while (fread(video_buffer.get(), 1, _frame_length, _sourceFile) ==
_frame_length &&
while (fread(video_buffer.get(), 1, frame_length_, source_file_) ==
frame_length_ &&
frameNum < 300)
{
EXPECT_EQ(0, ConvertToI420(kI420, video_buffer.get(), 0, 0,
_width, _height,
0, kRotateNone, &_videoFrame));
width_, height_,
0, kRotateNone, &video_frame_));
frameNum++;
uint8_t* frame = _videoFrame.buffer(kYPlane);
uint8_t* frame = video_frame_.buffer(kYPlane);
uint32_t yTmp = 0;
for (int yIdx = 0; yIdx < _width * _height; yIdx++)
for (int yIdx = 0; yIdx < width_ * height_; yIdx++)
{
yTmp = frame[yIdx] << 1;
if (yTmp > 255)
@ -69,8 +69,8 @@ TEST_F(VideoProcessingModuleTest, BrightnessDetection)
}
VideoProcessingModule::FrameStats stats;
ASSERT_EQ(0, _vpm->GetFrameStats(&stats, _videoFrame));
ASSERT_GE(brightnessWarning = _vpm->BrightnessDetection(_videoFrame,
ASSERT_EQ(0, vpm_->GetFrameStats(&stats, video_frame_));
ASSERT_GE(brightnessWarning = vpm_->BrightnessDetection(video_frame_,
stats), 0);
EXPECT_NE(VideoProcessingModule::kDarkWarning, brightnessWarning);
if (brightnessWarning == VideoProcessingModule::kBrightWarning)
@ -78,35 +78,35 @@ TEST_F(VideoProcessingModuleTest, BrightnessDetection)
warningCount++;
}
}
ASSERT_NE(0, feof(_sourceFile)) << "Error reading source file";
ASSERT_NE(0, feof(source_file_)) << "Error reading source file";
// Expect many brightness warnings
warningProportion = static_cast<float>(warningCount) / frameNum * 100;
printf("Bright foreman: %.1f %%\n", warningProportion);
EXPECT_GT(warningProportion, 95);
rewind(_sourceFile);
rewind(source_file_);
frameNum = 0;
warningCount = 0;
while (fread(video_buffer.get(), 1, _frame_length, _sourceFile) ==
_frame_length && frameNum < 300)
while (fread(video_buffer.get(), 1, frame_length_, source_file_) ==
frame_length_ && frameNum < 300)
{
EXPECT_EQ(0, ConvertToI420(kI420, video_buffer.get(), 0, 0,
_width, _height,
0, kRotateNone, &_videoFrame));
width_, height_,
0, kRotateNone, &video_frame_));
frameNum++;
uint8_t* y_plane = _videoFrame.buffer(kYPlane);
uint8_t* y_plane = video_frame_.buffer(kYPlane);
int32_t yTmp = 0;
for (int yIdx = 0; yIdx < _width * _height; yIdx++)
for (int yIdx = 0; yIdx < width_ * height_; yIdx++)
{
yTmp = y_plane[yIdx] >> 1;
y_plane[yIdx] = static_cast<uint8_t>(yTmp);
}
VideoProcessingModule::FrameStats stats;
ASSERT_EQ(0, _vpm->GetFrameStats(&stats, _videoFrame));
ASSERT_GE(brightnessWarning = _vpm->BrightnessDetection(_videoFrame,
ASSERT_EQ(0, vpm_->GetFrameStats(&stats, video_frame_));
ASSERT_GE(brightnessWarning = vpm_->BrightnessDetection(video_frame_,
stats), 0);
EXPECT_NE(VideoProcessingModule::kBrightWarning, brightnessWarning);
if (brightnessWarning == VideoProcessingModule::kDarkWarning)
@ -114,7 +114,7 @@ TEST_F(VideoProcessingModuleTest, BrightnessDetection)
warningCount++;
}
}
ASSERT_NE(0, feof(_sourceFile)) << "Error reading source file";
ASSERT_NE(0, feof(source_file_)) << "Error reading source file";
// Expect many darkness warnings
warningProportion = static_cast<float>(warningCount) / frameNum * 100;

View File

@ -23,14 +23,14 @@ TEST_F(VideoProcessingModuleTest, ColorEnhancement)
{
TickTime t0;
TickTime t1;
TickInterval accTicks;
TickInterval acc_ticks;
// Use a shorter version of the Foreman clip for this test.
fclose(_sourceFile);
fclose(source_file_);
const std::string video_file =
webrtc::test::ResourcePath("foreman_cif_short", "yuv");
_sourceFile = fopen(video_file.c_str(), "rb");
ASSERT_TRUE(_sourceFile != NULL) <<
source_file_ = fopen(video_file.c_str(), "rb");
ASSERT_TRUE(source_file_ != NULL) <<
"Cannot read source file: " + video_file + "\n";
std::string output_file = webrtc::test::OutputPath() +
@ -39,27 +39,27 @@ TEST_F(VideoProcessingModuleTest, ColorEnhancement)
ASSERT_TRUE(modFile != NULL) << "Could not open output file.\n";
uint32_t frameNum = 0;
scoped_array<uint8_t> video_buffer(new uint8_t[_frame_length]);
while (fread(video_buffer.get(), 1, _frame_length, _sourceFile) ==
_frame_length)
scoped_array<uint8_t> video_buffer(new uint8_t[frame_length_]);
while (fread(video_buffer.get(), 1, frame_length_, source_file_) ==
frame_length_)
{
// Using ConvertToI420 to add stride to the image.
EXPECT_EQ(0, ConvertToI420(kI420, video_buffer.get(), 0, 0,
_width, _height,
0, kRotateNone, &_videoFrame));
width_, height_,
0, kRotateNone, &video_frame_));
frameNum++;
t0 = TickTime::Now();
ASSERT_EQ(0, VideoProcessingModule::ColorEnhancement(&_videoFrame));
ASSERT_EQ(0, VideoProcessingModule::ColorEnhancement(&video_frame_));
t1 = TickTime::Now();
accTicks += t1 - t0;
if (PrintI420VideoFrame(_videoFrame, modFile) < 0) {
acc_ticks += t1 - t0;
if (PrintI420VideoFrame(video_frame_, modFile) < 0) {
return;
}
}
ASSERT_NE(0, feof(_sourceFile)) << "Error reading source file";
ASSERT_NE(0, feof(source_file_)) << "Error reading source file";
printf("\nTime per frame: %d us \n",
static_cast<int>(accTicks.Microseconds() / frameNum));
static_cast<int>(acc_ticks.Microseconds() / frameNum));
rewind(modFile);
printf("Comparing files...\n\n");
@ -82,62 +82,62 @@ TEST_F(VideoProcessingModuleTest, ColorEnhancement)
ASSERT_EQ(refLen, testLen) << "File lengths differ.";
I420VideoFrame refVideoFrame;
refVideoFrame.CreateEmptyFrame(_width, _height,
_width, _half_width, _half_width);
refVideoFrame.CreateEmptyFrame(width_, height_,
width_, half_width_, half_width_);
// Compare frame-by-frame.
scoped_array<uint8_t> ref_buffer(new uint8_t[_frame_length]);
while (fread(video_buffer.get(), 1, _frame_length, modFile) ==
_frame_length)
scoped_array<uint8_t> ref_buffer(new uint8_t[frame_length_]);
while (fread(video_buffer.get(), 1, frame_length_, modFile) ==
frame_length_)
{
// Using ConvertToI420 to add stride to the image.
EXPECT_EQ(0, ConvertToI420(kI420, video_buffer.get(), 0, 0,
_width, _height,
0, kRotateNone, &_videoFrame));
ASSERT_EQ(_frame_length, fread(ref_buffer.get(), 1, _frame_length,
width_, height_,
0, kRotateNone, &video_frame_));
ASSERT_EQ(frame_length_, fread(ref_buffer.get(), 1, frame_length_,
refFile));
EXPECT_EQ(0, ConvertToI420(kI420, ref_buffer.get(), 0, 0,
_width, _height,
width_, height_,
0, kRotateNone, &refVideoFrame));
EXPECT_EQ(0, memcmp(_videoFrame.buffer(kYPlane),
EXPECT_EQ(0, memcmp(video_frame_.buffer(kYPlane),
refVideoFrame.buffer(kYPlane),
_size_y));
EXPECT_EQ(0, memcmp(_videoFrame.buffer(kUPlane),
size_y_));
EXPECT_EQ(0, memcmp(video_frame_.buffer(kUPlane),
refVideoFrame.buffer(kUPlane),
_size_uv));
EXPECT_EQ(0, memcmp(_videoFrame.buffer(kVPlane),
size_uv_));
EXPECT_EQ(0, memcmp(video_frame_.buffer(kVPlane),
refVideoFrame.buffer(kVPlane),
_size_uv));
size_uv_));
}
ASSERT_NE(0, feof(_sourceFile)) << "Error reading source file";
ASSERT_NE(0, feof(source_file_)) << "Error reading source file";
// Verify that all color pixels are enhanced, and no luminance values are
// altered.
scoped_array<uint8_t> testFrame(new uint8_t[_frame_length]);
scoped_array<uint8_t> testFrame(new uint8_t[frame_length_]);
// Use value 128 as probe value, since we know that this will be changed
// in the enhancement.
memset(testFrame.get(), 128, _frame_length);
memset(testFrame.get(), 128, frame_length_);
I420VideoFrame testVideoFrame;
testVideoFrame.CreateEmptyFrame(_width, _height,
_width, _half_width, _half_width);
testVideoFrame.CreateEmptyFrame(width_, height_,
width_, half_width_, half_width_);
EXPECT_EQ(0, ConvertToI420(kI420, testFrame.get(), 0, 0,
_width, _height, 0, kRotateNone,
width_, height_, 0, kRotateNone,
&testVideoFrame));
ASSERT_EQ(0, VideoProcessingModule::ColorEnhancement(&testVideoFrame));
EXPECT_EQ(0, memcmp(testVideoFrame.buffer(kYPlane), testFrame.get(),
_size_y))
size_y_))
<< "Function is modifying the luminance.";
EXPECT_NE(0, memcmp(testVideoFrame.buffer(kUPlane),
testFrame.get() + _size_y, _size_uv)) <<
testFrame.get() + size_y_, size_uv_)) <<
"Function is not modifying all chrominance pixels";
EXPECT_NE(0, memcmp(testVideoFrame.buffer(kVPlane),
testFrame.get() + _size_y + _size_uv, _size_uv)) <<
testFrame.get() + size_y_ + size_uv_, size_uv_)) <<
"Function is not modifying all chrominance pixels";
ASSERT_EQ(0, fclose(refFile));

View File

@ -15,32 +15,30 @@
namespace webrtc {
TEST_F(VideoProcessingModuleTest, ContentAnalysis)
{
VPMContentAnalysis _ca_c(false);
VPMContentAnalysis _ca_sse(true);
VideoContentMetrics *_cM_c, *_cM_SSE;
TEST_F(VideoProcessingModuleTest, ContentAnalysis) {
VPMContentAnalysis ca__c(false);
VPMContentAnalysis ca__sse(true);
VideoContentMetrics *_cM_c, *_cM_SSE;
_ca_c.Initialize(_width,_height);
_ca_sse.Initialize(_width,_height);
ca__c.Initialize(width_,height_);
ca__sse.Initialize(width_,height_);
scoped_array<uint8_t> video_buffer(new uint8_t[_frame_length]);
while (fread(video_buffer.get(), 1, _frame_length, _sourceFile)
== _frame_length)
{
// Using ConvertToI420 to add stride to the image.
EXPECT_EQ(0, ConvertToI420(kI420, video_buffer.get(), 0, 0,
_width, _height,
0, kRotateNone, &_videoFrame));
_cM_c = _ca_c.ComputeContentMetrics(_videoFrame);
_cM_SSE = _ca_sse.ComputeContentMetrics(_videoFrame);
scoped_array<uint8_t> video_buffer(new uint8_t[frame_length_]);
while (fread(video_buffer.get(), 1, frame_length_, source_file_)
== frame_length_) {
// Using ConvertToI420 to add stride to the image.
EXPECT_EQ(0, ConvertToI420(kI420, video_buffer.get(), 0, 0,
width_, height_,
0, kRotateNone, &video_frame_));
_cM_c = ca__c.ComputeContentMetrics(video_frame_);
_cM_SSE = ca__sse.ComputeContentMetrics(video_frame_);
ASSERT_EQ(_cM_c->spatial_pred_err, _cM_SSE->spatial_pred_err);
ASSERT_EQ(_cM_c->spatial_pred_err_v, _cM_SSE->spatial_pred_err_v);
ASSERT_EQ(_cM_c->spatial_pred_err_h, _cM_SSE->spatial_pred_err_h);
ASSERT_EQ(_cM_c->motion_magnitude, _cM_SSE->motion_magnitude);
}
ASSERT_NE(0, feof(_sourceFile)) << "Error reading source file";
ASSERT_EQ(_cM_c->spatial_pred_err, _cM_SSE->spatial_pred_err);
ASSERT_EQ(_cM_c->spatial_pred_err_v, _cM_SSE->spatial_pred_err_v);
ASSERT_EQ(_cM_c->spatial_pred_err_h, _cM_SSE->spatial_pred_err_h);
ASSERT_EQ(_cM_c->motion_magnitude, _cM_SSE->motion_magnitude);
}
ASSERT_NE(0, feof(source_file_)) << "Error reading source file";
}
} // namespace webrtc

View File

@ -23,17 +23,17 @@ TEST_F(VideoProcessingModuleTest, Deflickering)
{
enum { NumRuns = 30 };
uint32_t frameNum = 0;
const uint32_t frameRate = 15;
const uint32_t frame_rate = 15;
int64_t minRuntime = 0;
int64_t avgRuntime = 0;
int64_t min_runtime = 0;
int64_t avg_runtime = 0;
// Close automatically opened Foreman.
fclose(_sourceFile);
fclose(source_file_);
const std::string input_file =
webrtc::test::ResourcePath("deflicker_before_cif_short", "yuv");
_sourceFile = fopen(input_file.c_str(), "rb");
ASSERT_TRUE(_sourceFile != NULL) <<
source_file_ = fopen(input_file.c_str(), "rb");
ASSERT_TRUE(source_file_ != NULL) <<
"Cannot read input file: " << input_file << "\n";
const std::string output_file =
@ -43,57 +43,57 @@ TEST_F(VideoProcessingModuleTest, Deflickering)
"Could not open output file: " << output_file << "\n";
printf("\nRun time [us / frame]:\n");
scoped_array<uint8_t> video_buffer(new uint8_t[_frame_length]);
for (uint32_t runIdx = 0; runIdx < NumRuns; runIdx++)
scoped_array<uint8_t> video_buffer(new uint8_t[frame_length_]);
for (uint32_t run_idx = 0; run_idx < NumRuns; run_idx++)
{
TickTime t0;
TickTime t1;
TickInterval accTicks;
TickInterval acc_ticks;
uint32_t timeStamp = 1;
frameNum = 0;
while (fread(video_buffer.get(), 1, _frame_length, _sourceFile) ==
_frame_length)
while (fread(video_buffer.get(), 1, frame_length_, source_file_) ==
frame_length_)
{
frameNum++;
EXPECT_EQ(0, ConvertToI420(kI420, video_buffer.get(), 0, 0,
_width, _height,
0, kRotateNone, &_videoFrame));
_videoFrame.set_timestamp(timeStamp);
width_, height_,
0, kRotateNone, &video_frame_));
video_frame_.set_timestamp(timeStamp);
t0 = TickTime::Now();
VideoProcessingModule::FrameStats stats;
ASSERT_EQ(0, _vpm->GetFrameStats(&stats, _videoFrame));
ASSERT_EQ(0, _vpm->Deflickering(&_videoFrame, &stats));
ASSERT_EQ(0, vpm_->GetFrameStats(&stats, video_frame_));
ASSERT_EQ(0, vpm_->Deflickering(&video_frame_, &stats));
t1 = TickTime::Now();
accTicks += (t1 - t0);
acc_ticks += (t1 - t0);
if (runIdx == 0)
if (run_idx == 0)
{
if (PrintI420VideoFrame(_videoFrame, deflickerFile) < 0) {
if (PrintI420VideoFrame(video_frame_, deflickerFile) < 0) {
return;
}
}
timeStamp += (90000 / frameRate);
timeStamp += (90000 / frame_rate);
}
ASSERT_NE(0, feof(_sourceFile)) << "Error reading source file";
ASSERT_NE(0, feof(source_file_)) << "Error reading source file";
printf("%u\n", static_cast<int>(accTicks.Microseconds() / frameNum));
if (accTicks.Microseconds() < minRuntime || runIdx == 0)
printf("%u\n", static_cast<int>(acc_ticks.Microseconds() / frameNum));
if (acc_ticks.Microseconds() < min_runtime || run_idx == 0)
{
minRuntime = accTicks.Microseconds();
min_runtime = acc_ticks.Microseconds();
}
avgRuntime += accTicks.Microseconds();
avg_runtime += acc_ticks.Microseconds();
rewind(_sourceFile);
rewind(source_file_);
}
ASSERT_EQ(0, fclose(deflickerFile));
// TODO(kjellander): Add verification of deflicker output file.
printf("\nAverage run time = %d us / frame\n",
static_cast<int>(avgRuntime / frameNum / NumRuns));
static_cast<int>(avg_runtime / frameNum / NumRuns));
printf("Min run time = %d us / frame\n\n",
static_cast<int>(minRuntime / frameNum));
static_cast<int>(min_runtime / frameNum));
}
} // namespace webrtc

View File

@ -25,8 +25,8 @@ TEST_F(VideoProcessingModuleTest, DISABLED_ON_ANDROID(Denoising))
enum { NumRuns = 10 };
uint32_t frameNum = 0;
int64_t minRuntime = 0;
int64_t avgRuntime = 0;
int64_t min_runtime = 0;
int64_t avg_runtime = 0;
const std::string denoise_filename =
webrtc::test::OutputPath() + "denoise_testfile.yuv";
@ -41,50 +41,50 @@ TEST_F(VideoProcessingModuleTest, DISABLED_ON_ANDROID(Denoising))
"Could not open noisy file: " << noise_filename << "\n";
printf("\nRun time [us / frame]:\n");
for (uint32_t runIdx = 0; runIdx < NumRuns; runIdx++)
for (uint32_t run_idx = 0; run_idx < NumRuns; run_idx++)
{
TickTime t0;
TickTime t1;
TickInterval accTicks;
TickInterval acc_ticks;
int32_t modifiedPixels = 0;
frameNum = 0;
scoped_array<uint8_t> video_buffer(new uint8_t[_frame_length]);
while (fread(video_buffer.get(), 1, _frame_length, _sourceFile) ==
_frame_length)
scoped_array<uint8_t> video_buffer(new uint8_t[frame_length_]);
while (fread(video_buffer.get(), 1, frame_length_, source_file_) ==
frame_length_)
{
EXPECT_EQ(0, ConvertToI420(kI420, video_buffer.get(), 0, 0,
_width, _height,
0, kRotateNone, &_videoFrame));
width_, height_,
0, kRotateNone, &video_frame_));
frameNum++;
uint8_t* sourceBuffer = _videoFrame.buffer(kYPlane);
uint8_t* sourceBuffer = video_frame_.buffer(kYPlane);
// Add noise to a part in video stream
// Random noise
// TODO: investigate the effectiveness of this test.
for (int ir = 0; ir < _height; ir++)
for (int ir = 0; ir < height_; ir++)
{
uint32_t ik = ir * _width;
for (int ic = 0; ic < _width; ic++)
uint32_t ik = ir * width_;
for (int ic = 0; ic < width_; ic++)
{
uint8_t r = rand() % 16;
r -= 8;
if (ir < _height / 4)
if (ir < height_ / 4)
r = 0;
if (ir >= 3 * _height / 4)
if (ir >= 3 * height_ / 4)
r = 0;
if (ic < _width / 4)
if (ic < width_ / 4)
r = 0;
if (ic >= 3 * _width / 4)
if (ic >= 3 * width_ / 4)
r = 0;
/*uint8_t pixelValue = 0;
if (ir >= _height / 2)
if (ir >= height_ / 2)
{ // Region 3 or 4
pixelValue = 170;
}
if (ic >= _width / 2)
if (ic >= width_ / 2)
{ // Region 2 or 4
pixelValue += 85;
}
@ -95,42 +95,42 @@ TEST_F(VideoProcessingModuleTest, DISABLED_ON_ANDROID(Denoising))
}
}
if (runIdx == 0)
if (run_idx == 0)
{
if (PrintI420VideoFrame(_videoFrame, noiseFile) < 0) {
if (PrintI420VideoFrame(video_frame_, noiseFile) < 0) {
return;
}
}
t0 = TickTime::Now();
ASSERT_GE(modifiedPixels = _vpm->Denoising(&_videoFrame), 0);
ASSERT_GE(modifiedPixels = vpm_->Denoising(&video_frame_), 0);
t1 = TickTime::Now();
accTicks += (t1 - t0);
acc_ticks += (t1 - t0);
if (runIdx == 0)
if (run_idx == 0)
{
if (PrintI420VideoFrame(_videoFrame, noiseFile) < 0) {
if (PrintI420VideoFrame(video_frame_, noiseFile) < 0) {
return;
}
}
}
ASSERT_NE(0, feof(_sourceFile)) << "Error reading source file";
ASSERT_NE(0, feof(source_file_)) << "Error reading source file";
printf("%u\n", static_cast<int>(accTicks.Microseconds() / frameNum));
if (accTicks.Microseconds() < minRuntime || runIdx == 0)
printf("%u\n", static_cast<int>(acc_ticks.Microseconds() / frameNum));
if (acc_ticks.Microseconds() < min_runtime || run_idx == 0)
{
minRuntime = accTicks.Microseconds();
min_runtime = acc_ticks.Microseconds();
}
avgRuntime += accTicks.Microseconds();
avg_runtime += acc_ticks.Microseconds();
rewind(_sourceFile);
rewind(source_file_);
}
ASSERT_EQ(0, fclose(denoiseFile));
ASSERT_EQ(0, fclose(noiseFile));
printf("\nAverage run time = %d us / frame\n",
static_cast<int>(avgRuntime / frameNum / NumRuns));
static_cast<int>(avg_runtime / frameNum / NumRuns));
printf("Min run time = %d us / frame\n\n",
static_cast<int>(minRuntime / frameNum));
static_cast<int>(min_runtime / frameNum));
}
} // namespace webrtc

View File

@ -18,297 +18,285 @@
namespace webrtc {
// The |sourceFrame| is scaled to |target_width|,|target_height|, using the
// The |sourceFrame| is scaled to |targetwidth_|,|targetheight_|, using the
// filter mode set to |mode|. The |expected_psnr| is used to verify basic
// quality when the resampled frame is scaled back up/down to the
// original/source size. |expected_psnr| is set to be ~0.1/0.05dB lower than
// actual PSNR verified under the same conditions.
void TestSize(const I420VideoFrame& sourceFrame, int target_width,
int target_height, int mode, double expected_psnr,
void TestSize(const I420VideoFrame& sourceFrame, int targetwidth_,
int targetheight_, int mode, double expected_psnr,
VideoProcessingModule* vpm);
bool CompareFrames(const webrtc::I420VideoFrame& frame1,
const webrtc::I420VideoFrame& frame2);
VideoProcessingModuleTest::VideoProcessingModuleTest() :
_vpm(NULL),
_sourceFile(NULL),
_width(352),
_half_width((_width + 1) / 2),
_height(288),
_size_y(_width * _height),
_size_uv(_half_width * ((_height + 1) / 2)),
_frame_length(CalcBufferSize(kI420, _width, _height))
{
}
VideoProcessingModuleTest::VideoProcessingModuleTest()
: vpm_(NULL),
source_file_(NULL),
width_(352),
half_width_((width_ + 1) / 2),
height_(288),
size_y_(width_ * height_),
size_uv_(half_width_ * ((height_ + 1) / 2)),
frame_length_(CalcBufferSize(kI420, width_, height_)) {}
void VideoProcessingModuleTest::SetUp()
{
_vpm = VideoProcessingModule::Create(0);
ASSERT_TRUE(_vpm != NULL);
void VideoProcessingModuleTest::SetUp() {
vpm_ = VideoProcessingModule::Create(0);
ASSERT_TRUE(vpm_ != NULL);
ASSERT_EQ(0, _videoFrame.CreateEmptyFrame(_width, _height, _width,
_half_width, _half_width));
ASSERT_EQ(0, video_frame_.CreateEmptyFrame(width_, height_, width_,
half_width_, half_width_));
const std::string video_file =
webrtc::test::ResourcePath("foreman_cif", "yuv");
_sourceFile = fopen(video_file.c_str(),"rb");
ASSERT_TRUE(_sourceFile != NULL) <<
source_file_ = fopen(video_file.c_str(),"rb");
ASSERT_TRUE(source_file_ != NULL) <<
"Cannot read source file: " + video_file + "\n";
}
void VideoProcessingModuleTest::TearDown()
{
if (_sourceFile != NULL) {
ASSERT_EQ(0, fclose(_sourceFile));
void VideoProcessingModuleTest::TearDown() {
if (source_file_ != NULL) {
ASSERT_EQ(0, fclose(source_file_));
}
_sourceFile = NULL;
source_file_ = NULL;
if (_vpm != NULL) {
VideoProcessingModule::Destroy(_vpm);
if (vpm_ != NULL) {
VideoProcessingModule::Destroy(vpm_);
}
_vpm = NULL;
vpm_ = NULL;
}
TEST_F(VideoProcessingModuleTest, HandleNullBuffer)
{
TEST_F(VideoProcessingModuleTest, HandleNullBuffer) {
// TODO(mikhal/stefan): Do we need this one?
VideoProcessingModule::FrameStats stats;
// Video frame with unallocated buffer.
I420VideoFrame videoFrame;
videoFrame.set_width(_width);
videoFrame.set_height(_height);
videoFrame.set_width(width_);
videoFrame.set_height(height_);
EXPECT_EQ(-3, _vpm->GetFrameStats(&stats, videoFrame));
EXPECT_EQ(-3, vpm_->GetFrameStats(&stats, videoFrame));
EXPECT_EQ(-1, _vpm->ColorEnhancement(&videoFrame));
EXPECT_EQ(-1, vpm_->ColorEnhancement(&videoFrame));
EXPECT_EQ(-1, _vpm->Deflickering(&videoFrame, &stats));
EXPECT_EQ(-1, vpm_->Deflickering(&videoFrame, &stats));
EXPECT_EQ(-1, _vpm->Denoising(&videoFrame));
EXPECT_EQ(-1, vpm_->Denoising(&videoFrame));
EXPECT_EQ(-3, _vpm->BrightnessDetection(videoFrame, stats));
EXPECT_EQ(-3, vpm_->BrightnessDetection(videoFrame, stats));
}
TEST_F(VideoProcessingModuleTest, HandleBadStats)
{
TEST_F(VideoProcessingModuleTest, HandleBadStats) {
VideoProcessingModule::FrameStats stats;
scoped_array<uint8_t> video_buffer(new uint8_t[_frame_length]);
ASSERT_EQ(_frame_length, fread(video_buffer.get(), 1, _frame_length,
_sourceFile));
scoped_array<uint8_t> video_buffer(new uint8_t[frame_length_]);
ASSERT_EQ(frame_length_, fread(video_buffer.get(), 1, frame_length_,
source_file_));
EXPECT_EQ(0, ConvertToI420(kI420, video_buffer.get(), 0, 0,
_width, _height,
0, kRotateNone, &_videoFrame));
width_, height_,
0, kRotateNone, &video_frame_));
EXPECT_EQ(-1, _vpm->Deflickering(&_videoFrame, &stats));
EXPECT_EQ(-1, vpm_->Deflickering(&video_frame_, &stats));
EXPECT_EQ(-3, _vpm->BrightnessDetection(_videoFrame, stats));
EXPECT_EQ(-3, vpm_->BrightnessDetection(video_frame_, stats));
}
TEST_F(VideoProcessingModuleTest, HandleBadSize)
{
TEST_F(VideoProcessingModuleTest, HandleBadSize) {
VideoProcessingModule::FrameStats stats;
_videoFrame.ResetSize();
_videoFrame.set_width(_width);
_videoFrame.set_height(0);
EXPECT_EQ(-3, _vpm->GetFrameStats(&stats, _videoFrame));
video_frame_.ResetSize();
video_frame_.set_width(width_);
video_frame_.set_height(0);
EXPECT_EQ(-3, vpm_->GetFrameStats(&stats, video_frame_));
EXPECT_EQ(-1, _vpm->ColorEnhancement(&_videoFrame));
EXPECT_EQ(-1, vpm_->ColorEnhancement(&video_frame_));
EXPECT_EQ(-1, _vpm->Deflickering(&_videoFrame, &stats));
EXPECT_EQ(-1, vpm_->Deflickering(&video_frame_, &stats));
EXPECT_EQ(-1, _vpm->Denoising(&_videoFrame));
EXPECT_EQ(-1, vpm_->Denoising(&video_frame_));
EXPECT_EQ(-3, _vpm->BrightnessDetection(_videoFrame, stats));
EXPECT_EQ(-3, vpm_->BrightnessDetection(video_frame_, stats));
EXPECT_EQ(VPM_PARAMETER_ERROR, _vpm->SetTargetResolution(0,0,0));
EXPECT_EQ(VPM_PARAMETER_ERROR, _vpm->SetMaxFrameRate(0));
EXPECT_EQ(VPM_PARAMETER_ERROR, vpm_->SetTargetResolution(0,0,0));
EXPECT_EQ(VPM_PARAMETER_ERROR, vpm_->SetMaxFramerate(0));
I420VideoFrame *outFrame = NULL;
EXPECT_EQ(VPM_PARAMETER_ERROR, _vpm->PreprocessFrame(_videoFrame,
&outFrame));
I420VideoFrame *out_frame = NULL;
EXPECT_EQ(VPM_PARAMETER_ERROR, vpm_->PreprocessFrame(video_frame_,
&out_frame));
}
TEST_F(VideoProcessingModuleTest, IdenticalResultsAfterReset)
{
I420VideoFrame videoFrame2;
TEST_F(VideoProcessingModuleTest, IdenticalResultsAfterReset) {
I420VideoFrame video_frame2;
VideoProcessingModule::FrameStats stats;
// Only testing non-static functions here.
scoped_array<uint8_t> video_buffer(new uint8_t[_frame_length]);
ASSERT_EQ(_frame_length, fread(video_buffer.get(), 1, _frame_length,
_sourceFile));
scoped_array<uint8_t> video_buffer(new uint8_t[frame_length_]);
ASSERT_EQ(frame_length_, fread(video_buffer.get(), 1, frame_length_,
source_file_));
EXPECT_EQ(0, ConvertToI420(kI420, video_buffer.get(), 0, 0,
_width, _height,
0, kRotateNone, &_videoFrame));
ASSERT_EQ(0, _vpm->GetFrameStats(&stats, _videoFrame));
ASSERT_EQ(0, videoFrame2.CopyFrame(_videoFrame));
ASSERT_EQ(0, _vpm->Deflickering(&_videoFrame, &stats));
_vpm->Reset();
width_, height_,
0, kRotateNone, &video_frame_));
ASSERT_EQ(0, vpm_->GetFrameStats(&stats, video_frame_));
ASSERT_EQ(0, video_frame2.CopyFrame(video_frame_));
ASSERT_EQ(0, vpm_->Deflickering(&video_frame_, &stats));
vpm_->Reset();
// Retrieve frame stats again in case Deflickering() has zeroed them.
ASSERT_EQ(0, _vpm->GetFrameStats(&stats, videoFrame2));
ASSERT_EQ(0, _vpm->Deflickering(&videoFrame2, &stats));
EXPECT_TRUE(CompareFrames(_videoFrame, videoFrame2));
ASSERT_EQ(0, vpm_->GetFrameStats(&stats, video_frame2));
ASSERT_EQ(0, vpm_->Deflickering(&video_frame2, &stats));
EXPECT_TRUE(CompareFrames(video_frame_, video_frame2));
ASSERT_EQ(_frame_length, fread(video_buffer.get(), 1, _frame_length,
_sourceFile));
ASSERT_EQ(frame_length_, fread(video_buffer.get(), 1, frame_length_,
source_file_));
// Using ConvertToI420 to add stride to the image.
EXPECT_EQ(0, ConvertToI420(kI420, video_buffer.get(), 0, 0,
_width, _height,
0, kRotateNone, &_videoFrame));
videoFrame2.CopyFrame(_videoFrame);
EXPECT_TRUE(CompareFrames(_videoFrame, videoFrame2));
ASSERT_GE(_vpm->Denoising(&_videoFrame), 0);
_vpm->Reset();
ASSERT_GE(_vpm->Denoising(&videoFrame2), 0);
EXPECT_TRUE(CompareFrames(_videoFrame, videoFrame2));
width_, height_,
0, kRotateNone, &video_frame_));
video_frame2.CopyFrame(video_frame_);
EXPECT_TRUE(CompareFrames(video_frame_, video_frame2));
ASSERT_GE(vpm_->Denoising(&video_frame_), 0);
vpm_->Reset();
ASSERT_GE(vpm_->Denoising(&video_frame2), 0);
EXPECT_TRUE(CompareFrames(video_frame_, video_frame2));
ASSERT_EQ(_frame_length, fread(video_buffer.get(), 1, _frame_length,
_sourceFile));
ASSERT_EQ(frame_length_, fread(video_buffer.get(), 1, frame_length_,
source_file_));
EXPECT_EQ(0, ConvertToI420(kI420, video_buffer.get(), 0, 0,
_width, _height,
0, kRotateNone, &_videoFrame));
ASSERT_EQ(0, _vpm->GetFrameStats(&stats, _videoFrame));
videoFrame2.CopyFrame(_videoFrame);
ASSERT_EQ(0, _vpm->BrightnessDetection(_videoFrame, stats));
_vpm->Reset();
ASSERT_EQ(0, _vpm->BrightnessDetection(videoFrame2, stats));
EXPECT_TRUE(CompareFrames(_videoFrame, videoFrame2));
width_, height_,
0, kRotateNone, &video_frame_));
ASSERT_EQ(0, vpm_->GetFrameStats(&stats, video_frame_));
video_frame2.CopyFrame(video_frame_);
ASSERT_EQ(0, vpm_->BrightnessDetection(video_frame_, stats));
vpm_->Reset();
ASSERT_EQ(0, vpm_->BrightnessDetection(video_frame2, stats));
EXPECT_TRUE(CompareFrames(video_frame_, video_frame2));
}
TEST_F(VideoProcessingModuleTest, FrameStats)
{
TEST_F(VideoProcessingModuleTest, FrameStats) {
VideoProcessingModule::FrameStats stats;
scoped_array<uint8_t> video_buffer(new uint8_t[_frame_length]);
ASSERT_EQ(_frame_length, fread(video_buffer.get(), 1, _frame_length,
_sourceFile));
scoped_array<uint8_t> video_buffer(new uint8_t[frame_length_]);
ASSERT_EQ(frame_length_, fread(video_buffer.get(), 1, frame_length_,
source_file_));
EXPECT_EQ(0, ConvertToI420(kI420, video_buffer.get(), 0, 0,
_width, _height,
0, kRotateNone, &_videoFrame));
width_, height_,
0, kRotateNone, &video_frame_));
EXPECT_FALSE(_vpm->ValidFrameStats(stats));
EXPECT_EQ(0, _vpm->GetFrameStats(&stats, _videoFrame));
EXPECT_TRUE(_vpm->ValidFrameStats(stats));
EXPECT_FALSE(vpm_->ValidFrameStats(stats));
EXPECT_EQ(0, vpm_->GetFrameStats(&stats, video_frame_));
EXPECT_TRUE(vpm_->ValidFrameStats(stats));
printf("\nFrameStats\n");
printf("mean: %u\nnumPixels: %u\nsubSamplWidth: "
printf("mean: %u\nnum_pixels: %u\nsubSamplWidth: "
"%u\nsumSamplHeight: %u\nsum: %u\n\n",
static_cast<unsigned int>(stats.mean),
static_cast<unsigned int>(stats.numPixels),
static_cast<unsigned int>(stats.num_pixels),
static_cast<unsigned int>(stats.subSamplHeight),
static_cast<unsigned int>(stats.subSamplWidth),
static_cast<unsigned int>(stats.sum));
_vpm->ClearFrameStats(&stats);
EXPECT_FALSE(_vpm->ValidFrameStats(stats));
vpm_->ClearFrameStats(&stats);
EXPECT_FALSE(vpm_->ValidFrameStats(stats));
}
TEST_F(VideoProcessingModuleTest, PreprocessorLogic)
{
TEST_F(VideoProcessingModuleTest, PreprocessorLogic) {
// Disable temporal sampling (frame dropping).
_vpm->EnableTemporalDecimation(false);
vpm_->EnableTemporalDecimation(false);
int resolution = 100;
EXPECT_EQ(VPM_OK, _vpm->SetMaxFrameRate(30));
EXPECT_EQ(VPM_OK, _vpm->SetTargetResolution(resolution, resolution, 15));
EXPECT_EQ(VPM_OK, _vpm->SetTargetResolution(resolution, resolution, 30));
EXPECT_EQ(VPM_OK, vpm_->SetMaxFramerate(30));
EXPECT_EQ(VPM_OK, vpm_->SetTargetResolution(resolution, resolution, 15));
EXPECT_EQ(VPM_OK, vpm_->SetTargetResolution(resolution, resolution, 30));
// Disable spatial sampling.
_vpm->SetInputFrameResampleMode(kNoRescaling);
EXPECT_EQ(VPM_OK, _vpm->SetTargetResolution(resolution, resolution, 30));
I420VideoFrame* outFrame = NULL;
vpm_->SetInputFrameResampleMode(kNoRescaling);
EXPECT_EQ(VPM_OK, vpm_->SetTargetResolution(resolution, resolution, 30));
I420VideoFrame* out_frame = NULL;
// Set rescaling => output frame != NULL.
_vpm->SetInputFrameResampleMode(kFastRescaling);
EXPECT_EQ(VPM_OK, _vpm->SetTargetResolution(resolution, resolution, 30));
EXPECT_EQ(VPM_OK, _vpm->PreprocessFrame(_videoFrame, &outFrame));
EXPECT_FALSE(outFrame == NULL);
if (outFrame) {
EXPECT_EQ(resolution, outFrame->width());
EXPECT_EQ(resolution, outFrame->height());
vpm_->SetInputFrameResampleMode(kFastRescaling);
EXPECT_EQ(VPM_OK, vpm_->SetTargetResolution(resolution, resolution, 30));
EXPECT_EQ(VPM_OK, vpm_->PreprocessFrame(video_frame_, &out_frame));
EXPECT_FALSE(out_frame == NULL);
if (out_frame) {
EXPECT_EQ(resolution, out_frame->width());
EXPECT_EQ(resolution, out_frame->height());
}
// No rescaling=> output frame = NULL.
_vpm->SetInputFrameResampleMode(kNoRescaling);
EXPECT_EQ(VPM_OK, _vpm->PreprocessFrame(_videoFrame, &outFrame));
EXPECT_TRUE(outFrame == NULL);
vpm_->SetInputFrameResampleMode(kNoRescaling);
EXPECT_EQ(VPM_OK, vpm_->PreprocessFrame(video_frame_, &out_frame));
EXPECT_TRUE(out_frame == NULL);
}
TEST_F(VideoProcessingModuleTest, Resampler)
{
TEST_F(VideoProcessingModuleTest, Resampler) {
enum { NumRuns = 1 };
int64_t minRuntime = 0;
int64_t avgRuntime = 0;
int64_t min_runtime = 0;
int64_t avg_runtime = 0;
TickTime t0;
TickTime t1;
TickInterval accTicks;
TickInterval acc_ticks;
rewind(_sourceFile);
ASSERT_TRUE(_sourceFile != NULL) <<
rewind(source_file_);
ASSERT_TRUE(source_file_ != NULL) <<
"Cannot read input file \n";
// CA not needed here
_vpm->EnableContentAnalysis(false);
vpm_->EnableContentAnalysis(false);
// no temporal decimation
_vpm->EnableTemporalDecimation(false);
vpm_->EnableTemporalDecimation(false);
// Reading test frame
scoped_array<uint8_t> video_buffer(new uint8_t[_frame_length]);
ASSERT_EQ(_frame_length, fread(video_buffer.get(), 1, _frame_length,
_sourceFile));
scoped_array<uint8_t> video_buffer(new uint8_t[frame_length_]);
ASSERT_EQ(frame_length_, fread(video_buffer.get(), 1, frame_length_,
source_file_));
// Using ConvertToI420 to add stride to the image.
EXPECT_EQ(0, ConvertToI420(kI420, video_buffer.get(), 0, 0,
_width, _height,
0, kRotateNone, &_videoFrame));
width_, height_,
0, kRotateNone, &video_frame_));
for (uint32_t runIdx = 0; runIdx < NumRuns; runIdx++)
{
// initiate test timer
for (uint32_t run_idx = 0; run_idx < NumRuns; run_idx++) {
// Initiate test timer.
t0 = TickTime::Now();
// Init the sourceFrame with a timestamp.
_videoFrame.set_render_time_ms(t0.MillisecondTimestamp());
_videoFrame.set_timestamp(t0.MillisecondTimestamp() * 90);
video_frame_.set_render_time_ms(t0.MillisecondTimestamp());
video_frame_.set_timestamp(t0.MillisecondTimestamp() * 90);
// Test scaling to different sizes: source is of |width|/|height| = 352/288.
// Scaling mode in VPM is currently fixed to kScaleBox (mode = 3).
TestSize(_videoFrame, 100, 50, 3, 24.0, _vpm);
TestSize(_videoFrame, 352/4, 288/4, 3, 25.2, _vpm);
TestSize(_videoFrame, 352/2, 288/2, 3, 28.1, _vpm);
TestSize(_videoFrame, 352, 288, 3, -1, _vpm); // no resampling.
TestSize(_videoFrame, 2*352, 2*288, 3, 32.2, _vpm);
TestSize(_videoFrame, 400, 256, 3, 31.3, _vpm);
TestSize(_videoFrame, 480, 640, 3, 32.15, _vpm);
TestSize(_videoFrame, 960, 720, 3, 32.2, _vpm);
TestSize(_videoFrame, 1280, 720, 3, 32.15, _vpm);
TestSize(video_frame_, 100, 50, 3, 24.0, vpm_);
TestSize(video_frame_, 352/4, 288/4, 3, 25.2, vpm_);
TestSize(video_frame_, 352/2, 288/2, 3, 28.1, vpm_);
TestSize(video_frame_, 352, 288, 3, -1, vpm_); // no resampling.
TestSize(video_frame_, 2*352, 2*288, 3, 32.2, vpm_);
TestSize(video_frame_, 400, 256, 3, 31.3, vpm_);
TestSize(video_frame_, 480, 640, 3, 32.15, vpm_);
TestSize(video_frame_, 960, 720, 3, 32.2, vpm_);
TestSize(video_frame_, 1280, 720, 3, 32.15, vpm_);
// Upsampling to odd size.
TestSize(_videoFrame, 501, 333, 3, 32.05, _vpm);
TestSize(video_frame_, 501, 333, 3, 32.05, vpm_);
// Downsample to odd size.
TestSize(_videoFrame, 281, 175, 3, 29.3, _vpm);
TestSize(video_frame_, 281, 175, 3, 29.3, vpm_);
// stop timer
t1 = TickTime::Now();
accTicks += (t1 - t0);
acc_ticks += (t1 - t0);
if (accTicks.Microseconds() < minRuntime || runIdx == 0) {
minRuntime = accTicks.Microseconds();
if (acc_ticks.Microseconds() < min_runtime || run_idx == 0) {
min_runtime = acc_ticks.Microseconds();
}
avgRuntime += accTicks.Microseconds();
avg_runtime += acc_ticks.Microseconds();
}
printf("\nAverage run time = %d us / frame\n",
//static_cast<int>(avgRuntime / frameNum / NumRuns));
static_cast<int>(avgRuntime));
//static_cast<int>(avg_runtime / frameNum / NumRuns));
static_cast<int>(avg_runtime));
printf("Min run time = %d us / frame\n\n",
//static_cast<int>(minRuntime / frameNum));
static_cast<int>(minRuntime));
//static_cast<int>(min_runtime / frameNum));
static_cast<int>(min_runtime));
}
void TestSize(const I420VideoFrame& source_frame, int target_width,
int target_height, int mode, double expected_psnr,
void TestSize(const I420VideoFrame& source_frame, int targetwidth_,
int targetheight_, int mode, double expected_psnr,
VideoProcessingModule* vpm) {
int source_width = source_frame.width();
int source_height = source_frame.height();
int sourcewidth_ = source_frame.width();
int sourceheight_ = source_frame.height();
I420VideoFrame* out_frame = NULL;
ASSERT_EQ(VPM_OK, vpm->SetTargetResolution(target_width, target_height, 30));
ASSERT_EQ(VPM_OK, vpm->SetTargetResolution(targetwidth_, targetheight_, 30));
ASSERT_EQ(VPM_OK, vpm->PreprocessFrame(source_frame, &out_frame));
if (out_frame) {
@ -321,19 +309,19 @@ void TestSize(const I420VideoFrame& source_frame, int target_width,
// (2) scale the resampled frame (|out_frame|) back to the original size and
// compute PSNR relative to |source_frame| (for automatic verification).
// (3) write out the processed frame for viewing.
if (target_width != static_cast<int>(source_width) ||
target_height != static_cast<int>(source_height)) {
if (targetwidth_ != static_cast<int>(sourcewidth_) ||
targetheight_ != static_cast<int>(sourceheight_)) {
// Write the processed frame to file for visual inspection.
std::ostringstream filename;
filename << webrtc::test::OutputPath() << "Resampler_"<< mode << "_" <<
"from_" << source_width << "x" << source_height << "_to_" <<
target_width << "x" << target_height << "_30Hz_P420.yuv";
"from_" << sourcewidth_ << "x" << sourceheight_ << "_to_" <<
targetwidth_ << "x" << targetheight_ << "_30Hz_P420.yuv";
std::cout << "Watch " << filename.str() << " and verify that it is okay."
<< std::endl;
FILE* stand_alone_file = fopen(filename.str().c_str(), "wb");
if (PrintI420VideoFrame(*out_frame, stand_alone_file) < 0) {
fprintf(stderr, "Failed to write frame for scaling to width/height: "
" %d %d \n", target_width, target_height);
" %d %d \n", targetwidth_, targetheight_);
return;
}
fclose(stand_alone_file);
@ -342,8 +330,8 @@ void TestSize(const I420VideoFrame& source_frame, int target_width,
resampled_source_frame.CopyFrame(*out_frame);
// Scale |resampled_source_frame| back to original/source size.
ASSERT_EQ(VPM_OK, vpm->SetTargetResolution(source_width,
source_height,
ASSERT_EQ(VPM_OK, vpm->SetTargetResolution(sourcewidth_,
sourceheight_,
30));
ASSERT_EQ(VPM_OK, vpm->PreprocessFrame(resampled_source_frame,
&out_frame));
@ -351,14 +339,14 @@ void TestSize(const I420VideoFrame& source_frame, int target_width,
// Write the processed frame to file for visual inspection.
std::ostringstream filename2;
filename2 << webrtc::test::OutputPath() << "Resampler_"<< mode << "_" <<
"from_" << target_width << "x" << target_height << "_to_" <<
source_width << "x" << source_height << "_30Hz_P420.yuv";
"from_" << targetwidth_ << "x" << targetheight_ << "_to_" <<
sourcewidth_ << "x" << sourceheight_ << "_30Hz_P420.yuv";
std::cout << "Watch " << filename2.str() << " and verify that it is okay."
<< std::endl;
stand_alone_file = fopen(filename2.str().c_str(), "wb");
if (PrintI420VideoFrame(*out_frame, stand_alone_file) < 0) {
fprintf(stderr, "Failed to write frame for scaling to width/height "
"%d %d \n", source_width, source_height);
"%d %d \n", sourcewidth_, sourceheight_);
return;
}
fclose(stand_alone_file);
@ -368,7 +356,7 @@ void TestSize(const I420VideoFrame& source_frame, int target_width,
EXPECT_GT(psnr, expected_psnr);
printf("PSNR: %f. PSNR is between source of size %d %d, and a modified "
"source which is scaled down/up to: %d %d, and back to source size \n",
psnr, source_width, source_height, target_width, target_height);
psnr, sourcewidth_, sourceheight_, targetwidth_, targetheight_);
}
}

View File

@ -18,31 +18,28 @@
namespace webrtc {
class VideoProcessingModuleTest : public ::testing::Test
{
protected:
VideoProcessingModuleTest();
virtual void SetUp();
virtual void TearDown();
static void SetUpTestCase()
{
Trace::CreateTrace();
std::string trace_file = webrtc::test::OutputPath() + "VPMTrace.txt";
ASSERT_EQ(0, Trace::SetTraceFile(trace_file.c_str()));
}
static void TearDownTestCase()
{
Trace::ReturnTrace();
}
VideoProcessingModule* _vpm;
FILE* _sourceFile;
I420VideoFrame _videoFrame;
const int _width;
const int _half_width;
const int _height;
const int _size_y;
const int _size_uv;
const unsigned int _frame_length;
class VideoProcessingModuleTest : public ::testing::Test {
protected:
VideoProcessingModuleTest();
virtual void SetUp();
virtual void TearDown();
static void SetUpTestCase() {
Trace::CreateTrace();
std::string trace_file = webrtc::test::OutputPath() + "VPMTrace.txt";
ASSERT_EQ(0, Trace::SetTraceFile(trace_file.c_str()));
}
static void TearDownTestCase() {
Trace::ReturnTrace();
}
VideoProcessingModule* vpm_;
FILE* source_file_;
I420VideoFrame video_frame_;
const int width_;
const int half_width_;
const int height_;
const int size_y_;
const int size_uv_;
const unsigned int frame_length_;
};
} // namespace webrtc