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; VideoContentMetrics* contentMetrics = NULL;
// setting user frame rate // setting user frame rate
_vpm->SetMaxFrameRate((uint32_t)(_nativeFrameRate+ 0.5f)); _vpm->SetMaxFramerate((uint32_t)(_nativeFrameRate+ 0.5f));
// for starters: keeping native values: // for starters: keeping native values:
_vpm->SetTargetResolution(_width, _height, _vpm->SetTargetResolution(_width, _height,
(uint32_t)(_frameRate+ 0.5f)); (uint32_t)(_frameRate+ 0.5f));

View File

@ -24,281 +24,274 @@
#include "webrtc/modules/video_processing/main/interface/video_processing_defines.h" #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 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() concurrently processed stream. Similarly, it is recommended to call Reset()
before switching to a new stream, but this is not absolutely required. before switching to a new stream, but this is not absolutely required.
The module provides basic thread safety by permitting only a single function The module provides basic thread safety by permitting only a single function
to execute concurrently. to execute concurrently.
*/ */
namespace webrtc { namespace webrtc {
class VideoProcessingModule : public Module class VideoProcessingModule : public Module {
{ public:
public: /**
/** Structure to hold frame statistics. Populate it with GetFrameStats().
Structure to hold frame statistics. Populate it with GetFrameStats(). */
*/ struct FrameStats {
struct FrameStats FrameStats() :
{ mean(0),
FrameStats() : sum(0),
mean(0), num_pixels(0),
sum(0), subSamplWidth(0),
numPixels(0), subSamplHeight(0) {
subSamplWidth(0), memset(hist, 0, sizeof(hist));
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;
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_UNINITIALIZED -5
#define VPM_UNIMPLEMENTED -6 #define VPM_UNIMPLEMENTED -6
enum VideoFrameResampling enum VideoFrameResampling {
{ kNoRescaling, // Disables rescaling.
// TODO: Do we still need crop/pad? kFastRescaling, // Point filter.
kNoRescaling, // disables rescaling kBiLinear, // Bi-linear interpolation.
kFastRescaling, // point kBox, // Box inteprolation.
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; 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++) { for (int i = 0; i < 256; i++) {
int val = i + delta; 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++) { for (int i = 0; i < num_pixels; i++) {
*tempPtr = static_cast<uint8_t>(lookUp[*tempPtr]); *temp_ptr = static_cast<uint8_t>(look_up[*temp_ptr]);
tempPtr++; temp_ptr++;
} }
return VPM_OK; return VPM_OK;
} }

View File

@ -17,180 +17,128 @@
namespace webrtc { namespace webrtc {
VPMBrightnessDetection::VPMBrightnessDetection() : VPMBrightnessDetection::VPMBrightnessDetection() :
_id(0) id_(0) {
{ Reset();
Reset();
} }
VPMBrightnessDetection::~VPMBrightnessDetection() VPMBrightnessDetection::~VPMBrightnessDetection() {}
{
int32_t VPMBrightnessDetection::ChangeUniqueId(const int32_t id) {
id_ = id;
return VPM_OK;
} }
int32_t void VPMBrightnessDetection::Reset() {
VPMBrightnessDetection::ChangeUniqueId(const int32_t id) frame_cnt_bright_ = 0;
{ frame_cnt_dark_ = 0;
_id = id;
return VPM_OK;
} }
void int32_t VPMBrightnessDetection::ProcessFrame(
VPMBrightnessDetection::Reset() const I420VideoFrame& frame,
{ const VideoProcessingModule::FrameStats& stats) {
_frameCntBright = 0; if (frame.IsZeroSize()) {
_frameCntDark = 0; WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoPreocessing, id_,
} "Null frame pointer");
return VPM_PARAMETER_ERROR;
}
int width = frame.width();
int height = frame.height();
int32_t if (!VideoProcessingModule::ValidFrameStats(stats)) {
VPMBrightnessDetection::ProcessFrame(const I420VideoFrame& frame, WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoPreocessing, id_,
const VideoProcessingModule::FrameStats& "Invalid frame stats");
stats) return VPM_PARAMETER_ERROR;
{ }
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)) const uint8_t frame_cnt_alarm = 2;
{
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoPreocessing, _id,
"Invalid frame stats");
return VPM_PARAMETER_ERROR;
}
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 // Get proportion in highest bins.
uint8_t lowTh = 20; unsigned char high_th = 230;
float propLow = 0; float prop_high = 0;
for (uint32_t i = 0; i < lowTh; i++) for (uint32_t i = high_th; i < 256; i++) {
{ prop_high += stats.hist[i];
propLow += stats.hist[i]; }
} prop_high /= stats.num_pixels;
propLow /= stats.numPixels;
// Get proportion in highest bins if (prop_high < 0.4) {
unsigned char highTh = 230; if (stats.mean < 90 || stats.mean > 170) {
float propHigh = 0; // Standard deviation of Y
for (uint32_t i = highTh; i < 256; i++) const uint8_t* buffer = frame.buffer(kYPlane);
{ float std_y = 0;
propHigh += stats.hist[i]; for (int h = 0; h < height; h += (1 << stats.subSamplHeight)) {
} int row = h*width;
propHigh /= stats.numPixels; 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) // Get percentiles.
{ uint32_t sum = 0;
if (stats.mean < 90 || stats.mean > 170) uint32_t median_y = 140;
{ uint32_t perc05 = 0;
// Standard deviation of Y uint32_t perc95 = 255;
const uint8_t* buffer = frame.buffer(kYPlane); float pos_perc05 = stats.num_pixels * 0.05f;
float stdY = 0; float pos_median = stats.num_pixels * 0.5f;
for (int h = 0; h < height; h += (1 << stats.subSamplHeight)) float posPerc95 = stats.num_pixels * 0.95f;
{ for (uint32_t i = 0; i < 256; i++) {
int row = h*width; sum += stats.hist[i];
for (int w = 0; w < width; w += (1 << stats.subSamplWidth)) if (sum < pos_perc05) perc05 = i; // 5th perc.
{ if (sum < pos_median) median_y = i; // 50th perc.
stdY += (buffer[w + row] - stats.mean) * (buffer[w + row] - if (sum < posPerc95)
stats.mean); perc95 = i; // 95th perc.
}
}
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;
}
}
else else
{ break;
_frameCntDark = 0; }
_frameCntBright = 0;
// 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;
} }
} // Check if image is too bright
else if ((std_y < 52) && (perc95 > 200) && (median_y > 160)) {
{ if (median_y > 185 || stats.mean > 185 || perc05 > 140 ||
_frameCntBright++; prop_high > 0.25) {
_frameCntDark = 0; frame_cnt_bright_++;
} } else {
frame_cnt_bright_ = 0;
if (_frameCntDark > frameCntAlarm) }
{ } else {
return VideoProcessingModule::kDarkWarning; frame_cnt_bright_ = 0;
} }
else if (_frameCntBright > frameCntAlarm) } else {
{ frame_cnt_dark_ = 0;
return VideoProcessingModule::kBrightWarning; frame_cnt_bright_ = 0;
}
else
{
return VideoProcessingModule::kNoWarning;
} }
} 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 * brightness_detection.h
*/ */
#ifndef VPM_BRIGHTNESS_DETECTION_H #ifndef MODULES_VIDEO_PROCESSING_MAIN_SOURCE_BRIGHTNESS_DETECTION_H
#define VPM_BRIGHTNESS_DETECTION_H #define MODULES_VIDEO_PROCESSING_MAIN_SOURCE_BRIGHTNESS_DETECTION_H
#include "webrtc/modules/video_processing/main/interface/video_processing.h" #include "webrtc/modules/video_processing/main/interface/video_processing.h"
#include "webrtc/typedefs.h" #include "webrtc/typedefs.h"
namespace webrtc { namespace webrtc {
class VPMBrightnessDetection class VPMBrightnessDetection {
{ public:
public: VPMBrightnessDetection();
VPMBrightnessDetection(); ~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, uint32_t frame_cnt_bright_;
const VideoProcessingModule::FrameStats& stats); uint32_t frame_cnt_dark_;
private:
int32_t _id;
uint32_t _frameCntBright;
uint32_t _frameCntDark;
}; };
} // 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 webrtc {
namespace VideoProcessing namespace VideoProcessing {
{ int32_t ColorEnhancement(I420VideoFrame* frame) {
int32_t assert(frame);
ColorEnhancement(I420VideoFrame* frame) // Pointers to U and V color pixels.
{ uint8_t* ptr_u;
assert(frame); uint8_t* ptr_v;
// pointers to U and V color pixels uint8_t temp_chroma;
uint8_t* ptrU; if (frame->IsZeroSize()) {
uint8_t* ptrV; WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoPreocessing,
uint8_t tempChroma; -1, "Null frame pointer");
return VPM_GENERAL_ERROR;
}
if (frame->IsZeroSize()) if (frame->width() == 0 || frame->height() == 0) {
{ WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoPreocessing,
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoPreocessing, -1, "Invalid frame size");
-1, "Null frame pointer"); return VPM_GENERAL_ERROR;
return VPM_GENERAL_ERROR; }
}
if (frame->width() == 0 || frame->height() == 0) // set pointers to first U and V pixels (skip luminance)
{ ptr_u = frame->buffer(kUPlane);
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoPreocessing, ptr_v = frame->buffer(kVPlane);
-1, "Invalid frame size"); int size_uv = ((frame->width() + 1) / 2) * ((frame->height() + 1) / 2);
return VPM_GENERAL_ERROR;
}
// set pointers to first U and V pixels (skip luminance) // Loop through all chrominance pixels and modify color
ptrU = frame->buffer(kUPlane); for (int ix = 0; ix < size_uv; ix++) {
ptrV = frame->buffer(kVPlane); temp_chroma = colorTable[*ptr_u][*ptr_v];
int size_uv = ((frame->width() + 1) / 2) * ((frame->height() + 1) / 2); *ptr_v = colorTable[*ptr_v][*ptr_u];
*ptr_u = temp_chroma;
// loop through all chrominance pixels and modify color ptr_u++;
for (int ix = 0; ix < size_uv; ix++) ptr_v++;
{ }
tempChroma = colorTable[*ptrU][*ptrV]; return VPM_OK;
*ptrV = colorTable[*ptrV][*ptrU]; }
*ptrU = tempChroma;
// increment pointers
ptrU++;
ptrV++;
}
return VPM_OK;
}
} // namespace } // namespace VideoProcessing
} // namespace webrtc } // namespace webrtc

View File

@ -11,19 +11,18 @@
/* /*
* color_enhancement.h * color_enhancement.h
*/ */
#ifndef VPM_COLOR_ENHANCEMENT_H #ifndef WEBRTC_MODULES_VIDEO_PROCESSING_COLOR_ENHANCEMENT_H
#define VPM_COLOR_ENHANCEMENT_H #define WEBRTC_MODULES_VIDEO_PROCESSING_COLOR_ENHANCEMENT_H
#include "webrtc/modules/video_processing/main/interface/video_processing.h" #include "webrtc/modules/video_processing/main/interface/video_processing.h"
#include "webrtc/typedefs.h" #include "webrtc/typedefs.h"
namespace webrtc { namespace webrtc {
namespace VideoProcessing namespace VideoProcessing {
{ int32_t ColorEnhancement(I420VideoFrame* frame);
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 #ifndef VPM_COLOR_ENHANCEMENT_PRIVATE_H
#define VPM_COLOR_ENHANCEMENT_PRIVATE_H #define VPM_COLOR_ENHANCEMENT_PRIVATE_H
@ -270,4 +280,4 @@ static const uint8_t colorTable[256][256] = {
} // namespace } // namespace
#endif // VPM_COLOR_ENHANCEMENT_PRIVATE_H #endif // VPM_COLOR_ENHANCEMENT_PRIVATE_H

View File

@ -17,229 +17,185 @@
namespace webrtc { namespace webrtc {
VPMContentAnalysis::VPMContentAnalysis(bool runtime_cpu_detection): VPMContentAnalysis::VPMContentAnalysis(bool runtime_cpu_detection)
_origFrame(NULL), : orig_frame_(NULL),
_prevFrame(NULL), prev_frame_(NULL),
_width(0), width_(0),
_height(0), height_(0),
_skipNum(1), skip_num_(1),
_border(8), border_(8),
_motionMagnitude(0.0f), motion_magnitude_(0.0f),
_spatialPredErr(0.0f), spatial_pred_err_(0.0f),
_spatialPredErrH(0.0f), spatial_pred_err_h_(0.0f),
_spatialPredErrV(0.0f), spatial_pred_err_v_(0.0f),
_firstFrame(true), first_frame_(true),
_CAInit(false), ca_Init_(false),
_cMetrics(NULL) content_metrics_(NULL) {
{ ComputeSpatialMetrics = &VPMContentAnalysis::ComputeSpatialMetrics_C;
ComputeSpatialMetrics = &VPMContentAnalysis::ComputeSpatialMetrics_C; TemporalDiffMetric = &VPMContentAnalysis::TemporalDiffMetric_C;
TemporalDiffMetric = &VPMContentAnalysis::TemporalDiffMetric_C;
if (runtime_cpu_detection) if (runtime_cpu_detection) {
{
#if defined(WEBRTC_ARCH_X86_FAMILY) #if defined(WEBRTC_ARCH_X86_FAMILY)
if (WebRtc_GetCPUInfo(kSSE2)) if (WebRtc_GetCPUInfo(kSSE2)) {
{ ComputeSpatialMetrics = &VPMContentAnalysis::ComputeSpatialMetrics_SSE2;
ComputeSpatialMetrics = TemporalDiffMetric = &VPMContentAnalysis::TemporalDiffMetric_SSE2;
&VPMContentAnalysis::ComputeSpatialMetrics_SSE2; }
TemporalDiffMetric = &VPMContentAnalysis::TemporalDiffMetric_SSE2;
}
#endif #endif
} }
Release();
Release();
} }
VPMContentAnalysis::~VPMContentAnalysis() VPMContentAnalysis::~VPMContentAnalysis() {
{ Release();
Release();
} }
VideoContentMetrics* VideoContentMetrics* VPMContentAnalysis::ComputeContentMetrics(
VPMContentAnalysis::ComputeContentMetrics(const I420VideoFrame& inputFrame) const I420VideoFrame& inputFrame) {
{ if (inputFrame.IsZeroSize())
if (inputFrame.IsZeroSize()) return NULL;
{
return NULL;
}
// Init if needed (native dimension change) // Init if needed (native dimension change).
if (_width != inputFrame.width() || _height != inputFrame.height()) if (width_ != inputFrame.width() || height_ != inputFrame.height()) {
{ if (VPM_OK != Initialize(inputFrame.width(), inputFrame.height()))
if (VPM_OK != Initialize(inputFrame.width(), inputFrame.height())) return NULL;
{ }
return NULL; // Only interested in the Y plane.
} orig_frame_ = inputFrame.buffer(kYPlane);
}
// Only interested in the Y plane.
_origFrame = inputFrame.buffer(kYPlane);
// compute spatial metrics: 3 spatial prediction errors // Compute spatial metrics: 3 spatial prediction errors.
(this->*ComputeSpatialMetrics)(); (this->*ComputeSpatialMetrics)();
// compute motion metrics // Compute motion metrics
if (_firstFrame == false) if (first_frame_ == false)
ComputeMotionMetrics(); ComputeMotionMetrics();
// saving current frame as previous one: Y only // Saving current frame as previous one: Y only.
memcpy(_prevFrame, _origFrame, _width * _height); memcpy(prev_frame_, orig_frame_, width_ * height_);
_firstFrame = false; first_frame_ = false;
_CAInit = true; ca_Init_ = true;
return ContentMetrics(); return ContentMetrics();
} }
int32_t int32_t VPMContentAnalysis::Release() {
VPMContentAnalysis::Release() if (content_metrics_ != NULL) {
{ delete content_metrics_;
if (_cMetrics != NULL) content_metrics_ = NULL;
{ }
delete _cMetrics;
_cMetrics = NULL;
}
if (_prevFrame != NULL) if (prev_frame_ != NULL) {
{ delete [] prev_frame_;
delete [] _prevFrame; prev_frame_ = NULL;
_prevFrame = NULL; }
}
_width = 0; width_ = 0;
_height = 0; height_ = 0;
_firstFrame = true; first_frame_ = true;
return VPM_OK; return VPM_OK;
} }
int32_t int32_t VPMContentAnalysis::Initialize(int width, int height) {
VPMContentAnalysis::Initialize(int width, int height) width_ = width;
{ height_ = height;
_width = width; first_frame_ = true;
_height = height;
_firstFrame = true;
// skip parameter: # of skipped rows: for complexity reduction // skip parameter: # of skipped rows: for complexity reduction
// temporal also currently uses it for column reduction. // temporal also currently uses it for column reduction.
_skipNum = 1; skip_num_ = 1;
// use skipNum = 2 for 4CIF, WHD // use skipNum = 2 for 4CIF, WHD
if ( (_height >= 576) && (_width >= 704) ) if ( (height_ >= 576) && (width_ >= 704) ) {
{ skip_num_ = 2;
_skipNum = 2; }
} // use skipNum = 4 for FULLL_HD images
// use skipNum = 4 for FULLL_HD images if ( (height_ >= 1080) && (width_ >= 1920) ) {
if ( (_height >= 1080) && (_width >= 1920) ) skip_num_ = 4;
{ }
_skipNum = 4;
}
if (_cMetrics != NULL) if (content_metrics_ != NULL) {
{ delete content_metrics_;
delete _cMetrics; }
}
if (_prevFrame != NULL) if (prev_frame_ != NULL) {
{ delete [] prev_frame_;
delete [] _prevFrame; }
}
// Spatial Metrics don't work on a border of 8. Minimum processing // 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. // block size is 16 pixels. So make sure the width and height support this.
if (_width <= 32 || _height <= 32) if (width_ <= 32 || height_ <= 32) {
{ ca_Init_ = false;
_CAInit = false; return VPM_PARAMETER_ERROR;
return VPM_PARAMETER_ERROR; }
}
_cMetrics = new VideoContentMetrics(); content_metrics_ = new VideoContentMetrics();
if (_cMetrics == NULL) if (content_metrics_ == NULL) {
{ return VPM_MEMORY;
return VPM_MEMORY; }
}
_prevFrame = new uint8_t[_width * _height] ; // Y only prev_frame_ = new uint8_t[width_ * height_]; // Y only.
if (_prevFrame == NULL) if (prev_frame_ == NULL) return VPM_MEMORY;
{
return VPM_MEMORY;
}
return VPM_OK; return VPM_OK;
} }
// Compute motion metrics: magnitude over non-zero motion vectors, // Compute motion metrics: magnitude over non-zero motion vectors,
// and size of zero cluster // and size of zero cluster
int32_t int32_t VPMContentAnalysis::ComputeMotionMetrics() {
VPMContentAnalysis::ComputeMotionMetrics() // Motion metrics: only one is derived from normalized
{ // (MAD) temporal difference
(this->*TemporalDiffMetric)();
// Motion metrics: only one is derived from normalized return VPM_OK;
// (MAD) temporal difference
(this->*TemporalDiffMetric)();
return VPM_OK;
} }
// Normalized temporal difference (MAD): used as a motion level metric // Normalized temporal difference (MAD): used as a motion level metric
// Normalize MAD by spatial contrast: images with more contrast // Normalize MAD by spatial contrast: images with more contrast
// (pixel variance) likely have larger temporal difference // (pixel variance) likely have larger temporal difference
// To reduce complexity, we compute the metric for a reduced set of points. // To reduce complexity, we compute the metric for a reduced set of points.
int32_t int32_t VPMContentAnalysis::TemporalDiffMetric_C() {
VPMContentAnalysis::TemporalDiffMetric_C() // size of original frame
{ int sizei = height_;
// size of original frame int sizej = width_;
int sizei = _height; uint32_t tempDiffSum = 0;
int sizej = _width; uint32_t pixelSum = 0;
uint64_t pixelSqSum = 0;
uint32_t tempDiffSum = 0; uint32_t num_pixels = 0; // Counter for # of pixels.
uint32_t pixelSum = 0; const int width_end = ((width_ - 2*border_) & -16) + border_;
uint64_t pixelSqSum = 0;
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) tempDiffSum += (uint32_t)abs((int16_t)(currPixel - prevPixel));
{ pixelSum += (uint32_t) currPixel;
for(int j = _border; j < width_end; j++) pixelSqSum += (uint64_t) (currPixel * currPixel);
{
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);
}
} }
}
// default // Default.
_motionMagnitude = 0.0f; motion_magnitude_ = 0.0f;
if (tempDiffSum == 0) if (tempDiffSum == 0) return VPM_OK;
{
return VPM_OK;
}
// normalize over all pixels // Normalize over all pixels.
float const tempDiffAvg = (float)tempDiffSum / (float)(numPixels); float const tempDiffAvg = (float)tempDiffSum / (float)(num_pixels);
float const pixelSumAvg = (float)pixelSum / (float)(numPixels); float const pixelSumAvg = (float)pixelSum / (float)(num_pixels);
float const pixelSqSumAvg = (float)pixelSqSum / (float)(numPixels); float const pixelSqSumAvg = (float)pixelSqSum / (float)(num_pixels);
float contrast = pixelSqSumAvg - (pixelSumAvg * pixelSumAvg); float contrast = pixelSqSumAvg - (pixelSumAvg * pixelSumAvg);
if (contrast > 0.0)
{
contrast = sqrt(contrast);
_motionMagnitude = tempDiffAvg/contrast;
}
return VPM_OK;
if (contrast > 0.0) {
contrast = sqrt(contrast);
motion_magnitude_ = tempDiffAvg/contrast;
}
return VPM_OK;
} }
// Compute spatial metrics: // Compute spatial metrics:
@ -249,88 +205,71 @@ VPMContentAnalysis::TemporalDiffMetric_C()
// The metrics are a simple estimate of the up-sampling prediction error, // The metrics are a simple estimate of the up-sampling prediction error,
// estimated assuming sub-sampling for decimation (no filtering), // estimated assuming sub-sampling for decimation (no filtering),
// and up-sampling back up with simple bilinear interpolation. // and up-sampling back up with simple bilinear interpolation.
int32_t int32_t VPMContentAnalysis::ComputeSpatialMetrics_C() {
VPMContentAnalysis::ComputeSpatialMetrics_C() const int sizei = height_;
{ const int sizej = width_;
//size of original frame
const int sizei = _height;
const int sizej = _width;
// pixel mean square average: used to normalize the spatial metrics // Pixel mean square average: used to normalize the spatial metrics.
uint32_t pixelMSA = 0; uint32_t pixelMSA = 0;
uint32_t spatialErrSum = 0; uint32_t spatialErrSum = 0;
uint32_t spatialErrVSum = 0; uint32_t spatialErrVSum = 0;
uint32_t spatialErrHSum = 0; uint32_t spatialErrHSum = 0;
// make sure work section is a multiple of 16 // make sure work section is a multiple of 16
const int width_end = ((sizej - 2*_border) & -16) + _border; const int width_end = ((sizej - 2*border_) & -16) + border_;
for(int i = _border; i < sizei - _border; i += _skipNum) for (int i = border_; i < sizei - border_; i += skip_num_) {
{ for (int j = border_; j < width_end; j++) {
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; uint16_t refPixel1 = orig_frame_[ssn1] << 1;
int ssn2 = (i + 1) * sizej + j; // bottom uint16_t refPixel2 = orig_frame_[ssn1] << 2;
int ssn3 = (i - 1) * sizej + j; // top
int ssn4 = i * sizej + j + 1; // right
int ssn5 = i * sizej + j - 1; // left
uint16_t refPixel1 = _origFrame[ssn1] << 1; uint8_t bottPixel = orig_frame_[ssn2];
uint16_t refPixel2 = _origFrame[ssn1] << 2; uint8_t topPixel = orig_frame_[ssn3];
uint8_t rightPixel = orig_frame_[ssn4];
uint8_t leftPixel = orig_frame_[ssn5];
uint8_t bottPixel = _origFrame[ssn2]; spatialErrSum += (uint32_t) abs((int16_t)(refPixel2
uint8_t topPixel = _origFrame[ssn3]; - (uint16_t)(bottPixel + topPixel + leftPixel + rightPixel)));
uint8_t rightPixel = _origFrame[ssn4]; spatialErrVSum += (uint32_t) abs((int16_t)(refPixel1
uint8_t leftPixel = _origFrame[ssn5]; - (uint16_t)(bottPixel + topPixel)));
spatialErrHSum += (uint32_t) abs((int16_t)(refPixel1
spatialErrSum += (uint32_t) abs((int16_t)(refPixel2 - (uint16_t)(leftPixel + rightPixel)));
- (uint16_t)(bottPixel + topPixel pixelMSA += orig_frame_[ssn1];
+ 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];
}
} }
}
// normalize over all pixels // Normalize over all pixels.
const float spatialErr = (float)(spatialErrSum >> 2); const float spatialErr = (float)(spatialErrSum >> 2);
const float spatialErrH = (float)(spatialErrHSum >> 1); const float spatialErrH = (float)(spatialErrHSum >> 1);
const float spatialErrV = (float)(spatialErrVSum >> 1); const float spatialErrV = (float)(spatialErrVSum >> 1);
const float norm = (float)pixelMSA; const float norm = (float)pixelMSA;
// 2X2: // 2X2:
_spatialPredErr = spatialErr / norm; spatial_pred_err_ = spatialErr / norm;
// 1X2:
// 1X2: spatial_pred_err_h_ = spatialErrH / norm;
_spatialPredErrH = spatialErrH / norm; // 2X1:
spatial_pred_err_v_ = spatialErrV / norm;
// 2X1: return VPM_OK;
_spatialPredErrV = spatialErrV / norm;
return VPM_OK;
} }
VideoContentMetrics* VideoContentMetrics* VPMContentAnalysis::ContentMetrics() {
VPMContentAnalysis::ContentMetrics() if (ca_Init_ == false) return NULL;
{
if (_CAInit == false)
{
return NULL;
}
_cMetrics->spatial_pred_err = _spatialPredErr; content_metrics_->spatial_pred_err = spatial_pred_err_;
_cMetrics->spatial_pred_err_h = _spatialPredErrH; content_metrics_->spatial_pred_err_h = spatial_pred_err_h_;
_cMetrics->spatial_pred_err_v = _spatialPredErrV; content_metrics_->spatial_pred_err_v = spatial_pred_err_v_;
// Motion metric: normalized temporal difference (MAD) // Motion metric: normalized temporal difference (MAD).
_cMetrics->motion_magnitude = _motionMagnitude; content_metrics_->motion_magnitude = motion_magnitude_;
return _cMetrics;
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. * be found in the AUTHORS file in the root of the source tree.
*/ */
#ifndef VPM_CONTENT_ANALYSIS_H #ifndef WEBRTC_MODULES_VIDEO_PROCESSING_MAIN_SOURCE_CONTENT_ANALYSIS_H
#define VPM_CONTENT_ANALYSIS_H #define WEBRTC_MODULES_VIDEO_PROCESSING_MAIN_SOURCE_CONTENT_ANALYSIS_H
#include "webrtc/common_video/interface/i420_video_frame.h" #include "webrtc/common_video/interface/i420_video_frame.h"
#include "webrtc/modules/interface/module_common_types.h" #include "webrtc/modules/interface/module_common_types.h"
@ -18,75 +18,71 @@
namespace webrtc { namespace webrtc {
class VPMContentAnalysis class VPMContentAnalysis {
{ public:
public: // When |runtime_cpu_detection| is true, runtime selection of an optimized
// When |runtime_cpu_detection| is true, runtime selection of an optimized // code path is allowed.
// code path is allowed. explicit VPMContentAnalysis(bool runtime_cpu_detection);
VPMContentAnalysis(bool runtime_cpu_detection); ~VPMContentAnalysis();
~VPMContentAnalysis();
// Initialize ContentAnalysis - should be called prior to // Initialize ContentAnalysis - should be called prior to
// extractContentFeature // extractContentFeature
// Inputs: width, height // Inputs: width, height
// Return value: 0 if OK, negative value upon error // Return value: 0 if OK, negative value upon error
int32_t Initialize(int width, int height); int32_t Initialize(int width, int height);
// Extract content Feature - main function of ContentAnalysis // Extract content Feature - main function of ContentAnalysis
// Input: new frame // Input: new frame
// Return value: pointer to structure containing content Analysis // Return value: pointer to structure containing content Analysis
// metrics or NULL value upon error // metrics or NULL value upon error
VideoContentMetrics* ComputeContentMetrics(const I420VideoFrame& VideoContentMetrics* ComputeContentMetrics(const I420VideoFrame&
inputFrame); inputFrame);
// Release all allocated memory // Release all allocated memory
// Output: 0 if OK, negative value upon error // Output: 0 if OK, negative value upon error
int32_t Release(); int32_t Release();
private: private:
// return motion metrics
VideoContentMetrics* ContentMetrics();
// return motion metrics // Normalized temporal difference metric: for motion magnitude
VideoContentMetrics* ContentMetrics(); typedef int32_t (VPMContentAnalysis::*TemporalDiffMetricFunc)();
TemporalDiffMetricFunc TemporalDiffMetric;
int32_t TemporalDiffMetric_C();
// Normalized temporal difference metric: for motion magnitude // Motion metric method: call 2 metrics (magnitude and size)
typedef int32_t (VPMContentAnalysis::*TemporalDiffMetricFunc)(); int32_t ComputeMotionMetrics();
TemporalDiffMetricFunc TemporalDiffMetric;
int32_t TemporalDiffMetric_C();
// Motion metric method: call 2 metrics (magnitude and size) // Spatial metric method: computes the 3 frame-average spatial
int32_t ComputeMotionMetrics(); // prediction errors (1x2,2x1,2x2)
typedef int32_t (VPMContentAnalysis::*ComputeSpatialMetricsFunc)();
// Spatial metric method: computes the 3 frame-average spatial ComputeSpatialMetricsFunc ComputeSpatialMetrics;
// prediction errors (1x2,2x1,2x2) int32_t ComputeSpatialMetrics_C();
typedef int32_t (VPMContentAnalysis::*ComputeSpatialMetricsFunc)();
ComputeSpatialMetricsFunc ComputeSpatialMetrics;
int32_t ComputeSpatialMetrics_C();
#if defined(WEBRTC_ARCH_X86_FAMILY) #if defined(WEBRTC_ARCH_X86_FAMILY)
int32_t ComputeSpatialMetrics_SSE2(); int32_t ComputeSpatialMetrics_SSE2();
int32_t TemporalDiffMetric_SSE2(); int32_t TemporalDiffMetric_SSE2();
#endif #endif
const uint8_t* _origFrame; const uint8_t* orig_frame_;
uint8_t* _prevFrame; uint8_t* prev_frame_;
int _width; int width_;
int _height; int height_;
int _skipNum; int skip_num_;
int _border; int border_;
// Content Metrics: // Content Metrics: Stores the local average of the metrics.
// stores the local average of the metrics float motion_magnitude_; // motion class
float _motionMagnitude; // motion class float spatial_pred_err_; // spatial class
float _spatialPredErr; // spatial class float spatial_pred_err_h_; // spatial class
float _spatialPredErrH; // spatial class float spatial_pred_err_v_; // spatial class
float _spatialPredErrV; // spatial class bool first_frame_;
bool _firstFrame; bool ca_Init_;
bool _CAInit;
VideoContentMetrics* _cMetrics; VideoContentMetrics* content_metrics_;
};
}; // end of VPMContentAnalysis class definition } // namespace webrtc
} // namespace #endif // WEBRTC_MODULES_VIDEO_PROCESSING_MAIN_SOURCE_CONTENT_ANALYSIS_H
#endif

View File

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

View File

@ -20,431 +20,386 @@
namespace webrtc { namespace webrtc {
// Detection constants // Detection constants
enum { kFrequencyDeviation = 39 }; // (Q4) Maximum allowed deviation for detection // (Q4) Maximum allowed deviation for detection.
enum { kMinFrequencyToDetect = 32 }; // (Q4) Minimum frequency that can be detected enum { kFrequencyDeviation = 39 };
enum { kNumFlickerBeforeDetect = 2 }; // Number of flickers before we accept detection // (Q4) Minimum frequency that can be detected.
enum { kMeanValueScaling = 4 }; // (Q4) In power of 2 enum { kMinFrequencyToDetect = 32 };
enum { kZeroCrossingDeadzone = 10 }; // Deadzone region in terms of pixel values // Number of flickers before we accept detection
enum { kNumFlickerBeforeDetect = 2 };
// Deflickering constants 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. // Compute the quantiles over 1 / DownsamplingFactor of the image.
enum { kDownsamplingFactor = 8 }; enum { kDownsamplingFactor = 8 };
enum { kLog2OfDownsamplingFactor = 3 }; enum { kLog2OfDownsamplingFactor = 3 };
// To generate in Matlab: // 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) // >> fprintf('%d, ', probUW16)
// Resolution reduced to avoid overflow when multiplying with the (potentially) large // Resolution reduced to avoid overflow when multiplying with the
// number of pixels. // (potentially) large number of pixels.
const uint16_t VPMDeflickering::_probUW16[kNumProbs] = const uint16_t VPMDeflickering::prob_uw16_[kNumProbs] = {102, 205, 410, 614,
{102, 205, 410, 614, 819, 1024, 1229, 1434, 1638, 1843, 1946, 1987}; // <Q11> 819, 1024, 1229, 1434, 1638, 1843, 1946, 1987}; // <Q11>
// To generate in Matlab: // To generate in Matlab:
// >> numQuants = 14; maxOnlyLength = 5; // >> 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); // >> 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> {16384, 18432, 20480, 22528, 24576, 26624, 28672, 30720, 32768}; // <Q15>
VPMDeflickering::VPMDeflickering() : VPMDeflickering::VPMDeflickering()
_id(0) : id_(0) {
{ Reset();
Reset();
} }
VPMDeflickering::~VPMDeflickering() VPMDeflickering::~VPMDeflickering() {}
{
int32_t VPMDeflickering::ChangeUniqueId(const int32_t id) {
id_ = id;
return 0;
} }
int32_t void VPMDeflickering::Reset() {
VPMDeflickering::ChangeUniqueId(const int32_t id) mean_buffer_length_ = 0;
{ detection_state_ = 0;
_id = id; 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; return 0;
} }
void // Size of luminance component.
VPMDeflickering::Reset() const uint32_t y_size = height * width;
{
_meanBufferLength = 0;
_detectionState = 0;
_frameRate = 0;
memset(_meanBuffer, 0, sizeof(int32_t) * kMeanBufferLength); const uint32_t y_sub_size = width * (((height - 1) >>
memset(_timestampBuffer, 0, sizeof(int32_t) * kMeanBufferLength); 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 webrtc::Sort(y_sorted, y_sub_size, webrtc::TYPE_UWord8);
_quantHistUW8[0][0] = 0;
_quantHistUW8[0][kNumQuants - 1] = 255; uint32_t prob_idx_uw32 = 0;
for (int32_t i = 0; i < kNumProbs; i++) quant_uw8[0] = 0;
{ quant_uw8[kNumQuants - 1] = 255;
_quantHistUW8[0][i + 1] = static_cast<uint8_t>((WEBRTC_SPL_UMUL_16_16(
_probUW16[i], 255) + (1 << 10)) >> 11); // Unsigned round. <Q0> // 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 // Get target quantiles.
VPMDeflickering::ProcessFrame(I420VideoFrame* frame, for (int32_t i = 0; i < kNumQuants - kMaxOnlyLength; i++) {
VideoProcessingModule::FrameStats* stats) 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) -
assert(frame); weight_uw16_[i], minquant_uw8[i])) >> 8); // <Q7>
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];
uint16_t tmpUW16; for (int32_t i = kNumQuants - kMaxOnlyLength; i < kNumQuants; i++) {
uint32_t tmpUW32; target_quant_uw16[i] = ((uint16_t)maxquant_uw8[i]) << 7;
int width = frame->width(); }
int height = frame->height();
if (frame->IsZeroSize()) // Compute the map from input to output pixels.
{ uint16_t mapUW16; // <Q7>
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoPreocessing, _id, for (int32_t i = 1; i < kNumQuants; i++) {
"Null frame pointer"); // As quant and targetQuant are limited to UWord8, it's safe to use Q7 here.
return VPM_GENERAL_ERROR; 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. mapUW16 = target_quant_uw16[i - 1];
if (height < 2) for (uint32_t j = quant_uw8[i - 1]; j < (uint32_t)(quant_uw8[i] + 1); j++) {
{ // Unsigned round. <Q0>
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoPreocessing, _id, map_uw8[j] = (uint8_t)((mapUW16 + (1 << 6)) >> 7);
"Invalid frame size"); mapUW16 += increment_uw16;
return VPM_GENERAL_ERROR;
} }
}
if (!VideoProcessingModule::ValidFrameStats(*stats)) // Map to the output frame.
{ uint8_t* buffer = frame->buffer(kYPlane);
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoPreocessing, _id, for (uint32_t i = 0; i < y_size; i++) {
"Invalid frame stats"); buffer[i] = map_uw8[buffer[i]];
return VPM_GENERAL_ERROR; }
}
if (PreDetection(frame->timestamp(), *stats) == -1) // Frame was altered, so reset stats.
{ VideoProcessingModule::ClearFrameStats(stats);
return VPM_GENERAL_ERROR;
}
// Flicker detection return VPM_OK;
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;
} }
/** /**
Performs some pre-detection operations. Must be called before Performs some pre-detection operations. Must be called before
DetectFlicker(). DetectFlicker().
\param[in] timestamp Timestamp of the current frame. \param[in] timestamp Timestamp of the current frame.
\param[in] stats Statistics of the current frame. \param[in] stats Statistics of the current frame.
\return 0: Success\n \return 0: Success\n
2: Detection not possible due to flickering frequency too close to 2: Detection not possible due to flickering frequency too close to
zero.\n zero.\n
-1: Error -1: Error
*/ */
int32_t int32_t VPMDeflickering::PreDetection(const uint32_t timestamp,
VPMDeflickering::PreDetection(const uint32_t timestamp, const VideoProcessingModule::FrameStats& stats) {
const VideoProcessingModule::FrameStats& stats) int32_t mean_val; // Mean value of frame (Q4)
{ uint32_t frame_rate = 0;
int32_t meanVal; // Mean value of frame (Q4) int32_t meanBufferLength; // Temp variable.
uint32_t frameRate = 0;
int32_t meanBufferLength; // Temp variable
meanVal = ((stats.sum << kMeanValueScaling) / stats.numPixels); mean_val = ((stats.sum << kmean_valueScaling) / stats.num_pixels);
/* Update mean value buffer. // Update mean value buffer.
* This should be done even though we might end up in an unreliable detection. // 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)); mean_buffer_length_ = 0;
_meanBuffer[0] = meanVal; return 2;
}
mean_buffer_length_ = meanBufferLength;
/* Update timestamp buffer. if ((timestamp_buffer_[mean_buffer_length_ - 1] != 0) &&
* This should be done even though we might end up in an unreliable detection. (mean_buffer_length_ != 1)) {
*/ frame_rate = ((90000 << 4) * (mean_buffer_length_ - 1));
memmove(_timestampBuffer + 1, _timestampBuffer, (kMeanBufferLength - 1) * frame_rate /=
sizeof(uint32_t)); (timestamp_buffer_[0] - timestamp_buffer_[mean_buffer_length_ - 1]);
_timestampBuffer[0] = timestamp; } 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) */ return VPM_OK;
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;
} }
/** /**
This function detects flicker in the video stream. As a side effect the mean value This function detects flicker in the video stream. As a side effect the
buffer is updated with the new mean value. mean value buffer is updated with the new mean value.
\return 0: No flickering detected\n \return 0: No flickering detected\n
1: Flickering detected\n 1: Flickering detected\n
2: Detection not possible due to unreliable frequency interval 2: Detection not possible due to unreliable frequency interval
-1: Error -1: Error
*/ */
int32_t VPMDeflickering::DetectFlicker() int32_t VPMDeflickering::DetectFlicker() {
{ uint32_t i;
/* Local variables */ int32_t freqEst; // (Q4) Frequency estimate to base detection upon
uint32_t i; int32_t ret_val = -1;
int32_t freqEst; // (Q4) Frequency estimate to base detection upon
int32_t retVal = -1;
/* Sanity check for _meanBufferLength */ /* Sanity check for mean_buffer_length_ */
if (_meanBufferLength < 2) if (mean_buffer_length_ < 2) {
{ /* Not possible to estimate frequency */
/* Not possible to estimate frequency */ return(2);
return(2); }
} // Count zero crossings with a dead zone to be robust against noise. If the
/* Count zero crossings with a dead zone to be robust against noise. // noise std is 2 pixel this corresponds to about 95% confidence interval.
* 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 deadzone = (kZeroCrossingDeadzone << kMeanValueScaling); // Q4 int32_t numZeros = 0; // Number of zeros that cross the dead-zone.
int32_t meanOfBuffer = 0; // Mean value of mean value buffer int32_t cntState = 0; // State variable for zero crossing regions.
int32_t numZeros = 0; // Number of zeros that cross the deadzone int32_t cntStateOld = 0; // Previous state for zero crossing regions.
int32_t cntState = 0; // State variable for zero crossing regions
int32_t cntStateOld = 0; // Previous state variable for zero crossing regions
for (i = 0; i < _meanBufferLength; i++) for (i = 0; i < mean_buffer_length_; i++) {
{ meanOfBuffer += mean_buffer_[i];
meanOfBuffer += _meanBuffer[i]; }
} meanOfBuffer += (mean_buffer_length_ >> 1); // Rounding, not truncation.
meanOfBuffer += (_meanBufferLength >> 1); // Rounding, not truncation meanOfBuffer /= mean_buffer_length_;
meanOfBuffer /= _meanBufferLength;
/* Count zero crossings */ // Count zero crossings.
cntStateOld = (_meanBuffer[0] >= (meanOfBuffer + deadzone)); cntStateOld = (mean_buffer_[0] >= (meanOfBuffer + deadzone));
cntStateOld -= (_meanBuffer[0] <= (meanOfBuffer - deadzone)); cntStateOld -= (mean_buffer_[0] <= (meanOfBuffer - deadzone));
for (i = 1; i < _meanBufferLength; i++) for (i = 1; i < mean_buffer_length_; i++) {
{ cntState = (mean_buffer_[i] >= (meanOfBuffer + deadzone));
cntState = (_meanBuffer[i] >= (meanOfBuffer + deadzone)); cntState -= (mean_buffer_[i] <= (meanOfBuffer - deadzone));
cntState -= (_meanBuffer[i] <= (meanOfBuffer - deadzone)); if (cntStateOld == 0) {
if (cntStateOld == 0) cntStateOld = -cntState;
{
cntStateOld = -cntState;
}
if (((cntState + cntStateOld) == 0) && (cntState != 0))
{
numZeros++;
cntStateOld = cntState;
}
} }
/* END count zero crossings */ if (((cntState + cntStateOld) == 0) && (cntState != 0)) {
numZeros++;
cntStateOld = cntState;
}
}
// END count zero crossings.
/* Frequency estimation according to: /* Frequency estimation according to:
* freqEst = numZeros * frameRate / 2 / _meanBufferLength; * freqEst = numZeros * frame_rate / 2 / mean_buffer_length_;
* *
* Resolution is set to Q4 * Resolution is set to Q4
*/ */
freqEst = ((numZeros * 90000) << 3); freqEst = ((numZeros * 90000) << 3);
freqEst /= (_timestampBuffer[0] - _timestampBuffer[_meanBufferLength - 1]); freqEst /=
(timestamp_buffer_[0] - timestamp_buffer_[mean_buffer_length_ - 1]);
/* Translate frequency estimate to regions close to 100 and 120 Hz */ /* Translate frequency estimate to regions close to 100 and 120 Hz */
uint8_t freqState = 0; // Current translation state; uint8_t freqState = 0; // Current translation state;
// (0) Not in interval, // (0) Not in interval,
// (1) Within valid interval, // (1) Within valid interval,
// (2) Out of range // (2) Out of range
int32_t freqAlias = freqEst; int32_t freqAlias = freqEst;
if (freqEst > kMinFrequencyToDetect) if (freqEst > kMinFrequencyToDetect) {
{ uint8_t aliasState = 1;
uint8_t aliasState = 1; while(freqState == 0) {
while(freqState == 0) /* Increase frequency */
{ freqAlias += (aliasState * frame_rate_);
/* Increase frequency */ freqAlias += ((freqEst << 1) * (1 - (aliasState << 1)));
freqAlias += (aliasState * _frameRate); /* Compute state */
freqAlias += ((freqEst << 1) * (1 - (aliasState << 1))); freqState = (abs(freqAlias - (100 << 4)) <= kFrequencyDeviation);
/* Compute state */ freqState += (abs(freqAlias - (120 << 4)) <= kFrequencyDeviation);
freqState = (abs(freqAlias - (100 << 4)) <= kFrequencyDeviation); freqState += 2 * (freqAlias > ((120 << 4) + kFrequencyDeviation));
freqState += (abs(freqAlias - (120 << 4)) <= kFrequencyDeviation); /* Switch alias state */
freqState += 2 * (freqAlias > ((120 << 4) + kFrequencyDeviation)); aliasState++;
/* Switch alias state */ aliasState &= 0x01;
aliasState++;
aliasState &= 0x01;
}
} }
/* Is frequency estimate within detection region? */ }
if (freqState == 1) /* Is frequency estimate within detection region? */
{ if (freqState == 1) {
retVal = 1; ret_val = 1;
}else if (freqState == 0) } else if (freqState == 0) {
{ ret_val = 2;
retVal = 2; } else {
}else ret_val = 0;
{ }
retVal = 0; return ret_val;
}
return retVal;
} }
} // namespace } // namespace webrtc

View File

@ -8,12 +8,8 @@
* be found in the AUTHORS file in the root of the source tree. * be found in the AUTHORS file in the root of the source tree.
*/ */
/* #ifndef WEBRTC_MODULES_VIDEO_PROCESSING_MAIN_SOURCEdeflickering__H
* deflickering.h #define WEBRTC_MODULES_VIDEO_PROCESSING_MAIN_SOURCEdeflickering__H
*/
#ifndef VPM_DEFLICKERING_H
#define VPM_DEFLICKERING_H
#include <string.h> // NULL #include <string.h> // NULL
@ -22,44 +18,43 @@
namespace webrtc { namespace webrtc {
class VPMDeflickering class VPMDeflickering {
{ public:
public: VPMDeflickering();
VPMDeflickering(); ~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, private:
VideoProcessingModule::FrameStats* stats); int32_t PreDetection(uint32_t timestamp,
private: const VideoProcessingModule::FrameStats& stats);
int32_t PreDetection(uint32_t timestamp,
const VideoProcessingModule::FrameStats& stats);
int32_t DetectFlicker(); int32_t DetectFlicker();
enum { kMeanBufferLength = 32 }; enum { kMeanBufferLength = 32 };
enum { kFrameHistorySize = 15 }; enum { kFrameHistory_size = 15 };
enum { kNumProbs = 12 }; enum { kNumProbs = 12 };
enum { kNumQuants = kNumProbs + 2 }; enum { kNumQuants = kNumProbs + 2 };
enum { kMaxOnlyLength = 5 }; enum { kMaxOnlyLength = 5 };
int32_t _id; int32_t id_;
uint32_t _meanBufferLength; uint32_t mean_buffer_length_;
uint8_t _detectionState; // 0: No flickering uint8_t detection_state_; // 0: No flickering
// 1: Flickering detected // 1: Flickering detected
// 2: In flickering // 2: In flickering
int32_t _meanBuffer[kMeanBufferLength]; int32_t mean_buffer_[kMeanBufferLength];
uint32_t _timestampBuffer[kMeanBufferLength]; uint32_t timestamp_buffer_[kMeanBufferLength];
uint32_t _frameRate; uint32_t frame_rate_;
static const uint16_t _probUW16[kNumProbs]; static const uint16_t prob_uw16_[kNumProbs];
static const uint16_t _weightUW16[kNumQuants - kMaxOnlyLength]; static const uint16_t weight_uw16_[kNumQuants - kMaxOnlyLength];
uint8_t _quantHistUW8[kFrameHistorySize][kNumQuants]; 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> #include <string.h>
namespace webrtc { 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) VPMDenoising::VPMDenoising()
enum { kSubsamplingWidth = 0 }; // Sub-sampling in width (unit: power of 2) : id_(0),
enum { kSubsamplingHeight = 0 }; // Sub-sampling in height (unit: power of 2) moment1_(NULL),
enum { kDenoiseFiltParam = 179 }; // (Q8) De-noising filter parameter moment2_(NULL) {
enum { kDenoiseFiltParamRec = 77 }; // (Q8) 1 - filter parameter Reset();
enum { kDenoiseThreshold = 19200 }; // (Q8) De-noising threshold level
VPMDenoising::VPMDenoising() :
_id(0),
_moment1(NULL),
_moment2(NULL)
{
Reset();
} }
VPMDenoising::~VPMDenoising() VPMDenoising::~VPMDenoising() {
{ if (moment1_) {
if (_moment1) delete [] moment1_;
{ moment1_ = NULL;
delete [] _moment1;
_moment1 = NULL;
}
if (_moment2)
{
delete [] _moment2;
_moment2 = NULL;
}
} }
int32_t if (moment2_) {
VPMDenoising::ChangeUniqueId(const int32_t id) delete [] moment2_;
{ moment2_ = NULL;
_id = id; }
return VPM_OK;
} }
void int32_t VPMDenoising::ChangeUniqueId(const int32_t id) {
VPMDenoising::Reset() id_ = id;
{ return VPM_OK;
_frameSize = 0;
_denoiseFrameCnt = 0;
if (_moment1)
{
delete [] _moment1;
_moment1 = NULL;
}
if (_moment2)
{
delete [] _moment2;
_moment2 = NULL;
}
} }
int32_t void VPMDenoising::Reset() {
VPMDenoising::ProcessFrame(I420VideoFrame* frame) frame_size_ = 0;
{ denoise_frame_cnt_ = 0;
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;
if (frame->IsZeroSize()) if (moment1_) {
{ delete [] moment1_;
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoPreocessing, _id, moment1_ = NULL;
"zero size frame"); }
return VPM_GENERAL_ERROR;
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(); denoise_frame_cnt_++;
int height = frame->height(); if (denoise_frame_cnt_ > kSubsamplingTime)
denoise_frame_cnt_ = 0;
/* Size of luminance component */ return num_pixels_changed;
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;
} }
} // namespace } // namespace

View File

@ -8,39 +8,35 @@
* be found in the AUTHORS file in the root of the source tree. * be found in the AUTHORS file in the root of the source tree.
*/ */
/* #ifndef WEBRTC_MODULES_VIDEO_PROCESSING_MAIN_SOURCE_DENOISING_H_
* denoising.h #define WEBRTC_MODULES_VIDEO_PROCESSING_MAIN_SOURCE_DENOISING_H_
*/
#ifndef VPM_DENOISING_H
#define VPM_DENOISING_H
#include "webrtc/modules/video_processing/main/interface/video_processing.h" #include "webrtc/modules/video_processing/main/interface/video_processing.h"
#include "webrtc/typedefs.h" #include "webrtc/typedefs.h"
namespace webrtc { namespace webrtc {
class VPMDenoising class VPMDenoising {
{ public:
public: VPMDenoising();
VPMDenoising(); ~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: private:
int32_t _id; int32_t id_;
uint32_t* _moment1; // (Q8) First order moment (mean) uint32_t* moment1_; // (Q8) First order moment (mean).
uint32_t* _moment2; // (Q8) Second order moment uint32_t* moment2_; // (Q8) Second order moment.
uint32_t _frameSize; // Size (# of pixels) of frame uint32_t frame_size_; // Size (# of pixels) of frame.
int _denoiseFrameCnt; // Counter for subsampling in time 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 { namespace webrtc {
VPMFramePreprocessor::VPMFramePreprocessor(): VPMFramePreprocessor::VPMFramePreprocessor()
_id(0), : id_(0),
_contentMetrics(NULL), content_metrics_(NULL),
_maxFrameRate(0), max_frame_rate_(0),
_resampledFrame(), resampled_frame_(),
_enableCA(false), enable_ca_(false),
_frameCnt(0) frame_cnt_(0) {
{ spatial_resampler_ = new VPMSimpleSpatialResampler();
_spatialResampler = new VPMSimpleSpatialResampler(); ca_ = new VPMContentAnalysis(true);
_ca = new VPMContentAnalysis(true); vd_ = new VPMVideoDecimator();
_vd = new VPMVideoDecimator();
} }
VPMFramePreprocessor::~VPMFramePreprocessor() VPMFramePreprocessor::~VPMFramePreprocessor() {
{ Reset();
Reset(); delete spatial_resampler_;
delete _spatialResampler; delete ca_;
delete _ca; delete vd_;
delete _vd;
} }
int32_t int32_t VPMFramePreprocessor::ChangeUniqueId(const int32_t id) {
VPMFramePreprocessor::ChangeUniqueId(const int32_t id) id_ = id;
{ return VPM_OK;
_id = id;
return VPM_OK;
} }
void void VPMFramePreprocessor::Reset() {
VPMFramePreprocessor::Reset() ca_->Release();
{ vd_->Reset();
_ca->Release(); content_metrics_ = NULL;
_vd->Reset(); spatial_resampler_->Reset();
_contentMetrics = NULL; enable_ca_ = false;
_spatialResampler->Reset(); frame_cnt_ = 0;
_enableCA = false;
_frameCnt = 0;
}
void
VPMFramePreprocessor::EnableTemporalDecimation(bool enable)
{
_vd->EnableTemporalDecimation(enable);
}
void
VPMFramePreprocessor::EnableContentAnalysis(bool enable)
{
_enableCA = enable;
} }
void
VPMFramePreprocessor::SetInputFrameResampleMode(VideoFrameResampling resamplingMode) void VPMFramePreprocessor::EnableTemporalDecimation(bool enable) {
{ vd_->EnableTemporalDecimation(enable);
_spatialResampler->SetInputFrameResampleMode(resamplingMode);
} }
void VPMFramePreprocessor::EnableContentAnalysis(bool enable) {
int32_t enable_ca_ = enable;
VPMFramePreprocessor::SetMaxFrameRate(uint32_t maxFrameRate) }
{
if (maxFrameRate == 0) void VPMFramePreprocessor::SetInputFrameResampleMode(
{ VideoFrameResampling resampling_mode) {
return VPM_PARAMETER_ERROR; 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 ++frame_cnt_;
_maxFrameRate = maxFrameRate; }
return VPM_OK;
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;
} }
void VideoContentMetrics* VPMFramePreprocessor::ContentMetrics() const {
VPMFramePreprocessor::UpdateIncomingFrameRate() return content_metrics_;
{
_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;
} }
} // namespace } // namespace

View File

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

View File

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

View File

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

View File

@ -17,219 +17,156 @@
namespace webrtc { namespace webrtc {
VPMVideoDecimator::VPMVideoDecimator() VPMVideoDecimator::VPMVideoDecimator()
: : overshoot_modifier_(0),
_overShootModifier(0), drop_count_(0),
_dropCount(0), keep_count_(0),
_keepCount(0), target_frame_rate_(30),
_targetFrameRate(30), incoming_frame_rate_(0.0f),
_incomingFrameRate(0.0f), max_frame_rate_(30),
_maxFrameRate(30), incoming_frame_times_(),
_incomingFrameTimes(), enable_temporal_decimation_(true) {
_enableTemporalDecimation(true) Reset();
{
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 void VPMVideoDecimator::EnableTemporalDecimation(bool enable) {
VPMVideoDecimator::Reset() enable_temporal_decimation_ = enable;
{
_overShootModifier = 0;
_dropCount = 0;
_keepCount = 0;
_targetFrameRate = 30;
_incomingFrameRate = 0.0f;
_maxFrameRate = 30;
memset(_incomingFrameTimes, 0, sizeof(_incomingFrameTimes));
_enableTemporalDecimation = true;
} }
void int32_t VPMVideoDecimator::SetMaxFramerate(uint32_t max_frame_rate) {
VPMVideoDecimator::EnableTemporalDecimation(bool enable) if (max_frame_rate == 0) return VPM_PARAMETER_ERROR;
{
_enableTemporalDecimation = enable;
}
int32_t
VPMVideoDecimator::SetMaxFrameRate(uint32_t maxFrameRate)
{
if (maxFrameRate == 0)
{
return VPM_PARAMETER_ERROR;
}
_maxFrameRate = maxFrameRate; max_frame_rate_ = max_frame_rate;
if (_targetFrameRate > _maxFrameRate)
{
_targetFrameRate = _maxFrameRate;
} if (target_frame_rate_ > max_frame_rate_)
return VPM_OK; target_frame_rate_ = max_frame_rate_;
return VPM_OK;
} }
int32_t int32_t VPMVideoDecimator::SetTargetframe_rate(uint32_t frame_rate) {
VPMVideoDecimator::SetTargetFrameRate(uint32_t frameRate) if (frame_rate == 0) return VPM_PARAMETER_ERROR;
{
if (frameRate == 0) if (frame_rate > max_frame_rate_) {
{ // Override.
return VPM_PARAMETER_ERROR; target_frame_rate_ = max_frame_rate_;
} } else {
if (frameRate > _maxFrameRate) target_frame_rate_ = frame_rate;
{ }
//override return VPM_OK;
_targetFrameRate = _maxFrameRate;
}
else
{
_targetFrameRate = frameRate;
}
return VPM_OK;
} }
bool bool VPMVideoDecimator::DropFrame() {
VPMVideoDecimator::DropFrame() if (!enable_temporal_decimation_) return false;
{
if (!_enableTemporalDecimation) if (incoming_frame_rate_ <= 0) return false;
{
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) if (overshoot && 2 * overshoot < (int32_t) incomingframe_rate) {
{ if (drop_count_) { // Just got here so drop to be sure.
return false; 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); return drop;
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;
} }
uint32_t uint32_t VPMVideoDecimator::Decimatedframe_rate() {
VPMVideoDecimator::DecimatedFrameRate() ProcessIncomingframe_rate(TickTime::MillisecondTimestamp());
{ if (!enable_temporal_decimation_) {
ProcessIncomingFrameRate(TickTime::MillisecondTimestamp()); return static_cast<uint32_t>(incoming_frame_rate_ + 0.5f);
if (!_enableTemporalDecimation) }
{ return VD_MIN(target_frame_rate_,
return static_cast<uint32_t>(_incomingFrameRate + 0.5f); static_cast<uint32_t>(incoming_frame_rate_ + 0.5f));
}
return VD_MIN(_targetFrameRate, static_cast<uint32_t>(_incomingFrameRate + 0.5f));
} }
uint32_t uint32_t VPMVideoDecimator::Inputframe_rate() {
VPMVideoDecimator::InputFrameRate() ProcessIncomingframe_rate(TickTime::MillisecondTimestamp());
{ return static_cast<uint32_t>(incoming_frame_rate_ + 0.5f);
ProcessIncomingFrameRate(TickTime::MillisecondTimestamp());
return static_cast<uint32_t>(_incomingFrameRate + 0.5f);
} }
void void VPMVideoDecimator::UpdateIncomingframe_rate() {
VPMVideoDecimator::UpdateIncomingFrameRate() int64_t now = TickTime::MillisecondTimestamp();
{ if (incoming_frame_times_[0] == 0) {
int64_t now = TickTime::MillisecondTimestamp(); // First no shift.
if(_incomingFrameTimes[0] == 0) } else {
{ // Shift.
// first no shift for (int i = kFrameCountHistory_size - 2; i >= 0; i--) {
} else incoming_frame_times_[i+1] = incoming_frame_times_[i];
{
// shift
for(int i = (kFrameCountHistorySize - 2); i >= 0 ; i--)
{
_incomingFrameTimes[i+1] = _incomingFrameTimes[i];
}
} }
_incomingFrameTimes[0] = now; }
ProcessIncomingFrameRate(now); incoming_frame_times_[0] = now;
ProcessIncomingframe_rate(now);
} }
void void VPMVideoDecimator::ProcessIncomingframe_rate(int64_t now) {
VPMVideoDecimator::ProcessIncomingFrameRate(int64_t now) int32_t num = 0;
{ int32_t nrOfFrames = 0;
int32_t num = 0; for (num = 1; num < (kFrameCountHistory_size - 1); num++) {
int32_t nrOfFrames = 0; // Don't use data older than 2sec.
for(num = 1; num < (kFrameCountHistorySize - 1); num++) if (incoming_frame_times_[num] <= 0 ||
{ now - incoming_frame_times_[num] > kFrameHistoryWindowMs) {
if (_incomingFrameTimes[num] <= 0 || break;
now - _incomingFrameTimes[num] > kFrameHistoryWindowMs) // don't use data older than 2sec } else {
{ nrOfFrames++;
break;
} else
{
nrOfFrames++;
}
} }
if (num > 1) }
{ if (num > 1) {
int64_t diff = now - _incomingFrameTimes[num-1]; int64_t diff = now - incoming_frame_times_[num-1];
_incomingFrameRate = 1.0; incoming_frame_rate_ = 1.0;
if(diff >0) if (diff > 0) {
{ incoming_frame_rate_ = nrOfFrames * 1000.0f / static_cast<float>(diff);
_incomingFrameRate = nrOfFrames * 1000.0f / static_cast<float>(diff);
}
}
else
{
_incomingFrameRate = static_cast<float>(nrOfFrames);
} }
} 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. * be found in the AUTHORS file in the root of the source tree.
*/ */
/* #ifndef WEBRTC_MODULES_VIDEO_PROCESSING_MAIN_SOURCE_VIDEO_DECIMATOR_H
* video_decimator.h #define WEBRTC_MODULES_VIDEO_PROCESSING_MAIN_SOURCE_VIDEO_DECIMATOR_H
*/
#ifndef VPM_VIDEO_DECIMATOR_H
#define VPM_VIDEO_DECIMATOR_H
#include "webrtc/modules/interface/module_common_types.h" #include "webrtc/modules/interface/module_common_types.h"
#include "webrtc/typedefs.h" #include "webrtc/typedefs.h"
namespace webrtc { namespace webrtc {
class VPMVideoDecimator class VPMVideoDecimator {
{ public:
public: VPMVideoDecimator();
VPMVideoDecimator(); ~VPMVideoDecimator();
~VPMVideoDecimator();
void Reset();
void EnableTemporalDecimation(bool enable);
int32_t SetMaxFrameRate(uint32_t maxFrameRate);
int32_t SetTargetFrameRate(uint32_t frameRate);
bool DropFrame(); void Reset();
void UpdateIncomingFrameRate();
// Get Decimated Frame Rate/Dimensions void EnableTemporalDecimation(bool enable);
uint32_t DecimatedFrameRate();
//Get input frame rate int32_t SetMaxFramerate(uint32_t max_frame_rate);
uint32_t InputFrameRate(); int32_t SetTargetframe_rate(uint32_t frame_rate);
private: bool DropFrame();
void ProcessIncomingFrameRate(int64_t now);
enum { kFrameCountHistorySize = 90}; void UpdateIncomingframe_rate();
enum { kFrameHistoryWindowMs = 2000};
// Temporal decimation // Get Decimated Frame Rate/Dimensions.
int32_t _overShootModifier; uint32_t Decimatedframe_rate();
uint32_t _dropCount;
uint32_t _keepCount;
uint32_t _targetFrameRate;
float _incomingFrameRate;
uint32_t _maxFrameRate;
int64_t _incomingFrameTimes[kFrameCountHistorySize];
bool _enableTemporalDecimation;
// 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 webrtc {
namespace namespace {
{ void SetSubSampling(VideoProcessingModule::FrameStats* stats,
void const int32_t width,
SetSubSampling(VideoProcessingModule::FrameStats* stats, const int32_t height) {
const int32_t width, if (width * height >= 640 * 480) {
const int32_t height) stats->subSamplWidth = 3;
{ stats->subSamplHeight = 3;
if (width * height >= 640 * 480) } else if (width * height >= 352 * 288) {
{ stats->subSamplWidth = 2;
stats->subSamplWidth = 3; stats->subSamplHeight = 2;
stats->subSamplHeight = 3; } else if (width * height >= 176 * 144) {
} stats->subSamplWidth = 1;
else if (width * height >= 352 * 288) stats->subSamplHeight = 1;
{ } else {
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;
stats->subSamplWidth = 0; stats->subSamplWidth = 0;
stats->subSamplHeight = 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 } // 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 { namespace webrtc {
class CriticalSectionWrapper; class CriticalSectionWrapper;
class VideoProcessingModuleImpl : public VideoProcessingModule class VideoProcessingModuleImpl : public VideoProcessingModule {
{ public:
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, // Frame pre-processor functions
const FrameStats& stats);
//Frame pre-processor functions // Enable temporal decimation
virtual void EnableTemporalDecimation(bool enable);
//Enable temporal decimation virtual void SetInputFrameResampleMode(VideoFrameResampling resampling_mode);
virtual void EnableTemporalDecimation(bool enable);
virtual void SetInputFrameResampleMode(VideoFrameResampling resamplingMode); // Enable content analysis
virtual void EnableContentAnalysis(bool enable);
//Enable content analysis // Set max frame rate
virtual void EnableContentAnalysis(bool enable); virtual int32_t SetMaxFramerate(uint32_t max_frame_rate);
//Set max frame rate // Set Target Resolution: frame rate and dimension
virtual int32_t SetMaxFrameRate(uint32_t maxFrameRate); virtual int32_t SetTargetResolution(uint32_t width,
uint32_t height,
// Set Target Resolution: frame rate and dimension uint32_t frame_rate);
virtual int32_t SetTargetResolution(uint32_t width,
uint32_t height,
uint32_t frameRate);
// Get decimated values: frame rate/dimension // Get decimated values: frame rate/dimension
virtual uint32_t DecimatedFrameRate(); virtual uint32_t Decimatedframe_rate();
virtual uint32_t DecimatedWidth() const; virtual uint32_t DecimatedWidth() const;
virtual uint32_t DecimatedHeight() const; virtual uint32_t DecimatedHeight() const;
// Preprocess: // Preprocess:
// Pre-process incoming frame: Sample when needed and compute content // Pre-process incoming frame: Sample when needed and compute content
// metrics when enabled. // metrics when enabled.
// If no resampling takes place - processedFrame is set to NULL. // If no resampling takes place - processed_frame is set to NULL.
virtual int32_t PreprocessFrame(const I420VideoFrame& frame, virtual int32_t PreprocessFrame(const I420VideoFrame& frame,
I420VideoFrame** processedFrame); I420VideoFrame** processed_frame);
virtual VideoContentMetrics* ContentMetrics() const; virtual VideoContentMetrics* ContentMetrics() const;
private: private:
int32_t _id; int32_t id_;
CriticalSectionWrapper& _mutex; CriticalSectionWrapper& mutex_;
VPMDeflickering deflickering_;
VPMDeflickering _deflickering; VPMDenoising denoising_;
VPMDenoising _denoising; VPMBrightnessDetection brightness_detection_;
VPMBrightnessDetection _brightnessDetection; VPMFramePreprocessor frame_pre_processor_;
VPMFramePreprocessor _framePreProcessor;
}; };
} // namespace } // namespace

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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