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:
parent
26f78f7ecb
commit
b43d8078a1
@ -249,7 +249,7 @@ QualityModesTest::Perform(const CmdArgs& args)
|
||||
|
||||
VideoContentMetrics* contentMetrics = NULL;
|
||||
// setting user frame rate
|
||||
_vpm->SetMaxFrameRate((uint32_t)(_nativeFrameRate+ 0.5f));
|
||||
_vpm->SetMaxFramerate((uint32_t)(_nativeFrameRate+ 0.5f));
|
||||
// for starters: keeping native values:
|
||||
_vpm->SetTargetResolution(_width, _height,
|
||||
(uint32_t)(_frameRate+ 0.5f));
|
||||
|
@ -24,281 +24,274 @@
|
||||
#include "webrtc/modules/video_processing/main/interface/video_processing_defines.h"
|
||||
|
||||
/**
|
||||
The module is largely intended to process video streams, except functionality
|
||||
The module is largely intended to process video streams, except functionality
|
||||
provided by static functions which operate independent of previous frames. It
|
||||
is recommended, but not required that a unique instance be used for each
|
||||
is recommended, but not required that a unique instance be used for each
|
||||
concurrently processed stream. Similarly, it is recommended to call Reset()
|
||||
before switching to a new stream, but this is not absolutely required.
|
||||
|
||||
|
||||
The module provides basic thread safety by permitting only a single function
|
||||
to execute concurrently.
|
||||
*/
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class VideoProcessingModule : public Module
|
||||
{
|
||||
public:
|
||||
/**
|
||||
Structure to hold frame statistics. Populate it with GetFrameStats().
|
||||
*/
|
||||
struct FrameStats
|
||||
{
|
||||
FrameStats() :
|
||||
mean(0),
|
||||
sum(0),
|
||||
numPixels(0),
|
||||
subSamplWidth(0),
|
||||
subSamplHeight(0)
|
||||
{
|
||||
memset(hist, 0, sizeof(hist));
|
||||
}
|
||||
|
||||
uint32_t hist[256]; /**< Histogram of frame */
|
||||
uint32_t mean; /**< Mean value of frame */
|
||||
uint32_t sum; /**< Sum of frame */
|
||||
uint32_t numPixels; /**< Number of pixels */
|
||||
uint8_t subSamplWidth; /**< Subsampling rate of width in powers
|
||||
of 2 */
|
||||
uint8_t subSamplHeight; /**< Subsampling rate of height in powers
|
||||
of 2 */
|
||||
};
|
||||
|
||||
/**
|
||||
Specifies the warning types returned by BrightnessDetection().
|
||||
*/
|
||||
enum BrightnessWarning
|
||||
{
|
||||
kNoWarning, /**< Frame has acceptable brightness */
|
||||
kDarkWarning, /**< Frame is too dark */
|
||||
kBrightWarning /**< Frame is too bright */
|
||||
};
|
||||
|
||||
/*
|
||||
Creates a VPM object.
|
||||
|
||||
\param[in] id
|
||||
Unique identifier of this object.
|
||||
|
||||
\return Pointer to a VPM object.
|
||||
*/
|
||||
static VideoProcessingModule* Create(int32_t id);
|
||||
|
||||
/**
|
||||
Destroys a VPM object.
|
||||
|
||||
\param[in] module
|
||||
Pointer to the VPM object to destroy.
|
||||
*/
|
||||
static void Destroy(VideoProcessingModule* module);
|
||||
|
||||
/**
|
||||
Not supported.
|
||||
*/
|
||||
virtual int32_t TimeUntilNextProcess() { return -1; }
|
||||
|
||||
/**
|
||||
Not supported.
|
||||
*/
|
||||
virtual int32_t Process() { return -1; }
|
||||
|
||||
/**
|
||||
Resets all processing components to their initial states. This should be
|
||||
called whenever a new video stream is started.
|
||||
*/
|
||||
virtual void Reset() = 0;
|
||||
|
||||
/**
|
||||
Retrieves statistics for the input frame. This function must be used to
|
||||
prepare a FrameStats struct for use in certain VPM functions.
|
||||
|
||||
\param[out] stats
|
||||
The frame statistics will be stored here on return.
|
||||
|
||||
\param[in] frame
|
||||
Reference to the video frame.
|
||||
|
||||
\return 0 on success, -1 on failure.
|
||||
*/
|
||||
static int32_t GetFrameStats(FrameStats* stats,
|
||||
const I420VideoFrame& frame);
|
||||
|
||||
/**
|
||||
Checks the validity of a FrameStats struct. Currently, valid implies only
|
||||
that is had changed from its initialized state.
|
||||
|
||||
\param[in] stats
|
||||
Frame statistics.
|
||||
|
||||
\return True on valid stats, false on invalid stats.
|
||||
*/
|
||||
static bool ValidFrameStats(const FrameStats& stats);
|
||||
|
||||
/**
|
||||
Returns a FrameStats struct to its intialized state.
|
||||
|
||||
\param[in,out] stats
|
||||
Frame statistics.
|
||||
*/
|
||||
static void ClearFrameStats(FrameStats* stats);
|
||||
|
||||
/**
|
||||
Enhances the color of an image through a constant mapping. Only the
|
||||
chrominance is altered. Has a fixed-point implementation.
|
||||
|
||||
\param[in,out] frame
|
||||
Pointer to the video frame.
|
||||
*/
|
||||
static int32_t ColorEnhancement(I420VideoFrame* frame);
|
||||
|
||||
/**
|
||||
Increases/decreases the luminance value.
|
||||
|
||||
\param[in,out] frame
|
||||
Pointer to the video frame.
|
||||
|
||||
\param[in] delta
|
||||
The amount to change the chrominance value of every single pixel.
|
||||
Can be < 0 also.
|
||||
|
||||
\return 0 on success, -1 on failure.
|
||||
*/
|
||||
static int32_t Brighten(I420VideoFrame* frame, int delta);
|
||||
|
||||
/**
|
||||
Detects and removes camera flicker from a video stream. Every frame from
|
||||
the stream must be passed in. A frame will only be altered if flicker has
|
||||
been detected. Has a fixed-point implementation.
|
||||
|
||||
\param[in,out] frame
|
||||
Pointer to the video frame.
|
||||
|
||||
\param[in,out] stats
|
||||
Frame statistics provided by GetFrameStats(). On return the stats will
|
||||
be reset to zero if the frame was altered. Call GetFrameStats() again
|
||||
if the statistics for the altered frame are required.
|
||||
|
||||
\return 0 on success, -1 on failure.
|
||||
*/
|
||||
virtual int32_t Deflickering(I420VideoFrame* frame, FrameStats* stats) = 0;
|
||||
|
||||
/**
|
||||
Denoises a video frame. Every frame from the stream should be passed in.
|
||||
Has a fixed-point implementation.
|
||||
|
||||
\param[in,out] frame
|
||||
Pointer to the video frame.
|
||||
|
||||
\return The number of modified pixels on success, -1 on failure.
|
||||
*/
|
||||
virtual int32_t Denoising(I420VideoFrame* frame) = 0;
|
||||
|
||||
/**
|
||||
Detects if a video frame is excessively bright or dark. Returns a
|
||||
warning if this is the case. Multiple frames should be passed in before
|
||||
expecting a warning. Has a floating-point implementation.
|
||||
|
||||
\param[in] frame
|
||||
Pointer to the video frame.
|
||||
|
||||
\param[in] stats
|
||||
Frame statistics provided by GetFrameStats().
|
||||
|
||||
\return A member of BrightnessWarning on success, -1 on error
|
||||
*/
|
||||
virtual int32_t BrightnessDetection(const I420VideoFrame& frame,
|
||||
const FrameStats& stats) = 0;
|
||||
|
||||
/**
|
||||
The following functions refer to the pre-processor unit within VPM. The
|
||||
pre-processor perfoms spatial/temporal decimation and content analysis on
|
||||
the frames prior to encoding.
|
||||
*/
|
||||
|
||||
/**
|
||||
Enable/disable temporal decimation
|
||||
|
||||
\param[in] enable when true, temporal decimation is enabled
|
||||
*/
|
||||
virtual void EnableTemporalDecimation(bool enable) = 0;
|
||||
|
||||
/**
|
||||
Set target resolution
|
||||
|
||||
\param[in] width
|
||||
Target width
|
||||
|
||||
\param[in] height
|
||||
Target height
|
||||
|
||||
\param[in] frameRate
|
||||
Target frameRate
|
||||
|
||||
\return VPM_OK on success, a negative value on error (see error codes)
|
||||
|
||||
*/
|
||||
virtual int32_t SetTargetResolution(uint32_t width,
|
||||
uint32_t height,
|
||||
uint32_t frameRate) = 0;
|
||||
|
||||
/**
|
||||
Set max frame rate
|
||||
\param[in] maxFrameRate: maximum frame rate (limited to native frame rate)
|
||||
|
||||
\return VPM_OK on success, a negative value on error (see error codes)
|
||||
*/
|
||||
virtual int32_t SetMaxFrameRate(uint32_t maxFrameRate) = 0;
|
||||
|
||||
/**
|
||||
Get decimated(target) frame rate
|
||||
*/
|
||||
virtual uint32_t DecimatedFrameRate() = 0;
|
||||
|
||||
/**
|
||||
Get decimated(target) frame width
|
||||
*/
|
||||
virtual uint32_t DecimatedWidth() const = 0;
|
||||
|
||||
/**
|
||||
Get decimated(target) frame height
|
||||
*/
|
||||
virtual uint32_t DecimatedHeight() const = 0 ;
|
||||
|
||||
/**
|
||||
Set the spatial resampling settings of the VPM: The resampler may either be
|
||||
disabled or one of the following:
|
||||
scaling to a close to target dimension followed by crop/pad
|
||||
|
||||
\param[in] resamplingMode
|
||||
Set resampling mode (a member of VideoFrameResampling)
|
||||
*/
|
||||
virtual void SetInputFrameResampleMode(VideoFrameResampling
|
||||
resamplingMode) = 0;
|
||||
|
||||
/**
|
||||
Get Processed (decimated) frame
|
||||
|
||||
\param[in] frame pointer to the video frame.
|
||||
\param[in] processedFrame pointer (double) to the processed frame. If no
|
||||
processing is required, processedFrame will be NULL.
|
||||
|
||||
\return VPM_OK on success, a negative value on error (see error codes)
|
||||
*/
|
||||
virtual int32_t PreprocessFrame(const I420VideoFrame& frame,
|
||||
I420VideoFrame** processedFrame) = 0;
|
||||
|
||||
/**
|
||||
Return content metrics for the last processed frame
|
||||
*/
|
||||
virtual VideoContentMetrics* ContentMetrics() const = 0 ;
|
||||
|
||||
/**
|
||||
Enable content analysis
|
||||
*/
|
||||
virtual void EnableContentAnalysis(bool enable) = 0;
|
||||
class VideoProcessingModule : public Module {
|
||||
public:
|
||||
/**
|
||||
Structure to hold frame statistics. Populate it with GetFrameStats().
|
||||
*/
|
||||
struct FrameStats {
|
||||
FrameStats() :
|
||||
mean(0),
|
||||
sum(0),
|
||||
num_pixels(0),
|
||||
subSamplWidth(0),
|
||||
subSamplHeight(0) {
|
||||
memset(hist, 0, sizeof(hist));
|
||||
}
|
||||
|
||||
uint32_t hist[256]; // FRame histogram.
|
||||
uint32_t mean; // Frame Mean value.
|
||||
uint32_t sum; // Sum of frame.
|
||||
uint32_t num_pixels; // Number of pixels.
|
||||
uint8_t subSamplWidth; // Subsampling rate of width in powers of 2.
|
||||
uint8_t subSamplHeight; // Subsampling rate of height in powers of 2.
|
||||
};
|
||||
|
||||
} // namespace
|
||||
/**
|
||||
Specifies the warning types returned by BrightnessDetection().
|
||||
*/
|
||||
enum BrightnessWarning {
|
||||
kNoWarning, // Frame has acceptable brightness.
|
||||
kDarkWarning, // Frame is too dark.
|
||||
kBrightWarning // Frame is too bright.
|
||||
};
|
||||
|
||||
#endif
|
||||
/*
|
||||
Creates a VPM object.
|
||||
|
||||
\param[in] id
|
||||
Unique identifier of this object.
|
||||
|
||||
\return Pointer to a VPM object.
|
||||
*/
|
||||
static VideoProcessingModule* Create(int32_t id);
|
||||
|
||||
/**
|
||||
Destroys a VPM object.
|
||||
|
||||
\param[in] module
|
||||
Pointer to the VPM object to destroy.
|
||||
*/
|
||||
static void Destroy(VideoProcessingModule* module);
|
||||
|
||||
/**
|
||||
Not supported.
|
||||
*/
|
||||
virtual int32_t TimeUntilNextProcess() { return -1; }
|
||||
|
||||
/**
|
||||
Not supported.
|
||||
*/
|
||||
virtual int32_t Process() { return -1; }
|
||||
|
||||
/**
|
||||
Resets all processing components to their initial states. This should be
|
||||
called whenever a new video stream is started.
|
||||
*/
|
||||
virtual void Reset() = 0;
|
||||
|
||||
/**
|
||||
Retrieves statistics for the input frame. This function must be used to
|
||||
prepare a FrameStats struct for use in certain VPM functions.
|
||||
|
||||
\param[out] stats
|
||||
The frame statistics will be stored here on return.
|
||||
|
||||
\param[in] frame
|
||||
Reference to the video frame.
|
||||
|
||||
\return 0 on success, -1 on failure.
|
||||
*/
|
||||
static int32_t GetFrameStats(FrameStats* stats,
|
||||
const I420VideoFrame& frame);
|
||||
|
||||
/**
|
||||
Checks the validity of a FrameStats struct. Currently, valid implies only
|
||||
that is had changed from its initialized state.
|
||||
|
||||
\param[in] stats
|
||||
Frame statistics.
|
||||
|
||||
\return True on valid stats, false on invalid stats.
|
||||
*/
|
||||
static bool ValidFrameStats(const FrameStats& stats);
|
||||
|
||||
/**
|
||||
Returns a FrameStats struct to its intialized state.
|
||||
|
||||
\param[in,out] stats
|
||||
Frame statistics.
|
||||
*/
|
||||
static void ClearFrameStats(FrameStats* stats);
|
||||
|
||||
/**
|
||||
Enhances the color of an image through a constant mapping. Only the
|
||||
chrominance is altered. Has a fixed-point implementation.
|
||||
|
||||
\param[in,out] frame
|
||||
Pointer to the video frame.
|
||||
*/
|
||||
static int32_t ColorEnhancement(I420VideoFrame* frame);
|
||||
|
||||
/**
|
||||
Increases/decreases the luminance value.
|
||||
|
||||
\param[in,out] frame
|
||||
Pointer to the video frame.
|
||||
|
||||
\param[in] delta
|
||||
The amount to change the chrominance value of every single pixel.
|
||||
Can be < 0 also.
|
||||
|
||||
\return 0 on success, -1 on failure.
|
||||
*/
|
||||
static int32_t Brighten(I420VideoFrame* frame, int delta);
|
||||
|
||||
/**
|
||||
Detects and removes camera flicker from a video stream. Every frame from
|
||||
the stream must be passed in. A frame will only be altered if flicker has
|
||||
been detected. Has a fixed-point implementation.
|
||||
|
||||
\param[in,out] frame
|
||||
Pointer to the video frame.
|
||||
|
||||
\param[in,out] stats
|
||||
Frame statistics provided by GetFrameStats(). On return the stats will
|
||||
be reset to zero if the frame was altered. Call GetFrameStats() again
|
||||
if the statistics for the altered frame are required.
|
||||
|
||||
\return 0 on success, -1 on failure.
|
||||
*/
|
||||
virtual int32_t Deflickering(I420VideoFrame* frame, FrameStats* stats) = 0;
|
||||
|
||||
/**
|
||||
Denoises a video frame. Every frame from the stream should be passed in.
|
||||
Has a fixed-point implementation.
|
||||
|
||||
\param[in,out] frame
|
||||
Pointer to the video frame.
|
||||
|
||||
\return The number of modified pixels on success, -1 on failure.
|
||||
*/
|
||||
virtual int32_t Denoising(I420VideoFrame* frame) = 0;
|
||||
|
||||
/**
|
||||
Detects if a video frame is excessively bright or dark. Returns a
|
||||
warning if this is the case. Multiple frames should be passed in before
|
||||
expecting a warning. Has a floating-point implementation.
|
||||
|
||||
\param[in] frame
|
||||
Pointer to the video frame.
|
||||
|
||||
\param[in] stats
|
||||
Frame statistics provided by GetFrameStats().
|
||||
|
||||
\return A member of BrightnessWarning on success, -1 on error
|
||||
*/
|
||||
virtual int32_t BrightnessDetection(const I420VideoFrame& frame,
|
||||
const FrameStats& stats) = 0;
|
||||
|
||||
/**
|
||||
The following functions refer to the pre-processor unit within VPM. The
|
||||
pre-processor perfoms spatial/temporal decimation and content analysis on
|
||||
the frames prior to encoding.
|
||||
*/
|
||||
|
||||
/**
|
||||
Enable/disable temporal decimation
|
||||
|
||||
\param[in] enable when true, temporal decimation is enabled
|
||||
*/
|
||||
virtual void EnableTemporalDecimation(bool enable) = 0;
|
||||
|
||||
/**
|
||||
Set target resolution
|
||||
|
||||
\param[in] width
|
||||
Target width
|
||||
|
||||
\param[in] height
|
||||
Target height
|
||||
|
||||
\param[in] frame_rate
|
||||
Target frame_rate
|
||||
|
||||
\return VPM_OK on success, a negative value on error (see error codes)
|
||||
|
||||
*/
|
||||
virtual int32_t SetTargetResolution(uint32_t width,
|
||||
uint32_t height,
|
||||
uint32_t frame_rate) = 0;
|
||||
|
||||
/**
|
||||
Set max frame rate
|
||||
\param[in] max_frame_rate: maximum frame rate (limited to native frame rate)
|
||||
|
||||
\return VPM_OK on success, a negative value on error (see error codes)
|
||||
*/
|
||||
virtual int32_t SetMaxFramerate(uint32_t max_frame_rate) = 0;
|
||||
|
||||
/**
|
||||
Get decimated(target) frame rate
|
||||
*/
|
||||
virtual uint32_t Decimatedframe_rate() = 0;
|
||||
|
||||
/**
|
||||
Get decimated(target) frame width
|
||||
*/
|
||||
virtual uint32_t DecimatedWidth() const = 0;
|
||||
|
||||
/**
|
||||
Get decimated(target) frame height
|
||||
*/
|
||||
virtual uint32_t DecimatedHeight() const = 0 ;
|
||||
|
||||
/**
|
||||
Set the spatial resampling settings of the VPM: The resampler may either be
|
||||
disabled or one of the following:
|
||||
scaling to a close to target dimension followed by crop/pad
|
||||
|
||||
\param[in] resampling_mode
|
||||
Set resampling mode (a member of VideoFrameResampling)
|
||||
*/
|
||||
virtual void SetInputFrameResampleMode(VideoFrameResampling
|
||||
resampling_mode) = 0;
|
||||
|
||||
/**
|
||||
Get Processed (decimated) frame
|
||||
|
||||
\param[in] frame pointer to the video frame.
|
||||
\param[in] processed_frame pointer (double) to the processed frame. If no
|
||||
processing is required, processed_frame will be NULL.
|
||||
|
||||
\return VPM_OK on success, a negative value on error (see error codes)
|
||||
*/
|
||||
virtual int32_t PreprocessFrame(const I420VideoFrame& frame,
|
||||
I420VideoFrame** processed_frame) = 0;
|
||||
|
||||
/**
|
||||
Return content metrics for the last processed frame
|
||||
*/
|
||||
virtual VideoContentMetrics* ContentMetrics() const = 0 ;
|
||||
|
||||
/**
|
||||
Enable content analysis
|
||||
*/
|
||||
virtual void EnableContentAnalysis(bool enable) = 0;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // WEBRTC_MODULES_INTERFACE_VIDEO_PROCESSING_H
|
||||
|
@ -29,15 +29,13 @@ namespace webrtc {
|
||||
#define VPM_UNINITIALIZED -5
|
||||
#define VPM_UNIMPLEMENTED -6
|
||||
|
||||
enum VideoFrameResampling
|
||||
{
|
||||
// TODO: Do we still need crop/pad?
|
||||
kNoRescaling, // disables rescaling
|
||||
kFastRescaling, // point
|
||||
kBiLinear, // bi-linear interpolation
|
||||
kBox, // Box inteprolation
|
||||
enum VideoFrameResampling {
|
||||
kNoRescaling, // Disables rescaling.
|
||||
kFastRescaling, // Point filter.
|
||||
kBiLinear, // Bi-linear interpolation.
|
||||
kBox, // Box inteprolation.
|
||||
};
|
||||
|
||||
} // namespace
|
||||
} // namespace webrtc
|
||||
|
||||
#endif
|
||||
#endif // WEBRTC_MODULES_INTERFACE_VIDEO_PROCESSING_DEFINES_H
|
||||
|
@ -31,19 +31,19 @@ int32_t Brighten(I420VideoFrame* frame, int delta) {
|
||||
return VPM_PARAMETER_ERROR;
|
||||
}
|
||||
|
||||
int numPixels = frame->width() * frame->height();
|
||||
int num_pixels = frame->width() * frame->height();
|
||||
|
||||
int lookUp[256];
|
||||
int look_up[256];
|
||||
for (int i = 0; i < 256; i++) {
|
||||
int val = i + delta;
|
||||
lookUp[i] = ((((val < 0) ? 0 : val) > 255) ? 255 : val);
|
||||
look_up[i] = ((((val < 0) ? 0 : val) > 255) ? 255 : val);
|
||||
}
|
||||
|
||||
uint8_t* tempPtr = frame->buffer(kYPlane);
|
||||
uint8_t* temp_ptr = frame->buffer(kYPlane);
|
||||
|
||||
for (int i = 0; i < numPixels; i++) {
|
||||
*tempPtr = static_cast<uint8_t>(lookUp[*tempPtr]);
|
||||
tempPtr++;
|
||||
for (int i = 0; i < num_pixels; i++) {
|
||||
*temp_ptr = static_cast<uint8_t>(look_up[*temp_ptr]);
|
||||
temp_ptr++;
|
||||
}
|
||||
return VPM_OK;
|
||||
}
|
||||
|
@ -17,180 +17,128 @@
|
||||
namespace webrtc {
|
||||
|
||||
VPMBrightnessDetection::VPMBrightnessDetection() :
|
||||
_id(0)
|
||||
{
|
||||
Reset();
|
||||
id_(0) {
|
||||
Reset();
|
||||
}
|
||||
|
||||
VPMBrightnessDetection::~VPMBrightnessDetection()
|
||||
{
|
||||
VPMBrightnessDetection::~VPMBrightnessDetection() {}
|
||||
|
||||
int32_t VPMBrightnessDetection::ChangeUniqueId(const int32_t id) {
|
||||
id_ = id;
|
||||
return VPM_OK;
|
||||
}
|
||||
|
||||
int32_t
|
||||
VPMBrightnessDetection::ChangeUniqueId(const int32_t id)
|
||||
{
|
||||
_id = id;
|
||||
return VPM_OK;
|
||||
void VPMBrightnessDetection::Reset() {
|
||||
frame_cnt_bright_ = 0;
|
||||
frame_cnt_dark_ = 0;
|
||||
}
|
||||
|
||||
void
|
||||
VPMBrightnessDetection::Reset()
|
||||
{
|
||||
_frameCntBright = 0;
|
||||
_frameCntDark = 0;
|
||||
}
|
||||
int32_t VPMBrightnessDetection::ProcessFrame(
|
||||
const I420VideoFrame& frame,
|
||||
const VideoProcessingModule::FrameStats& stats) {
|
||||
if (frame.IsZeroSize()) {
|
||||
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoPreocessing, id_,
|
||||
"Null frame pointer");
|
||||
return VPM_PARAMETER_ERROR;
|
||||
}
|
||||
int width = frame.width();
|
||||
int height = frame.height();
|
||||
|
||||
int32_t
|
||||
VPMBrightnessDetection::ProcessFrame(const I420VideoFrame& frame,
|
||||
const VideoProcessingModule::FrameStats&
|
||||
stats)
|
||||
{
|
||||
if (frame.IsZeroSize())
|
||||
{
|
||||
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoPreocessing, _id,
|
||||
"Null frame pointer");
|
||||
return VPM_PARAMETER_ERROR;
|
||||
}
|
||||
int width = frame.width();
|
||||
int height = frame.height();
|
||||
if (!VideoProcessingModule::ValidFrameStats(stats)) {
|
||||
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoPreocessing, id_,
|
||||
"Invalid frame stats");
|
||||
return VPM_PARAMETER_ERROR;
|
||||
}
|
||||
|
||||
if (!VideoProcessingModule::ValidFrameStats(stats))
|
||||
{
|
||||
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoPreocessing, _id,
|
||||
"Invalid frame stats");
|
||||
return VPM_PARAMETER_ERROR;
|
||||
}
|
||||
const uint8_t frame_cnt_alarm = 2;
|
||||
|
||||
const uint8_t frameCntAlarm = 2;
|
||||
// Get proportion in lowest bins.
|
||||
uint8_t low_th = 20;
|
||||
float prop_low = 0;
|
||||
for (uint32_t i = 0; i < low_th; i++) {
|
||||
prop_low += stats.hist[i];
|
||||
}
|
||||
prop_low /= stats.num_pixels;
|
||||
|
||||
// Get proportion in lowest bins
|
||||
uint8_t lowTh = 20;
|
||||
float propLow = 0;
|
||||
for (uint32_t i = 0; i < lowTh; i++)
|
||||
{
|
||||
propLow += stats.hist[i];
|
||||
}
|
||||
propLow /= stats.numPixels;
|
||||
// Get proportion in highest bins.
|
||||
unsigned char high_th = 230;
|
||||
float prop_high = 0;
|
||||
for (uint32_t i = high_th; i < 256; i++) {
|
||||
prop_high += stats.hist[i];
|
||||
}
|
||||
prop_high /= stats.num_pixels;
|
||||
|
||||
// Get proportion in highest bins
|
||||
unsigned char highTh = 230;
|
||||
float propHigh = 0;
|
||||
for (uint32_t i = highTh; i < 256; i++)
|
||||
{
|
||||
propHigh += stats.hist[i];
|
||||
}
|
||||
propHigh /= stats.numPixels;
|
||||
if (prop_high < 0.4) {
|
||||
if (stats.mean < 90 || stats.mean > 170) {
|
||||
// Standard deviation of Y
|
||||
const uint8_t* buffer = frame.buffer(kYPlane);
|
||||
float std_y = 0;
|
||||
for (int h = 0; h < height; h += (1 << stats.subSamplHeight)) {
|
||||
int row = h*width;
|
||||
for (int w = 0; w < width; w += (1 << stats.subSamplWidth)) {
|
||||
std_y += (buffer[w + row] - stats.mean) * (buffer[w + row] -
|
||||
stats.mean);
|
||||
}
|
||||
}
|
||||
std_y = sqrt(std_y / stats.num_pixels);
|
||||
|
||||
if(propHigh < 0.4)
|
||||
{
|
||||
if (stats.mean < 90 || stats.mean > 170)
|
||||
{
|
||||
// Standard deviation of Y
|
||||
const uint8_t* buffer = frame.buffer(kYPlane);
|
||||
float stdY = 0;
|
||||
for (int h = 0; h < height; h += (1 << stats.subSamplHeight))
|
||||
{
|
||||
int row = h*width;
|
||||
for (int w = 0; w < width; w += (1 << stats.subSamplWidth))
|
||||
{
|
||||
stdY += (buffer[w + row] - stats.mean) * (buffer[w + row] -
|
||||
stats.mean);
|
||||
}
|
||||
}
|
||||
stdY = sqrt(stdY / stats.numPixels);
|
||||
|
||||
// Get percentiles
|
||||
uint32_t sum = 0;
|
||||
uint32_t medianY = 140;
|
||||
uint32_t perc05 = 0;
|
||||
uint32_t perc95 = 255;
|
||||
float posPerc05 = stats.numPixels * 0.05f;
|
||||
float posMedian = stats.numPixels * 0.5f;
|
||||
float posPerc95 = stats.numPixels * 0.95f;
|
||||
for (uint32_t i = 0; i < 256; i++)
|
||||
{
|
||||
sum += stats.hist[i];
|
||||
|
||||
if (sum < posPerc05)
|
||||
{
|
||||
perc05 = i; // 5th perc
|
||||
}
|
||||
if (sum < posMedian)
|
||||
{
|
||||
medianY = i; // 50th perc
|
||||
}
|
||||
if (sum < posPerc95)
|
||||
{
|
||||
perc95 = i; // 95th perc
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if image is too dark
|
||||
if ((stdY < 55) && (perc05 < 50))
|
||||
{
|
||||
if (medianY < 60 || stats.mean < 80 || perc95 < 130 ||
|
||||
propLow > 0.20)
|
||||
{
|
||||
_frameCntDark++;
|
||||
}
|
||||
else
|
||||
{
|
||||
_frameCntDark = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_frameCntDark = 0;
|
||||
}
|
||||
|
||||
// Check if image is too bright
|
||||
if ((stdY < 52) && (perc95 > 200) && (medianY > 160))
|
||||
{
|
||||
if (medianY > 185 || stats.mean > 185 || perc05 > 140 ||
|
||||
propHigh > 0.25)
|
||||
{
|
||||
_frameCntBright++;
|
||||
}
|
||||
else
|
||||
{
|
||||
_frameCntBright = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_frameCntBright = 0;
|
||||
}
|
||||
|
||||
}
|
||||
// Get percentiles.
|
||||
uint32_t sum = 0;
|
||||
uint32_t median_y = 140;
|
||||
uint32_t perc05 = 0;
|
||||
uint32_t perc95 = 255;
|
||||
float pos_perc05 = stats.num_pixels * 0.05f;
|
||||
float pos_median = stats.num_pixels * 0.5f;
|
||||
float posPerc95 = stats.num_pixels * 0.95f;
|
||||
for (uint32_t i = 0; i < 256; i++) {
|
||||
sum += stats.hist[i];
|
||||
if (sum < pos_perc05) perc05 = i; // 5th perc.
|
||||
if (sum < pos_median) median_y = i; // 50th perc.
|
||||
if (sum < posPerc95)
|
||||
perc95 = i; // 95th perc.
|
||||
else
|
||||
{
|
||||
_frameCntDark = 0;
|
||||
_frameCntBright = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
// Check if image is too dark
|
||||
if ((std_y < 55) && (perc05 < 50)) {
|
||||
if (median_y < 60 || stats.mean < 80 || perc95 < 130 ||
|
||||
prop_low > 0.20) {
|
||||
frame_cnt_dark_++;
|
||||
} else {
|
||||
frame_cnt_dark_ = 0;
|
||||
}
|
||||
} else {
|
||||
frame_cnt_dark_ = 0;
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
_frameCntBright++;
|
||||
_frameCntDark = 0;
|
||||
}
|
||||
|
||||
if (_frameCntDark > frameCntAlarm)
|
||||
{
|
||||
return VideoProcessingModule::kDarkWarning;
|
||||
}
|
||||
else if (_frameCntBright > frameCntAlarm)
|
||||
{
|
||||
return VideoProcessingModule::kBrightWarning;
|
||||
}
|
||||
else
|
||||
{
|
||||
return VideoProcessingModule::kNoWarning;
|
||||
// Check if image is too bright
|
||||
if ((std_y < 52) && (perc95 > 200) && (median_y > 160)) {
|
||||
if (median_y > 185 || stats.mean > 185 || perc05 > 140 ||
|
||||
prop_high > 0.25) {
|
||||
frame_cnt_bright_++;
|
||||
} else {
|
||||
frame_cnt_bright_ = 0;
|
||||
}
|
||||
} else {
|
||||
frame_cnt_bright_ = 0;
|
||||
}
|
||||
} else {
|
||||
frame_cnt_dark_ = 0;
|
||||
frame_cnt_bright_ = 0;
|
||||
}
|
||||
} else {
|
||||
frame_cnt_bright_++;
|
||||
frame_cnt_dark_ = 0;
|
||||
}
|
||||
|
||||
if (frame_cnt_dark_ > frame_cnt_alarm) {
|
||||
return VideoProcessingModule::kDarkWarning;
|
||||
} else if (frame_cnt_bright_ > frame_cnt_alarm) {
|
||||
return VideoProcessingModule::kBrightWarning;
|
||||
} else {
|
||||
return VideoProcessingModule::kNoWarning;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace webrtc
|
||||
|
@ -11,34 +11,30 @@
|
||||
/*
|
||||
* brightness_detection.h
|
||||
*/
|
||||
#ifndef VPM_BRIGHTNESS_DETECTION_H
|
||||
#define VPM_BRIGHTNESS_DETECTION_H
|
||||
|
||||
#ifndef MODULES_VIDEO_PROCESSING_MAIN_SOURCE_BRIGHTNESS_DETECTION_H
|
||||
#define MODULES_VIDEO_PROCESSING_MAIN_SOURCE_BRIGHTNESS_DETECTION_H
|
||||
#include "webrtc/modules/video_processing/main/interface/video_processing.h"
|
||||
#include "webrtc/typedefs.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class VPMBrightnessDetection
|
||||
{
|
||||
public:
|
||||
VPMBrightnessDetection();
|
||||
~VPMBrightnessDetection();
|
||||
class VPMBrightnessDetection {
|
||||
public:
|
||||
VPMBrightnessDetection();
|
||||
~VPMBrightnessDetection();
|
||||
int32_t ChangeUniqueId(int32_t id);
|
||||
|
||||
int32_t ChangeUniqueId(int32_t id);
|
||||
void Reset();
|
||||
int32_t ProcessFrame(const I420VideoFrame& frame,
|
||||
const VideoProcessingModule::FrameStats& stats);
|
||||
|
||||
void Reset();
|
||||
private:
|
||||
int32_t id_;
|
||||
|
||||
int32_t ProcessFrame(const I420VideoFrame& frame,
|
||||
const VideoProcessingModule::FrameStats& stats);
|
||||
|
||||
private:
|
||||
int32_t _id;
|
||||
|
||||
uint32_t _frameCntBright;
|
||||
uint32_t _frameCntDark;
|
||||
uint32_t frame_cnt_bright_;
|
||||
uint32_t frame_cnt_dark_;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // VPM_BRIGHTNESS_DETECTION_H
|
||||
#endif // MODULES_VIDEO_PROCESSING_MAIN_SOURCE_BRIGHTNESS_DETECTION_H
|
||||
|
@ -16,50 +16,42 @@
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
namespace VideoProcessing
|
||||
{
|
||||
int32_t
|
||||
ColorEnhancement(I420VideoFrame* frame)
|
||||
{
|
||||
assert(frame);
|
||||
// pointers to U and V color pixels
|
||||
uint8_t* ptrU;
|
||||
uint8_t* ptrV;
|
||||
uint8_t tempChroma;
|
||||
namespace VideoProcessing {
|
||||
int32_t ColorEnhancement(I420VideoFrame* frame) {
|
||||
assert(frame);
|
||||
// Pointers to U and V color pixels.
|
||||
uint8_t* ptr_u;
|
||||
uint8_t* ptr_v;
|
||||
uint8_t temp_chroma;
|
||||
if (frame->IsZeroSize()) {
|
||||
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoPreocessing,
|
||||
-1, "Null frame pointer");
|
||||
return VPM_GENERAL_ERROR;
|
||||
}
|
||||
|
||||
if (frame->IsZeroSize())
|
||||
{
|
||||
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoPreocessing,
|
||||
-1, "Null frame pointer");
|
||||
return VPM_GENERAL_ERROR;
|
||||
}
|
||||
if (frame->width() == 0 || frame->height() == 0) {
|
||||
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoPreocessing,
|
||||
-1, "Invalid frame size");
|
||||
return VPM_GENERAL_ERROR;
|
||||
}
|
||||
|
||||
if (frame->width() == 0 || frame->height() == 0)
|
||||
{
|
||||
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoPreocessing,
|
||||
-1, "Invalid frame size");
|
||||
return VPM_GENERAL_ERROR;
|
||||
}
|
||||
// set pointers to first U and V pixels (skip luminance)
|
||||
ptr_u = frame->buffer(kUPlane);
|
||||
ptr_v = frame->buffer(kVPlane);
|
||||
int size_uv = ((frame->width() + 1) / 2) * ((frame->height() + 1) / 2);
|
||||
|
||||
// set pointers to first U and V pixels (skip luminance)
|
||||
ptrU = frame->buffer(kUPlane);
|
||||
ptrV = frame->buffer(kVPlane);
|
||||
int size_uv = ((frame->width() + 1) / 2) * ((frame->height() + 1) / 2);
|
||||
// Loop through all chrominance pixels and modify color
|
||||
for (int ix = 0; ix < size_uv; ix++) {
|
||||
temp_chroma = colorTable[*ptr_u][*ptr_v];
|
||||
*ptr_v = colorTable[*ptr_v][*ptr_u];
|
||||
*ptr_u = temp_chroma;
|
||||
|
||||
// loop through all chrominance pixels and modify color
|
||||
for (int ix = 0; ix < size_uv; ix++)
|
||||
{
|
||||
tempChroma = colorTable[*ptrU][*ptrV];
|
||||
*ptrV = colorTable[*ptrV][*ptrU];
|
||||
*ptrU = tempChroma;
|
||||
|
||||
// increment pointers
|
||||
ptrU++;
|
||||
ptrV++;
|
||||
}
|
||||
return VPM_OK;
|
||||
}
|
||||
ptr_u++;
|
||||
ptr_v++;
|
||||
}
|
||||
return VPM_OK;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace VideoProcessing
|
||||
|
||||
} // namespace webrtc
|
||||
|
@ -11,19 +11,18 @@
|
||||
/*
|
||||
* color_enhancement.h
|
||||
*/
|
||||
#ifndef VPM_COLOR_ENHANCEMENT_H
|
||||
#define VPM_COLOR_ENHANCEMENT_H
|
||||
#ifndef WEBRTC_MODULES_VIDEO_PROCESSING_COLOR_ENHANCEMENT_H
|
||||
#define WEBRTC_MODULES_VIDEO_PROCESSING_COLOR_ENHANCEMENT_H
|
||||
|
||||
#include "webrtc/modules/video_processing/main/interface/video_processing.h"
|
||||
#include "webrtc/typedefs.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
namespace VideoProcessing
|
||||
{
|
||||
int32_t ColorEnhancement(I420VideoFrame* frame);
|
||||
namespace VideoProcessing {
|
||||
int32_t ColorEnhancement(I420VideoFrame* frame);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // VPM_COLOR_ENHANCEMENT_H
|
||||
#endif // WEBRTC_MODULES_VIDEO_PROCESSING_COLOR_ENHANCEMENT_H
|
||||
|
@ -1,3 +1,13 @@
|
||||
/*
|
||||
* Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef VPM_COLOR_ENHANCEMENT_PRIVATE_H
|
||||
#define VPM_COLOR_ENHANCEMENT_PRIVATE_H
|
||||
|
||||
@ -270,4 +280,4 @@ static const uint8_t colorTable[256][256] = {
|
||||
|
||||
} // namespace
|
||||
|
||||
#endif // VPM_COLOR_ENHANCEMENT_PRIVATE_H
|
||||
#endif // VPM_COLOR_ENHANCEMENT_PRIVATE_H
|
@ -17,229 +17,185 @@
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
VPMContentAnalysis::VPMContentAnalysis(bool runtime_cpu_detection):
|
||||
_origFrame(NULL),
|
||||
_prevFrame(NULL),
|
||||
_width(0),
|
||||
_height(0),
|
||||
_skipNum(1),
|
||||
_border(8),
|
||||
_motionMagnitude(0.0f),
|
||||
_spatialPredErr(0.0f),
|
||||
_spatialPredErrH(0.0f),
|
||||
_spatialPredErrV(0.0f),
|
||||
_firstFrame(true),
|
||||
_CAInit(false),
|
||||
_cMetrics(NULL)
|
||||
{
|
||||
ComputeSpatialMetrics = &VPMContentAnalysis::ComputeSpatialMetrics_C;
|
||||
TemporalDiffMetric = &VPMContentAnalysis::TemporalDiffMetric_C;
|
||||
VPMContentAnalysis::VPMContentAnalysis(bool runtime_cpu_detection)
|
||||
: orig_frame_(NULL),
|
||||
prev_frame_(NULL),
|
||||
width_(0),
|
||||
height_(0),
|
||||
skip_num_(1),
|
||||
border_(8),
|
||||
motion_magnitude_(0.0f),
|
||||
spatial_pred_err_(0.0f),
|
||||
spatial_pred_err_h_(0.0f),
|
||||
spatial_pred_err_v_(0.0f),
|
||||
first_frame_(true),
|
||||
ca_Init_(false),
|
||||
content_metrics_(NULL) {
|
||||
ComputeSpatialMetrics = &VPMContentAnalysis::ComputeSpatialMetrics_C;
|
||||
TemporalDiffMetric = &VPMContentAnalysis::TemporalDiffMetric_C;
|
||||
|
||||
if (runtime_cpu_detection)
|
||||
{
|
||||
if (runtime_cpu_detection) {
|
||||
#if defined(WEBRTC_ARCH_X86_FAMILY)
|
||||
if (WebRtc_GetCPUInfo(kSSE2))
|
||||
{
|
||||
ComputeSpatialMetrics =
|
||||
&VPMContentAnalysis::ComputeSpatialMetrics_SSE2;
|
||||
TemporalDiffMetric = &VPMContentAnalysis::TemporalDiffMetric_SSE2;
|
||||
}
|
||||
if (WebRtc_GetCPUInfo(kSSE2)) {
|
||||
ComputeSpatialMetrics = &VPMContentAnalysis::ComputeSpatialMetrics_SSE2;
|
||||
TemporalDiffMetric = &VPMContentAnalysis::TemporalDiffMetric_SSE2;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
Release();
|
||||
}
|
||||
Release();
|
||||
}
|
||||
|
||||
VPMContentAnalysis::~VPMContentAnalysis()
|
||||
{
|
||||
Release();
|
||||
VPMContentAnalysis::~VPMContentAnalysis() {
|
||||
Release();
|
||||
}
|
||||
|
||||
|
||||
VideoContentMetrics*
|
||||
VPMContentAnalysis::ComputeContentMetrics(const I420VideoFrame& inputFrame)
|
||||
{
|
||||
if (inputFrame.IsZeroSize())
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
VideoContentMetrics* VPMContentAnalysis::ComputeContentMetrics(
|
||||
const I420VideoFrame& inputFrame) {
|
||||
if (inputFrame.IsZeroSize())
|
||||
return NULL;
|
||||
|
||||
// Init if needed (native dimension change)
|
||||
if (_width != inputFrame.width() || _height != inputFrame.height())
|
||||
{
|
||||
if (VPM_OK != Initialize(inputFrame.width(), inputFrame.height()))
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
// Only interested in the Y plane.
|
||||
_origFrame = inputFrame.buffer(kYPlane);
|
||||
// Init if needed (native dimension change).
|
||||
if (width_ != inputFrame.width() || height_ != inputFrame.height()) {
|
||||
if (VPM_OK != Initialize(inputFrame.width(), inputFrame.height()))
|
||||
return NULL;
|
||||
}
|
||||
// Only interested in the Y plane.
|
||||
orig_frame_ = inputFrame.buffer(kYPlane);
|
||||
|
||||
// compute spatial metrics: 3 spatial prediction errors
|
||||
(this->*ComputeSpatialMetrics)();
|
||||
// Compute spatial metrics: 3 spatial prediction errors.
|
||||
(this->*ComputeSpatialMetrics)();
|
||||
|
||||
// compute motion metrics
|
||||
if (_firstFrame == false)
|
||||
ComputeMotionMetrics();
|
||||
// Compute motion metrics
|
||||
if (first_frame_ == false)
|
||||
ComputeMotionMetrics();
|
||||
|
||||
// saving current frame as previous one: Y only
|
||||
memcpy(_prevFrame, _origFrame, _width * _height);
|
||||
// Saving current frame as previous one: Y only.
|
||||
memcpy(prev_frame_, orig_frame_, width_ * height_);
|
||||
|
||||
_firstFrame = false;
|
||||
_CAInit = true;
|
||||
first_frame_ = false;
|
||||
ca_Init_ = true;
|
||||
|
||||
return ContentMetrics();
|
||||
return ContentMetrics();
|
||||
}
|
||||
|
||||
int32_t
|
||||
VPMContentAnalysis::Release()
|
||||
{
|
||||
if (_cMetrics != NULL)
|
||||
{
|
||||
delete _cMetrics;
|
||||
_cMetrics = NULL;
|
||||
}
|
||||
int32_t VPMContentAnalysis::Release() {
|
||||
if (content_metrics_ != NULL) {
|
||||
delete content_metrics_;
|
||||
content_metrics_ = NULL;
|
||||
}
|
||||
|
||||
if (_prevFrame != NULL)
|
||||
{
|
||||
delete [] _prevFrame;
|
||||
_prevFrame = NULL;
|
||||
}
|
||||
if (prev_frame_ != NULL) {
|
||||
delete [] prev_frame_;
|
||||
prev_frame_ = NULL;
|
||||
}
|
||||
|
||||
_width = 0;
|
||||
_height = 0;
|
||||
_firstFrame = true;
|
||||
width_ = 0;
|
||||
height_ = 0;
|
||||
first_frame_ = true;
|
||||
|
||||
return VPM_OK;
|
||||
return VPM_OK;
|
||||
}
|
||||
|
||||
int32_t
|
||||
VPMContentAnalysis::Initialize(int width, int height)
|
||||
{
|
||||
_width = width;
|
||||
_height = height;
|
||||
_firstFrame = true;
|
||||
int32_t VPMContentAnalysis::Initialize(int width, int height) {
|
||||
width_ = width;
|
||||
height_ = height;
|
||||
first_frame_ = true;
|
||||
|
||||
// skip parameter: # of skipped rows: for complexity reduction
|
||||
// temporal also currently uses it for column reduction.
|
||||
_skipNum = 1;
|
||||
// skip parameter: # of skipped rows: for complexity reduction
|
||||
// temporal also currently uses it for column reduction.
|
||||
skip_num_ = 1;
|
||||
|
||||
// use skipNum = 2 for 4CIF, WHD
|
||||
if ( (_height >= 576) && (_width >= 704) )
|
||||
{
|
||||
_skipNum = 2;
|
||||
}
|
||||
// use skipNum = 4 for FULLL_HD images
|
||||
if ( (_height >= 1080) && (_width >= 1920) )
|
||||
{
|
||||
_skipNum = 4;
|
||||
}
|
||||
// use skipNum = 2 for 4CIF, WHD
|
||||
if ( (height_ >= 576) && (width_ >= 704) ) {
|
||||
skip_num_ = 2;
|
||||
}
|
||||
// use skipNum = 4 for FULLL_HD images
|
||||
if ( (height_ >= 1080) && (width_ >= 1920) ) {
|
||||
skip_num_ = 4;
|
||||
}
|
||||
|
||||
if (_cMetrics != NULL)
|
||||
{
|
||||
delete _cMetrics;
|
||||
}
|
||||
if (content_metrics_ != NULL) {
|
||||
delete content_metrics_;
|
||||
}
|
||||
|
||||
if (_prevFrame != NULL)
|
||||
{
|
||||
delete [] _prevFrame;
|
||||
}
|
||||
if (prev_frame_ != NULL) {
|
||||
delete [] prev_frame_;
|
||||
}
|
||||
|
||||
// Spatial Metrics don't work on a border of 8. Minimum processing
|
||||
// block size is 16 pixels. So make sure the width and height support this.
|
||||
if (_width <= 32 || _height <= 32)
|
||||
{
|
||||
_CAInit = false;
|
||||
return VPM_PARAMETER_ERROR;
|
||||
}
|
||||
// Spatial Metrics don't work on a border of 8. Minimum processing
|
||||
// block size is 16 pixels. So make sure the width and height support this.
|
||||
if (width_ <= 32 || height_ <= 32) {
|
||||
ca_Init_ = false;
|
||||
return VPM_PARAMETER_ERROR;
|
||||
}
|
||||
|
||||
_cMetrics = new VideoContentMetrics();
|
||||
if (_cMetrics == NULL)
|
||||
{
|
||||
return VPM_MEMORY;
|
||||
}
|
||||
content_metrics_ = new VideoContentMetrics();
|
||||
if (content_metrics_ == NULL) {
|
||||
return VPM_MEMORY;
|
||||
}
|
||||
|
||||
_prevFrame = new uint8_t[_width * _height] ; // Y only
|
||||
if (_prevFrame == NULL)
|
||||
{
|
||||
return VPM_MEMORY;
|
||||
}
|
||||
prev_frame_ = new uint8_t[width_ * height_]; // Y only.
|
||||
if (prev_frame_ == NULL) return VPM_MEMORY;
|
||||
|
||||
return VPM_OK;
|
||||
return VPM_OK;
|
||||
}
|
||||
|
||||
|
||||
// Compute motion metrics: magnitude over non-zero motion vectors,
|
||||
// and size of zero cluster
|
||||
int32_t
|
||||
VPMContentAnalysis::ComputeMotionMetrics()
|
||||
{
|
||||
|
||||
// Motion metrics: only one is derived from normalized
|
||||
// (MAD) temporal difference
|
||||
(this->*TemporalDiffMetric)();
|
||||
|
||||
return VPM_OK;
|
||||
int32_t VPMContentAnalysis::ComputeMotionMetrics() {
|
||||
// Motion metrics: only one is derived from normalized
|
||||
// (MAD) temporal difference
|
||||
(this->*TemporalDiffMetric)();
|
||||
return VPM_OK;
|
||||
}
|
||||
|
||||
// Normalized temporal difference (MAD): used as a motion level metric
|
||||
// Normalize MAD by spatial contrast: images with more contrast
|
||||
// (pixel variance) likely have larger temporal difference
|
||||
// To reduce complexity, we compute the metric for a reduced set of points.
|
||||
int32_t
|
||||
VPMContentAnalysis::TemporalDiffMetric_C()
|
||||
{
|
||||
// size of original frame
|
||||
int sizei = _height;
|
||||
int sizej = _width;
|
||||
int32_t VPMContentAnalysis::TemporalDiffMetric_C() {
|
||||
// size of original frame
|
||||
int sizei = height_;
|
||||
int sizej = width_;
|
||||
uint32_t tempDiffSum = 0;
|
||||
uint32_t pixelSum = 0;
|
||||
uint64_t pixelSqSum = 0;
|
||||
|
||||
uint32_t tempDiffSum = 0;
|
||||
uint32_t pixelSum = 0;
|
||||
uint64_t pixelSqSum = 0;
|
||||
uint32_t num_pixels = 0; // Counter for # of pixels.
|
||||
const int width_end = ((width_ - 2*border_) & -16) + border_;
|
||||
|
||||
uint32_t numPixels = 0; // counter for # of pixels
|
||||
for (int i = border_; i < sizei - border_; i += skip_num_) {
|
||||
for (int j = border_; j < width_end; j++) {
|
||||
num_pixels += 1;
|
||||
int ssn = i * sizej + j;
|
||||
|
||||
const int width_end = ((_width - 2*_border) & -16) + _border;
|
||||
uint8_t currPixel = orig_frame_[ssn];
|
||||
uint8_t prevPixel = prev_frame_[ssn];
|
||||
|
||||
for(int i = _border; i < sizei - _border; i += _skipNum)
|
||||
{
|
||||
for(int j = _border; j < width_end; j++)
|
||||
{
|
||||
numPixels += 1;
|
||||
int ssn = i * sizej + j;
|
||||
|
||||
uint8_t currPixel = _origFrame[ssn];
|
||||
uint8_t prevPixel = _prevFrame[ssn];
|
||||
|
||||
tempDiffSum += (uint32_t)
|
||||
abs((int16_t)(currPixel - prevPixel));
|
||||
pixelSum += (uint32_t) currPixel;
|
||||
pixelSqSum += (uint64_t) (currPixel * currPixel);
|
||||
}
|
||||
tempDiffSum += (uint32_t)abs((int16_t)(currPixel - prevPixel));
|
||||
pixelSum += (uint32_t) currPixel;
|
||||
pixelSqSum += (uint64_t) (currPixel * currPixel);
|
||||
}
|
||||
}
|
||||
|
||||
// default
|
||||
_motionMagnitude = 0.0f;
|
||||
// Default.
|
||||
motion_magnitude_ = 0.0f;
|
||||
|
||||
if (tempDiffSum == 0)
|
||||
{
|
||||
return VPM_OK;
|
||||
}
|
||||
if (tempDiffSum == 0) return VPM_OK;
|
||||
|
||||
// normalize over all pixels
|
||||
float const tempDiffAvg = (float)tempDiffSum / (float)(numPixels);
|
||||
float const pixelSumAvg = (float)pixelSum / (float)(numPixels);
|
||||
float const pixelSqSumAvg = (float)pixelSqSum / (float)(numPixels);
|
||||
float contrast = pixelSqSumAvg - (pixelSumAvg * pixelSumAvg);
|
||||
|
||||
if (contrast > 0.0)
|
||||
{
|
||||
contrast = sqrt(contrast);
|
||||
_motionMagnitude = tempDiffAvg/contrast;
|
||||
}
|
||||
|
||||
return VPM_OK;
|
||||
// Normalize over all pixels.
|
||||
float const tempDiffAvg = (float)tempDiffSum / (float)(num_pixels);
|
||||
float const pixelSumAvg = (float)pixelSum / (float)(num_pixels);
|
||||
float const pixelSqSumAvg = (float)pixelSqSum / (float)(num_pixels);
|
||||
float contrast = pixelSqSumAvg - (pixelSumAvg * pixelSumAvg);
|
||||
|
||||
if (contrast > 0.0) {
|
||||
contrast = sqrt(contrast);
|
||||
motion_magnitude_ = tempDiffAvg/contrast;
|
||||
}
|
||||
return VPM_OK;
|
||||
}
|
||||
|
||||
// Compute spatial metrics:
|
||||
@ -249,88 +205,71 @@ VPMContentAnalysis::TemporalDiffMetric_C()
|
||||
// The metrics are a simple estimate of the up-sampling prediction error,
|
||||
// estimated assuming sub-sampling for decimation (no filtering),
|
||||
// and up-sampling back up with simple bilinear interpolation.
|
||||
int32_t
|
||||
VPMContentAnalysis::ComputeSpatialMetrics_C()
|
||||
{
|
||||
//size of original frame
|
||||
const int sizei = _height;
|
||||
const int sizej = _width;
|
||||
int32_t VPMContentAnalysis::ComputeSpatialMetrics_C() {
|
||||
const int sizei = height_;
|
||||
const int sizej = width_;
|
||||
|
||||
// pixel mean square average: used to normalize the spatial metrics
|
||||
uint32_t pixelMSA = 0;
|
||||
// Pixel mean square average: used to normalize the spatial metrics.
|
||||
uint32_t pixelMSA = 0;
|
||||
|
||||
uint32_t spatialErrSum = 0;
|
||||
uint32_t spatialErrVSum = 0;
|
||||
uint32_t spatialErrHSum = 0;
|
||||
uint32_t spatialErrSum = 0;
|
||||
uint32_t spatialErrVSum = 0;
|
||||
uint32_t spatialErrHSum = 0;
|
||||
|
||||
// make sure work section is a multiple of 16
|
||||
const int width_end = ((sizej - 2*_border) & -16) + _border;
|
||||
// make sure work section is a multiple of 16
|
||||
const int width_end = ((sizej - 2*border_) & -16) + border_;
|
||||
|
||||
for(int i = _border; i < sizei - _border; i += _skipNum)
|
||||
{
|
||||
for(int j = _border; j < width_end; j++)
|
||||
{
|
||||
for (int i = border_; i < sizei - border_; i += skip_num_) {
|
||||
for (int j = border_; j < width_end; j++) {
|
||||
int ssn1= i * sizej + j;
|
||||
int ssn2 = (i + 1) * sizej + j; // bottom
|
||||
int ssn3 = (i - 1) * sizej + j; // top
|
||||
int ssn4 = i * sizej + j + 1; // right
|
||||
int ssn5 = i * sizej + j - 1; // left
|
||||
|
||||
int ssn1= i * sizej + j;
|
||||
int ssn2 = (i + 1) * sizej + j; // bottom
|
||||
int ssn3 = (i - 1) * sizej + j; // top
|
||||
int ssn4 = i * sizej + j + 1; // right
|
||||
int ssn5 = i * sizej + j - 1; // left
|
||||
uint16_t refPixel1 = orig_frame_[ssn1] << 1;
|
||||
uint16_t refPixel2 = orig_frame_[ssn1] << 2;
|
||||
|
||||
uint16_t refPixel1 = _origFrame[ssn1] << 1;
|
||||
uint16_t refPixel2 = _origFrame[ssn1] << 2;
|
||||
uint8_t bottPixel = orig_frame_[ssn2];
|
||||
uint8_t topPixel = orig_frame_[ssn3];
|
||||
uint8_t rightPixel = orig_frame_[ssn4];
|
||||
uint8_t leftPixel = orig_frame_[ssn5];
|
||||
|
||||
uint8_t bottPixel = _origFrame[ssn2];
|
||||
uint8_t topPixel = _origFrame[ssn3];
|
||||
uint8_t rightPixel = _origFrame[ssn4];
|
||||
uint8_t leftPixel = _origFrame[ssn5];
|
||||
|
||||
spatialErrSum += (uint32_t) abs((int16_t)(refPixel2
|
||||
- (uint16_t)(bottPixel + topPixel
|
||||
+ leftPixel + rightPixel)));
|
||||
spatialErrVSum += (uint32_t) abs((int16_t)(refPixel1
|
||||
- (uint16_t)(bottPixel + topPixel)));
|
||||
spatialErrHSum += (uint32_t) abs((int16_t)(refPixel1
|
||||
- (uint16_t)(leftPixel + rightPixel)));
|
||||
|
||||
pixelMSA += _origFrame[ssn1];
|
||||
}
|
||||
spatialErrSum += (uint32_t) abs((int16_t)(refPixel2
|
||||
- (uint16_t)(bottPixel + topPixel + leftPixel + rightPixel)));
|
||||
spatialErrVSum += (uint32_t) abs((int16_t)(refPixel1
|
||||
- (uint16_t)(bottPixel + topPixel)));
|
||||
spatialErrHSum += (uint32_t) abs((int16_t)(refPixel1
|
||||
- (uint16_t)(leftPixel + rightPixel)));
|
||||
pixelMSA += orig_frame_[ssn1];
|
||||
}
|
||||
}
|
||||
|
||||
// normalize over all pixels
|
||||
const float spatialErr = (float)(spatialErrSum >> 2);
|
||||
const float spatialErrH = (float)(spatialErrHSum >> 1);
|
||||
const float spatialErrV = (float)(spatialErrVSum >> 1);
|
||||
const float norm = (float)pixelMSA;
|
||||
// Normalize over all pixels.
|
||||
const float spatialErr = (float)(spatialErrSum >> 2);
|
||||
const float spatialErrH = (float)(spatialErrHSum >> 1);
|
||||
const float spatialErrV = (float)(spatialErrVSum >> 1);
|
||||
const float norm = (float)pixelMSA;
|
||||
|
||||
// 2X2:
|
||||
_spatialPredErr = spatialErr / norm;
|
||||
|
||||
// 1X2:
|
||||
_spatialPredErrH = spatialErrH / norm;
|
||||
|
||||
// 2X1:
|
||||
_spatialPredErrV = spatialErrV / norm;
|
||||
|
||||
return VPM_OK;
|
||||
// 2X2:
|
||||
spatial_pred_err_ = spatialErr / norm;
|
||||
// 1X2:
|
||||
spatial_pred_err_h_ = spatialErrH / norm;
|
||||
// 2X1:
|
||||
spatial_pred_err_v_ = spatialErrV / norm;
|
||||
return VPM_OK;
|
||||
}
|
||||
|
||||
VideoContentMetrics*
|
||||
VPMContentAnalysis::ContentMetrics()
|
||||
{
|
||||
if (_CAInit == false)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
VideoContentMetrics* VPMContentAnalysis::ContentMetrics() {
|
||||
if (ca_Init_ == false) return NULL;
|
||||
|
||||
_cMetrics->spatial_pred_err = _spatialPredErr;
|
||||
_cMetrics->spatial_pred_err_h = _spatialPredErrH;
|
||||
_cMetrics->spatial_pred_err_v = _spatialPredErrV;
|
||||
// Motion metric: normalized temporal difference (MAD)
|
||||
_cMetrics->motion_magnitude = _motionMagnitude;
|
||||
|
||||
return _cMetrics;
|
||||
content_metrics_->spatial_pred_err = spatial_pred_err_;
|
||||
content_metrics_->spatial_pred_err_h = spatial_pred_err_h_;
|
||||
content_metrics_->spatial_pred_err_v = spatial_pred_err_v_;
|
||||
// Motion metric: normalized temporal difference (MAD).
|
||||
content_metrics_->motion_magnitude = motion_magnitude_;
|
||||
|
||||
return content_metrics_;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace webrtc
|
||||
|
@ -8,8 +8,8 @@
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef VPM_CONTENT_ANALYSIS_H
|
||||
#define VPM_CONTENT_ANALYSIS_H
|
||||
#ifndef WEBRTC_MODULES_VIDEO_PROCESSING_MAIN_SOURCE_CONTENT_ANALYSIS_H
|
||||
#define WEBRTC_MODULES_VIDEO_PROCESSING_MAIN_SOURCE_CONTENT_ANALYSIS_H
|
||||
|
||||
#include "webrtc/common_video/interface/i420_video_frame.h"
|
||||
#include "webrtc/modules/interface/module_common_types.h"
|
||||
@ -18,75 +18,71 @@
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class VPMContentAnalysis
|
||||
{
|
||||
public:
|
||||
// When |runtime_cpu_detection| is true, runtime selection of an optimized
|
||||
// code path is allowed.
|
||||
VPMContentAnalysis(bool runtime_cpu_detection);
|
||||
~VPMContentAnalysis();
|
||||
class VPMContentAnalysis {
|
||||
public:
|
||||
// When |runtime_cpu_detection| is true, runtime selection of an optimized
|
||||
// code path is allowed.
|
||||
explicit VPMContentAnalysis(bool runtime_cpu_detection);
|
||||
~VPMContentAnalysis();
|
||||
|
||||
// Initialize ContentAnalysis - should be called prior to
|
||||
// extractContentFeature
|
||||
// Inputs: width, height
|
||||
// Return value: 0 if OK, negative value upon error
|
||||
int32_t Initialize(int width, int height);
|
||||
// Initialize ContentAnalysis - should be called prior to
|
||||
// extractContentFeature
|
||||
// Inputs: width, height
|
||||
// Return value: 0 if OK, negative value upon error
|
||||
int32_t Initialize(int width, int height);
|
||||
|
||||
// Extract content Feature - main function of ContentAnalysis
|
||||
// Input: new frame
|
||||
// Return value: pointer to structure containing content Analysis
|
||||
// metrics or NULL value upon error
|
||||
VideoContentMetrics* ComputeContentMetrics(const I420VideoFrame&
|
||||
inputFrame);
|
||||
// Extract content Feature - main function of ContentAnalysis
|
||||
// Input: new frame
|
||||
// Return value: pointer to structure containing content Analysis
|
||||
// metrics or NULL value upon error
|
||||
VideoContentMetrics* ComputeContentMetrics(const I420VideoFrame&
|
||||
inputFrame);
|
||||
|
||||
// Release all allocated memory
|
||||
// Output: 0 if OK, negative value upon error
|
||||
int32_t Release();
|
||||
// Release all allocated memory
|
||||
// Output: 0 if OK, negative value upon error
|
||||
int32_t Release();
|
||||
|
||||
private:
|
||||
private:
|
||||
// return motion metrics
|
||||
VideoContentMetrics* ContentMetrics();
|
||||
|
||||
// return motion metrics
|
||||
VideoContentMetrics* ContentMetrics();
|
||||
// Normalized temporal difference metric: for motion magnitude
|
||||
typedef int32_t (VPMContentAnalysis::*TemporalDiffMetricFunc)();
|
||||
TemporalDiffMetricFunc TemporalDiffMetric;
|
||||
int32_t TemporalDiffMetric_C();
|
||||
|
||||
// Normalized temporal difference metric: for motion magnitude
|
||||
typedef int32_t (VPMContentAnalysis::*TemporalDiffMetricFunc)();
|
||||
TemporalDiffMetricFunc TemporalDiffMetric;
|
||||
int32_t TemporalDiffMetric_C();
|
||||
// Motion metric method: call 2 metrics (magnitude and size)
|
||||
int32_t ComputeMotionMetrics();
|
||||
|
||||
// Motion metric method: call 2 metrics (magnitude and size)
|
||||
int32_t ComputeMotionMetrics();
|
||||
|
||||
// Spatial metric method: computes the 3 frame-average spatial
|
||||
// prediction errors (1x2,2x1,2x2)
|
||||
typedef int32_t (VPMContentAnalysis::*ComputeSpatialMetricsFunc)();
|
||||
ComputeSpatialMetricsFunc ComputeSpatialMetrics;
|
||||
int32_t ComputeSpatialMetrics_C();
|
||||
// Spatial metric method: computes the 3 frame-average spatial
|
||||
// prediction errors (1x2,2x1,2x2)
|
||||
typedef int32_t (VPMContentAnalysis::*ComputeSpatialMetricsFunc)();
|
||||
ComputeSpatialMetricsFunc ComputeSpatialMetrics;
|
||||
int32_t ComputeSpatialMetrics_C();
|
||||
|
||||
#if defined(WEBRTC_ARCH_X86_FAMILY)
|
||||
int32_t ComputeSpatialMetrics_SSE2();
|
||||
int32_t TemporalDiffMetric_SSE2();
|
||||
int32_t ComputeSpatialMetrics_SSE2();
|
||||
int32_t TemporalDiffMetric_SSE2();
|
||||
#endif
|
||||
|
||||
const uint8_t* _origFrame;
|
||||
uint8_t* _prevFrame;
|
||||
int _width;
|
||||
int _height;
|
||||
int _skipNum;
|
||||
int _border;
|
||||
const uint8_t* orig_frame_;
|
||||
uint8_t* prev_frame_;
|
||||
int width_;
|
||||
int height_;
|
||||
int skip_num_;
|
||||
int border_;
|
||||
|
||||
// Content Metrics:
|
||||
// stores the local average of the metrics
|
||||
float _motionMagnitude; // motion class
|
||||
float _spatialPredErr; // spatial class
|
||||
float _spatialPredErrH; // spatial class
|
||||
float _spatialPredErrV; // spatial class
|
||||
bool _firstFrame;
|
||||
bool _CAInit;
|
||||
// Content Metrics: Stores the local average of the metrics.
|
||||
float motion_magnitude_; // motion class
|
||||
float spatial_pred_err_; // spatial class
|
||||
float spatial_pred_err_h_; // spatial class
|
||||
float spatial_pred_err_v_; // spatial class
|
||||
bool first_frame_;
|
||||
bool ca_Init_;
|
||||
|
||||
VideoContentMetrics* _cMetrics;
|
||||
VideoContentMetrics* content_metrics_;
|
||||
};
|
||||
|
||||
}; // end of VPMContentAnalysis class definition
|
||||
} // namespace webrtc
|
||||
|
||||
} // namespace
|
||||
|
||||
#endif
|
||||
#endif // WEBRTC_MODULES_VIDEO_PROCESSING_MAIN_SOURCE_CONTENT_ANALYSIS_H
|
||||
|
@ -15,284 +15,248 @@
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
int32_t
|
||||
VPMContentAnalysis::TemporalDiffMetric_SSE2()
|
||||
{
|
||||
uint32_t numPixels = 0; // counter for # of pixels
|
||||
int32_t VPMContentAnalysis::TemporalDiffMetric_SSE2() {
|
||||
uint32_t num_pixels = 0; // counter for # of pixels
|
||||
const uint8_t* imgBufO = orig_frame_ + border_*width_ + border_;
|
||||
const uint8_t* imgBufP = prev_frame_ + border_*width_ + border_;
|
||||
|
||||
const uint8_t* imgBufO = _origFrame + _border*_width + _border;
|
||||
const uint8_t* imgBufP = _prevFrame + _border*_width + _border;
|
||||
const int32_t width_end = ((width_ - 2*border_) & -16) + border_;
|
||||
|
||||
const int32_t width_end = ((_width - 2*_border) & -16) + _border;
|
||||
__m128i sad_64 = _mm_setzero_si128();
|
||||
__m128i sum_64 = _mm_setzero_si128();
|
||||
__m128i sqsum_64 = _mm_setzero_si128();
|
||||
const __m128i z = _mm_setzero_si128();
|
||||
|
||||
__m128i sad_64 = _mm_setzero_si128();
|
||||
__m128i sum_64 = _mm_setzero_si128();
|
||||
__m128i sqsum_64 = _mm_setzero_si128();
|
||||
const __m128i z = _mm_setzero_si128();
|
||||
for (uint16_t i = 0; i < (height_ - 2*border_); i += skip_num_) {
|
||||
__m128i sqsum_32 = _mm_setzero_si128();
|
||||
|
||||
for(uint16_t i = 0; i < (_height - 2*_border); i += _skipNum)
|
||||
{
|
||||
__m128i sqsum_32 = _mm_setzero_si128();
|
||||
const uint8_t *lineO = imgBufO;
|
||||
const uint8_t *lineP = imgBufP;
|
||||
|
||||
const uint8_t *lineO = imgBufO;
|
||||
const uint8_t *lineP = imgBufP;
|
||||
// Work on 16 pixels at a time. For HD content with a width of 1920
|
||||
// this loop will run ~67 times (depending on border). Maximum for
|
||||
// abs(o-p) and sum(o) will be 255. _mm_sad_epu8 produces 2 64 bit
|
||||
// results which are then accumulated. There is no chance of
|
||||
// rollover for these two accumulators.
|
||||
// o*o will have a maximum of 255*255 = 65025. This will roll over
|
||||
// a 16 bit accumulator as 67*65025 > 65535, but will fit in a
|
||||
// 32 bit accumulator.
|
||||
for (uint16_t j = 0; j < width_end - border_; j += 16) {
|
||||
const __m128i o = _mm_loadu_si128((__m128i*)(lineO));
|
||||
const __m128i p = _mm_loadu_si128((__m128i*)(lineP));
|
||||
|
||||
// Work on 16 pixels at a time. For HD content with a width of 1920
|
||||
// this loop will run ~67 times (depending on border). Maximum for
|
||||
// abs(o-p) and sum(o) will be 255. _mm_sad_epu8 produces 2 64 bit
|
||||
// results which are then accumulated. There is no chance of
|
||||
// rollover for these two accumulators.
|
||||
// o*o will have a maximum of 255*255 = 65025. This will roll over
|
||||
// a 16 bit accumulator as 67*65025 > 65535, but will fit in a
|
||||
// 32 bit accumulator.
|
||||
for(uint16_t j = 0; j < width_end - _border; j += 16)
|
||||
{
|
||||
const __m128i o = _mm_loadu_si128((__m128i*)(lineO));
|
||||
const __m128i p = _mm_loadu_si128((__m128i*)(lineP));
|
||||
lineO += 16;
|
||||
lineP += 16;
|
||||
|
||||
lineO += 16;
|
||||
lineP += 16;
|
||||
// Abs pixel difference between frames.
|
||||
sad_64 = _mm_add_epi64 (sad_64, _mm_sad_epu8(o, p));
|
||||
|
||||
// abs pixel difference between frames
|
||||
sad_64 = _mm_add_epi64 (sad_64, _mm_sad_epu8(o, p));
|
||||
// sum of all pixels in frame
|
||||
sum_64 = _mm_add_epi64 (sum_64, _mm_sad_epu8(o, z));
|
||||
|
||||
// sum of all pixels in frame
|
||||
sum_64 = _mm_add_epi64 (sum_64, _mm_sad_epu8(o, z));
|
||||
// Squared sum of all pixels in frame.
|
||||
const __m128i olo = _mm_unpacklo_epi8(o,z);
|
||||
const __m128i ohi = _mm_unpackhi_epi8(o,z);
|
||||
|
||||
// squared sum of all pixels in frame
|
||||
const __m128i olo = _mm_unpacklo_epi8(o,z);
|
||||
const __m128i ohi = _mm_unpackhi_epi8(o,z);
|
||||
const __m128i sqsum_32_lo = _mm_madd_epi16(olo, olo);
|
||||
const __m128i sqsum_32_hi = _mm_madd_epi16(ohi, ohi);
|
||||
|
||||
const __m128i sqsum_32_lo = _mm_madd_epi16(olo, olo);
|
||||
const __m128i sqsum_32_hi = _mm_madd_epi16(ohi, ohi);
|
||||
|
||||
sqsum_32 = _mm_add_epi32(sqsum_32, sqsum_32_lo);
|
||||
sqsum_32 = _mm_add_epi32(sqsum_32, sqsum_32_hi);
|
||||
}
|
||||
|
||||
// Add to 64 bit running sum as to not roll over.
|
||||
sqsum_64 = _mm_add_epi64(sqsum_64,
|
||||
_mm_add_epi64(_mm_unpackhi_epi32(sqsum_32,z),
|
||||
_mm_unpacklo_epi32(sqsum_32,z)));
|
||||
|
||||
imgBufO += _width * _skipNum;
|
||||
imgBufP += _width * _skipNum;
|
||||
numPixels += (width_end - _border);
|
||||
sqsum_32 = _mm_add_epi32(sqsum_32, sqsum_32_lo);
|
||||
sqsum_32 = _mm_add_epi32(sqsum_32, sqsum_32_hi);
|
||||
}
|
||||
|
||||
__m128i sad_final_128;
|
||||
__m128i sum_final_128;
|
||||
__m128i sqsum_final_128;
|
||||
// Add to 64 bit running sum as to not roll over.
|
||||
sqsum_64 = _mm_add_epi64(sqsum_64,
|
||||
_mm_add_epi64(_mm_unpackhi_epi32(sqsum_32,z),
|
||||
_mm_unpacklo_epi32(sqsum_32,z)));
|
||||
|
||||
// bring sums out of vector registers and into integer register
|
||||
// domain, summing them along the way
|
||||
_mm_store_si128 (&sad_final_128, sad_64);
|
||||
_mm_store_si128 (&sum_final_128, sum_64);
|
||||
_mm_store_si128 (&sqsum_final_128, sqsum_64);
|
||||
imgBufO += width_ * skip_num_;
|
||||
imgBufP += width_ * skip_num_;
|
||||
num_pixels += (width_end - border_);
|
||||
}
|
||||
|
||||
uint64_t *sad_final_64 =
|
||||
reinterpret_cast<uint64_t*>(&sad_final_128);
|
||||
uint64_t *sum_final_64 =
|
||||
reinterpret_cast<uint64_t*>(&sum_final_128);
|
||||
uint64_t *sqsum_final_64 =
|
||||
reinterpret_cast<uint64_t*>(&sqsum_final_128);
|
||||
__m128i sad_final_128;
|
||||
__m128i sum_final_128;
|
||||
__m128i sqsum_final_128;
|
||||
|
||||
const uint32_t pixelSum = sum_final_64[0] + sum_final_64[1];
|
||||
const uint64_t pixelSqSum = sqsum_final_64[0] + sqsum_final_64[1];
|
||||
const uint32_t tempDiffSum = sad_final_64[0] + sad_final_64[1];
|
||||
// Bring sums out of vector registers and into integer register
|
||||
// domain, summing them along the way.
|
||||
_mm_store_si128 (&sad_final_128, sad_64);
|
||||
_mm_store_si128 (&sum_final_128, sum_64);
|
||||
_mm_store_si128 (&sqsum_final_128, sqsum_64);
|
||||
|
||||
// default
|
||||
_motionMagnitude = 0.0f;
|
||||
uint64_t *sad_final_64 = reinterpret_cast<uint64_t*>(&sad_final_128);
|
||||
uint64_t *sum_final_64 = reinterpret_cast<uint64_t*>(&sum_final_128);
|
||||
uint64_t *sqsum_final_64 = reinterpret_cast<uint64_t*>(&sqsum_final_128);
|
||||
|
||||
if (tempDiffSum == 0)
|
||||
{
|
||||
return VPM_OK;
|
||||
}
|
||||
const uint32_t pixelSum = sum_final_64[0] + sum_final_64[1];
|
||||
const uint64_t pixelSqSum = sqsum_final_64[0] + sqsum_final_64[1];
|
||||
const uint32_t tempDiffSum = sad_final_64[0] + sad_final_64[1];
|
||||
|
||||
// normalize over all pixels
|
||||
const float tempDiffAvg = (float)tempDiffSum / (float)(numPixels);
|
||||
const float pixelSumAvg = (float)pixelSum / (float)(numPixels);
|
||||
const float pixelSqSumAvg = (float)pixelSqSum / (float)(numPixels);
|
||||
float contrast = pixelSqSumAvg - (pixelSumAvg * pixelSumAvg);
|
||||
// Default.
|
||||
motion_magnitude_ = 0.0f;
|
||||
|
||||
if (contrast > 0.0)
|
||||
{
|
||||
contrast = sqrt(contrast);
|
||||
_motionMagnitude = tempDiffAvg/contrast;
|
||||
}
|
||||
if (tempDiffSum == 0) return VPM_OK;
|
||||
|
||||
return VPM_OK;
|
||||
// Normalize over all pixels.
|
||||
const float tempDiffAvg = (float)tempDiffSum / (float)(num_pixels);
|
||||
const float pixelSumAvg = (float)pixelSum / (float)(num_pixels);
|
||||
const float pixelSqSumAvg = (float)pixelSqSum / (float)(num_pixels);
|
||||
float contrast = pixelSqSumAvg - (pixelSumAvg * pixelSumAvg);
|
||||
|
||||
if (contrast > 0.0) {
|
||||
contrast = sqrt(contrast);
|
||||
motion_magnitude_ = tempDiffAvg/contrast;
|
||||
}
|
||||
|
||||
return VPM_OK;
|
||||
}
|
||||
|
||||
int32_t
|
||||
VPMContentAnalysis::ComputeSpatialMetrics_SSE2()
|
||||
{
|
||||
const uint8_t* imgBuf = _origFrame + _border*_width;
|
||||
const int32_t width_end = ((_width - 2*_border) & -16) + _border;
|
||||
int32_t VPMContentAnalysis::ComputeSpatialMetrics_SSE2() {
|
||||
const uint8_t* imgBuf = orig_frame_ + border_*width_;
|
||||
const int32_t width_end = ((width_ - 2 * border_) & -16) + border_;
|
||||
|
||||
__m128i se_32 = _mm_setzero_si128();
|
||||
__m128i sev_32 = _mm_setzero_si128();
|
||||
__m128i seh_32 = _mm_setzero_si128();
|
||||
__m128i msa_32 = _mm_setzero_si128();
|
||||
const __m128i z = _mm_setzero_si128();
|
||||
__m128i se_32 = _mm_setzero_si128();
|
||||
__m128i sev_32 = _mm_setzero_si128();
|
||||
__m128i seh_32 = _mm_setzero_si128();
|
||||
__m128i msa_32 = _mm_setzero_si128();
|
||||
const __m128i z = _mm_setzero_si128();
|
||||
|
||||
// Error is accumulated as a 32 bit value. Looking at HD content with a
|
||||
// height of 1080 lines, or about 67 macro blocks. If the 16 bit row
|
||||
// value is maxed out at 65529 for every row, 65529*1080 = 70777800, which
|
||||
// will not roll over a 32 bit accumulator.
|
||||
// _skipNum is also used to reduce the number of rows
|
||||
for(int32_t i = 0; i < (_height - 2*_border); i += _skipNum)
|
||||
{
|
||||
__m128i se_16 = _mm_setzero_si128();
|
||||
__m128i sev_16 = _mm_setzero_si128();
|
||||
__m128i seh_16 = _mm_setzero_si128();
|
||||
__m128i msa_16 = _mm_setzero_si128();
|
||||
// Error is accumulated as a 32 bit value. Looking at HD content with a
|
||||
// height of 1080 lines, or about 67 macro blocks. If the 16 bit row
|
||||
// value is maxed out at 65529 for every row, 65529*1080 = 70777800, which
|
||||
// will not roll over a 32 bit accumulator.
|
||||
// skip_num_ is also used to reduce the number of rows
|
||||
for (int32_t i = 0; i < (height_ - 2*border_); i += skip_num_) {
|
||||
__m128i se_16 = _mm_setzero_si128();
|
||||
__m128i sev_16 = _mm_setzero_si128();
|
||||
__m128i seh_16 = _mm_setzero_si128();
|
||||
__m128i msa_16 = _mm_setzero_si128();
|
||||
|
||||
// Row error is accumulated as a 16 bit value. There are 8
|
||||
// accumulators. Max value of a 16 bit number is 65529. Looking
|
||||
// at HD content, 1080p, has a width of 1920, 120 macro blocks.
|
||||
// A mb at a time is processed at a time. Absolute max error at
|
||||
// a point would be abs(0-255+255+255+255) which equals 1020.
|
||||
// 120*1020 = 122400. The probability of hitting this is quite low
|
||||
// on well behaved content. A specially crafted image could roll over.
|
||||
// _border could also be adjusted to concentrate on just the center of
|
||||
// the images for an HD capture in order to reduce the possiblity of
|
||||
// rollover.
|
||||
const uint8_t *lineTop = imgBuf - _width + _border;
|
||||
const uint8_t *lineCen = imgBuf + _border;
|
||||
const uint8_t *lineBot = imgBuf + _width + _border;
|
||||
// Row error is accumulated as a 16 bit value. There are 8
|
||||
// accumulators. Max value of a 16 bit number is 65529. Looking
|
||||
// at HD content, 1080p, has a width of 1920, 120 macro blocks.
|
||||
// A mb at a time is processed at a time. Absolute max error at
|
||||
// a point would be abs(0-255+255+255+255) which equals 1020.
|
||||
// 120*1020 = 122400. The probability of hitting this is quite low
|
||||
// on well behaved content. A specially crafted image could roll over.
|
||||
// border_ could also be adjusted to concentrate on just the center of
|
||||
// the images for an HD capture in order to reduce the possiblity of
|
||||
// rollover.
|
||||
const uint8_t *lineTop = imgBuf - width_ + border_;
|
||||
const uint8_t *lineCen = imgBuf + border_;
|
||||
const uint8_t *lineBot = imgBuf + width_ + border_;
|
||||
|
||||
for(int32_t j = 0; j < width_end - _border; j += 16)
|
||||
{
|
||||
const __m128i t = _mm_loadu_si128((__m128i*)(lineTop));
|
||||
const __m128i l = _mm_loadu_si128((__m128i*)(lineCen - 1));
|
||||
const __m128i c = _mm_loadu_si128((__m128i*)(lineCen));
|
||||
const __m128i r = _mm_loadu_si128((__m128i*)(lineCen + 1));
|
||||
const __m128i b = _mm_loadu_si128((__m128i*)(lineBot));
|
||||
for (int32_t j = 0; j < width_end - border_; j += 16) {
|
||||
const __m128i t = _mm_loadu_si128((__m128i*)(lineTop));
|
||||
const __m128i l = _mm_loadu_si128((__m128i*)(lineCen - 1));
|
||||
const __m128i c = _mm_loadu_si128((__m128i*)(lineCen));
|
||||
const __m128i r = _mm_loadu_si128((__m128i*)(lineCen + 1));
|
||||
const __m128i b = _mm_loadu_si128((__m128i*)(lineBot));
|
||||
|
||||
lineTop += 16;
|
||||
lineCen += 16;
|
||||
lineBot += 16;
|
||||
lineTop += 16;
|
||||
lineCen += 16;
|
||||
lineBot += 16;
|
||||
|
||||
// center pixel unpacked
|
||||
__m128i clo = _mm_unpacklo_epi8(c,z);
|
||||
__m128i chi = _mm_unpackhi_epi8(c,z);
|
||||
// center pixel unpacked
|
||||
__m128i clo = _mm_unpacklo_epi8(c,z);
|
||||
__m128i chi = _mm_unpackhi_epi8(c,z);
|
||||
|
||||
// left right pixels unpacked and added together
|
||||
const __m128i lrlo = _mm_add_epi16(_mm_unpacklo_epi8(l,z),
|
||||
_mm_unpacklo_epi8(r,z));
|
||||
const __m128i lrhi = _mm_add_epi16(_mm_unpackhi_epi8(l,z),
|
||||
_mm_unpackhi_epi8(r,z));
|
||||
// left right pixels unpacked and added together
|
||||
const __m128i lrlo = _mm_add_epi16(_mm_unpacklo_epi8(l,z),
|
||||
_mm_unpacklo_epi8(r,z));
|
||||
const __m128i lrhi = _mm_add_epi16(_mm_unpackhi_epi8(l,z),
|
||||
_mm_unpackhi_epi8(r,z));
|
||||
|
||||
// top & bottom pixels unpacked and added together
|
||||
const __m128i tblo = _mm_add_epi16(_mm_unpacklo_epi8(t,z),
|
||||
_mm_unpacklo_epi8(b,z));
|
||||
const __m128i tbhi = _mm_add_epi16(_mm_unpackhi_epi8(t,z),
|
||||
_mm_unpackhi_epi8(b,z));
|
||||
// top & bottom pixels unpacked and added together
|
||||
const __m128i tblo = _mm_add_epi16(_mm_unpacklo_epi8(t,z),
|
||||
_mm_unpacklo_epi8(b,z));
|
||||
const __m128i tbhi = _mm_add_epi16(_mm_unpackhi_epi8(t,z),
|
||||
_mm_unpackhi_epi8(b,z));
|
||||
|
||||
// running sum of all pixels
|
||||
msa_16 = _mm_add_epi16(msa_16, _mm_add_epi16(chi, clo));
|
||||
// running sum of all pixels
|
||||
msa_16 = _mm_add_epi16(msa_16, _mm_add_epi16(chi, clo));
|
||||
|
||||
clo = _mm_slli_epi16(clo, 1);
|
||||
chi = _mm_slli_epi16(chi, 1);
|
||||
const __m128i sevtlo = _mm_subs_epi16(clo, tblo);
|
||||
const __m128i sevthi = _mm_subs_epi16(chi, tbhi);
|
||||
const __m128i sehtlo = _mm_subs_epi16(clo, lrlo);
|
||||
const __m128i sehthi = _mm_subs_epi16(chi, lrhi);
|
||||
clo = _mm_slli_epi16(clo, 1);
|
||||
chi = _mm_slli_epi16(chi, 1);
|
||||
const __m128i sevtlo = _mm_subs_epi16(clo, tblo);
|
||||
const __m128i sevthi = _mm_subs_epi16(chi, tbhi);
|
||||
const __m128i sehtlo = _mm_subs_epi16(clo, lrlo);
|
||||
const __m128i sehthi = _mm_subs_epi16(chi, lrhi);
|
||||
|
||||
clo = _mm_slli_epi16(clo, 1);
|
||||
chi = _mm_slli_epi16(chi, 1);
|
||||
const __m128i setlo = _mm_subs_epi16(clo,
|
||||
_mm_add_epi16(lrlo, tblo));
|
||||
const __m128i sethi = _mm_subs_epi16(chi,
|
||||
_mm_add_epi16(lrhi, tbhi));
|
||||
clo = _mm_slli_epi16(clo, 1);
|
||||
chi = _mm_slli_epi16(chi, 1);
|
||||
const __m128i setlo = _mm_subs_epi16(clo, _mm_add_epi16(lrlo, tblo));
|
||||
const __m128i sethi = _mm_subs_epi16(chi, _mm_add_epi16(lrhi, tbhi));
|
||||
|
||||
// Add to 16 bit running sum
|
||||
se_16 = _mm_add_epi16(se_16,
|
||||
_mm_max_epi16(setlo,
|
||||
_mm_subs_epi16(z, setlo)));
|
||||
se_16 = _mm_add_epi16(se_16,
|
||||
_mm_max_epi16(sethi,
|
||||
_mm_subs_epi16(z, sethi)));
|
||||
sev_16 = _mm_add_epi16(sev_16,
|
||||
_mm_max_epi16(sevtlo,
|
||||
_mm_subs_epi16(z, sevtlo)));
|
||||
sev_16 = _mm_add_epi16(sev_16,
|
||||
_mm_max_epi16(sevthi,
|
||||
_mm_subs_epi16(z, sevthi)));
|
||||
seh_16 = _mm_add_epi16(seh_16,
|
||||
_mm_max_epi16(sehtlo,
|
||||
_mm_subs_epi16(z, sehtlo)));
|
||||
seh_16 = _mm_add_epi16(seh_16,
|
||||
_mm_max_epi16(sehthi,
|
||||
_mm_subs_epi16(z, sehthi)));
|
||||
}
|
||||
|
||||
// Add to 32 bit running sum as to not roll over.
|
||||
se_32 = _mm_add_epi32(se_32,
|
||||
_mm_add_epi32(_mm_unpackhi_epi16(se_16,z),
|
||||
_mm_unpacklo_epi16(se_16,z)));
|
||||
sev_32 = _mm_add_epi32(sev_32,
|
||||
_mm_add_epi32(_mm_unpackhi_epi16(sev_16,z),
|
||||
_mm_unpacklo_epi16(sev_16,z)));
|
||||
seh_32 = _mm_add_epi32(seh_32,
|
||||
_mm_add_epi32(_mm_unpackhi_epi16(seh_16,z),
|
||||
_mm_unpacklo_epi16(seh_16,z)));
|
||||
msa_32 = _mm_add_epi32(msa_32,
|
||||
_mm_add_epi32(_mm_unpackhi_epi16(msa_16,z),
|
||||
_mm_unpacklo_epi16(msa_16,z)));
|
||||
|
||||
imgBuf += _width * _skipNum;
|
||||
// Add to 16 bit running sum
|
||||
se_16 = _mm_add_epi16(se_16, _mm_max_epi16(setlo,
|
||||
_mm_subs_epi16(z, setlo)));
|
||||
se_16 = _mm_add_epi16(se_16, _mm_max_epi16(sethi,
|
||||
_mm_subs_epi16(z, sethi)));
|
||||
sev_16 = _mm_add_epi16(sev_16, _mm_max_epi16(sevtlo,
|
||||
_mm_subs_epi16(z, sevtlo)));
|
||||
sev_16 = _mm_add_epi16(sev_16, _mm_max_epi16(sevthi,
|
||||
_mm_subs_epi16(z, sevthi)));
|
||||
seh_16 = _mm_add_epi16(seh_16, _mm_max_epi16(sehtlo,
|
||||
_mm_subs_epi16(z, sehtlo)));
|
||||
seh_16 = _mm_add_epi16(seh_16, _mm_max_epi16(sehthi,
|
||||
_mm_subs_epi16(z, sehthi)));
|
||||
}
|
||||
|
||||
__m128i se_128;
|
||||
__m128i sev_128;
|
||||
__m128i seh_128;
|
||||
__m128i msa_128;
|
||||
// Add to 32 bit running sum as to not roll over.
|
||||
se_32 = _mm_add_epi32(se_32, _mm_add_epi32(_mm_unpackhi_epi16(se_16,z),
|
||||
_mm_unpacklo_epi16(se_16,z)));
|
||||
sev_32 = _mm_add_epi32(sev_32, _mm_add_epi32(_mm_unpackhi_epi16(sev_16,z),
|
||||
_mm_unpacklo_epi16(sev_16,z)));
|
||||
seh_32 = _mm_add_epi32(seh_32, _mm_add_epi32(_mm_unpackhi_epi16(seh_16,z),
|
||||
_mm_unpacklo_epi16(seh_16,z)));
|
||||
msa_32 = _mm_add_epi32(msa_32, _mm_add_epi32(_mm_unpackhi_epi16(msa_16,z),
|
||||
_mm_unpacklo_epi16(msa_16,z)));
|
||||
|
||||
// bring sums out of vector registers and into integer register
|
||||
// domain, summing them along the way
|
||||
_mm_store_si128 (&se_128,
|
||||
_mm_add_epi64(_mm_unpackhi_epi32(se_32,z),
|
||||
_mm_unpacklo_epi32(se_32,z)));
|
||||
_mm_store_si128 (&sev_128,
|
||||
_mm_add_epi64(_mm_unpackhi_epi32(sev_32,z),
|
||||
_mm_unpacklo_epi32(sev_32,z)));
|
||||
_mm_store_si128 (&seh_128,
|
||||
_mm_add_epi64(_mm_unpackhi_epi32(seh_32,z),
|
||||
_mm_unpacklo_epi32(seh_32,z)));
|
||||
_mm_store_si128 (&msa_128,
|
||||
_mm_add_epi64(_mm_unpackhi_epi32(msa_32,z),
|
||||
_mm_unpacklo_epi32(msa_32,z)));
|
||||
imgBuf += width_ * skip_num_;
|
||||
}
|
||||
|
||||
uint64_t *se_64 =
|
||||
reinterpret_cast<uint64_t*>(&se_128);
|
||||
uint64_t *sev_64 =
|
||||
reinterpret_cast<uint64_t*>(&sev_128);
|
||||
uint64_t *seh_64 =
|
||||
reinterpret_cast<uint64_t*>(&seh_128);
|
||||
uint64_t *msa_64 =
|
||||
reinterpret_cast<uint64_t*>(&msa_128);
|
||||
__m128i se_128;
|
||||
__m128i sev_128;
|
||||
__m128i seh_128;
|
||||
__m128i msa_128;
|
||||
|
||||
const uint32_t spatialErrSum = se_64[0] + se_64[1];
|
||||
const uint32_t spatialErrVSum = sev_64[0] + sev_64[1];
|
||||
const uint32_t spatialErrHSum = seh_64[0] + seh_64[1];
|
||||
const uint32_t pixelMSA = msa_64[0] + msa_64[1];
|
||||
// Bring sums out of vector registers and into integer register
|
||||
// domain, summing them along the way.
|
||||
_mm_store_si128 (&se_128, _mm_add_epi64(_mm_unpackhi_epi32(se_32,z),
|
||||
_mm_unpacklo_epi32(se_32,z)));
|
||||
_mm_store_si128 (&sev_128, _mm_add_epi64(_mm_unpackhi_epi32(sev_32,z),
|
||||
_mm_unpacklo_epi32(sev_32,z)));
|
||||
_mm_store_si128 (&seh_128, _mm_add_epi64(_mm_unpackhi_epi32(seh_32,z),
|
||||
_mm_unpacklo_epi32(seh_32,z)));
|
||||
_mm_store_si128 (&msa_128, _mm_add_epi64(_mm_unpackhi_epi32(msa_32,z),
|
||||
_mm_unpacklo_epi32(msa_32,z)));
|
||||
|
||||
// normalize over all pixels
|
||||
const float spatialErr = (float)(spatialErrSum >> 2);
|
||||
const float spatialErrH = (float)(spatialErrHSum >> 1);
|
||||
const float spatialErrV = (float)(spatialErrVSum >> 1);
|
||||
const float norm = (float)pixelMSA;
|
||||
uint64_t *se_64 = reinterpret_cast<uint64_t*>(&se_128);
|
||||
uint64_t *sev_64 = reinterpret_cast<uint64_t*>(&sev_128);
|
||||
uint64_t *seh_64 = reinterpret_cast<uint64_t*>(&seh_128);
|
||||
uint64_t *msa_64 = reinterpret_cast<uint64_t*>(&msa_128);
|
||||
|
||||
// 2X2:
|
||||
_spatialPredErr = spatialErr / norm;
|
||||
const uint32_t spatialErrSum = se_64[0] + se_64[1];
|
||||
const uint32_t spatialErrVSum = sev_64[0] + sev_64[1];
|
||||
const uint32_t spatialErrHSum = seh_64[0] + seh_64[1];
|
||||
const uint32_t pixelMSA = msa_64[0] + msa_64[1];
|
||||
|
||||
// 1X2:
|
||||
_spatialPredErrH = spatialErrH / norm;
|
||||
// Normalize over all pixels.
|
||||
const float spatialErr = (float)(spatialErrSum >> 2);
|
||||
const float spatialErrH = (float)(spatialErrHSum >> 1);
|
||||
const float spatialErrV = (float)(spatialErrVSum >> 1);
|
||||
const float norm = (float)pixelMSA;
|
||||
|
||||
// 2X1:
|
||||
_spatialPredErrV = spatialErrV / norm;
|
||||
// 2X2:
|
||||
spatial_pred_err_ = spatialErr / norm;
|
||||
|
||||
// 1X2:
|
||||
spatial_pred_err_h_ = spatialErrH / norm;
|
||||
|
||||
// 2X1:
|
||||
spatial_pred_err_v_ = spatialErrV / norm;
|
||||
|
||||
return VPM_OK;
|
||||
}
|
||||
|
@ -20,431 +20,386 @@
|
||||
namespace webrtc {
|
||||
|
||||
// Detection constants
|
||||
enum { kFrequencyDeviation = 39 }; // (Q4) Maximum allowed deviation for detection
|
||||
enum { kMinFrequencyToDetect = 32 }; // (Q4) Minimum frequency that can be detected
|
||||
enum { kNumFlickerBeforeDetect = 2 }; // Number of flickers before we accept detection
|
||||
enum { kMeanValueScaling = 4 }; // (Q4) In power of 2
|
||||
enum { kZeroCrossingDeadzone = 10 }; // Deadzone region in terms of pixel values
|
||||
|
||||
// Deflickering constants
|
||||
// (Q4) Maximum allowed deviation for detection.
|
||||
enum { kFrequencyDeviation = 39 };
|
||||
// (Q4) Minimum frequency that can be detected.
|
||||
enum { kMinFrequencyToDetect = 32 };
|
||||
// Number of flickers before we accept detection
|
||||
enum { kNumFlickerBeforeDetect = 2 };
|
||||
enum { kmean_valueScaling = 4 }; // (Q4) In power of 2
|
||||
// Dead-zone region in terms of pixel values
|
||||
enum { kZeroCrossingDeadzone = 10 };
|
||||
// Deflickering constants.
|
||||
// Compute the quantiles over 1 / DownsamplingFactor of the image.
|
||||
enum { kDownsamplingFactor = 8 };
|
||||
enum { kLog2OfDownsamplingFactor = 3 };
|
||||
|
||||
// To generate in Matlab:
|
||||
// >> probUW16 = round(2^11 * [0.05,0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,0.95,0.97]);
|
||||
// >> probUW16 = round(2^11 *
|
||||
// [0.05,0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,0.95,0.97]);
|
||||
// >> fprintf('%d, ', probUW16)
|
||||
// Resolution reduced to avoid overflow when multiplying with the (potentially) large
|
||||
// number of pixels.
|
||||
const uint16_t VPMDeflickering::_probUW16[kNumProbs] =
|
||||
{102, 205, 410, 614, 819, 1024, 1229, 1434, 1638, 1843, 1946, 1987}; // <Q11>
|
||||
// Resolution reduced to avoid overflow when multiplying with the
|
||||
// (potentially) large number of pixels.
|
||||
const uint16_t VPMDeflickering::prob_uw16_[kNumProbs] = {102, 205, 410, 614,
|
||||
819, 1024, 1229, 1434, 1638, 1843, 1946, 1987}; // <Q11>
|
||||
|
||||
// To generate in Matlab:
|
||||
// >> numQuants = 14; maxOnlyLength = 5;
|
||||
// >> weightUW16 = round(2^15 * [linspace(0.5, 1.0, numQuants - maxOnlyLength)]);
|
||||
// >> weightUW16 = round(2^15 *
|
||||
// [linspace(0.5, 1.0, numQuants - maxOnlyLength)]);
|
||||
// >> fprintf('%d, %d,\n ', weightUW16);
|
||||
const uint16_t VPMDeflickering::_weightUW16[kNumQuants - kMaxOnlyLength] =
|
||||
const uint16_t VPMDeflickering::weight_uw16_[kNumQuants - kMaxOnlyLength] =
|
||||
{16384, 18432, 20480, 22528, 24576, 26624, 28672, 30720, 32768}; // <Q15>
|
||||
|
||||
VPMDeflickering::VPMDeflickering() :
|
||||
_id(0)
|
||||
{
|
||||
Reset();
|
||||
|
||||
VPMDeflickering::VPMDeflickering()
|
||||
: id_(0) {
|
||||
Reset();
|
||||
}
|
||||
|
||||
VPMDeflickering::~VPMDeflickering()
|
||||
{
|
||||
VPMDeflickering::~VPMDeflickering() {}
|
||||
|
||||
int32_t VPMDeflickering::ChangeUniqueId(const int32_t id) {
|
||||
id_ = id;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t
|
||||
VPMDeflickering::ChangeUniqueId(const int32_t id)
|
||||
{
|
||||
_id = id;
|
||||
void VPMDeflickering::Reset() {
|
||||
mean_buffer_length_ = 0;
|
||||
detection_state_ = 0;
|
||||
frame_rate_ = 0;
|
||||
|
||||
memset(mean_buffer_, 0, sizeof(int32_t) * kMeanBufferLength);
|
||||
memset(timestamp_buffer_, 0, sizeof(int32_t) * kMeanBufferLength);
|
||||
|
||||
// Initialize the history with a uniformly distributed histogram.
|
||||
quant_hist_uw8_[0][0] = 0;
|
||||
quant_hist_uw8_[0][kNumQuants - 1] = 255;
|
||||
for (int32_t i = 0; i < kNumProbs; i++) {
|
||||
quant_hist_uw8_[0][i + 1] = static_cast<uint8_t>((WEBRTC_SPL_UMUL_16_16(
|
||||
prob_uw16_[i], 255) + (1 << 10)) >> 11); // Unsigned round. <Q0>
|
||||
}
|
||||
|
||||
for (int32_t i = 1; i < kFrameHistory_size; i++) {
|
||||
memcpy(quant_hist_uw8_[i], quant_hist_uw8_[0],
|
||||
sizeof(uint8_t) * kNumQuants);
|
||||
}
|
||||
}
|
||||
|
||||
int32_t VPMDeflickering::ProcessFrame(I420VideoFrame* frame,
|
||||
VideoProcessingModule::FrameStats* stats) {
|
||||
assert(frame);
|
||||
uint32_t frame_memory;
|
||||
uint8_t quant_uw8[kNumQuants];
|
||||
uint8_t maxquant_uw8[kNumQuants];
|
||||
uint8_t minquant_uw8[kNumQuants];
|
||||
uint16_t target_quant_uw16[kNumQuants];
|
||||
uint16_t increment_uw16;
|
||||
uint8_t map_uw8[256];
|
||||
|
||||
uint16_t tmp_uw16;
|
||||
uint32_t tmp_uw32;
|
||||
int width = frame->width();
|
||||
int height = frame->height();
|
||||
|
||||
if (frame->IsZeroSize()) {
|
||||
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoPreocessing, id_,
|
||||
"Null frame pointer");
|
||||
return VPM_GENERAL_ERROR;
|
||||
}
|
||||
|
||||
// Stricter height check due to subsampling size calculation below.
|
||||
if (height < 2) {
|
||||
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoPreocessing, id_,
|
||||
"Invalid frame size");
|
||||
return VPM_GENERAL_ERROR;
|
||||
}
|
||||
|
||||
if (!VideoProcessingModule::ValidFrameStats(*stats)) {
|
||||
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoPreocessing, id_,
|
||||
"Invalid frame stats");
|
||||
return VPM_GENERAL_ERROR;
|
||||
}
|
||||
|
||||
if (PreDetection(frame->timestamp(), *stats) == -1) return VPM_GENERAL_ERROR;
|
||||
|
||||
// Flicker detection
|
||||
int32_t det_flicker = DetectFlicker();
|
||||
if (det_flicker < 0) {
|
||||
return VPM_GENERAL_ERROR;
|
||||
} else if (det_flicker != 1) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
VPMDeflickering::Reset()
|
||||
{
|
||||
_meanBufferLength = 0;
|
||||
_detectionState = 0;
|
||||
_frameRate = 0;
|
||||
// Size of luminance component.
|
||||
const uint32_t y_size = height * width;
|
||||
|
||||
memset(_meanBuffer, 0, sizeof(int32_t) * kMeanBufferLength);
|
||||
memset(_timestampBuffer, 0, sizeof(int32_t) * kMeanBufferLength);
|
||||
const uint32_t y_sub_size = width * (((height - 1) >>
|
||||
kLog2OfDownsamplingFactor) + 1);
|
||||
uint8_t* y_sorted = new uint8_t[y_sub_size];
|
||||
uint32_t sort_row_idx = 0;
|
||||
for (int i = 0; i < height; i += kDownsamplingFactor) {
|
||||
memcpy(y_sorted + sort_row_idx * width,
|
||||
frame->buffer(kYPlane) + i * width, width);
|
||||
sort_row_idx++;
|
||||
}
|
||||
|
||||
// Initialize the history with a uniformly distributed histogram
|
||||
_quantHistUW8[0][0] = 0;
|
||||
_quantHistUW8[0][kNumQuants - 1] = 255;
|
||||
for (int32_t i = 0; i < kNumProbs; i++)
|
||||
{
|
||||
_quantHistUW8[0][i + 1] = static_cast<uint8_t>((WEBRTC_SPL_UMUL_16_16(
|
||||
_probUW16[i], 255) + (1 << 10)) >> 11); // Unsigned round. <Q0>
|
||||
webrtc::Sort(y_sorted, y_sub_size, webrtc::TYPE_UWord8);
|
||||
|
||||
uint32_t prob_idx_uw32 = 0;
|
||||
quant_uw8[0] = 0;
|
||||
quant_uw8[kNumQuants - 1] = 255;
|
||||
|
||||
// Ensure we won't get an overflow below.
|
||||
// In practice, the number of subsampled pixels will not become this large.
|
||||
if (y_sub_size > (1 << 21) - 1) {
|
||||
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoPreocessing, id_,
|
||||
"Subsampled number of pixels too large");
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (int32_t i = 0; i < kNumProbs; i++) {
|
||||
// <Q0>.
|
||||
prob_idx_uw32 = WEBRTC_SPL_UMUL_32_16(y_sub_size, prob_uw16_[i]) >> 11;
|
||||
quant_uw8[i + 1] = y_sorted[prob_idx_uw32];
|
||||
}
|
||||
|
||||
delete [] y_sorted;
|
||||
y_sorted = NULL;
|
||||
|
||||
// Shift history for new frame.
|
||||
memmove(quant_hist_uw8_[1], quant_hist_uw8_[0],
|
||||
(kFrameHistory_size - 1) * kNumQuants * sizeof(uint8_t));
|
||||
// Store current frame in history.
|
||||
memcpy(quant_hist_uw8_[0], quant_uw8, kNumQuants * sizeof(uint8_t));
|
||||
|
||||
// We use a frame memory equal to the ceiling of half the frame rate to
|
||||
// ensure we capture an entire period of flicker.
|
||||
frame_memory = (frame_rate_ + (1 << 5)) >> 5; // Unsigned ceiling. <Q0>
|
||||
// frame_rate_ in Q4.
|
||||
if (frame_memory > kFrameHistory_size) {
|
||||
frame_memory = kFrameHistory_size;
|
||||
}
|
||||
|
||||
// Get maximum and minimum.
|
||||
for (int32_t i = 0; i < kNumQuants; i++) {
|
||||
maxquant_uw8[i] = 0;
|
||||
minquant_uw8[i] = 255;
|
||||
for (uint32_t j = 0; j < frame_memory; j++) {
|
||||
if (quant_hist_uw8_[j][i] > maxquant_uw8[i]) {
|
||||
maxquant_uw8[i] = quant_hist_uw8_[j][i];
|
||||
}
|
||||
|
||||
if (quant_hist_uw8_[j][i] < minquant_uw8[i]) {
|
||||
minquant_uw8[i] = quant_hist_uw8_[j][i];
|
||||
}
|
||||
}
|
||||
|
||||
for (int32_t i = 1; i < kFrameHistorySize; i++)
|
||||
{
|
||||
memcpy(_quantHistUW8[i], _quantHistUW8[0], sizeof(uint8_t) * kNumQuants);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int32_t
|
||||
VPMDeflickering::ProcessFrame(I420VideoFrame* frame,
|
||||
VideoProcessingModule::FrameStats* stats)
|
||||
{
|
||||
assert(frame);
|
||||
uint32_t frameMemory;
|
||||
uint8_t quantUW8[kNumQuants];
|
||||
uint8_t maxQuantUW8[kNumQuants];
|
||||
uint8_t minQuantUW8[kNumQuants];
|
||||
uint16_t targetQuantUW16[kNumQuants];
|
||||
uint16_t incrementUW16;
|
||||
uint8_t mapUW8[256];
|
||||
// Get target quantiles.
|
||||
for (int32_t i = 0; i < kNumQuants - kMaxOnlyLength; i++) {
|
||||
target_quant_uw16[i] = static_cast<uint16_t>((WEBRTC_SPL_UMUL_16_16(
|
||||
weight_uw16_[i], maxquant_uw8[i]) + WEBRTC_SPL_UMUL_16_16((1 << 15) -
|
||||
weight_uw16_[i], minquant_uw8[i])) >> 8); // <Q7>
|
||||
}
|
||||
|
||||
uint16_t tmpUW16;
|
||||
uint32_t tmpUW32;
|
||||
int width = frame->width();
|
||||
int height = frame->height();
|
||||
for (int32_t i = kNumQuants - kMaxOnlyLength; i < kNumQuants; i++) {
|
||||
target_quant_uw16[i] = ((uint16_t)maxquant_uw8[i]) << 7;
|
||||
}
|
||||
|
||||
if (frame->IsZeroSize())
|
||||
{
|
||||
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoPreocessing, _id,
|
||||
"Null frame pointer");
|
||||
return VPM_GENERAL_ERROR;
|
||||
// Compute the map from input to output pixels.
|
||||
uint16_t mapUW16; // <Q7>
|
||||
for (int32_t i = 1; i < kNumQuants; i++) {
|
||||
// As quant and targetQuant are limited to UWord8, it's safe to use Q7 here.
|
||||
tmp_uw32 = static_cast<uint32_t>(target_quant_uw16[i] -
|
||||
target_quant_uw16[i - 1]);
|
||||
tmp_uw16 = static_cast<uint16_t>(quant_uw8[i] - quant_uw8[i - 1]); // <Q0>
|
||||
|
||||
if (tmp_uw16 > 0) {
|
||||
increment_uw16 = static_cast<uint16_t>(WebRtcSpl_DivU32U16(tmp_uw32,
|
||||
tmp_uw16)); // <Q7>
|
||||
} else {
|
||||
// The value is irrelevant; the loop below will only iterate once.
|
||||
increment_uw16 = 0;
|
||||
}
|
||||
|
||||
// Stricter height check due to subsampling size calculation below.
|
||||
if (height < 2)
|
||||
{
|
||||
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoPreocessing, _id,
|
||||
"Invalid frame size");
|
||||
return VPM_GENERAL_ERROR;
|
||||
mapUW16 = target_quant_uw16[i - 1];
|
||||
for (uint32_t j = quant_uw8[i - 1]; j < (uint32_t)(quant_uw8[i] + 1); j++) {
|
||||
// Unsigned round. <Q0>
|
||||
map_uw8[j] = (uint8_t)((mapUW16 + (1 << 6)) >> 7);
|
||||
mapUW16 += increment_uw16;
|
||||
}
|
||||
}
|
||||
|
||||
if (!VideoProcessingModule::ValidFrameStats(*stats))
|
||||
{
|
||||
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoPreocessing, _id,
|
||||
"Invalid frame stats");
|
||||
return VPM_GENERAL_ERROR;
|
||||
}
|
||||
// Map to the output frame.
|
||||
uint8_t* buffer = frame->buffer(kYPlane);
|
||||
for (uint32_t i = 0; i < y_size; i++) {
|
||||
buffer[i] = map_uw8[buffer[i]];
|
||||
}
|
||||
|
||||
if (PreDetection(frame->timestamp(), *stats) == -1)
|
||||
{
|
||||
return VPM_GENERAL_ERROR;
|
||||
}
|
||||
// Frame was altered, so reset stats.
|
||||
VideoProcessingModule::ClearFrameStats(stats);
|
||||
|
||||
// Flicker detection
|
||||
int32_t detFlicker = DetectFlicker();
|
||||
if (detFlicker < 0)
|
||||
{ // Error
|
||||
return VPM_GENERAL_ERROR;
|
||||
}
|
||||
else if (detFlicker != 1)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Size of luminance component
|
||||
const uint32_t ySize = height * width;
|
||||
|
||||
const uint32_t ySubSize = width * (((height - 1) >>
|
||||
kLog2OfDownsamplingFactor) + 1);
|
||||
uint8_t* ySorted = new uint8_t[ySubSize];
|
||||
uint32_t sortRowIdx = 0;
|
||||
for (int i = 0; i < height; i += kDownsamplingFactor)
|
||||
{
|
||||
memcpy(ySorted + sortRowIdx * width,
|
||||
frame->buffer(kYPlane) + i * width, width);
|
||||
sortRowIdx++;
|
||||
}
|
||||
|
||||
webrtc::Sort(ySorted, ySubSize, webrtc::TYPE_UWord8);
|
||||
|
||||
uint32_t probIdxUW32 = 0;
|
||||
quantUW8[0] = 0;
|
||||
quantUW8[kNumQuants - 1] = 255;
|
||||
|
||||
// Ensure we won't get an overflow below.
|
||||
// In practice, the number of subsampled pixels will not become this large.
|
||||
if (ySubSize > (1 << 21) - 1)
|
||||
{
|
||||
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoPreocessing, _id,
|
||||
"Subsampled number of pixels too large");
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (int32_t i = 0; i < kNumProbs; i++)
|
||||
{
|
||||
probIdxUW32 = WEBRTC_SPL_UMUL_32_16(ySubSize, _probUW16[i]) >> 11; // <Q0>
|
||||
quantUW8[i + 1] = ySorted[probIdxUW32];
|
||||
}
|
||||
|
||||
delete [] ySorted;
|
||||
ySorted = NULL;
|
||||
|
||||
// Shift history for new frame.
|
||||
memmove(_quantHistUW8[1], _quantHistUW8[0], (kFrameHistorySize - 1) * kNumQuants *
|
||||
sizeof(uint8_t));
|
||||
// Store current frame in history.
|
||||
memcpy(_quantHistUW8[0], quantUW8, kNumQuants * sizeof(uint8_t));
|
||||
|
||||
// We use a frame memory equal to the ceiling of half the frame rate to ensure we
|
||||
// capture an entire period of flicker.
|
||||
frameMemory = (_frameRate + (1 << 5)) >> 5; // Unsigned ceiling. <Q0>
|
||||
// _frameRate in Q4.
|
||||
if (frameMemory > kFrameHistorySize)
|
||||
{
|
||||
frameMemory = kFrameHistorySize;
|
||||
}
|
||||
|
||||
// Get maximum and minimum.
|
||||
for (int32_t i = 0; i < kNumQuants; i++)
|
||||
{
|
||||
maxQuantUW8[i] = 0;
|
||||
minQuantUW8[i] = 255;
|
||||
for (uint32_t j = 0; j < frameMemory; j++)
|
||||
{
|
||||
if (_quantHistUW8[j][i] > maxQuantUW8[i])
|
||||
{
|
||||
maxQuantUW8[i] = _quantHistUW8[j][i];
|
||||
}
|
||||
|
||||
if (_quantHistUW8[j][i] < minQuantUW8[i])
|
||||
{
|
||||
minQuantUW8[i] = _quantHistUW8[j][i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get target quantiles.
|
||||
for (int32_t i = 0; i < kNumQuants - kMaxOnlyLength; i++)
|
||||
{
|
||||
targetQuantUW16[i] = static_cast<uint16_t>((WEBRTC_SPL_UMUL_16_16(
|
||||
_weightUW16[i], maxQuantUW8[i]) + WEBRTC_SPL_UMUL_16_16((1 << 15) -
|
||||
_weightUW16[i], minQuantUW8[i])) >> 8); // <Q7>
|
||||
}
|
||||
|
||||
for (int32_t i = kNumQuants - kMaxOnlyLength; i < kNumQuants; i++)
|
||||
{
|
||||
targetQuantUW16[i] = ((uint16_t)maxQuantUW8[i]) << 7;
|
||||
}
|
||||
|
||||
// Compute the map from input to output pixels.
|
||||
uint16_t mapUW16; // <Q7>
|
||||
for (int32_t i = 1; i < kNumQuants; i++)
|
||||
{
|
||||
// As quant and targetQuant are limited to UWord8, we're safe to use Q7 here.
|
||||
tmpUW32 = static_cast<uint32_t>(targetQuantUW16[i] -
|
||||
targetQuantUW16[i - 1]); // <Q7>
|
||||
tmpUW16 = static_cast<uint16_t>(quantUW8[i] - quantUW8[i - 1]); // <Q0>
|
||||
|
||||
if (tmpUW16 > 0)
|
||||
{
|
||||
incrementUW16 = static_cast<uint16_t>(WebRtcSpl_DivU32U16(tmpUW32,
|
||||
tmpUW16)); // <Q7>
|
||||
}
|
||||
else
|
||||
{
|
||||
// The value is irrelevant; the loop below will only iterate once.
|
||||
incrementUW16 = 0;
|
||||
}
|
||||
|
||||
mapUW16 = targetQuantUW16[i - 1];
|
||||
for (uint32_t j = quantUW8[i - 1]; j < (uint32_t)(quantUW8[i] + 1); j++)
|
||||
{
|
||||
mapUW8[j] = (uint8_t)((mapUW16 + (1 << 6)) >> 7); // Unsigned round. <Q0>
|
||||
mapUW16 += incrementUW16;
|
||||
}
|
||||
}
|
||||
|
||||
// Map to the output frame.
|
||||
uint8_t* buffer = frame->buffer(kYPlane);
|
||||
for (uint32_t i = 0; i < ySize; i++)
|
||||
{
|
||||
buffer[i] = mapUW8[buffer[i]];
|
||||
}
|
||||
|
||||
// Frame was altered, so reset stats.
|
||||
VideoProcessingModule::ClearFrameStats(stats);
|
||||
|
||||
return 0;
|
||||
return VPM_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
Performs some pre-detection operations. Must be called before
|
||||
Performs some pre-detection operations. Must be called before
|
||||
DetectFlicker().
|
||||
|
||||
\param[in] timestamp Timestamp of the current frame.
|
||||
\param[in] stats Statistics of the current frame.
|
||||
|
||||
|
||||
\return 0: Success\n
|
||||
2: Detection not possible due to flickering frequency too close to
|
||||
zero.\n
|
||||
-1: Error
|
||||
*/
|
||||
int32_t
|
||||
VPMDeflickering::PreDetection(const uint32_t timestamp,
|
||||
const VideoProcessingModule::FrameStats& stats)
|
||||
{
|
||||
int32_t meanVal; // Mean value of frame (Q4)
|
||||
uint32_t frameRate = 0;
|
||||
int32_t meanBufferLength; // Temp variable
|
||||
int32_t VPMDeflickering::PreDetection(const uint32_t timestamp,
|
||||
const VideoProcessingModule::FrameStats& stats) {
|
||||
int32_t mean_val; // Mean value of frame (Q4)
|
||||
uint32_t frame_rate = 0;
|
||||
int32_t meanBufferLength; // Temp variable.
|
||||
|
||||
meanVal = ((stats.sum << kMeanValueScaling) / stats.numPixels);
|
||||
/* Update mean value buffer.
|
||||
* This should be done even though we might end up in an unreliable detection.
|
||||
mean_val = ((stats.sum << kmean_valueScaling) / stats.num_pixels);
|
||||
// Update mean value buffer.
|
||||
// This should be done even though we might end up in an unreliable detection.
|
||||
memmove(mean_buffer_ + 1, mean_buffer_,
|
||||
(kMeanBufferLength - 1) * sizeof(int32_t));
|
||||
mean_buffer_[0] = mean_val;
|
||||
|
||||
// Update timestamp buffer.
|
||||
// This should be done even though we might end up in an unreliable detection.
|
||||
memmove(timestamp_buffer_ + 1, timestamp_buffer_, (kMeanBufferLength - 1) *
|
||||
sizeof(uint32_t));
|
||||
timestamp_buffer_[0] = timestamp;
|
||||
|
||||
/* Compute current frame rate (Q4) */
|
||||
if (timestamp_buffer_[kMeanBufferLength - 1] != 0) {
|
||||
frame_rate = ((90000 << 4) * (kMeanBufferLength - 1));
|
||||
frame_rate /=
|
||||
(timestamp_buffer_[0] - timestamp_buffer_[kMeanBufferLength - 1]);
|
||||
} else if (timestamp_buffer_[1] != 0) {
|
||||
frame_rate = (90000 << 4) / (timestamp_buffer_[0] - timestamp_buffer_[1]);
|
||||
}
|
||||
|
||||
/* Determine required size of mean value buffer (mean_buffer_length_) */
|
||||
if (frame_rate == 0) {
|
||||
meanBufferLength = 1;
|
||||
} else {
|
||||
meanBufferLength =
|
||||
(kNumFlickerBeforeDetect * frame_rate) / kMinFrequencyToDetect;
|
||||
}
|
||||
/* Sanity check of buffer length */
|
||||
if (meanBufferLength >= kMeanBufferLength) {
|
||||
/* Too long buffer. The flickering frequency is too close to zero, which
|
||||
* makes the estimation unreliable.
|
||||
*/
|
||||
memmove(_meanBuffer + 1, _meanBuffer, (kMeanBufferLength - 1) * sizeof(int32_t));
|
||||
_meanBuffer[0] = meanVal;
|
||||
mean_buffer_length_ = 0;
|
||||
return 2;
|
||||
}
|
||||
mean_buffer_length_ = meanBufferLength;
|
||||
|
||||
/* Update timestamp buffer.
|
||||
* This should be done even though we might end up in an unreliable detection.
|
||||
*/
|
||||
memmove(_timestampBuffer + 1, _timestampBuffer, (kMeanBufferLength - 1) *
|
||||
sizeof(uint32_t));
|
||||
_timestampBuffer[0] = timestamp;
|
||||
if ((timestamp_buffer_[mean_buffer_length_ - 1] != 0) &&
|
||||
(mean_buffer_length_ != 1)) {
|
||||
frame_rate = ((90000 << 4) * (mean_buffer_length_ - 1));
|
||||
frame_rate /=
|
||||
(timestamp_buffer_[0] - timestamp_buffer_[mean_buffer_length_ - 1]);
|
||||
} else if (timestamp_buffer_[1] != 0) {
|
||||
frame_rate = (90000 << 4) / (timestamp_buffer_[0] - timestamp_buffer_[1]);
|
||||
}
|
||||
frame_rate_ = frame_rate;
|
||||
|
||||
/* Compute current frame rate (Q4) */
|
||||
if (_timestampBuffer[kMeanBufferLength - 1] != 0)
|
||||
{
|
||||
frameRate = ((90000 << 4) * (kMeanBufferLength - 1));
|
||||
frameRate /= (_timestampBuffer[0] - _timestampBuffer[kMeanBufferLength - 1]);
|
||||
}else if (_timestampBuffer[1] != 0)
|
||||
{
|
||||
frameRate = (90000 << 4) / (_timestampBuffer[0] - _timestampBuffer[1]);
|
||||
}
|
||||
|
||||
/* Determine required size of mean value buffer (_meanBufferLength) */
|
||||
if (frameRate == 0) {
|
||||
meanBufferLength = 1;
|
||||
}
|
||||
else {
|
||||
meanBufferLength = (kNumFlickerBeforeDetect * frameRate) / kMinFrequencyToDetect;
|
||||
}
|
||||
/* Sanity check of buffer length */
|
||||
if (meanBufferLength >= kMeanBufferLength)
|
||||
{
|
||||
/* Too long buffer. The flickering frequency is too close to zero, which
|
||||
* makes the estimation unreliable.
|
||||
*/
|
||||
_meanBufferLength = 0;
|
||||
return 2;
|
||||
}
|
||||
_meanBufferLength = meanBufferLength;
|
||||
|
||||
if ((_timestampBuffer[_meanBufferLength - 1] != 0) && (_meanBufferLength != 1))
|
||||
{
|
||||
frameRate = ((90000 << 4) * (_meanBufferLength - 1));
|
||||
frameRate /= (_timestampBuffer[0] - _timestampBuffer[_meanBufferLength - 1]);
|
||||
}else if (_timestampBuffer[1] != 0)
|
||||
{
|
||||
frameRate = (90000 << 4) / (_timestampBuffer[0] - _timestampBuffer[1]);
|
||||
}
|
||||
_frameRate = frameRate;
|
||||
|
||||
return 0;
|
||||
return VPM_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
This function detects flicker in the video stream. As a side effect the mean value
|
||||
buffer is updated with the new mean value.
|
||||
|
||||
This function detects flicker in the video stream. As a side effect the
|
||||
mean value buffer is updated with the new mean value.
|
||||
|
||||
\return 0: No flickering detected\n
|
||||
1: Flickering detected\n
|
||||
2: Detection not possible due to unreliable frequency interval
|
||||
-1: Error
|
||||
*/
|
||||
int32_t VPMDeflickering::DetectFlicker()
|
||||
{
|
||||
/* Local variables */
|
||||
uint32_t i;
|
||||
int32_t freqEst; // (Q4) Frequency estimate to base detection upon
|
||||
int32_t retVal = -1;
|
||||
int32_t VPMDeflickering::DetectFlicker() {
|
||||
uint32_t i;
|
||||
int32_t freqEst; // (Q4) Frequency estimate to base detection upon
|
||||
int32_t ret_val = -1;
|
||||
|
||||
/* Sanity check for _meanBufferLength */
|
||||
if (_meanBufferLength < 2)
|
||||
{
|
||||
/* Not possible to estimate frequency */
|
||||
return(2);
|
||||
}
|
||||
/* Count zero crossings with a dead zone to be robust against noise.
|
||||
* If the noise std is 2 pixel this corresponds to about 95% confidence interval.
|
||||
*/
|
||||
int32_t deadzone = (kZeroCrossingDeadzone << kMeanValueScaling); // Q4
|
||||
int32_t meanOfBuffer = 0; // Mean value of mean value buffer
|
||||
int32_t numZeros = 0; // Number of zeros that cross the deadzone
|
||||
int32_t cntState = 0; // State variable for zero crossing regions
|
||||
int32_t cntStateOld = 0; // Previous state variable for zero crossing regions
|
||||
/* Sanity check for mean_buffer_length_ */
|
||||
if (mean_buffer_length_ < 2) {
|
||||
/* Not possible to estimate frequency */
|
||||
return(2);
|
||||
}
|
||||
// Count zero crossings with a dead zone to be robust against noise. If the
|
||||
// noise std is 2 pixel this corresponds to about 95% confidence interval.
|
||||
int32_t deadzone = (kZeroCrossingDeadzone << kmean_valueScaling); // Q4
|
||||
int32_t meanOfBuffer = 0; // Mean value of mean value buffer.
|
||||
int32_t numZeros = 0; // Number of zeros that cross the dead-zone.
|
||||
int32_t cntState = 0; // State variable for zero crossing regions.
|
||||
int32_t cntStateOld = 0; // Previous state for zero crossing regions.
|
||||
|
||||
for (i = 0; i < _meanBufferLength; i++)
|
||||
{
|
||||
meanOfBuffer += _meanBuffer[i];
|
||||
}
|
||||
meanOfBuffer += (_meanBufferLength >> 1); // Rounding, not truncation
|
||||
meanOfBuffer /= _meanBufferLength;
|
||||
for (i = 0; i < mean_buffer_length_; i++) {
|
||||
meanOfBuffer += mean_buffer_[i];
|
||||
}
|
||||
meanOfBuffer += (mean_buffer_length_ >> 1); // Rounding, not truncation.
|
||||
meanOfBuffer /= mean_buffer_length_;
|
||||
|
||||
/* Count zero crossings */
|
||||
cntStateOld = (_meanBuffer[0] >= (meanOfBuffer + deadzone));
|
||||
cntStateOld -= (_meanBuffer[0] <= (meanOfBuffer - deadzone));
|
||||
for (i = 1; i < _meanBufferLength; i++)
|
||||
{
|
||||
cntState = (_meanBuffer[i] >= (meanOfBuffer + deadzone));
|
||||
cntState -= (_meanBuffer[i] <= (meanOfBuffer - deadzone));
|
||||
if (cntStateOld == 0)
|
||||
{
|
||||
cntStateOld = -cntState;
|
||||
}
|
||||
if (((cntState + cntStateOld) == 0) && (cntState != 0))
|
||||
{
|
||||
numZeros++;
|
||||
cntStateOld = cntState;
|
||||
}
|
||||
// Count zero crossings.
|
||||
cntStateOld = (mean_buffer_[0] >= (meanOfBuffer + deadzone));
|
||||
cntStateOld -= (mean_buffer_[0] <= (meanOfBuffer - deadzone));
|
||||
for (i = 1; i < mean_buffer_length_; i++) {
|
||||
cntState = (mean_buffer_[i] >= (meanOfBuffer + deadzone));
|
||||
cntState -= (mean_buffer_[i] <= (meanOfBuffer - deadzone));
|
||||
if (cntStateOld == 0) {
|
||||
cntStateOld = -cntState;
|
||||
}
|
||||
/* END count zero crossings */
|
||||
if (((cntState + cntStateOld) == 0) && (cntState != 0)) {
|
||||
numZeros++;
|
||||
cntStateOld = cntState;
|
||||
}
|
||||
}
|
||||
// END count zero crossings.
|
||||
|
||||
/* Frequency estimation according to:
|
||||
* freqEst = numZeros * frameRate / 2 / _meanBufferLength;
|
||||
*
|
||||
* Resolution is set to Q4
|
||||
*/
|
||||
freqEst = ((numZeros * 90000) << 3);
|
||||
freqEst /= (_timestampBuffer[0] - _timestampBuffer[_meanBufferLength - 1]);
|
||||
/* Frequency estimation according to:
|
||||
* freqEst = numZeros * frame_rate / 2 / mean_buffer_length_;
|
||||
*
|
||||
* Resolution is set to Q4
|
||||
*/
|
||||
freqEst = ((numZeros * 90000) << 3);
|
||||
freqEst /=
|
||||
(timestamp_buffer_[0] - timestamp_buffer_[mean_buffer_length_ - 1]);
|
||||
|
||||
/* Translate frequency estimate to regions close to 100 and 120 Hz */
|
||||
uint8_t freqState = 0; // Current translation state;
|
||||
// (0) Not in interval,
|
||||
// (1) Within valid interval,
|
||||
// (2) Out of range
|
||||
int32_t freqAlias = freqEst;
|
||||
if (freqEst > kMinFrequencyToDetect)
|
||||
{
|
||||
uint8_t aliasState = 1;
|
||||
while(freqState == 0)
|
||||
{
|
||||
/* Increase frequency */
|
||||
freqAlias += (aliasState * _frameRate);
|
||||
freqAlias += ((freqEst << 1) * (1 - (aliasState << 1)));
|
||||
/* Compute state */
|
||||
freqState = (abs(freqAlias - (100 << 4)) <= kFrequencyDeviation);
|
||||
freqState += (abs(freqAlias - (120 << 4)) <= kFrequencyDeviation);
|
||||
freqState += 2 * (freqAlias > ((120 << 4) + kFrequencyDeviation));
|
||||
/* Switch alias state */
|
||||
aliasState++;
|
||||
aliasState &= 0x01;
|
||||
}
|
||||
/* Translate frequency estimate to regions close to 100 and 120 Hz */
|
||||
uint8_t freqState = 0; // Current translation state;
|
||||
// (0) Not in interval,
|
||||
// (1) Within valid interval,
|
||||
// (2) Out of range
|
||||
int32_t freqAlias = freqEst;
|
||||
if (freqEst > kMinFrequencyToDetect) {
|
||||
uint8_t aliasState = 1;
|
||||
while(freqState == 0) {
|
||||
/* Increase frequency */
|
||||
freqAlias += (aliasState * frame_rate_);
|
||||
freqAlias += ((freqEst << 1) * (1 - (aliasState << 1)));
|
||||
/* Compute state */
|
||||
freqState = (abs(freqAlias - (100 << 4)) <= kFrequencyDeviation);
|
||||
freqState += (abs(freqAlias - (120 << 4)) <= kFrequencyDeviation);
|
||||
freqState += 2 * (freqAlias > ((120 << 4) + kFrequencyDeviation));
|
||||
/* Switch alias state */
|
||||
aliasState++;
|
||||
aliasState &= 0x01;
|
||||
}
|
||||
/* Is frequency estimate within detection region? */
|
||||
if (freqState == 1)
|
||||
{
|
||||
retVal = 1;
|
||||
}else if (freqState == 0)
|
||||
{
|
||||
retVal = 2;
|
||||
}else
|
||||
{
|
||||
retVal = 0;
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
/* Is frequency estimate within detection region? */
|
||||
if (freqState == 1) {
|
||||
ret_val = 1;
|
||||
} else if (freqState == 0) {
|
||||
ret_val = 2;
|
||||
} else {
|
||||
ret_val = 0;
|
||||
}
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace webrtc
|
||||
|
@ -8,12 +8,8 @@
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
/*
|
||||
* deflickering.h
|
||||
*/
|
||||
|
||||
#ifndef VPM_DEFLICKERING_H
|
||||
#define VPM_DEFLICKERING_H
|
||||
#ifndef WEBRTC_MODULES_VIDEO_PROCESSING_MAIN_SOURCEdeflickering__H
|
||||
#define WEBRTC_MODULES_VIDEO_PROCESSING_MAIN_SOURCEdeflickering__H
|
||||
|
||||
#include <string.h> // NULL
|
||||
|
||||
@ -22,44 +18,43 @@
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class VPMDeflickering
|
||||
{
|
||||
public:
|
||||
VPMDeflickering();
|
||||
~VPMDeflickering();
|
||||
class VPMDeflickering {
|
||||
public:
|
||||
VPMDeflickering();
|
||||
~VPMDeflickering();
|
||||
|
||||
int32_t ChangeUniqueId(int32_t id);
|
||||
int32_t ChangeUniqueId(int32_t id);
|
||||
|
||||
void Reset();
|
||||
void Reset();
|
||||
int32_t ProcessFrame(I420VideoFrame* frame,
|
||||
VideoProcessingModule::FrameStats* stats);
|
||||
|
||||
int32_t ProcessFrame(I420VideoFrame* frame,
|
||||
VideoProcessingModule::FrameStats* stats);
|
||||
private:
|
||||
int32_t PreDetection(uint32_t timestamp,
|
||||
const VideoProcessingModule::FrameStats& stats);
|
||||
private:
|
||||
int32_t PreDetection(uint32_t timestamp,
|
||||
const VideoProcessingModule::FrameStats& stats);
|
||||
|
||||
int32_t DetectFlicker();
|
||||
int32_t DetectFlicker();
|
||||
|
||||
enum { kMeanBufferLength = 32 };
|
||||
enum { kFrameHistorySize = 15 };
|
||||
enum { kNumProbs = 12 };
|
||||
enum { kNumQuants = kNumProbs + 2 };
|
||||
enum { kMaxOnlyLength = 5 };
|
||||
enum { kMeanBufferLength = 32 };
|
||||
enum { kFrameHistory_size = 15 };
|
||||
enum { kNumProbs = 12 };
|
||||
enum { kNumQuants = kNumProbs + 2 };
|
||||
enum { kMaxOnlyLength = 5 };
|
||||
|
||||
int32_t _id;
|
||||
int32_t id_;
|
||||
|
||||
uint32_t _meanBufferLength;
|
||||
uint8_t _detectionState; // 0: No flickering
|
||||
// 1: Flickering detected
|
||||
// 2: In flickering
|
||||
int32_t _meanBuffer[kMeanBufferLength];
|
||||
uint32_t _timestampBuffer[kMeanBufferLength];
|
||||
uint32_t _frameRate;
|
||||
static const uint16_t _probUW16[kNumProbs];
|
||||
static const uint16_t _weightUW16[kNumQuants - kMaxOnlyLength];
|
||||
uint8_t _quantHistUW8[kFrameHistorySize][kNumQuants];
|
||||
uint32_t mean_buffer_length_;
|
||||
uint8_t detection_state_; // 0: No flickering
|
||||
// 1: Flickering detected
|
||||
// 2: In flickering
|
||||
int32_t mean_buffer_[kMeanBufferLength];
|
||||
uint32_t timestamp_buffer_[kMeanBufferLength];
|
||||
uint32_t frame_rate_;
|
||||
static const uint16_t prob_uw16_[kNumProbs];
|
||||
static const uint16_t weight_uw16_[kNumQuants - kMaxOnlyLength];
|
||||
uint8_t quant_hist_uw8_[kFrameHistory_size][kNumQuants];
|
||||
};
|
||||
|
||||
} // namespace
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // VPM_DEFLICKERING_H
|
||||
#endif // WEBRTC_MODULES_VIDEO_PROCESSING_MAIN_SOURCEdeflickering__H
|
||||
|
@ -14,167 +14,146 @@
|
||||
#include <string.h>
|
||||
|
||||
namespace webrtc {
|
||||
// Down-sampling in time (unit: number of frames)
|
||||
enum { kSubsamplingTime = 0 };
|
||||
// Sub-sampling in width (unit: power of 2.
|
||||
enum { kSubsamplingWidth = 0 };
|
||||
// Sub-sampling in height (unit: power of 2)
|
||||
enum { kSubsamplingHeight = 0 };
|
||||
// (Q8) De-noising filter parameter
|
||||
enum { kDenoiseFiltParam = 179 };
|
||||
// (Q8) 1 - filter parameter
|
||||
enum { kDenoiseFiltParamRec = 77 };
|
||||
// (Q8) De-noising threshold level
|
||||
enum { kDenoiseThreshold = 19200 };
|
||||
|
||||
enum { kSubsamplingTime = 0 }; // Down-sampling in time (unit: number of frames)
|
||||
enum { kSubsamplingWidth = 0 }; // Sub-sampling in width (unit: power of 2)
|
||||
enum { kSubsamplingHeight = 0 }; // Sub-sampling in height (unit: power of 2)
|
||||
enum { kDenoiseFiltParam = 179 }; // (Q8) De-noising filter parameter
|
||||
enum { kDenoiseFiltParamRec = 77 }; // (Q8) 1 - filter parameter
|
||||
enum { kDenoiseThreshold = 19200 }; // (Q8) De-noising threshold level
|
||||
|
||||
VPMDenoising::VPMDenoising() :
|
||||
_id(0),
|
||||
_moment1(NULL),
|
||||
_moment2(NULL)
|
||||
{
|
||||
Reset();
|
||||
VPMDenoising::VPMDenoising()
|
||||
: id_(0),
|
||||
moment1_(NULL),
|
||||
moment2_(NULL) {
|
||||
Reset();
|
||||
}
|
||||
|
||||
VPMDenoising::~VPMDenoising()
|
||||
{
|
||||
if (_moment1)
|
||||
{
|
||||
delete [] _moment1;
|
||||
_moment1 = NULL;
|
||||
}
|
||||
|
||||
if (_moment2)
|
||||
{
|
||||
delete [] _moment2;
|
||||
_moment2 = NULL;
|
||||
}
|
||||
VPMDenoising::~VPMDenoising() {
|
||||
if (moment1_) {
|
||||
delete [] moment1_;
|
||||
moment1_ = NULL;
|
||||
}
|
||||
|
||||
int32_t
|
||||
VPMDenoising::ChangeUniqueId(const int32_t id)
|
||||
{
|
||||
_id = id;
|
||||
return VPM_OK;
|
||||
if (moment2_) {
|
||||
delete [] moment2_;
|
||||
moment2_ = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
VPMDenoising::Reset()
|
||||
{
|
||||
_frameSize = 0;
|
||||
_denoiseFrameCnt = 0;
|
||||
|
||||
if (_moment1)
|
||||
{
|
||||
delete [] _moment1;
|
||||
_moment1 = NULL;
|
||||
}
|
||||
|
||||
if (_moment2)
|
||||
{
|
||||
delete [] _moment2;
|
||||
_moment2 = NULL;
|
||||
}
|
||||
int32_t VPMDenoising::ChangeUniqueId(const int32_t id) {
|
||||
id_ = id;
|
||||
return VPM_OK;
|
||||
}
|
||||
|
||||
int32_t
|
||||
VPMDenoising::ProcessFrame(I420VideoFrame* frame)
|
||||
{
|
||||
assert(frame);
|
||||
int32_t thevar;
|
||||
int k;
|
||||
int jsub, ksub;
|
||||
int32_t diff0;
|
||||
uint32_t tmpMoment1;
|
||||
uint32_t tmpMoment2;
|
||||
uint32_t tmp;
|
||||
int32_t numPixelsChanged = 0;
|
||||
void VPMDenoising::Reset() {
|
||||
frame_size_ = 0;
|
||||
denoise_frame_cnt_ = 0;
|
||||
|
||||
if (frame->IsZeroSize())
|
||||
{
|
||||
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoPreocessing, _id,
|
||||
"zero size frame");
|
||||
return VPM_GENERAL_ERROR;
|
||||
if (moment1_) {
|
||||
delete [] moment1_;
|
||||
moment1_ = NULL;
|
||||
}
|
||||
|
||||
if (moment2_) {
|
||||
delete [] moment2_;
|
||||
moment2_ = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
int32_t VPMDenoising::ProcessFrame(I420VideoFrame* frame) {
|
||||
assert(frame);
|
||||
int32_t thevar;
|
||||
int k;
|
||||
int jsub, ksub;
|
||||
int32_t diff0;
|
||||
uint32_t tmp_moment1;
|
||||
uint32_t tmp_moment2;
|
||||
uint32_t tmp;
|
||||
int32_t num_pixels_changed = 0;
|
||||
|
||||
if (frame->IsZeroSize()) {
|
||||
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoPreocessing, id_,
|
||||
"zero size frame");
|
||||
return VPM_GENERAL_ERROR;
|
||||
}
|
||||
|
||||
int width = frame->width();
|
||||
int height = frame->height();
|
||||
|
||||
/* Size of luminance component */
|
||||
const uint32_t y_size = height * width;
|
||||
|
||||
/* Initialization */
|
||||
if (y_size != frame_size_) {
|
||||
delete [] moment1_;
|
||||
moment1_ = NULL;
|
||||
|
||||
delete [] moment2_;
|
||||
moment2_ = NULL;
|
||||
}
|
||||
frame_size_ = y_size;
|
||||
|
||||
if (!moment1_) {
|
||||
moment1_ = new uint32_t[y_size];
|
||||
memset(moment1_, 0, sizeof(uint32_t)*y_size);
|
||||
}
|
||||
|
||||
if (!moment2_) {
|
||||
moment2_ = new uint32_t[y_size];
|
||||
memset(moment2_, 0, sizeof(uint32_t)*y_size);
|
||||
}
|
||||
|
||||
/* Apply de-noising on each pixel, but update variance sub-sampled */
|
||||
uint8_t* buffer = frame->buffer(kYPlane);
|
||||
for (int i = 0; i < height; i++) { // Collect over height
|
||||
k = i * width;
|
||||
ksub = ((i >> kSubsamplingHeight) << kSubsamplingHeight) * width;
|
||||
for (int j = 0; j < width; j++) { // Collect over width
|
||||
jsub = ((j >> kSubsamplingWidth) << kSubsamplingWidth);
|
||||
/* Update mean value for every pixel and every frame */
|
||||
tmp_moment1 = moment1_[k + j];
|
||||
tmp_moment1 *= kDenoiseFiltParam; // Q16
|
||||
tmp_moment1 += ((kDenoiseFiltParamRec * ((uint32_t)buffer[k + j])) << 8);
|
||||
tmp_moment1 >>= 8; // Q8
|
||||
moment1_[k + j] = tmp_moment1;
|
||||
|
||||
tmp_moment2 = moment2_[ksub + jsub];
|
||||
if ((ksub == k) && (jsub == j) && (denoise_frame_cnt_ == 0)) {
|
||||
tmp = ((uint32_t)buffer[k + j] *
|
||||
(uint32_t)buffer[k + j]);
|
||||
tmp_moment2 *= kDenoiseFiltParam; // Q16
|
||||
tmp_moment2 += ((kDenoiseFiltParamRec * tmp) << 8);
|
||||
tmp_moment2 >>= 8; // Q8
|
||||
}
|
||||
moment2_[k + j] = tmp_moment2;
|
||||
/* Current event = deviation from mean value */
|
||||
diff0 = ((int32_t)buffer[k + j] << 8) - moment1_[k + j];
|
||||
/* Recent events = variance (variations over time) */
|
||||
thevar = moment2_[k + j];
|
||||
thevar -= ((moment1_[k + j] * moment1_[k + j]) >> 8);
|
||||
// De-noising criteria, i.e., when should we replace a pixel by its mean.
|
||||
// 1) recent events are minor.
|
||||
// 2) current events are minor.
|
||||
if ((thevar < kDenoiseThreshold)
|
||||
&& ((diff0 * diff0 >> 8) < kDenoiseThreshold)) {
|
||||
// Replace with mean.
|
||||
buffer[k + j] = (uint8_t)(moment1_[k + j] >> 8);
|
||||
num_pixels_changed++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int width = frame->width();
|
||||
int height = frame->height();
|
||||
denoise_frame_cnt_++;
|
||||
if (denoise_frame_cnt_ > kSubsamplingTime)
|
||||
denoise_frame_cnt_ = 0;
|
||||
|
||||
/* Size of luminance component */
|
||||
const uint32_t ysize = height * width;
|
||||
|
||||
/* Initialization */
|
||||
if (ysize != _frameSize)
|
||||
{
|
||||
delete [] _moment1;
|
||||
_moment1 = NULL;
|
||||
|
||||
delete [] _moment2;
|
||||
_moment2 = NULL;
|
||||
}
|
||||
_frameSize = ysize;
|
||||
|
||||
if (!_moment1)
|
||||
{
|
||||
_moment1 = new uint32_t[ysize];
|
||||
memset(_moment1, 0, sizeof(uint32_t)*ysize);
|
||||
}
|
||||
|
||||
if (!_moment2)
|
||||
{
|
||||
_moment2 = new uint32_t[ysize];
|
||||
memset(_moment2, 0, sizeof(uint32_t)*ysize);
|
||||
}
|
||||
|
||||
/* Apply de-noising on each pixel, but update variance sub-sampled */
|
||||
uint8_t* buffer = frame->buffer(kYPlane);
|
||||
for (int i = 0; i < height; i++)
|
||||
{ // Collect over height
|
||||
k = i * width;
|
||||
ksub = ((i >> kSubsamplingHeight) << kSubsamplingHeight) * width;
|
||||
for (int j = 0; j < width; j++)
|
||||
{ // Collect over width
|
||||
jsub = ((j >> kSubsamplingWidth) << kSubsamplingWidth);
|
||||
/* Update mean value for every pixel and every frame */
|
||||
tmpMoment1 = _moment1[k + j];
|
||||
tmpMoment1 *= kDenoiseFiltParam; // Q16
|
||||
tmpMoment1 += ((kDenoiseFiltParamRec *
|
||||
((uint32_t)buffer[k + j])) << 8);
|
||||
tmpMoment1 >>= 8; // Q8
|
||||
_moment1[k + j] = tmpMoment1;
|
||||
|
||||
tmpMoment2 = _moment2[ksub + jsub];
|
||||
if ((ksub == k) && (jsub == j) && (_denoiseFrameCnt == 0))
|
||||
{
|
||||
tmp = ((uint32_t)buffer[k + j] *
|
||||
(uint32_t)buffer[k + j]);
|
||||
tmpMoment2 *= kDenoiseFiltParam; // Q16
|
||||
tmpMoment2 += ((kDenoiseFiltParamRec * tmp)<<8);
|
||||
tmpMoment2 >>= 8; // Q8
|
||||
}
|
||||
_moment2[k + j] = tmpMoment2;
|
||||
/* Current event = deviation from mean value */
|
||||
diff0 = ((int32_t)buffer[k + j] << 8) - _moment1[k + j];
|
||||
/* Recent events = variance (variations over time) */
|
||||
thevar = _moment2[k + j];
|
||||
thevar -= ((_moment1[k + j] * _moment1[k + j]) >> 8);
|
||||
/***************************************************************************
|
||||
* De-noising criteria, i.e., when should we replace a pixel by its mean
|
||||
*
|
||||
* 1) recent events are minor
|
||||
* 2) current events are minor
|
||||
***************************************************************************/
|
||||
if ((thevar < kDenoiseThreshold)
|
||||
&& ((diff0 * diff0 >> 8) < kDenoiseThreshold))
|
||||
{ // Replace with mean
|
||||
buffer[k + j] = (uint8_t)(_moment1[k + j] >> 8);
|
||||
numPixelsChanged++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Update frame counter */
|
||||
_denoiseFrameCnt++;
|
||||
if (_denoiseFrameCnt > kSubsamplingTime)
|
||||
{
|
||||
_denoiseFrameCnt = 0;
|
||||
}
|
||||
|
||||
return numPixelsChanged;
|
||||
return num_pixels_changed;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
@ -8,39 +8,35 @@
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
/*
|
||||
* denoising.h
|
||||
*/
|
||||
#ifndef VPM_DENOISING_H
|
||||
#define VPM_DENOISING_H
|
||||
#ifndef WEBRTC_MODULES_VIDEO_PROCESSING_MAIN_SOURCE_DENOISING_H_
|
||||
#define WEBRTC_MODULES_VIDEO_PROCESSING_MAIN_SOURCE_DENOISING_H_
|
||||
|
||||
#include "webrtc/modules/video_processing/main/interface/video_processing.h"
|
||||
#include "webrtc/typedefs.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class VPMDenoising
|
||||
{
|
||||
public:
|
||||
VPMDenoising();
|
||||
~VPMDenoising();
|
||||
class VPMDenoising {
|
||||
public:
|
||||
VPMDenoising();
|
||||
~VPMDenoising();
|
||||
|
||||
int32_t ChangeUniqueId(int32_t id);
|
||||
int32_t ChangeUniqueId(int32_t id);
|
||||
|
||||
void Reset();
|
||||
void Reset();
|
||||
|
||||
int32_t ProcessFrame(I420VideoFrame* frame);
|
||||
int32_t ProcessFrame(I420VideoFrame* frame);
|
||||
|
||||
private:
|
||||
int32_t _id;
|
||||
private:
|
||||
int32_t id_;
|
||||
|
||||
uint32_t* _moment1; // (Q8) First order moment (mean)
|
||||
uint32_t* _moment2; // (Q8) Second order moment
|
||||
uint32_t _frameSize; // Size (# of pixels) of frame
|
||||
int _denoiseFrameCnt; // Counter for subsampling in time
|
||||
uint32_t* moment1_; // (Q8) First order moment (mean).
|
||||
uint32_t* moment2_; // (Q8) Second order moment.
|
||||
uint32_t frame_size_; // Size (# of pixels) of frame.
|
||||
int denoise_frame_cnt_; // Counter for subsampling in time.
|
||||
};
|
||||
|
||||
} // namespace
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // WEBRTC_MODULES_VIDEO_PROCESSING_MAIN_SOURCE_DENOISING_H_
|
||||
|
||||
#endif // VPM_DENOISING_H
|
||||
|
||||
|
@ -13,177 +13,137 @@
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
VPMFramePreprocessor::VPMFramePreprocessor():
|
||||
_id(0),
|
||||
_contentMetrics(NULL),
|
||||
_maxFrameRate(0),
|
||||
_resampledFrame(),
|
||||
_enableCA(false),
|
||||
_frameCnt(0)
|
||||
{
|
||||
_spatialResampler = new VPMSimpleSpatialResampler();
|
||||
_ca = new VPMContentAnalysis(true);
|
||||
_vd = new VPMVideoDecimator();
|
||||
VPMFramePreprocessor::VPMFramePreprocessor()
|
||||
: id_(0),
|
||||
content_metrics_(NULL),
|
||||
max_frame_rate_(0),
|
||||
resampled_frame_(),
|
||||
enable_ca_(false),
|
||||
frame_cnt_(0) {
|
||||
spatial_resampler_ = new VPMSimpleSpatialResampler();
|
||||
ca_ = new VPMContentAnalysis(true);
|
||||
vd_ = new VPMVideoDecimator();
|
||||
}
|
||||
|
||||
VPMFramePreprocessor::~VPMFramePreprocessor()
|
||||
{
|
||||
Reset();
|
||||
delete _spatialResampler;
|
||||
delete _ca;
|
||||
delete _vd;
|
||||
VPMFramePreprocessor::~VPMFramePreprocessor() {
|
||||
Reset();
|
||||
delete spatial_resampler_;
|
||||
delete ca_;
|
||||
delete vd_;
|
||||
}
|
||||
|
||||
int32_t
|
||||
VPMFramePreprocessor::ChangeUniqueId(const int32_t id)
|
||||
{
|
||||
_id = id;
|
||||
return VPM_OK;
|
||||
int32_t VPMFramePreprocessor::ChangeUniqueId(const int32_t id) {
|
||||
id_ = id;
|
||||
return VPM_OK;
|
||||
}
|
||||
|
||||
void
|
||||
VPMFramePreprocessor::Reset()
|
||||
{
|
||||
_ca->Release();
|
||||
_vd->Reset();
|
||||
_contentMetrics = NULL;
|
||||
_spatialResampler->Reset();
|
||||
_enableCA = false;
|
||||
_frameCnt = 0;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VPMFramePreprocessor::EnableTemporalDecimation(bool enable)
|
||||
{
|
||||
_vd->EnableTemporalDecimation(enable);
|
||||
}
|
||||
void
|
||||
VPMFramePreprocessor::EnableContentAnalysis(bool enable)
|
||||
{
|
||||
_enableCA = enable;
|
||||
void VPMFramePreprocessor::Reset() {
|
||||
ca_->Release();
|
||||
vd_->Reset();
|
||||
content_metrics_ = NULL;
|
||||
spatial_resampler_->Reset();
|
||||
enable_ca_ = false;
|
||||
frame_cnt_ = 0;
|
||||
}
|
||||
|
||||
void
|
||||
VPMFramePreprocessor::SetInputFrameResampleMode(VideoFrameResampling resamplingMode)
|
||||
{
|
||||
_spatialResampler->SetInputFrameResampleMode(resamplingMode);
|
||||
|
||||
void VPMFramePreprocessor::EnableTemporalDecimation(bool enable) {
|
||||
vd_->EnableTemporalDecimation(enable);
|
||||
}
|
||||
|
||||
|
||||
int32_t
|
||||
VPMFramePreprocessor::SetMaxFrameRate(uint32_t maxFrameRate)
|
||||
{
|
||||
if (maxFrameRate == 0)
|
||||
{
|
||||
return VPM_PARAMETER_ERROR;
|
||||
void VPMFramePreprocessor::EnableContentAnalysis(bool enable) {
|
||||
enable_ca_ = enable;
|
||||
}
|
||||
|
||||
void VPMFramePreprocessor::SetInputFrameResampleMode(
|
||||
VideoFrameResampling resampling_mode) {
|
||||
spatial_resampler_->SetInputFrameResampleMode(resampling_mode);
|
||||
}
|
||||
|
||||
int32_t VPMFramePreprocessor::SetMaxFramerate(uint32_t max_frame_rate) {
|
||||
if (max_frame_rate == 0) return VPM_PARAMETER_ERROR;
|
||||
|
||||
// Max allowed frame_rate.
|
||||
max_frame_rate_ = max_frame_rate;
|
||||
return vd_->SetMaxFramerate(max_frame_rate);
|
||||
}
|
||||
|
||||
int32_t VPMFramePreprocessor::SetTargetResolution(
|
||||
uint32_t width, uint32_t height, uint32_t frame_rate) {
|
||||
if ( (width == 0) || (height == 0) || (frame_rate == 0)) {
|
||||
return VPM_PARAMETER_ERROR;
|
||||
}
|
||||
int32_t ret_val = 0;
|
||||
ret_val = spatial_resampler_->SetTargetFrameSize(width, height);
|
||||
|
||||
if (ret_val < 0) return ret_val;
|
||||
|
||||
ret_val = vd_->SetTargetframe_rate(frame_rate);
|
||||
if (ret_val < 0) return ret_val;
|
||||
|
||||
return VPM_OK;
|
||||
}
|
||||
|
||||
void VPMFramePreprocessor::UpdateIncomingframe_rate() {
|
||||
vd_->UpdateIncomingframe_rate();
|
||||
}
|
||||
|
||||
uint32_t VPMFramePreprocessor::Decimatedframe_rate() {
|
||||
return vd_->Decimatedframe_rate();
|
||||
}
|
||||
|
||||
|
||||
uint32_t VPMFramePreprocessor::DecimatedWidth() const {
|
||||
return spatial_resampler_->TargetWidth();
|
||||
}
|
||||
|
||||
|
||||
uint32_t VPMFramePreprocessor::DecimatedHeight() const {
|
||||
return spatial_resampler_->TargetHeight();
|
||||
}
|
||||
|
||||
|
||||
int32_t VPMFramePreprocessor::PreprocessFrame(const I420VideoFrame& frame,
|
||||
I420VideoFrame** processed_frame) {
|
||||
if (frame.IsZeroSize()) {
|
||||
return VPM_PARAMETER_ERROR;
|
||||
}
|
||||
|
||||
vd_->UpdateIncomingframe_rate();
|
||||
|
||||
if (vd_->DropFrame()) {
|
||||
WEBRTC_TRACE(webrtc::kTraceStream, webrtc::kTraceVideo, id_,
|
||||
"Drop frame due to frame rate");
|
||||
return 1; // drop 1 frame
|
||||
}
|
||||
|
||||
// Resizing incoming frame if needed. Otherwise, remains NULL.
|
||||
// We are not allowed to resample the input frame (must make a copy of it).
|
||||
*processed_frame = NULL;
|
||||
if (spatial_resampler_->ApplyResample(frame.width(), frame.height())) {
|
||||
int32_t ret = spatial_resampler_->ResampleFrame(frame, &resampled_frame_);
|
||||
if (ret != VPM_OK) return ret;
|
||||
*processed_frame = &resampled_frame_;
|
||||
}
|
||||
|
||||
// Perform content analysis on the frame to be encoded.
|
||||
if (enable_ca_) {
|
||||
// Compute new metrics every |kSkipFramesCA| frames, starting with
|
||||
// the first frame.
|
||||
if (frame_cnt_ % kSkipFrameCA == 0) {
|
||||
if (*processed_frame == NULL) {
|
||||
content_metrics_ = ca_->ComputeContentMetrics(frame);
|
||||
} else {
|
||||
content_metrics_ = ca_->ComputeContentMetrics(resampled_frame_);
|
||||
}
|
||||
}
|
||||
//Max allowed frame rate
|
||||
_maxFrameRate = maxFrameRate;
|
||||
|
||||
return _vd->SetMaxFrameRate(maxFrameRate);
|
||||
}
|
||||
|
||||
|
||||
int32_t
|
||||
VPMFramePreprocessor::SetTargetResolution(uint32_t width, uint32_t height, uint32_t frameRate)
|
||||
{
|
||||
if ( (width == 0) || (height == 0) || (frameRate == 0))
|
||||
{
|
||||
return VPM_PARAMETER_ERROR;
|
||||
}
|
||||
int32_t retVal = 0;
|
||||
retVal = _spatialResampler->SetTargetFrameSize(width, height);
|
||||
if (retVal < 0)
|
||||
{
|
||||
return retVal;
|
||||
}
|
||||
retVal = _vd->SetTargetFrameRate(frameRate);
|
||||
if (retVal < 0)
|
||||
{
|
||||
return retVal;
|
||||
}
|
||||
|
||||
return VPM_OK;
|
||||
++frame_cnt_;
|
||||
}
|
||||
return VPM_OK;
|
||||
}
|
||||
|
||||
void
|
||||
VPMFramePreprocessor::UpdateIncomingFrameRate()
|
||||
{
|
||||
_vd->UpdateIncomingFrameRate();
|
||||
}
|
||||
|
||||
uint32_t
|
||||
VPMFramePreprocessor::DecimatedFrameRate()
|
||||
{
|
||||
return _vd->DecimatedFrameRate();
|
||||
}
|
||||
|
||||
|
||||
uint32_t
|
||||
VPMFramePreprocessor::DecimatedWidth() const
|
||||
{
|
||||
return _spatialResampler->TargetWidth();
|
||||
}
|
||||
|
||||
|
||||
uint32_t
|
||||
VPMFramePreprocessor::DecimatedHeight() const
|
||||
{
|
||||
return _spatialResampler->TargetHeight();
|
||||
}
|
||||
|
||||
|
||||
int32_t
|
||||
VPMFramePreprocessor::PreprocessFrame(const I420VideoFrame& frame,
|
||||
I420VideoFrame** processedFrame)
|
||||
{
|
||||
if (frame.IsZeroSize())
|
||||
{
|
||||
return VPM_PARAMETER_ERROR;
|
||||
}
|
||||
|
||||
_vd->UpdateIncomingFrameRate();
|
||||
|
||||
if (_vd->DropFrame())
|
||||
{
|
||||
WEBRTC_TRACE(webrtc::kTraceStream, webrtc::kTraceVideo, _id,
|
||||
"Drop frame due to frame rate");
|
||||
return 1; // drop 1 frame
|
||||
}
|
||||
|
||||
// Resizing incoming frame if needed. Otherwise, remains NULL.
|
||||
// We are not allowed to resample the input frame (must make a copy of it).
|
||||
*processedFrame = NULL;
|
||||
if (_spatialResampler->ApplyResample(frame.width(), frame.height())) {
|
||||
int32_t ret = _spatialResampler->ResampleFrame(frame, &_resampledFrame);
|
||||
if (ret != VPM_OK)
|
||||
return ret;
|
||||
*processedFrame = &_resampledFrame;
|
||||
}
|
||||
|
||||
// Perform content analysis on the frame to be encoded.
|
||||
if (_enableCA)
|
||||
{
|
||||
// Compute new metrics every |kSkipFramesCA| frames, starting with
|
||||
// the first frame.
|
||||
if (_frameCnt % kSkipFrameCA == 0) {
|
||||
if (*processedFrame == NULL) {
|
||||
_contentMetrics = _ca->ComputeContentMetrics(frame);
|
||||
} else {
|
||||
_contentMetrics = _ca->ComputeContentMetrics(_resampledFrame);
|
||||
}
|
||||
}
|
||||
++_frameCnt;
|
||||
}
|
||||
return VPM_OK;
|
||||
}
|
||||
|
||||
|
||||
VideoContentMetrics*
|
||||
VPMFramePreprocessor::ContentMetrics() const
|
||||
{
|
||||
return _contentMetrics;
|
||||
VideoContentMetrics* VPMFramePreprocessor::ContentMetrics() const {
|
||||
return content_metrics_;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
@ -11,8 +11,8 @@
|
||||
/*
|
||||
* frame_preprocessor.h
|
||||
*/
|
||||
#ifndef VPM_FRAME_PREPROCESSOR_H
|
||||
#define VPM_FRAME_PREPROCESSOR_H
|
||||
#ifndef WEBRTC_MODULES_VIDEO_PROCESSING_MAIN_SOURCE_FRAME_PREPROCESSOR_H
|
||||
#define WEBRTC_MODULES_VIDEO_PROCESSING_MAIN_SOURCE_FRAME_PREPROCESSOR_H
|
||||
|
||||
#include "webrtc/modules/video_processing/main/interface/video_processing.h"
|
||||
#include "webrtc/modules/video_processing/main/source/content_analysis.h"
|
||||
@ -22,65 +22,62 @@
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class VPMFramePreprocessor {
|
||||
public:
|
||||
VPMFramePreprocessor();
|
||||
~VPMFramePreprocessor();
|
||||
|
||||
class VPMFramePreprocessor
|
||||
{
|
||||
public:
|
||||
int32_t ChangeUniqueId(const int32_t id);
|
||||
|
||||
VPMFramePreprocessor();
|
||||
~VPMFramePreprocessor();
|
||||
void Reset();
|
||||
|
||||
int32_t ChangeUniqueId(const int32_t id);
|
||||
// Enable temporal decimation.
|
||||
void EnableTemporalDecimation(bool enable);
|
||||
|
||||
void Reset();
|
||||
void SetInputFrameResampleMode(VideoFrameResampling resampling_mode);
|
||||
|
||||
// Enable temporal decimation
|
||||
void EnableTemporalDecimation(bool enable);
|
||||
// Enable content analysis.
|
||||
void EnableContentAnalysis(bool enable);
|
||||
|
||||
void SetInputFrameResampleMode(VideoFrameResampling resamplingMode);
|
||||
// Set max frame rate.
|
||||
int32_t SetMaxFramerate(uint32_t max_frame_rate);
|
||||
|
||||
//Enable content analysis
|
||||
void EnableContentAnalysis(bool enable);
|
||||
// Set target resolution: frame rate and dimension.
|
||||
int32_t SetTargetResolution(uint32_t width, uint32_t height,
|
||||
uint32_t frame_rate);
|
||||
|
||||
//Set max frame rate
|
||||
int32_t SetMaxFrameRate(uint32_t maxFrameRate);
|
||||
// Update incoming frame rate/dimension.
|
||||
void UpdateIncomingframe_rate();
|
||||
|
||||
//Set target resolution: frame rate and dimension
|
||||
int32_t SetTargetResolution(uint32_t width, uint32_t height,
|
||||
uint32_t frameRate);
|
||||
int32_t updateIncomingFrameSize(uint32_t width, uint32_t height);
|
||||
|
||||
//Update incoming frame rate/dimension
|
||||
void UpdateIncomingFrameRate();
|
||||
// Set decimated values: frame rate/dimension.
|
||||
uint32_t Decimatedframe_rate();
|
||||
uint32_t DecimatedWidth() const;
|
||||
uint32_t DecimatedHeight() const;
|
||||
|
||||
int32_t updateIncomingFrameSize(uint32_t width, uint32_t height);
|
||||
// Preprocess output:
|
||||
int32_t PreprocessFrame(const I420VideoFrame& frame,
|
||||
I420VideoFrame** processed_frame);
|
||||
VideoContentMetrics* ContentMetrics() const;
|
||||
|
||||
//Set decimated values: frame rate/dimension
|
||||
uint32_t DecimatedFrameRate();
|
||||
uint32_t DecimatedWidth() const;
|
||||
uint32_t DecimatedHeight() const;
|
||||
private:
|
||||
// The content does not change so much every frame, so to reduce complexity
|
||||
// we can compute new content metrics every |kSkipFrameCA| frames.
|
||||
enum { kSkipFrameCA = 2 };
|
||||
|
||||
//Preprocess output:
|
||||
int32_t PreprocessFrame(const I420VideoFrame& frame,
|
||||
I420VideoFrame** processedFrame);
|
||||
VideoContentMetrics* ContentMetrics() const;
|
||||
int32_t id_;
|
||||
VideoContentMetrics* content_metrics_;
|
||||
uint32_t max_frame_rate_;
|
||||
I420VideoFrame resampled_frame_;
|
||||
VPMSpatialResampler* spatial_resampler_;
|
||||
VPMContentAnalysis* ca_;
|
||||
VPMVideoDecimator* vd_;
|
||||
bool enable_ca_;
|
||||
int frame_cnt_;
|
||||
|
||||
private:
|
||||
// The content does not change so much every frame, so to reduce complexity
|
||||
// we can compute new content metrics every |kSkipFrameCA| frames.
|
||||
enum { kSkipFrameCA = 2 };
|
||||
};
|
||||
|
||||
int32_t _id;
|
||||
VideoContentMetrics* _contentMetrics;
|
||||
uint32_t _maxFrameRate;
|
||||
I420VideoFrame _resampledFrame;
|
||||
VPMSpatialResampler* _spatialResampler;
|
||||
VPMContentAnalysis* _ca;
|
||||
VPMVideoDecimator* _vd;
|
||||
bool _enableCA;
|
||||
int _frameCnt;
|
||||
|
||||
}; // end of VPMFramePreprocessor class definition
|
||||
} // namespace webrtc
|
||||
|
||||
} // namespace
|
||||
|
||||
#endif // VPM_FRAME_PREPROCESS_H
|
||||
#endif // WEBRTC_MODULES_VIDEO_PROCESSING_MAIN_SOURCE_FRAME_PREPROCESSOR_H
|
||||
|
@ -14,109 +14,85 @@
|
||||
namespace webrtc {
|
||||
|
||||
VPMSimpleSpatialResampler::VPMSimpleSpatialResampler()
|
||||
:
|
||||
_resamplingMode(kFastRescaling),
|
||||
_targetWidth(0),
|
||||
_targetHeight(0),
|
||||
_scaler()
|
||||
{
|
||||
}
|
||||
: resampling_mode_(kFastRescaling),
|
||||
target_width_(0),
|
||||
target_height_(0),
|
||||
scaler_() {}
|
||||
|
||||
VPMSimpleSpatialResampler::~VPMSimpleSpatialResampler()
|
||||
{
|
||||
//
|
||||
}
|
||||
VPMSimpleSpatialResampler::~VPMSimpleSpatialResampler() {}
|
||||
|
||||
|
||||
int32_t
|
||||
VPMSimpleSpatialResampler::SetTargetFrameSize(int32_t width,
|
||||
int32_t height)
|
||||
{
|
||||
if (_resamplingMode == kNoRescaling) {
|
||||
return VPM_OK;
|
||||
}
|
||||
int32_t VPMSimpleSpatialResampler::SetTargetFrameSize(int32_t width,
|
||||
int32_t height) {
|
||||
if (resampling_mode_ == kNoRescaling) return VPM_OK;
|
||||
|
||||
if (width < 1 || height < 1) {
|
||||
return VPM_PARAMETER_ERROR;
|
||||
}
|
||||
if (width < 1 || height < 1) return VPM_PARAMETER_ERROR;
|
||||
|
||||
_targetWidth = width;
|
||||
_targetHeight = height;
|
||||
target_width_ = width;
|
||||
target_height_ = height;
|
||||
|
||||
return VPM_OK;
|
||||
}
|
||||
|
||||
void
|
||||
VPMSimpleSpatialResampler::SetInputFrameResampleMode(VideoFrameResampling
|
||||
resamplingMode)
|
||||
{
|
||||
_resamplingMode = resamplingMode;
|
||||
void VPMSimpleSpatialResampler::SetInputFrameResampleMode(
|
||||
VideoFrameResampling resampling_mode) {
|
||||
resampling_mode_ = resampling_mode;
|
||||
}
|
||||
|
||||
void
|
||||
VPMSimpleSpatialResampler::Reset()
|
||||
{
|
||||
_resamplingMode = kFastRescaling;
|
||||
_targetWidth = 0;
|
||||
_targetHeight = 0;
|
||||
void VPMSimpleSpatialResampler::Reset() {
|
||||
resampling_mode_ = kFastRescaling;
|
||||
target_width_ = 0;
|
||||
target_height_ = 0;
|
||||
}
|
||||
|
||||
int32_t
|
||||
VPMSimpleSpatialResampler::ResampleFrame(const I420VideoFrame& inFrame,
|
||||
I420VideoFrame* outFrame)
|
||||
{
|
||||
int32_t VPMSimpleSpatialResampler::ResampleFrame(const I420VideoFrame& inFrame,
|
||||
I420VideoFrame* outFrame) {
|
||||
// Don't copy if frame remains as is.
|
||||
if (_resamplingMode == kNoRescaling)
|
||||
if (resampling_mode_ == kNoRescaling)
|
||||
return VPM_OK;
|
||||
// Check if re-sampling is needed
|
||||
else if ((inFrame.width() == _targetWidth) &&
|
||||
(inFrame.height() == _targetHeight)) {
|
||||
else if ((inFrame.width() == target_width_) &&
|
||||
(inFrame.height() == target_height_)) {
|
||||
return VPM_OK;
|
||||
}
|
||||
|
||||
// Setting scaler
|
||||
// TODO(mikhal/marpan): Should we allow for setting the filter mode in
|
||||
// _scale.Set() with |_resamplingMode|?
|
||||
int retVal = 0;
|
||||
retVal = _scaler.Set(inFrame.width(), inFrame.height(),
|
||||
_targetWidth, _targetHeight, kI420, kI420, kScaleBox);
|
||||
if (retVal < 0)
|
||||
return retVal;
|
||||
// _scale.Set() with |resampling_mode_|?
|
||||
int ret_val = 0;
|
||||
ret_val = scaler_.Set(inFrame.width(), inFrame.height(),
|
||||
target_width_, target_height_, kI420, kI420, kScaleBox);
|
||||
if (ret_val < 0)
|
||||
return ret_val;
|
||||
|
||||
retVal = _scaler.Scale(inFrame, outFrame);
|
||||
ret_val = scaler_.Scale(inFrame, outFrame);
|
||||
|
||||
// Setting time parameters to the output frame.
|
||||
// Timestamp will be reset in Scale call above, so we should set it after.
|
||||
outFrame->set_timestamp(inFrame.timestamp());
|
||||
outFrame->set_render_time_ms(inFrame.render_time_ms());
|
||||
|
||||
if (retVal == 0)
|
||||
if (ret_val == 0)
|
||||
return VPM_OK;
|
||||
else
|
||||
return VPM_SCALE_ERROR;
|
||||
}
|
||||
|
||||
int32_t
|
||||
VPMSimpleSpatialResampler::TargetHeight()
|
||||
{
|
||||
return _targetHeight;
|
||||
int32_t VPMSimpleSpatialResampler::TargetHeight() {
|
||||
return target_height_;
|
||||
}
|
||||
|
||||
int32_t
|
||||
VPMSimpleSpatialResampler::TargetWidth()
|
||||
{
|
||||
return _targetWidth;
|
||||
int32_t VPMSimpleSpatialResampler::TargetWidth() {
|
||||
return target_width_;
|
||||
}
|
||||
|
||||
bool
|
||||
VPMSimpleSpatialResampler::ApplyResample(int32_t width,
|
||||
int32_t height)
|
||||
{
|
||||
if ((width == _targetWidth && height == _targetHeight) ||
|
||||
_resamplingMode == kNoRescaling)
|
||||
bool VPMSimpleSpatialResampler::ApplyResample(int32_t width,
|
||||
int32_t height) {
|
||||
if ((width == target_width_ && height == target_height_) ||
|
||||
resampling_mode_ == kNoRescaling)
|
||||
return false;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace webrtc
|
||||
|
@ -8,12 +8,8 @@
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
/*
|
||||
* spatial_resampler.h
|
||||
*/
|
||||
|
||||
#ifndef VPM_SPATIAL_RESAMPLER_H
|
||||
#define VPM_SPATIAL_RESAMPLER_H
|
||||
#ifndef WEBRTC_MODULES_VIDEO_PROCESSING_MAIN_SOURCE_SPATIAL_RESAMPLER_H
|
||||
#define WEBRTC_MODULES_VIDEO_PROCESSING_MAIN_SOURCE_SPATIAL_RESAMPLER_H
|
||||
|
||||
#include "webrtc/typedefs.h"
|
||||
|
||||
@ -25,13 +21,12 @@
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class VPMSpatialResampler
|
||||
{
|
||||
public:
|
||||
class VPMSpatialResampler {
|
||||
public:
|
||||
virtual ~VPMSpatialResampler() {};
|
||||
virtual int32_t SetTargetFrameSize(int32_t width, int32_t height) = 0;
|
||||
virtual void SetInputFrameResampleMode(VideoFrameResampling
|
||||
resamplingMode) = 0;
|
||||
resampling_mode) = 0;
|
||||
virtual void Reset() = 0;
|
||||
virtual int32_t ResampleFrame(const I420VideoFrame& inFrame,
|
||||
I420VideoFrame* outFrame) = 0;
|
||||
@ -40,13 +35,12 @@ public:
|
||||
virtual bool ApplyResample(int32_t width, int32_t height) = 0;
|
||||
};
|
||||
|
||||
class VPMSimpleSpatialResampler : public VPMSpatialResampler
|
||||
{
|
||||
public:
|
||||
class VPMSimpleSpatialResampler : public VPMSpatialResampler {
|
||||
public:
|
||||
VPMSimpleSpatialResampler();
|
||||
~VPMSimpleSpatialResampler();
|
||||
virtual int32_t SetTargetFrameSize(int32_t width, int32_t height);
|
||||
virtual void SetInputFrameResampleMode(VideoFrameResampling resamplingMode);
|
||||
virtual void SetInputFrameResampleMode(VideoFrameResampling resampling_mode);
|
||||
virtual void Reset();
|
||||
virtual int32_t ResampleFrame(const I420VideoFrame& inFrame,
|
||||
I420VideoFrame* outFrame);
|
||||
@ -54,14 +48,14 @@ public:
|
||||
virtual int32_t TargetHeight();
|
||||
virtual bool ApplyResample(int32_t width, int32_t height);
|
||||
|
||||
private:
|
||||
private:
|
||||
|
||||
VideoFrameResampling _resamplingMode;
|
||||
int32_t _targetWidth;
|
||||
int32_t _targetHeight;
|
||||
Scaler _scaler;
|
||||
VideoFrameResampling resampling_mode_;
|
||||
int32_t target_width_;
|
||||
int32_t target_height_;
|
||||
Scaler scaler_;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
} // namespace webrtc
|
||||
|
||||
#endif
|
||||
#endif // WEBRTC_MODULES_VIDEO_PROCESSING_MAIN_SOURCE_SPATIAL_RESAMPLER_H
|
||||
|
@ -17,219 +17,156 @@
|
||||
namespace webrtc {
|
||||
|
||||
VPMVideoDecimator::VPMVideoDecimator()
|
||||
:
|
||||
_overShootModifier(0),
|
||||
_dropCount(0),
|
||||
_keepCount(0),
|
||||
_targetFrameRate(30),
|
||||
_incomingFrameRate(0.0f),
|
||||
_maxFrameRate(30),
|
||||
_incomingFrameTimes(),
|
||||
_enableTemporalDecimation(true)
|
||||
{
|
||||
Reset();
|
||||
: overshoot_modifier_(0),
|
||||
drop_count_(0),
|
||||
keep_count_(0),
|
||||
target_frame_rate_(30),
|
||||
incoming_frame_rate_(0.0f),
|
||||
max_frame_rate_(30),
|
||||
incoming_frame_times_(),
|
||||
enable_temporal_decimation_(true) {
|
||||
Reset();
|
||||
}
|
||||
|
||||
VPMVideoDecimator::~VPMVideoDecimator()
|
||||
{
|
||||
//
|
||||
VPMVideoDecimator::~VPMVideoDecimator() {}
|
||||
|
||||
void VPMVideoDecimator::Reset() {
|
||||
overshoot_modifier_ = 0;
|
||||
drop_count_ = 0;
|
||||
keep_count_ = 0;
|
||||
target_frame_rate_ = 30;
|
||||
incoming_frame_rate_ = 0.0f;
|
||||
max_frame_rate_ = 30;
|
||||
memset(incoming_frame_times_, 0, sizeof(incoming_frame_times_));
|
||||
enable_temporal_decimation_ = true;
|
||||
}
|
||||
|
||||
void
|
||||
VPMVideoDecimator::Reset()
|
||||
{
|
||||
_overShootModifier = 0;
|
||||
_dropCount = 0;
|
||||
_keepCount = 0;
|
||||
_targetFrameRate = 30;
|
||||
_incomingFrameRate = 0.0f;
|
||||
_maxFrameRate = 30;
|
||||
memset(_incomingFrameTimes, 0, sizeof(_incomingFrameTimes));
|
||||
_enableTemporalDecimation = true;
|
||||
void VPMVideoDecimator::EnableTemporalDecimation(bool enable) {
|
||||
enable_temporal_decimation_ = enable;
|
||||
}
|
||||
|
||||
void
|
||||
VPMVideoDecimator::EnableTemporalDecimation(bool enable)
|
||||
{
|
||||
_enableTemporalDecimation = enable;
|
||||
}
|
||||
int32_t
|
||||
VPMVideoDecimator::SetMaxFrameRate(uint32_t maxFrameRate)
|
||||
{
|
||||
if (maxFrameRate == 0)
|
||||
{
|
||||
return VPM_PARAMETER_ERROR;
|
||||
}
|
||||
int32_t VPMVideoDecimator::SetMaxFramerate(uint32_t max_frame_rate) {
|
||||
if (max_frame_rate == 0) return VPM_PARAMETER_ERROR;
|
||||
|
||||
_maxFrameRate = maxFrameRate;
|
||||
|
||||
if (_targetFrameRate > _maxFrameRate)
|
||||
{
|
||||
_targetFrameRate = _maxFrameRate;
|
||||
max_frame_rate_ = max_frame_rate;
|
||||
|
||||
}
|
||||
return VPM_OK;
|
||||
if (target_frame_rate_ > max_frame_rate_)
|
||||
target_frame_rate_ = max_frame_rate_;
|
||||
|
||||
return VPM_OK;
|
||||
}
|
||||
|
||||
int32_t
|
||||
VPMVideoDecimator::SetTargetFrameRate(uint32_t frameRate)
|
||||
{
|
||||
if (frameRate == 0)
|
||||
{
|
||||
return VPM_PARAMETER_ERROR;
|
||||
}
|
||||
if (frameRate > _maxFrameRate)
|
||||
{
|
||||
//override
|
||||
_targetFrameRate = _maxFrameRate;
|
||||
}
|
||||
else
|
||||
{
|
||||
_targetFrameRate = frameRate;
|
||||
}
|
||||
return VPM_OK;
|
||||
int32_t VPMVideoDecimator::SetTargetframe_rate(uint32_t frame_rate) {
|
||||
if (frame_rate == 0) return VPM_PARAMETER_ERROR;
|
||||
|
||||
if (frame_rate > max_frame_rate_) {
|
||||
// Override.
|
||||
target_frame_rate_ = max_frame_rate_;
|
||||
} else {
|
||||
target_frame_rate_ = frame_rate;
|
||||
}
|
||||
return VPM_OK;
|
||||
}
|
||||
|
||||
bool
|
||||
VPMVideoDecimator::DropFrame()
|
||||
{
|
||||
if (!_enableTemporalDecimation)
|
||||
{
|
||||
return false;
|
||||
bool VPMVideoDecimator::DropFrame() {
|
||||
if (!enable_temporal_decimation_) return false;
|
||||
|
||||
if (incoming_frame_rate_ <= 0) return false;
|
||||
|
||||
const uint32_t incomingframe_rate =
|
||||
static_cast<uint32_t>(incoming_frame_rate_ + 0.5f);
|
||||
|
||||
if (target_frame_rate_ == 0) return true;
|
||||
|
||||
bool drop = false;
|
||||
if (incomingframe_rate > target_frame_rate_) {
|
||||
int32_t overshoot =
|
||||
overshoot_modifier_ + (incomingframe_rate - target_frame_rate_);
|
||||
if (overshoot < 0) {
|
||||
overshoot = 0;
|
||||
overshoot_modifier_ = 0;
|
||||
}
|
||||
|
||||
if (_incomingFrameRate <= 0)
|
||||
{
|
||||
return false;
|
||||
if (overshoot && 2 * overshoot < (int32_t) incomingframe_rate) {
|
||||
if (drop_count_) { // Just got here so drop to be sure.
|
||||
drop_count_ = 0;
|
||||
return true;
|
||||
}
|
||||
const uint32_t dropVar = incomingframe_rate / overshoot;
|
||||
|
||||
if (keep_count_ >= dropVar) {
|
||||
drop = true;
|
||||
overshoot_modifier_ = -((int32_t) incomingframe_rate % overshoot) / 3;
|
||||
keep_count_ = 1;
|
||||
} else {
|
||||
keep_count_++;
|
||||
}
|
||||
} else {
|
||||
keep_count_ = 0;
|
||||
const uint32_t dropVar = overshoot / target_frame_rate_;
|
||||
if (drop_count_ < dropVar) {
|
||||
drop = true;
|
||||
drop_count_++;
|
||||
} else {
|
||||
overshoot_modifier_ = overshoot % target_frame_rate_;
|
||||
drop = false;
|
||||
drop_count_ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
const uint32_t incomingFrameRate = static_cast<uint32_t>(_incomingFrameRate + 0.5f);
|
||||
|
||||
if (_targetFrameRate == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool drop = false;
|
||||
if (incomingFrameRate > _targetFrameRate)
|
||||
{
|
||||
int32_t overshoot = _overShootModifier + (incomingFrameRate - _targetFrameRate);
|
||||
if(overshoot < 0)
|
||||
{
|
||||
overshoot = 0;
|
||||
_overShootModifier = 0;
|
||||
}
|
||||
|
||||
if (overshoot && 2 * overshoot < (int32_t) incomingFrameRate)
|
||||
{
|
||||
|
||||
if (_dropCount) // Just got here so drop to be sure.
|
||||
{
|
||||
_dropCount = 0;
|
||||
return true;
|
||||
}
|
||||
const uint32_t dropVar = incomingFrameRate / overshoot;
|
||||
|
||||
if (_keepCount >= dropVar)
|
||||
{
|
||||
drop = true;
|
||||
_overShootModifier = -((int32_t) incomingFrameRate % overshoot) / 3;
|
||||
_keepCount = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
_keepCount++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_keepCount = 0;
|
||||
const uint32_t dropVar = overshoot / _targetFrameRate;
|
||||
if (_dropCount < dropVar)
|
||||
{
|
||||
drop = true;
|
||||
_dropCount++;
|
||||
}
|
||||
else
|
||||
{
|
||||
_overShootModifier = overshoot % _targetFrameRate;
|
||||
drop = false;
|
||||
_dropCount = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return drop;
|
||||
}
|
||||
return drop;
|
||||
}
|
||||
|
||||
|
||||
uint32_t
|
||||
VPMVideoDecimator::DecimatedFrameRate()
|
||||
{
|
||||
ProcessIncomingFrameRate(TickTime::MillisecondTimestamp());
|
||||
if (!_enableTemporalDecimation)
|
||||
{
|
||||
return static_cast<uint32_t>(_incomingFrameRate + 0.5f);
|
||||
}
|
||||
return VD_MIN(_targetFrameRate, static_cast<uint32_t>(_incomingFrameRate + 0.5f));
|
||||
uint32_t VPMVideoDecimator::Decimatedframe_rate() {
|
||||
ProcessIncomingframe_rate(TickTime::MillisecondTimestamp());
|
||||
if (!enable_temporal_decimation_) {
|
||||
return static_cast<uint32_t>(incoming_frame_rate_ + 0.5f);
|
||||
}
|
||||
return VD_MIN(target_frame_rate_,
|
||||
static_cast<uint32_t>(incoming_frame_rate_ + 0.5f));
|
||||
}
|
||||
|
||||
uint32_t
|
||||
VPMVideoDecimator::InputFrameRate()
|
||||
{
|
||||
ProcessIncomingFrameRate(TickTime::MillisecondTimestamp());
|
||||
return static_cast<uint32_t>(_incomingFrameRate + 0.5f);
|
||||
uint32_t VPMVideoDecimator::Inputframe_rate() {
|
||||
ProcessIncomingframe_rate(TickTime::MillisecondTimestamp());
|
||||
return static_cast<uint32_t>(incoming_frame_rate_ + 0.5f);
|
||||
}
|
||||
|
||||
void
|
||||
VPMVideoDecimator::UpdateIncomingFrameRate()
|
||||
{
|
||||
int64_t now = TickTime::MillisecondTimestamp();
|
||||
if(_incomingFrameTimes[0] == 0)
|
||||
{
|
||||
// first no shift
|
||||
} else
|
||||
{
|
||||
// shift
|
||||
for(int i = (kFrameCountHistorySize - 2); i >= 0 ; i--)
|
||||
{
|
||||
_incomingFrameTimes[i+1] = _incomingFrameTimes[i];
|
||||
}
|
||||
void VPMVideoDecimator::UpdateIncomingframe_rate() {
|
||||
int64_t now = TickTime::MillisecondTimestamp();
|
||||
if (incoming_frame_times_[0] == 0) {
|
||||
// First no shift.
|
||||
} else {
|
||||
// Shift.
|
||||
for (int i = kFrameCountHistory_size - 2; i >= 0; i--) {
|
||||
incoming_frame_times_[i+1] = incoming_frame_times_[i];
|
||||
}
|
||||
_incomingFrameTimes[0] = now;
|
||||
ProcessIncomingFrameRate(now);
|
||||
}
|
||||
incoming_frame_times_[0] = now;
|
||||
ProcessIncomingframe_rate(now);
|
||||
}
|
||||
|
||||
void
|
||||
VPMVideoDecimator::ProcessIncomingFrameRate(int64_t now)
|
||||
{
|
||||
int32_t num = 0;
|
||||
int32_t nrOfFrames = 0;
|
||||
for(num = 1; num < (kFrameCountHistorySize - 1); num++)
|
||||
{
|
||||
if (_incomingFrameTimes[num] <= 0 ||
|
||||
now - _incomingFrameTimes[num] > kFrameHistoryWindowMs) // don't use data older than 2sec
|
||||
{
|
||||
break;
|
||||
} else
|
||||
{
|
||||
nrOfFrames++;
|
||||
}
|
||||
void VPMVideoDecimator::ProcessIncomingframe_rate(int64_t now) {
|
||||
int32_t num = 0;
|
||||
int32_t nrOfFrames = 0;
|
||||
for (num = 1; num < (kFrameCountHistory_size - 1); num++) {
|
||||
// Don't use data older than 2sec.
|
||||
if (incoming_frame_times_[num] <= 0 ||
|
||||
now - incoming_frame_times_[num] > kFrameHistoryWindowMs) {
|
||||
break;
|
||||
} else {
|
||||
nrOfFrames++;
|
||||
}
|
||||
if (num > 1)
|
||||
{
|
||||
int64_t diff = now - _incomingFrameTimes[num-1];
|
||||
_incomingFrameRate = 1.0;
|
||||
if(diff >0)
|
||||
{
|
||||
_incomingFrameRate = nrOfFrames * 1000.0f / static_cast<float>(diff);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_incomingFrameRate = static_cast<float>(nrOfFrames);
|
||||
}
|
||||
if (num > 1) {
|
||||
int64_t diff = now - incoming_frame_times_[num-1];
|
||||
incoming_frame_rate_ = 1.0;
|
||||
if (diff > 0) {
|
||||
incoming_frame_rate_ = nrOfFrames * 1000.0f / static_cast<float>(diff);
|
||||
}
|
||||
} else {
|
||||
incoming_frame_rate_ = static_cast<float>(nrOfFrames);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace webrtc
|
||||
|
@ -8,58 +8,53 @@
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
/*
|
||||
* video_decimator.h
|
||||
*/
|
||||
#ifndef VPM_VIDEO_DECIMATOR_H
|
||||
#define VPM_VIDEO_DECIMATOR_H
|
||||
#ifndef WEBRTC_MODULES_VIDEO_PROCESSING_MAIN_SOURCE_VIDEO_DECIMATOR_H
|
||||
#define WEBRTC_MODULES_VIDEO_PROCESSING_MAIN_SOURCE_VIDEO_DECIMATOR_H
|
||||
|
||||
#include "webrtc/modules/interface/module_common_types.h"
|
||||
#include "webrtc/typedefs.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class VPMVideoDecimator
|
||||
{
|
||||
public:
|
||||
VPMVideoDecimator();
|
||||
~VPMVideoDecimator();
|
||||
|
||||
void Reset();
|
||||
|
||||
void EnableTemporalDecimation(bool enable);
|
||||
|
||||
int32_t SetMaxFrameRate(uint32_t maxFrameRate);
|
||||
int32_t SetTargetFrameRate(uint32_t frameRate);
|
||||
class VPMVideoDecimator {
|
||||
public:
|
||||
VPMVideoDecimator();
|
||||
~VPMVideoDecimator();
|
||||
|
||||
bool DropFrame();
|
||||
|
||||
void UpdateIncomingFrameRate();
|
||||
void Reset();
|
||||
|
||||
// Get Decimated Frame Rate/Dimensions
|
||||
uint32_t DecimatedFrameRate();
|
||||
void EnableTemporalDecimation(bool enable);
|
||||
|
||||
//Get input frame rate
|
||||
uint32_t InputFrameRate();
|
||||
int32_t SetMaxFramerate(uint32_t max_frame_rate);
|
||||
int32_t SetTargetframe_rate(uint32_t frame_rate);
|
||||
|
||||
private:
|
||||
void ProcessIncomingFrameRate(int64_t now);
|
||||
bool DropFrame();
|
||||
|
||||
enum { kFrameCountHistorySize = 90};
|
||||
enum { kFrameHistoryWindowMs = 2000};
|
||||
void UpdateIncomingframe_rate();
|
||||
|
||||
// Temporal decimation
|
||||
int32_t _overShootModifier;
|
||||
uint32_t _dropCount;
|
||||
uint32_t _keepCount;
|
||||
uint32_t _targetFrameRate;
|
||||
float _incomingFrameRate;
|
||||
uint32_t _maxFrameRate;
|
||||
int64_t _incomingFrameTimes[kFrameCountHistorySize];
|
||||
bool _enableTemporalDecimation;
|
||||
// Get Decimated Frame Rate/Dimensions.
|
||||
uint32_t Decimatedframe_rate();
|
||||
|
||||
// Get input frame rate.
|
||||
uint32_t Inputframe_rate();
|
||||
|
||||
private:
|
||||
void ProcessIncomingframe_rate(int64_t now);
|
||||
|
||||
enum { kFrameCountHistory_size = 90};
|
||||
enum { kFrameHistoryWindowMs = 2000};
|
||||
|
||||
// Temporal decimation.
|
||||
int32_t overshoot_modifier_;
|
||||
uint32_t drop_count_;
|
||||
uint32_t keep_count_;
|
||||
uint32_t target_frame_rate_;
|
||||
float incoming_frame_rate_;
|
||||
uint32_t max_frame_rate_;
|
||||
int64_t incoming_frame_times_[kFrameCountHistory_size];
|
||||
bool enable_temporal_decimation_;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
} // namespace webrtc
|
||||
|
||||
#endif
|
||||
#endif // WEBRTC_MODULES_VIDEO_PROCESSING_MAIN_SOURCE_VIDEO_DECIMATOR_H
|
||||
|
@ -17,277 +17,204 @@
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
namespace
|
||||
{
|
||||
void
|
||||
SetSubSampling(VideoProcessingModule::FrameStats* stats,
|
||||
const int32_t width,
|
||||
const int32_t height)
|
||||
{
|
||||
if (width * height >= 640 * 480)
|
||||
{
|
||||
stats->subSamplWidth = 3;
|
||||
stats->subSamplHeight = 3;
|
||||
}
|
||||
else if (width * height >= 352 * 288)
|
||||
{
|
||||
stats->subSamplWidth = 2;
|
||||
stats->subSamplHeight = 2;
|
||||
}
|
||||
else if (width * height >= 176 * 144)
|
||||
{
|
||||
stats->subSamplWidth = 1;
|
||||
stats->subSamplHeight = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
stats->subSamplWidth = 0;
|
||||
stats->subSamplHeight = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
VideoProcessingModule*
|
||||
VideoProcessingModule::Create(const int32_t id)
|
||||
{
|
||||
|
||||
return new VideoProcessingModuleImpl(id);
|
||||
}
|
||||
|
||||
void
|
||||
VideoProcessingModule::Destroy(VideoProcessingModule* module)
|
||||
{
|
||||
if (module)
|
||||
{
|
||||
delete static_cast<VideoProcessingModuleImpl*>(module);
|
||||
}
|
||||
}
|
||||
|
||||
int32_t
|
||||
VideoProcessingModuleImpl::ChangeUniqueId(const int32_t id)
|
||||
{
|
||||
CriticalSectionScoped mutex(&_mutex);
|
||||
_id = id;
|
||||
_brightnessDetection.ChangeUniqueId(id);
|
||||
_deflickering.ChangeUniqueId(id);
|
||||
_denoising.ChangeUniqueId(id);
|
||||
_framePreProcessor.ChangeUniqueId(id);
|
||||
return VPM_OK;
|
||||
}
|
||||
|
||||
int32_t
|
||||
VideoProcessingModuleImpl::Id() const
|
||||
{
|
||||
CriticalSectionScoped mutex(&_mutex);
|
||||
return _id;
|
||||
}
|
||||
|
||||
VideoProcessingModuleImpl::VideoProcessingModuleImpl(const int32_t id) :
|
||||
_id(id),
|
||||
_mutex(*CriticalSectionWrapper::CreateCriticalSection())
|
||||
{
|
||||
_brightnessDetection.ChangeUniqueId(id);
|
||||
_deflickering.ChangeUniqueId(id);
|
||||
_denoising.ChangeUniqueId(id);
|
||||
_framePreProcessor.ChangeUniqueId(id);
|
||||
WEBRTC_TRACE(webrtc::kTraceMemory, webrtc::kTraceVideoPreocessing, _id,
|
||||
"Created");
|
||||
}
|
||||
|
||||
|
||||
VideoProcessingModuleImpl::~VideoProcessingModuleImpl()
|
||||
{
|
||||
WEBRTC_TRACE(webrtc::kTraceMemory, webrtc::kTraceVideoPreocessing, _id,
|
||||
"Destroyed");
|
||||
|
||||
delete &_mutex;
|
||||
}
|
||||
|
||||
void
|
||||
VideoProcessingModuleImpl::Reset()
|
||||
{
|
||||
CriticalSectionScoped mutex(&_mutex);
|
||||
_deflickering.Reset();
|
||||
_denoising.Reset();
|
||||
_brightnessDetection.Reset();
|
||||
_framePreProcessor.Reset();
|
||||
|
||||
}
|
||||
|
||||
int32_t
|
||||
VideoProcessingModule::GetFrameStats(FrameStats* stats,
|
||||
const I420VideoFrame& frame)
|
||||
{
|
||||
if (frame.IsZeroSize())
|
||||
{
|
||||
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoPreocessing, -1,
|
||||
"zero size frame");
|
||||
return VPM_PARAMETER_ERROR;
|
||||
}
|
||||
|
||||
int width = frame.width();
|
||||
int height = frame.height();
|
||||
|
||||
ClearFrameStats(stats); // The histogram needs to be zeroed out.
|
||||
SetSubSampling(stats, width, height);
|
||||
|
||||
const uint8_t* buffer = frame.buffer(kYPlane);
|
||||
// Compute histogram and sum of frame
|
||||
for (int i = 0; i < height; i += (1 << stats->subSamplHeight))
|
||||
{
|
||||
int k = i * width;
|
||||
for (int j = 0; j < width; j += (1 << stats->subSamplWidth))
|
||||
{
|
||||
stats->hist[buffer[k + j]]++;
|
||||
stats->sum += buffer[k + j];
|
||||
}
|
||||
}
|
||||
|
||||
stats->numPixels = (width * height) / ((1 << stats->subSamplWidth) *
|
||||
(1 << stats->subSamplHeight));
|
||||
assert(stats->numPixels > 0);
|
||||
|
||||
// Compute mean value of frame
|
||||
stats->mean = stats->sum / stats->numPixels;
|
||||
|
||||
return VPM_OK;
|
||||
}
|
||||
|
||||
bool
|
||||
VideoProcessingModule::ValidFrameStats(const FrameStats& stats)
|
||||
{
|
||||
if (stats.numPixels == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
VideoProcessingModule::ClearFrameStats(FrameStats* stats)
|
||||
{
|
||||
stats->mean = 0;
|
||||
stats->sum = 0;
|
||||
stats->numPixels = 0;
|
||||
namespace {
|
||||
void SetSubSampling(VideoProcessingModule::FrameStats* stats,
|
||||
const int32_t width,
|
||||
const int32_t height) {
|
||||
if (width * height >= 640 * 480) {
|
||||
stats->subSamplWidth = 3;
|
||||
stats->subSamplHeight = 3;
|
||||
} else if (width * height >= 352 * 288) {
|
||||
stats->subSamplWidth = 2;
|
||||
stats->subSamplHeight = 2;
|
||||
} else if (width * height >= 176 * 144) {
|
||||
stats->subSamplWidth = 1;
|
||||
stats->subSamplHeight = 1;
|
||||
} else {
|
||||
stats->subSamplWidth = 0;
|
||||
stats->subSamplHeight = 0;
|
||||
memset(stats->hist, 0, sizeof(stats->hist));
|
||||
}
|
||||
}
|
||||
|
||||
int32_t
|
||||
VideoProcessingModule::ColorEnhancement(I420VideoFrame* frame)
|
||||
{
|
||||
return VideoProcessing::ColorEnhancement(frame);
|
||||
}
|
||||
|
||||
int32_t
|
||||
VideoProcessingModule::Brighten(I420VideoFrame* frame, int delta)
|
||||
{
|
||||
return VideoProcessing::Brighten(frame, delta);
|
||||
}
|
||||
|
||||
int32_t
|
||||
VideoProcessingModuleImpl::Deflickering(I420VideoFrame* frame,
|
||||
FrameStats* stats)
|
||||
{
|
||||
CriticalSectionScoped mutex(&_mutex);
|
||||
return _deflickering.ProcessFrame(frame, stats);
|
||||
}
|
||||
|
||||
int32_t
|
||||
VideoProcessingModuleImpl::Denoising(I420VideoFrame* frame)
|
||||
{
|
||||
CriticalSectionScoped mutex(&_mutex);
|
||||
return _denoising.ProcessFrame(frame);
|
||||
}
|
||||
|
||||
int32_t
|
||||
VideoProcessingModuleImpl::BrightnessDetection(const I420VideoFrame& frame,
|
||||
const FrameStats& stats)
|
||||
{
|
||||
CriticalSectionScoped mutex(&_mutex);
|
||||
return _brightnessDetection.ProcessFrame(frame, stats);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VideoProcessingModuleImpl::EnableTemporalDecimation(bool enable)
|
||||
{
|
||||
CriticalSectionScoped mutex(&_mutex);
|
||||
_framePreProcessor.EnableTemporalDecimation(enable);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VideoProcessingModuleImpl::SetInputFrameResampleMode(VideoFrameResampling
|
||||
resamplingMode)
|
||||
{
|
||||
CriticalSectionScoped cs(&_mutex);
|
||||
_framePreProcessor.SetInputFrameResampleMode(resamplingMode);
|
||||
}
|
||||
|
||||
int32_t
|
||||
VideoProcessingModuleImpl::SetMaxFrameRate(uint32_t maxFrameRate)
|
||||
{
|
||||
CriticalSectionScoped cs(&_mutex);
|
||||
return _framePreProcessor.SetMaxFrameRate(maxFrameRate);
|
||||
|
||||
}
|
||||
|
||||
int32_t
|
||||
VideoProcessingModuleImpl::SetTargetResolution(uint32_t width,
|
||||
uint32_t height,
|
||||
uint32_t frameRate)
|
||||
{
|
||||
CriticalSectionScoped cs(&_mutex);
|
||||
return _framePreProcessor.SetTargetResolution(width, height, frameRate);
|
||||
}
|
||||
|
||||
|
||||
uint32_t
|
||||
VideoProcessingModuleImpl::DecimatedFrameRate()
|
||||
{
|
||||
CriticalSectionScoped cs(&_mutex);
|
||||
return _framePreProcessor.DecimatedFrameRate();
|
||||
}
|
||||
|
||||
|
||||
uint32_t
|
||||
VideoProcessingModuleImpl::DecimatedWidth() const
|
||||
{
|
||||
CriticalSectionScoped cs(&_mutex);
|
||||
return _framePreProcessor.DecimatedWidth();
|
||||
}
|
||||
|
||||
uint32_t
|
||||
VideoProcessingModuleImpl::DecimatedHeight() const
|
||||
{
|
||||
CriticalSectionScoped cs(&_mutex);
|
||||
return _framePreProcessor.DecimatedHeight();
|
||||
}
|
||||
|
||||
int32_t
|
||||
VideoProcessingModuleImpl::PreprocessFrame(const I420VideoFrame& frame,
|
||||
I420VideoFrame **processedFrame)
|
||||
{
|
||||
CriticalSectionScoped mutex(&_mutex);
|
||||
return _framePreProcessor.PreprocessFrame(frame, processedFrame);
|
||||
}
|
||||
|
||||
VideoContentMetrics*
|
||||
VideoProcessingModuleImpl::ContentMetrics() const
|
||||
{
|
||||
CriticalSectionScoped mutex(&_mutex);
|
||||
return _framePreProcessor.ContentMetrics();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VideoProcessingModuleImpl::EnableContentAnalysis(bool enable)
|
||||
{
|
||||
CriticalSectionScoped mutex(&_mutex);
|
||||
_framePreProcessor.EnableContentAnalysis(enable);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
VideoProcessingModule* VideoProcessingModule::Create(const int32_t id) {
|
||||
return new VideoProcessingModuleImpl(id);
|
||||
}
|
||||
|
||||
void VideoProcessingModule::Destroy(VideoProcessingModule* module) {
|
||||
if (module)
|
||||
delete static_cast<VideoProcessingModuleImpl*>(module);
|
||||
}
|
||||
|
||||
int32_t VideoProcessingModuleImpl::ChangeUniqueId(const int32_t id) {
|
||||
CriticalSectionScoped mutex(&mutex_);
|
||||
id_ = id;
|
||||
brightness_detection_.ChangeUniqueId(id);
|
||||
deflickering_.ChangeUniqueId(id);
|
||||
denoising_.ChangeUniqueId(id);
|
||||
frame_pre_processor_.ChangeUniqueId(id);
|
||||
return VPM_OK;
|
||||
}
|
||||
|
||||
int32_t VideoProcessingModuleImpl::Id() const {
|
||||
CriticalSectionScoped mutex(&mutex_);
|
||||
return id_;
|
||||
}
|
||||
|
||||
VideoProcessingModuleImpl::VideoProcessingModuleImpl(const int32_t id)
|
||||
: id_(id),
|
||||
mutex_(*CriticalSectionWrapper::CreateCriticalSection()) {
|
||||
brightness_detection_.ChangeUniqueId(id);
|
||||
deflickering_.ChangeUniqueId(id);
|
||||
denoising_.ChangeUniqueId(id);
|
||||
frame_pre_processor_.ChangeUniqueId(id);
|
||||
WEBRTC_TRACE(webrtc::kTraceMemory, webrtc::kTraceVideoPreocessing, id_,
|
||||
"Created");
|
||||
}
|
||||
|
||||
VideoProcessingModuleImpl::~VideoProcessingModuleImpl() {
|
||||
WEBRTC_TRACE(webrtc::kTraceMemory, webrtc::kTraceVideoPreocessing, id_,
|
||||
"Destroyed");
|
||||
delete &mutex_;
|
||||
}
|
||||
|
||||
void VideoProcessingModuleImpl::Reset() {
|
||||
CriticalSectionScoped mutex(&mutex_);
|
||||
deflickering_.Reset();
|
||||
denoising_.Reset();
|
||||
brightness_detection_.Reset();
|
||||
frame_pre_processor_.Reset();
|
||||
}
|
||||
|
||||
int32_t VideoProcessingModule::GetFrameStats(FrameStats* stats,
|
||||
const I420VideoFrame& frame) {
|
||||
if (frame.IsZeroSize()) {
|
||||
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoPreocessing, -1,
|
||||
"zero size frame");
|
||||
return VPM_PARAMETER_ERROR;
|
||||
}
|
||||
|
||||
int width = frame.width();
|
||||
int height = frame.height();
|
||||
|
||||
ClearFrameStats(stats); // The histogram needs to be zeroed out.
|
||||
SetSubSampling(stats, width, height);
|
||||
|
||||
const uint8_t* buffer = frame.buffer(kYPlane);
|
||||
// Compute histogram and sum of frame
|
||||
for (int i = 0; i < height; i += (1 << stats->subSamplHeight)) {
|
||||
int k = i * width;
|
||||
for (int j = 0; j < width; j += (1 << stats->subSamplWidth)) {
|
||||
stats->hist[buffer[k + j]]++;
|
||||
stats->sum += buffer[k + j];
|
||||
}
|
||||
}
|
||||
|
||||
stats->num_pixels = (width * height) / ((1 << stats->subSamplWidth) *
|
||||
(1 << stats->subSamplHeight));
|
||||
assert(stats->num_pixels > 0);
|
||||
|
||||
// Compute mean value of frame
|
||||
stats->mean = stats->sum / stats->num_pixels;
|
||||
|
||||
return VPM_OK;
|
||||
}
|
||||
|
||||
bool VideoProcessingModule::ValidFrameStats(const FrameStats& stats) {
|
||||
if (stats.num_pixels == 0) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void VideoProcessingModule::ClearFrameStats(FrameStats* stats) {
|
||||
stats->mean = 0;
|
||||
stats->sum = 0;
|
||||
stats->num_pixels = 0;
|
||||
stats->subSamplWidth = 0;
|
||||
stats->subSamplHeight = 0;
|
||||
memset(stats->hist, 0, sizeof(stats->hist));
|
||||
}
|
||||
|
||||
int32_t VideoProcessingModule::ColorEnhancement(I420VideoFrame* frame) {
|
||||
return VideoProcessing::ColorEnhancement(frame);
|
||||
}
|
||||
|
||||
int32_t VideoProcessingModule::Brighten(I420VideoFrame* frame, int delta) {
|
||||
return VideoProcessing::Brighten(frame, delta);
|
||||
}
|
||||
|
||||
int32_t VideoProcessingModuleImpl::Deflickering(I420VideoFrame* frame,
|
||||
FrameStats* stats) {
|
||||
CriticalSectionScoped mutex(&mutex_);
|
||||
return deflickering_.ProcessFrame(frame, stats);
|
||||
}
|
||||
|
||||
int32_t VideoProcessingModuleImpl::Denoising(I420VideoFrame* frame) {
|
||||
CriticalSectionScoped mutex(&mutex_);
|
||||
return denoising_.ProcessFrame(frame);
|
||||
}
|
||||
|
||||
int32_t VideoProcessingModuleImpl::BrightnessDetection(
|
||||
const I420VideoFrame& frame,
|
||||
const FrameStats& stats) {
|
||||
CriticalSectionScoped mutex(&mutex_);
|
||||
return brightness_detection_.ProcessFrame(frame, stats);
|
||||
}
|
||||
|
||||
|
||||
void VideoProcessingModuleImpl::EnableTemporalDecimation(bool enable) {
|
||||
CriticalSectionScoped mutex(&mutex_);
|
||||
frame_pre_processor_.EnableTemporalDecimation(enable);
|
||||
}
|
||||
|
||||
|
||||
void VideoProcessingModuleImpl::SetInputFrameResampleMode(VideoFrameResampling
|
||||
resampling_mode) {
|
||||
CriticalSectionScoped cs(&mutex_);
|
||||
frame_pre_processor_.SetInputFrameResampleMode(resampling_mode);
|
||||
}
|
||||
|
||||
int32_t VideoProcessingModuleImpl::SetMaxFramerate(uint32_t max_frame_rate) {
|
||||
CriticalSectionScoped cs(&mutex_);
|
||||
return frame_pre_processor_.SetMaxFramerate(max_frame_rate);
|
||||
}
|
||||
|
||||
int32_t VideoProcessingModuleImpl::SetTargetResolution(uint32_t width,
|
||||
uint32_t height,
|
||||
uint32_t frame_rate) {
|
||||
CriticalSectionScoped cs(&mutex_);
|
||||
return frame_pre_processor_.SetTargetResolution(width, height, frame_rate);
|
||||
}
|
||||
|
||||
uint32_t VideoProcessingModuleImpl::Decimatedframe_rate() {
|
||||
CriticalSectionScoped cs(&mutex_);
|
||||
return frame_pre_processor_.Decimatedframe_rate();
|
||||
}
|
||||
|
||||
uint32_t VideoProcessingModuleImpl::DecimatedWidth() const {
|
||||
CriticalSectionScoped cs(&mutex_);
|
||||
return frame_pre_processor_.DecimatedWidth();
|
||||
}
|
||||
|
||||
uint32_t VideoProcessingModuleImpl::DecimatedHeight() const {
|
||||
CriticalSectionScoped cs(&mutex_);
|
||||
return frame_pre_processor_.DecimatedHeight();
|
||||
}
|
||||
|
||||
int32_t VideoProcessingModuleImpl::PreprocessFrame(
|
||||
const I420VideoFrame& frame,
|
||||
I420VideoFrame **processed_frame) {
|
||||
CriticalSectionScoped mutex(&mutex_);
|
||||
return frame_pre_processor_.PreprocessFrame(frame, processed_frame);
|
||||
}
|
||||
|
||||
VideoContentMetrics* VideoProcessingModuleImpl::ContentMetrics() const {
|
||||
CriticalSectionScoped mutex(&mutex_);
|
||||
return frame_pre_processor_.ContentMetrics();
|
||||
}
|
||||
|
||||
void VideoProcessingModuleImpl::EnableContentAnalysis(bool enable) {
|
||||
CriticalSectionScoped mutex(&mutex_);
|
||||
frame_pre_processor_.EnableContentAnalysis(enable);
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
@ -22,67 +22,64 @@
|
||||
namespace webrtc {
|
||||
class CriticalSectionWrapper;
|
||||
|
||||
class VideoProcessingModuleImpl : public VideoProcessingModule
|
||||
{
|
||||
public:
|
||||
class VideoProcessingModuleImpl : public VideoProcessingModule {
|
||||
public:
|
||||
VideoProcessingModuleImpl(int32_t id);
|
||||
|
||||
VideoProcessingModuleImpl(int32_t id);
|
||||
virtual ~VideoProcessingModuleImpl();
|
||||
|
||||
virtual ~VideoProcessingModuleImpl();
|
||||
int32_t Id() const;
|
||||
|
||||
int32_t Id() const;
|
||||
virtual int32_t ChangeUniqueId(const int32_t id);
|
||||
|
||||
virtual int32_t ChangeUniqueId(const int32_t id);
|
||||
virtual void Reset();
|
||||
|
||||
virtual void Reset();
|
||||
virtual int32_t Deflickering(I420VideoFrame* frame, FrameStats* stats);
|
||||
|
||||
virtual int32_t Deflickering(I420VideoFrame* frame, FrameStats* stats);
|
||||
virtual int32_t Denoising(I420VideoFrame* frame);
|
||||
|
||||
virtual int32_t Denoising(I420VideoFrame* frame);
|
||||
virtual int32_t BrightnessDetection(const I420VideoFrame& frame,
|
||||
const FrameStats& stats);
|
||||
|
||||
virtual int32_t BrightnessDetection(const I420VideoFrame& frame,
|
||||
const FrameStats& stats);
|
||||
// Frame pre-processor functions
|
||||
|
||||
//Frame pre-processor functions
|
||||
// Enable temporal decimation
|
||||
virtual void EnableTemporalDecimation(bool enable);
|
||||
|
||||
//Enable temporal decimation
|
||||
virtual void EnableTemporalDecimation(bool enable);
|
||||
virtual void SetInputFrameResampleMode(VideoFrameResampling resampling_mode);
|
||||
|
||||
virtual void SetInputFrameResampleMode(VideoFrameResampling resamplingMode);
|
||||
// Enable content analysis
|
||||
virtual void EnableContentAnalysis(bool enable);
|
||||
|
||||
//Enable content analysis
|
||||
virtual void EnableContentAnalysis(bool enable);
|
||||
// Set max frame rate
|
||||
virtual int32_t SetMaxFramerate(uint32_t max_frame_rate);
|
||||
|
||||
//Set max frame rate
|
||||
virtual int32_t SetMaxFrameRate(uint32_t maxFrameRate);
|
||||
|
||||
// Set Target Resolution: frame rate and dimension
|
||||
virtual int32_t SetTargetResolution(uint32_t width,
|
||||
uint32_t height,
|
||||
uint32_t frameRate);
|
||||
// Set Target Resolution: frame rate and dimension
|
||||
virtual int32_t SetTargetResolution(uint32_t width,
|
||||
uint32_t height,
|
||||
uint32_t frame_rate);
|
||||
|
||||
|
||||
// Get decimated values: frame rate/dimension
|
||||
virtual uint32_t DecimatedFrameRate();
|
||||
virtual uint32_t DecimatedWidth() const;
|
||||
virtual uint32_t DecimatedHeight() const;
|
||||
// Get decimated values: frame rate/dimension
|
||||
virtual uint32_t Decimatedframe_rate();
|
||||
virtual uint32_t DecimatedWidth() const;
|
||||
virtual uint32_t DecimatedHeight() const;
|
||||
|
||||
// Preprocess:
|
||||
// Pre-process incoming frame: Sample when needed and compute content
|
||||
// metrics when enabled.
|
||||
// If no resampling takes place - processedFrame is set to NULL.
|
||||
virtual int32_t PreprocessFrame(const I420VideoFrame& frame,
|
||||
I420VideoFrame** processedFrame);
|
||||
virtual VideoContentMetrics* ContentMetrics() const;
|
||||
// Preprocess:
|
||||
// Pre-process incoming frame: Sample when needed and compute content
|
||||
// metrics when enabled.
|
||||
// If no resampling takes place - processed_frame is set to NULL.
|
||||
virtual int32_t PreprocessFrame(const I420VideoFrame& frame,
|
||||
I420VideoFrame** processed_frame);
|
||||
virtual VideoContentMetrics* ContentMetrics() const;
|
||||
|
||||
private:
|
||||
int32_t _id;
|
||||
CriticalSectionWrapper& _mutex;
|
||||
|
||||
VPMDeflickering _deflickering;
|
||||
VPMDenoising _denoising;
|
||||
VPMBrightnessDetection _brightnessDetection;
|
||||
VPMFramePreprocessor _framePreProcessor;
|
||||
private:
|
||||
int32_t id_;
|
||||
CriticalSectionWrapper& mutex_;
|
||||
VPMDeflickering deflickering_;
|
||||
VPMDenoising denoising_;
|
||||
VPMBrightnessDetection brightness_detection_;
|
||||
VPMFramePreprocessor frame_pre_processor_;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
@ -19,24 +19,24 @@ TEST_F(VideoProcessingModuleTest, BrightnessDetection)
|
||||
uint32_t frameNum = 0;
|
||||
int32_t brightnessWarning = 0;
|
||||
uint32_t warningCount = 0;
|
||||
scoped_array<uint8_t> video_buffer(new uint8_t[_frame_length]);
|
||||
while (fread(video_buffer.get(), 1, _frame_length, _sourceFile) ==
|
||||
_frame_length)
|
||||
scoped_array<uint8_t> video_buffer(new uint8_t[frame_length_]);
|
||||
while (fread(video_buffer.get(), 1, frame_length_, source_file_) ==
|
||||
frame_length_)
|
||||
{
|
||||
EXPECT_EQ(0, ConvertToI420(kI420, video_buffer.get(), 0, 0,
|
||||
_width, _height,
|
||||
0, kRotateNone, &_videoFrame));
|
||||
width_, height_,
|
||||
0, kRotateNone, &video_frame_));
|
||||
frameNum++;
|
||||
VideoProcessingModule::FrameStats stats;
|
||||
ASSERT_EQ(0, _vpm->GetFrameStats(&stats, _videoFrame));
|
||||
ASSERT_GE(brightnessWarning = _vpm->BrightnessDetection(_videoFrame,
|
||||
ASSERT_EQ(0, vpm_->GetFrameStats(&stats, video_frame_));
|
||||
ASSERT_GE(brightnessWarning = vpm_->BrightnessDetection(video_frame_,
|
||||
stats), 0);
|
||||
if (brightnessWarning != VideoProcessingModule::kNoWarning)
|
||||
{
|
||||
warningCount++;
|
||||
}
|
||||
}
|
||||
ASSERT_NE(0, feof(_sourceFile)) << "Error reading source file";
|
||||
ASSERT_NE(0, feof(source_file_)) << "Error reading source file";
|
||||
|
||||
// Expect few warnings
|
||||
float warningProportion = static_cast<float>(warningCount) / frameNum * 100;
|
||||
@ -44,21 +44,21 @@ TEST_F(VideoProcessingModuleTest, BrightnessDetection)
|
||||
printf("Stock foreman: %.1f %%\n", warningProportion);
|
||||
EXPECT_LT(warningProportion, 10);
|
||||
|
||||
rewind(_sourceFile);
|
||||
rewind(source_file_);
|
||||
frameNum = 0;
|
||||
warningCount = 0;
|
||||
while (fread(video_buffer.get(), 1, _frame_length, _sourceFile) ==
|
||||
_frame_length &&
|
||||
while (fread(video_buffer.get(), 1, frame_length_, source_file_) ==
|
||||
frame_length_ &&
|
||||
frameNum < 300)
|
||||
{
|
||||
EXPECT_EQ(0, ConvertToI420(kI420, video_buffer.get(), 0, 0,
|
||||
_width, _height,
|
||||
0, kRotateNone, &_videoFrame));
|
||||
width_, height_,
|
||||
0, kRotateNone, &video_frame_));
|
||||
frameNum++;
|
||||
|
||||
uint8_t* frame = _videoFrame.buffer(kYPlane);
|
||||
uint8_t* frame = video_frame_.buffer(kYPlane);
|
||||
uint32_t yTmp = 0;
|
||||
for (int yIdx = 0; yIdx < _width * _height; yIdx++)
|
||||
for (int yIdx = 0; yIdx < width_ * height_; yIdx++)
|
||||
{
|
||||
yTmp = frame[yIdx] << 1;
|
||||
if (yTmp > 255)
|
||||
@ -69,8 +69,8 @@ TEST_F(VideoProcessingModuleTest, BrightnessDetection)
|
||||
}
|
||||
|
||||
VideoProcessingModule::FrameStats stats;
|
||||
ASSERT_EQ(0, _vpm->GetFrameStats(&stats, _videoFrame));
|
||||
ASSERT_GE(brightnessWarning = _vpm->BrightnessDetection(_videoFrame,
|
||||
ASSERT_EQ(0, vpm_->GetFrameStats(&stats, video_frame_));
|
||||
ASSERT_GE(brightnessWarning = vpm_->BrightnessDetection(video_frame_,
|
||||
stats), 0);
|
||||
EXPECT_NE(VideoProcessingModule::kDarkWarning, brightnessWarning);
|
||||
if (brightnessWarning == VideoProcessingModule::kBrightWarning)
|
||||
@ -78,35 +78,35 @@ TEST_F(VideoProcessingModuleTest, BrightnessDetection)
|
||||
warningCount++;
|
||||
}
|
||||
}
|
||||
ASSERT_NE(0, feof(_sourceFile)) << "Error reading source file";
|
||||
ASSERT_NE(0, feof(source_file_)) << "Error reading source file";
|
||||
|
||||
// Expect many brightness warnings
|
||||
warningProportion = static_cast<float>(warningCount) / frameNum * 100;
|
||||
printf("Bright foreman: %.1f %%\n", warningProportion);
|
||||
EXPECT_GT(warningProportion, 95);
|
||||
|
||||
rewind(_sourceFile);
|
||||
rewind(source_file_);
|
||||
frameNum = 0;
|
||||
warningCount = 0;
|
||||
while (fread(video_buffer.get(), 1, _frame_length, _sourceFile) ==
|
||||
_frame_length && frameNum < 300)
|
||||
while (fread(video_buffer.get(), 1, frame_length_, source_file_) ==
|
||||
frame_length_ && frameNum < 300)
|
||||
{
|
||||
EXPECT_EQ(0, ConvertToI420(kI420, video_buffer.get(), 0, 0,
|
||||
_width, _height,
|
||||
0, kRotateNone, &_videoFrame));
|
||||
width_, height_,
|
||||
0, kRotateNone, &video_frame_));
|
||||
frameNum++;
|
||||
|
||||
uint8_t* y_plane = _videoFrame.buffer(kYPlane);
|
||||
uint8_t* y_plane = video_frame_.buffer(kYPlane);
|
||||
int32_t yTmp = 0;
|
||||
for (int yIdx = 0; yIdx < _width * _height; yIdx++)
|
||||
for (int yIdx = 0; yIdx < width_ * height_; yIdx++)
|
||||
{
|
||||
yTmp = y_plane[yIdx] >> 1;
|
||||
y_plane[yIdx] = static_cast<uint8_t>(yTmp);
|
||||
}
|
||||
|
||||
VideoProcessingModule::FrameStats stats;
|
||||
ASSERT_EQ(0, _vpm->GetFrameStats(&stats, _videoFrame));
|
||||
ASSERT_GE(brightnessWarning = _vpm->BrightnessDetection(_videoFrame,
|
||||
ASSERT_EQ(0, vpm_->GetFrameStats(&stats, video_frame_));
|
||||
ASSERT_GE(brightnessWarning = vpm_->BrightnessDetection(video_frame_,
|
||||
stats), 0);
|
||||
EXPECT_NE(VideoProcessingModule::kBrightWarning, brightnessWarning);
|
||||
if (brightnessWarning == VideoProcessingModule::kDarkWarning)
|
||||
@ -114,7 +114,7 @@ TEST_F(VideoProcessingModuleTest, BrightnessDetection)
|
||||
warningCount++;
|
||||
}
|
||||
}
|
||||
ASSERT_NE(0, feof(_sourceFile)) << "Error reading source file";
|
||||
ASSERT_NE(0, feof(source_file_)) << "Error reading source file";
|
||||
|
||||
// Expect many darkness warnings
|
||||
warningProportion = static_cast<float>(warningCount) / frameNum * 100;
|
||||
|
@ -23,14 +23,14 @@ TEST_F(VideoProcessingModuleTest, ColorEnhancement)
|
||||
{
|
||||
TickTime t0;
|
||||
TickTime t1;
|
||||
TickInterval accTicks;
|
||||
TickInterval acc_ticks;
|
||||
|
||||
// Use a shorter version of the Foreman clip for this test.
|
||||
fclose(_sourceFile);
|
||||
fclose(source_file_);
|
||||
const std::string video_file =
|
||||
webrtc::test::ResourcePath("foreman_cif_short", "yuv");
|
||||
_sourceFile = fopen(video_file.c_str(), "rb");
|
||||
ASSERT_TRUE(_sourceFile != NULL) <<
|
||||
source_file_ = fopen(video_file.c_str(), "rb");
|
||||
ASSERT_TRUE(source_file_ != NULL) <<
|
||||
"Cannot read source file: " + video_file + "\n";
|
||||
|
||||
std::string output_file = webrtc::test::OutputPath() +
|
||||
@ -39,27 +39,27 @@ TEST_F(VideoProcessingModuleTest, ColorEnhancement)
|
||||
ASSERT_TRUE(modFile != NULL) << "Could not open output file.\n";
|
||||
|
||||
uint32_t frameNum = 0;
|
||||
scoped_array<uint8_t> video_buffer(new uint8_t[_frame_length]);
|
||||
while (fread(video_buffer.get(), 1, _frame_length, _sourceFile) ==
|
||||
_frame_length)
|
||||
scoped_array<uint8_t> video_buffer(new uint8_t[frame_length_]);
|
||||
while (fread(video_buffer.get(), 1, frame_length_, source_file_) ==
|
||||
frame_length_)
|
||||
{
|
||||
// Using ConvertToI420 to add stride to the image.
|
||||
EXPECT_EQ(0, ConvertToI420(kI420, video_buffer.get(), 0, 0,
|
||||
_width, _height,
|
||||
0, kRotateNone, &_videoFrame));
|
||||
width_, height_,
|
||||
0, kRotateNone, &video_frame_));
|
||||
frameNum++;
|
||||
t0 = TickTime::Now();
|
||||
ASSERT_EQ(0, VideoProcessingModule::ColorEnhancement(&_videoFrame));
|
||||
ASSERT_EQ(0, VideoProcessingModule::ColorEnhancement(&video_frame_));
|
||||
t1 = TickTime::Now();
|
||||
accTicks += t1 - t0;
|
||||
if (PrintI420VideoFrame(_videoFrame, modFile) < 0) {
|
||||
acc_ticks += t1 - t0;
|
||||
if (PrintI420VideoFrame(video_frame_, modFile) < 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
ASSERT_NE(0, feof(_sourceFile)) << "Error reading source file";
|
||||
ASSERT_NE(0, feof(source_file_)) << "Error reading source file";
|
||||
|
||||
printf("\nTime per frame: %d us \n",
|
||||
static_cast<int>(accTicks.Microseconds() / frameNum));
|
||||
static_cast<int>(acc_ticks.Microseconds() / frameNum));
|
||||
rewind(modFile);
|
||||
|
||||
printf("Comparing files...\n\n");
|
||||
@ -82,62 +82,62 @@ TEST_F(VideoProcessingModuleTest, ColorEnhancement)
|
||||
ASSERT_EQ(refLen, testLen) << "File lengths differ.";
|
||||
|
||||
I420VideoFrame refVideoFrame;
|
||||
refVideoFrame.CreateEmptyFrame(_width, _height,
|
||||
_width, _half_width, _half_width);
|
||||
refVideoFrame.CreateEmptyFrame(width_, height_,
|
||||
width_, half_width_, half_width_);
|
||||
|
||||
// Compare frame-by-frame.
|
||||
scoped_array<uint8_t> ref_buffer(new uint8_t[_frame_length]);
|
||||
while (fread(video_buffer.get(), 1, _frame_length, modFile) ==
|
||||
_frame_length)
|
||||
scoped_array<uint8_t> ref_buffer(new uint8_t[frame_length_]);
|
||||
while (fread(video_buffer.get(), 1, frame_length_, modFile) ==
|
||||
frame_length_)
|
||||
{
|
||||
// Using ConvertToI420 to add stride to the image.
|
||||
EXPECT_EQ(0, ConvertToI420(kI420, video_buffer.get(), 0, 0,
|
||||
_width, _height,
|
||||
0, kRotateNone, &_videoFrame));
|
||||
ASSERT_EQ(_frame_length, fread(ref_buffer.get(), 1, _frame_length,
|
||||
width_, height_,
|
||||
0, kRotateNone, &video_frame_));
|
||||
ASSERT_EQ(frame_length_, fread(ref_buffer.get(), 1, frame_length_,
|
||||
refFile));
|
||||
EXPECT_EQ(0, ConvertToI420(kI420, ref_buffer.get(), 0, 0,
|
||||
_width, _height,
|
||||
width_, height_,
|
||||
0, kRotateNone, &refVideoFrame));
|
||||
EXPECT_EQ(0, memcmp(_videoFrame.buffer(kYPlane),
|
||||
EXPECT_EQ(0, memcmp(video_frame_.buffer(kYPlane),
|
||||
refVideoFrame.buffer(kYPlane),
|
||||
_size_y));
|
||||
EXPECT_EQ(0, memcmp(_videoFrame.buffer(kUPlane),
|
||||
size_y_));
|
||||
EXPECT_EQ(0, memcmp(video_frame_.buffer(kUPlane),
|
||||
refVideoFrame.buffer(kUPlane),
|
||||
_size_uv));
|
||||
EXPECT_EQ(0, memcmp(_videoFrame.buffer(kVPlane),
|
||||
size_uv_));
|
||||
EXPECT_EQ(0, memcmp(video_frame_.buffer(kVPlane),
|
||||
refVideoFrame.buffer(kVPlane),
|
||||
_size_uv));
|
||||
size_uv_));
|
||||
}
|
||||
ASSERT_NE(0, feof(_sourceFile)) << "Error reading source file";
|
||||
ASSERT_NE(0, feof(source_file_)) << "Error reading source file";
|
||||
|
||||
// Verify that all color pixels are enhanced, and no luminance values are
|
||||
// altered.
|
||||
|
||||
scoped_array<uint8_t> testFrame(new uint8_t[_frame_length]);
|
||||
scoped_array<uint8_t> testFrame(new uint8_t[frame_length_]);
|
||||
|
||||
// Use value 128 as probe value, since we know that this will be changed
|
||||
// in the enhancement.
|
||||
memset(testFrame.get(), 128, _frame_length);
|
||||
memset(testFrame.get(), 128, frame_length_);
|
||||
|
||||
I420VideoFrame testVideoFrame;
|
||||
testVideoFrame.CreateEmptyFrame(_width, _height,
|
||||
_width, _half_width, _half_width);
|
||||
testVideoFrame.CreateEmptyFrame(width_, height_,
|
||||
width_, half_width_, half_width_);
|
||||
EXPECT_EQ(0, ConvertToI420(kI420, testFrame.get(), 0, 0,
|
||||
_width, _height, 0, kRotateNone,
|
||||
width_, height_, 0, kRotateNone,
|
||||
&testVideoFrame));
|
||||
|
||||
ASSERT_EQ(0, VideoProcessingModule::ColorEnhancement(&testVideoFrame));
|
||||
|
||||
EXPECT_EQ(0, memcmp(testVideoFrame.buffer(kYPlane), testFrame.get(),
|
||||
_size_y))
|
||||
size_y_))
|
||||
<< "Function is modifying the luminance.";
|
||||
|
||||
EXPECT_NE(0, memcmp(testVideoFrame.buffer(kUPlane),
|
||||
testFrame.get() + _size_y, _size_uv)) <<
|
||||
testFrame.get() + size_y_, size_uv_)) <<
|
||||
"Function is not modifying all chrominance pixels";
|
||||
EXPECT_NE(0, memcmp(testVideoFrame.buffer(kVPlane),
|
||||
testFrame.get() + _size_y + _size_uv, _size_uv)) <<
|
||||
testFrame.get() + size_y_ + size_uv_, size_uv_)) <<
|
||||
"Function is not modifying all chrominance pixels";
|
||||
|
||||
ASSERT_EQ(0, fclose(refFile));
|
||||
|
@ -15,32 +15,30 @@
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
TEST_F(VideoProcessingModuleTest, ContentAnalysis)
|
||||
{
|
||||
VPMContentAnalysis _ca_c(false);
|
||||
VPMContentAnalysis _ca_sse(true);
|
||||
VideoContentMetrics *_cM_c, *_cM_SSE;
|
||||
TEST_F(VideoProcessingModuleTest, ContentAnalysis) {
|
||||
VPMContentAnalysis ca__c(false);
|
||||
VPMContentAnalysis ca__sse(true);
|
||||
VideoContentMetrics *_cM_c, *_cM_SSE;
|
||||
|
||||
_ca_c.Initialize(_width,_height);
|
||||
_ca_sse.Initialize(_width,_height);
|
||||
ca__c.Initialize(width_,height_);
|
||||
ca__sse.Initialize(width_,height_);
|
||||
|
||||
scoped_array<uint8_t> video_buffer(new uint8_t[_frame_length]);
|
||||
while (fread(video_buffer.get(), 1, _frame_length, _sourceFile)
|
||||
== _frame_length)
|
||||
{
|
||||
// Using ConvertToI420 to add stride to the image.
|
||||
EXPECT_EQ(0, ConvertToI420(kI420, video_buffer.get(), 0, 0,
|
||||
_width, _height,
|
||||
0, kRotateNone, &_videoFrame));
|
||||
_cM_c = _ca_c.ComputeContentMetrics(_videoFrame);
|
||||
_cM_SSE = _ca_sse.ComputeContentMetrics(_videoFrame);
|
||||
scoped_array<uint8_t> video_buffer(new uint8_t[frame_length_]);
|
||||
while (fread(video_buffer.get(), 1, frame_length_, source_file_)
|
||||
== frame_length_) {
|
||||
// Using ConvertToI420 to add stride to the image.
|
||||
EXPECT_EQ(0, ConvertToI420(kI420, video_buffer.get(), 0, 0,
|
||||
width_, height_,
|
||||
0, kRotateNone, &video_frame_));
|
||||
_cM_c = ca__c.ComputeContentMetrics(video_frame_);
|
||||
_cM_SSE = ca__sse.ComputeContentMetrics(video_frame_);
|
||||
|
||||
ASSERT_EQ(_cM_c->spatial_pred_err, _cM_SSE->spatial_pred_err);
|
||||
ASSERT_EQ(_cM_c->spatial_pred_err_v, _cM_SSE->spatial_pred_err_v);
|
||||
ASSERT_EQ(_cM_c->spatial_pred_err_h, _cM_SSE->spatial_pred_err_h);
|
||||
ASSERT_EQ(_cM_c->motion_magnitude, _cM_SSE->motion_magnitude);
|
||||
}
|
||||
ASSERT_NE(0, feof(_sourceFile)) << "Error reading source file";
|
||||
ASSERT_EQ(_cM_c->spatial_pred_err, _cM_SSE->spatial_pred_err);
|
||||
ASSERT_EQ(_cM_c->spatial_pred_err_v, _cM_SSE->spatial_pred_err_v);
|
||||
ASSERT_EQ(_cM_c->spatial_pred_err_h, _cM_SSE->spatial_pred_err_h);
|
||||
ASSERT_EQ(_cM_c->motion_magnitude, _cM_SSE->motion_magnitude);
|
||||
}
|
||||
ASSERT_NE(0, feof(source_file_)) << "Error reading source file";
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
@ -23,17 +23,17 @@ TEST_F(VideoProcessingModuleTest, Deflickering)
|
||||
{
|
||||
enum { NumRuns = 30 };
|
||||
uint32_t frameNum = 0;
|
||||
const uint32_t frameRate = 15;
|
||||
const uint32_t frame_rate = 15;
|
||||
|
||||
int64_t minRuntime = 0;
|
||||
int64_t avgRuntime = 0;
|
||||
int64_t min_runtime = 0;
|
||||
int64_t avg_runtime = 0;
|
||||
|
||||
// Close automatically opened Foreman.
|
||||
fclose(_sourceFile);
|
||||
fclose(source_file_);
|
||||
const std::string input_file =
|
||||
webrtc::test::ResourcePath("deflicker_before_cif_short", "yuv");
|
||||
_sourceFile = fopen(input_file.c_str(), "rb");
|
||||
ASSERT_TRUE(_sourceFile != NULL) <<
|
||||
source_file_ = fopen(input_file.c_str(), "rb");
|
||||
ASSERT_TRUE(source_file_ != NULL) <<
|
||||
"Cannot read input file: " << input_file << "\n";
|
||||
|
||||
const std::string output_file =
|
||||
@ -43,57 +43,57 @@ TEST_F(VideoProcessingModuleTest, Deflickering)
|
||||
"Could not open output file: " << output_file << "\n";
|
||||
|
||||
printf("\nRun time [us / frame]:\n");
|
||||
scoped_array<uint8_t> video_buffer(new uint8_t[_frame_length]);
|
||||
for (uint32_t runIdx = 0; runIdx < NumRuns; runIdx++)
|
||||
scoped_array<uint8_t> video_buffer(new uint8_t[frame_length_]);
|
||||
for (uint32_t run_idx = 0; run_idx < NumRuns; run_idx++)
|
||||
{
|
||||
TickTime t0;
|
||||
TickTime t1;
|
||||
TickInterval accTicks;
|
||||
TickInterval acc_ticks;
|
||||
uint32_t timeStamp = 1;
|
||||
|
||||
frameNum = 0;
|
||||
while (fread(video_buffer.get(), 1, _frame_length, _sourceFile) ==
|
||||
_frame_length)
|
||||
while (fread(video_buffer.get(), 1, frame_length_, source_file_) ==
|
||||
frame_length_)
|
||||
{
|
||||
frameNum++;
|
||||
EXPECT_EQ(0, ConvertToI420(kI420, video_buffer.get(), 0, 0,
|
||||
_width, _height,
|
||||
0, kRotateNone, &_videoFrame));
|
||||
_videoFrame.set_timestamp(timeStamp);
|
||||
width_, height_,
|
||||
0, kRotateNone, &video_frame_));
|
||||
video_frame_.set_timestamp(timeStamp);
|
||||
|
||||
t0 = TickTime::Now();
|
||||
VideoProcessingModule::FrameStats stats;
|
||||
ASSERT_EQ(0, _vpm->GetFrameStats(&stats, _videoFrame));
|
||||
ASSERT_EQ(0, _vpm->Deflickering(&_videoFrame, &stats));
|
||||
ASSERT_EQ(0, vpm_->GetFrameStats(&stats, video_frame_));
|
||||
ASSERT_EQ(0, vpm_->Deflickering(&video_frame_, &stats));
|
||||
t1 = TickTime::Now();
|
||||
accTicks += (t1 - t0);
|
||||
acc_ticks += (t1 - t0);
|
||||
|
||||
if (runIdx == 0)
|
||||
if (run_idx == 0)
|
||||
{
|
||||
if (PrintI420VideoFrame(_videoFrame, deflickerFile) < 0) {
|
||||
if (PrintI420VideoFrame(video_frame_, deflickerFile) < 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
timeStamp += (90000 / frameRate);
|
||||
timeStamp += (90000 / frame_rate);
|
||||
}
|
||||
ASSERT_NE(0, feof(_sourceFile)) << "Error reading source file";
|
||||
ASSERT_NE(0, feof(source_file_)) << "Error reading source file";
|
||||
|
||||
printf("%u\n", static_cast<int>(accTicks.Microseconds() / frameNum));
|
||||
if (accTicks.Microseconds() < minRuntime || runIdx == 0)
|
||||
printf("%u\n", static_cast<int>(acc_ticks.Microseconds() / frameNum));
|
||||
if (acc_ticks.Microseconds() < min_runtime || run_idx == 0)
|
||||
{
|
||||
minRuntime = accTicks.Microseconds();
|
||||
min_runtime = acc_ticks.Microseconds();
|
||||
}
|
||||
avgRuntime += accTicks.Microseconds();
|
||||
avg_runtime += acc_ticks.Microseconds();
|
||||
|
||||
rewind(_sourceFile);
|
||||
rewind(source_file_);
|
||||
}
|
||||
ASSERT_EQ(0, fclose(deflickerFile));
|
||||
// TODO(kjellander): Add verification of deflicker output file.
|
||||
|
||||
printf("\nAverage run time = %d us / frame\n",
|
||||
static_cast<int>(avgRuntime / frameNum / NumRuns));
|
||||
static_cast<int>(avg_runtime / frameNum / NumRuns));
|
||||
printf("Min run time = %d us / frame\n\n",
|
||||
static_cast<int>(minRuntime / frameNum));
|
||||
static_cast<int>(min_runtime / frameNum));
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
@ -25,8 +25,8 @@ TEST_F(VideoProcessingModuleTest, DISABLED_ON_ANDROID(Denoising))
|
||||
enum { NumRuns = 10 };
|
||||
uint32_t frameNum = 0;
|
||||
|
||||
int64_t minRuntime = 0;
|
||||
int64_t avgRuntime = 0;
|
||||
int64_t min_runtime = 0;
|
||||
int64_t avg_runtime = 0;
|
||||
|
||||
const std::string denoise_filename =
|
||||
webrtc::test::OutputPath() + "denoise_testfile.yuv";
|
||||
@ -41,50 +41,50 @@ TEST_F(VideoProcessingModuleTest, DISABLED_ON_ANDROID(Denoising))
|
||||
"Could not open noisy file: " << noise_filename << "\n";
|
||||
|
||||
printf("\nRun time [us / frame]:\n");
|
||||
for (uint32_t runIdx = 0; runIdx < NumRuns; runIdx++)
|
||||
for (uint32_t run_idx = 0; run_idx < NumRuns; run_idx++)
|
||||
{
|
||||
TickTime t0;
|
||||
TickTime t1;
|
||||
TickInterval accTicks;
|
||||
TickInterval acc_ticks;
|
||||
int32_t modifiedPixels = 0;
|
||||
|
||||
frameNum = 0;
|
||||
scoped_array<uint8_t> video_buffer(new uint8_t[_frame_length]);
|
||||
while (fread(video_buffer.get(), 1, _frame_length, _sourceFile) ==
|
||||
_frame_length)
|
||||
scoped_array<uint8_t> video_buffer(new uint8_t[frame_length_]);
|
||||
while (fread(video_buffer.get(), 1, frame_length_, source_file_) ==
|
||||
frame_length_)
|
||||
{
|
||||
EXPECT_EQ(0, ConvertToI420(kI420, video_buffer.get(), 0, 0,
|
||||
_width, _height,
|
||||
0, kRotateNone, &_videoFrame));
|
||||
width_, height_,
|
||||
0, kRotateNone, &video_frame_));
|
||||
frameNum++;
|
||||
uint8_t* sourceBuffer = _videoFrame.buffer(kYPlane);
|
||||
uint8_t* sourceBuffer = video_frame_.buffer(kYPlane);
|
||||
|
||||
// Add noise to a part in video stream
|
||||
// Random noise
|
||||
// TODO: investigate the effectiveness of this test.
|
||||
|
||||
for (int ir = 0; ir < _height; ir++)
|
||||
for (int ir = 0; ir < height_; ir++)
|
||||
{
|
||||
uint32_t ik = ir * _width;
|
||||
for (int ic = 0; ic < _width; ic++)
|
||||
uint32_t ik = ir * width_;
|
||||
for (int ic = 0; ic < width_; ic++)
|
||||
{
|
||||
uint8_t r = rand() % 16;
|
||||
r -= 8;
|
||||
if (ir < _height / 4)
|
||||
if (ir < height_ / 4)
|
||||
r = 0;
|
||||
if (ir >= 3 * _height / 4)
|
||||
if (ir >= 3 * height_ / 4)
|
||||
r = 0;
|
||||
if (ic < _width / 4)
|
||||
if (ic < width_ / 4)
|
||||
r = 0;
|
||||
if (ic >= 3 * _width / 4)
|
||||
if (ic >= 3 * width_ / 4)
|
||||
r = 0;
|
||||
|
||||
/*uint8_t pixelValue = 0;
|
||||
if (ir >= _height / 2)
|
||||
if (ir >= height_ / 2)
|
||||
{ // Region 3 or 4
|
||||
pixelValue = 170;
|
||||
}
|
||||
if (ic >= _width / 2)
|
||||
if (ic >= width_ / 2)
|
||||
{ // Region 2 or 4
|
||||
pixelValue += 85;
|
||||
}
|
||||
@ -95,42 +95,42 @@ TEST_F(VideoProcessingModuleTest, DISABLED_ON_ANDROID(Denoising))
|
||||
}
|
||||
}
|
||||
|
||||
if (runIdx == 0)
|
||||
if (run_idx == 0)
|
||||
{
|
||||
if (PrintI420VideoFrame(_videoFrame, noiseFile) < 0) {
|
||||
if (PrintI420VideoFrame(video_frame_, noiseFile) < 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
t0 = TickTime::Now();
|
||||
ASSERT_GE(modifiedPixels = _vpm->Denoising(&_videoFrame), 0);
|
||||
ASSERT_GE(modifiedPixels = vpm_->Denoising(&video_frame_), 0);
|
||||
t1 = TickTime::Now();
|
||||
accTicks += (t1 - t0);
|
||||
acc_ticks += (t1 - t0);
|
||||
|
||||
if (runIdx == 0)
|
||||
if (run_idx == 0)
|
||||
{
|
||||
if (PrintI420VideoFrame(_videoFrame, noiseFile) < 0) {
|
||||
if (PrintI420VideoFrame(video_frame_, noiseFile) < 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
ASSERT_NE(0, feof(_sourceFile)) << "Error reading source file";
|
||||
ASSERT_NE(0, feof(source_file_)) << "Error reading source file";
|
||||
|
||||
printf("%u\n", static_cast<int>(accTicks.Microseconds() / frameNum));
|
||||
if (accTicks.Microseconds() < minRuntime || runIdx == 0)
|
||||
printf("%u\n", static_cast<int>(acc_ticks.Microseconds() / frameNum));
|
||||
if (acc_ticks.Microseconds() < min_runtime || run_idx == 0)
|
||||
{
|
||||
minRuntime = accTicks.Microseconds();
|
||||
min_runtime = acc_ticks.Microseconds();
|
||||
}
|
||||
avgRuntime += accTicks.Microseconds();
|
||||
avg_runtime += acc_ticks.Microseconds();
|
||||
|
||||
rewind(_sourceFile);
|
||||
rewind(source_file_);
|
||||
}
|
||||
ASSERT_EQ(0, fclose(denoiseFile));
|
||||
ASSERT_EQ(0, fclose(noiseFile));
|
||||
printf("\nAverage run time = %d us / frame\n",
|
||||
static_cast<int>(avgRuntime / frameNum / NumRuns));
|
||||
static_cast<int>(avg_runtime / frameNum / NumRuns));
|
||||
printf("Min run time = %d us / frame\n\n",
|
||||
static_cast<int>(minRuntime / frameNum));
|
||||
static_cast<int>(min_runtime / frameNum));
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
@ -18,297 +18,285 @@
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// The |sourceFrame| is scaled to |target_width|,|target_height|, using the
|
||||
// The |sourceFrame| is scaled to |targetwidth_|,|targetheight_|, using the
|
||||
// filter mode set to |mode|. The |expected_psnr| is used to verify basic
|
||||
// quality when the resampled frame is scaled back up/down to the
|
||||
// original/source size. |expected_psnr| is set to be ~0.1/0.05dB lower than
|
||||
// actual PSNR verified under the same conditions.
|
||||
void TestSize(const I420VideoFrame& sourceFrame, int target_width,
|
||||
int target_height, int mode, double expected_psnr,
|
||||
void TestSize(const I420VideoFrame& sourceFrame, int targetwidth_,
|
||||
int targetheight_, int mode, double expected_psnr,
|
||||
VideoProcessingModule* vpm);
|
||||
bool CompareFrames(const webrtc::I420VideoFrame& frame1,
|
||||
const webrtc::I420VideoFrame& frame2);
|
||||
|
||||
VideoProcessingModuleTest::VideoProcessingModuleTest() :
|
||||
_vpm(NULL),
|
||||
_sourceFile(NULL),
|
||||
_width(352),
|
||||
_half_width((_width + 1) / 2),
|
||||
_height(288),
|
||||
_size_y(_width * _height),
|
||||
_size_uv(_half_width * ((_height + 1) / 2)),
|
||||
_frame_length(CalcBufferSize(kI420, _width, _height))
|
||||
{
|
||||
}
|
||||
VideoProcessingModuleTest::VideoProcessingModuleTest()
|
||||
: vpm_(NULL),
|
||||
source_file_(NULL),
|
||||
width_(352),
|
||||
half_width_((width_ + 1) / 2),
|
||||
height_(288),
|
||||
size_y_(width_ * height_),
|
||||
size_uv_(half_width_ * ((height_ + 1) / 2)),
|
||||
frame_length_(CalcBufferSize(kI420, width_, height_)) {}
|
||||
|
||||
void VideoProcessingModuleTest::SetUp()
|
||||
{
|
||||
_vpm = VideoProcessingModule::Create(0);
|
||||
ASSERT_TRUE(_vpm != NULL);
|
||||
void VideoProcessingModuleTest::SetUp() {
|
||||
vpm_ = VideoProcessingModule::Create(0);
|
||||
ASSERT_TRUE(vpm_ != NULL);
|
||||
|
||||
ASSERT_EQ(0, _videoFrame.CreateEmptyFrame(_width, _height, _width,
|
||||
_half_width, _half_width));
|
||||
ASSERT_EQ(0, video_frame_.CreateEmptyFrame(width_, height_, width_,
|
||||
half_width_, half_width_));
|
||||
|
||||
const std::string video_file =
|
||||
webrtc::test::ResourcePath("foreman_cif", "yuv");
|
||||
_sourceFile = fopen(video_file.c_str(),"rb");
|
||||
ASSERT_TRUE(_sourceFile != NULL) <<
|
||||
source_file_ = fopen(video_file.c_str(),"rb");
|
||||
ASSERT_TRUE(source_file_ != NULL) <<
|
||||
"Cannot read source file: " + video_file + "\n";
|
||||
}
|
||||
|
||||
void VideoProcessingModuleTest::TearDown()
|
||||
{
|
||||
if (_sourceFile != NULL) {
|
||||
ASSERT_EQ(0, fclose(_sourceFile));
|
||||
void VideoProcessingModuleTest::TearDown() {
|
||||
if (source_file_ != NULL) {
|
||||
ASSERT_EQ(0, fclose(source_file_));
|
||||
}
|
||||
_sourceFile = NULL;
|
||||
source_file_ = NULL;
|
||||
|
||||
if (_vpm != NULL) {
|
||||
VideoProcessingModule::Destroy(_vpm);
|
||||
if (vpm_ != NULL) {
|
||||
VideoProcessingModule::Destroy(vpm_);
|
||||
}
|
||||
_vpm = NULL;
|
||||
vpm_ = NULL;
|
||||
}
|
||||
|
||||
TEST_F(VideoProcessingModuleTest, HandleNullBuffer)
|
||||
{
|
||||
TEST_F(VideoProcessingModuleTest, HandleNullBuffer) {
|
||||
// TODO(mikhal/stefan): Do we need this one?
|
||||
VideoProcessingModule::FrameStats stats;
|
||||
// Video frame with unallocated buffer.
|
||||
I420VideoFrame videoFrame;
|
||||
videoFrame.set_width(_width);
|
||||
videoFrame.set_height(_height);
|
||||
videoFrame.set_width(width_);
|
||||
videoFrame.set_height(height_);
|
||||
|
||||
EXPECT_EQ(-3, _vpm->GetFrameStats(&stats, videoFrame));
|
||||
EXPECT_EQ(-3, vpm_->GetFrameStats(&stats, videoFrame));
|
||||
|
||||
EXPECT_EQ(-1, _vpm->ColorEnhancement(&videoFrame));
|
||||
EXPECT_EQ(-1, vpm_->ColorEnhancement(&videoFrame));
|
||||
|
||||
EXPECT_EQ(-1, _vpm->Deflickering(&videoFrame, &stats));
|
||||
EXPECT_EQ(-1, vpm_->Deflickering(&videoFrame, &stats));
|
||||
|
||||
EXPECT_EQ(-1, _vpm->Denoising(&videoFrame));
|
||||
EXPECT_EQ(-1, vpm_->Denoising(&videoFrame));
|
||||
|
||||
EXPECT_EQ(-3, _vpm->BrightnessDetection(videoFrame, stats));
|
||||
EXPECT_EQ(-3, vpm_->BrightnessDetection(videoFrame, stats));
|
||||
}
|
||||
|
||||
TEST_F(VideoProcessingModuleTest, HandleBadStats)
|
||||
{
|
||||
TEST_F(VideoProcessingModuleTest, HandleBadStats) {
|
||||
VideoProcessingModule::FrameStats stats;
|
||||
scoped_array<uint8_t> video_buffer(new uint8_t[_frame_length]);
|
||||
ASSERT_EQ(_frame_length, fread(video_buffer.get(), 1, _frame_length,
|
||||
_sourceFile));
|
||||
scoped_array<uint8_t> video_buffer(new uint8_t[frame_length_]);
|
||||
ASSERT_EQ(frame_length_, fread(video_buffer.get(), 1, frame_length_,
|
||||
source_file_));
|
||||
EXPECT_EQ(0, ConvertToI420(kI420, video_buffer.get(), 0, 0,
|
||||
_width, _height,
|
||||
0, kRotateNone, &_videoFrame));
|
||||
width_, height_,
|
||||
0, kRotateNone, &video_frame_));
|
||||
|
||||
EXPECT_EQ(-1, _vpm->Deflickering(&_videoFrame, &stats));
|
||||
EXPECT_EQ(-1, vpm_->Deflickering(&video_frame_, &stats));
|
||||
|
||||
EXPECT_EQ(-3, _vpm->BrightnessDetection(_videoFrame, stats));
|
||||
EXPECT_EQ(-3, vpm_->BrightnessDetection(video_frame_, stats));
|
||||
}
|
||||
|
||||
TEST_F(VideoProcessingModuleTest, HandleBadSize)
|
||||
{
|
||||
TEST_F(VideoProcessingModuleTest, HandleBadSize) {
|
||||
VideoProcessingModule::FrameStats stats;
|
||||
|
||||
_videoFrame.ResetSize();
|
||||
_videoFrame.set_width(_width);
|
||||
_videoFrame.set_height(0);
|
||||
EXPECT_EQ(-3, _vpm->GetFrameStats(&stats, _videoFrame));
|
||||
video_frame_.ResetSize();
|
||||
video_frame_.set_width(width_);
|
||||
video_frame_.set_height(0);
|
||||
EXPECT_EQ(-3, vpm_->GetFrameStats(&stats, video_frame_));
|
||||
|
||||
EXPECT_EQ(-1, _vpm->ColorEnhancement(&_videoFrame));
|
||||
EXPECT_EQ(-1, vpm_->ColorEnhancement(&video_frame_));
|
||||
|
||||
EXPECT_EQ(-1, _vpm->Deflickering(&_videoFrame, &stats));
|
||||
EXPECT_EQ(-1, vpm_->Deflickering(&video_frame_, &stats));
|
||||
|
||||
EXPECT_EQ(-1, _vpm->Denoising(&_videoFrame));
|
||||
EXPECT_EQ(-1, vpm_->Denoising(&video_frame_));
|
||||
|
||||
EXPECT_EQ(-3, _vpm->BrightnessDetection(_videoFrame, stats));
|
||||
EXPECT_EQ(-3, vpm_->BrightnessDetection(video_frame_, stats));
|
||||
|
||||
EXPECT_EQ(VPM_PARAMETER_ERROR, _vpm->SetTargetResolution(0,0,0));
|
||||
EXPECT_EQ(VPM_PARAMETER_ERROR, _vpm->SetMaxFrameRate(0));
|
||||
EXPECT_EQ(VPM_PARAMETER_ERROR, vpm_->SetTargetResolution(0,0,0));
|
||||
EXPECT_EQ(VPM_PARAMETER_ERROR, vpm_->SetMaxFramerate(0));
|
||||
|
||||
I420VideoFrame *outFrame = NULL;
|
||||
EXPECT_EQ(VPM_PARAMETER_ERROR, _vpm->PreprocessFrame(_videoFrame,
|
||||
&outFrame));
|
||||
I420VideoFrame *out_frame = NULL;
|
||||
EXPECT_EQ(VPM_PARAMETER_ERROR, vpm_->PreprocessFrame(video_frame_,
|
||||
&out_frame));
|
||||
}
|
||||
|
||||
TEST_F(VideoProcessingModuleTest, IdenticalResultsAfterReset)
|
||||
{
|
||||
I420VideoFrame videoFrame2;
|
||||
TEST_F(VideoProcessingModuleTest, IdenticalResultsAfterReset) {
|
||||
I420VideoFrame video_frame2;
|
||||
VideoProcessingModule::FrameStats stats;
|
||||
// Only testing non-static functions here.
|
||||
scoped_array<uint8_t> video_buffer(new uint8_t[_frame_length]);
|
||||
ASSERT_EQ(_frame_length, fread(video_buffer.get(), 1, _frame_length,
|
||||
_sourceFile));
|
||||
scoped_array<uint8_t> video_buffer(new uint8_t[frame_length_]);
|
||||
ASSERT_EQ(frame_length_, fread(video_buffer.get(), 1, frame_length_,
|
||||
source_file_));
|
||||
EXPECT_EQ(0, ConvertToI420(kI420, video_buffer.get(), 0, 0,
|
||||
_width, _height,
|
||||
0, kRotateNone, &_videoFrame));
|
||||
ASSERT_EQ(0, _vpm->GetFrameStats(&stats, _videoFrame));
|
||||
ASSERT_EQ(0, videoFrame2.CopyFrame(_videoFrame));
|
||||
ASSERT_EQ(0, _vpm->Deflickering(&_videoFrame, &stats));
|
||||
_vpm->Reset();
|
||||
width_, height_,
|
||||
0, kRotateNone, &video_frame_));
|
||||
ASSERT_EQ(0, vpm_->GetFrameStats(&stats, video_frame_));
|
||||
ASSERT_EQ(0, video_frame2.CopyFrame(video_frame_));
|
||||
ASSERT_EQ(0, vpm_->Deflickering(&video_frame_, &stats));
|
||||
vpm_->Reset();
|
||||
// Retrieve frame stats again in case Deflickering() has zeroed them.
|
||||
ASSERT_EQ(0, _vpm->GetFrameStats(&stats, videoFrame2));
|
||||
ASSERT_EQ(0, _vpm->Deflickering(&videoFrame2, &stats));
|
||||
EXPECT_TRUE(CompareFrames(_videoFrame, videoFrame2));
|
||||
ASSERT_EQ(0, vpm_->GetFrameStats(&stats, video_frame2));
|
||||
ASSERT_EQ(0, vpm_->Deflickering(&video_frame2, &stats));
|
||||
EXPECT_TRUE(CompareFrames(video_frame_, video_frame2));
|
||||
|
||||
ASSERT_EQ(_frame_length, fread(video_buffer.get(), 1, _frame_length,
|
||||
_sourceFile));
|
||||
ASSERT_EQ(frame_length_, fread(video_buffer.get(), 1, frame_length_,
|
||||
source_file_));
|
||||
// Using ConvertToI420 to add stride to the image.
|
||||
EXPECT_EQ(0, ConvertToI420(kI420, video_buffer.get(), 0, 0,
|
||||
_width, _height,
|
||||
0, kRotateNone, &_videoFrame));
|
||||
videoFrame2.CopyFrame(_videoFrame);
|
||||
EXPECT_TRUE(CompareFrames(_videoFrame, videoFrame2));
|
||||
ASSERT_GE(_vpm->Denoising(&_videoFrame), 0);
|
||||
_vpm->Reset();
|
||||
ASSERT_GE(_vpm->Denoising(&videoFrame2), 0);
|
||||
EXPECT_TRUE(CompareFrames(_videoFrame, videoFrame2));
|
||||
width_, height_,
|
||||
0, kRotateNone, &video_frame_));
|
||||
video_frame2.CopyFrame(video_frame_);
|
||||
EXPECT_TRUE(CompareFrames(video_frame_, video_frame2));
|
||||
ASSERT_GE(vpm_->Denoising(&video_frame_), 0);
|
||||
vpm_->Reset();
|
||||
ASSERT_GE(vpm_->Denoising(&video_frame2), 0);
|
||||
EXPECT_TRUE(CompareFrames(video_frame_, video_frame2));
|
||||
|
||||
ASSERT_EQ(_frame_length, fread(video_buffer.get(), 1, _frame_length,
|
||||
_sourceFile));
|
||||
ASSERT_EQ(frame_length_, fread(video_buffer.get(), 1, frame_length_,
|
||||
source_file_));
|
||||
EXPECT_EQ(0, ConvertToI420(kI420, video_buffer.get(), 0, 0,
|
||||
_width, _height,
|
||||
0, kRotateNone, &_videoFrame));
|
||||
ASSERT_EQ(0, _vpm->GetFrameStats(&stats, _videoFrame));
|
||||
videoFrame2.CopyFrame(_videoFrame);
|
||||
ASSERT_EQ(0, _vpm->BrightnessDetection(_videoFrame, stats));
|
||||
_vpm->Reset();
|
||||
ASSERT_EQ(0, _vpm->BrightnessDetection(videoFrame2, stats));
|
||||
EXPECT_TRUE(CompareFrames(_videoFrame, videoFrame2));
|
||||
width_, height_,
|
||||
0, kRotateNone, &video_frame_));
|
||||
ASSERT_EQ(0, vpm_->GetFrameStats(&stats, video_frame_));
|
||||
video_frame2.CopyFrame(video_frame_);
|
||||
ASSERT_EQ(0, vpm_->BrightnessDetection(video_frame_, stats));
|
||||
vpm_->Reset();
|
||||
ASSERT_EQ(0, vpm_->BrightnessDetection(video_frame2, stats));
|
||||
EXPECT_TRUE(CompareFrames(video_frame_, video_frame2));
|
||||
}
|
||||
|
||||
TEST_F(VideoProcessingModuleTest, FrameStats)
|
||||
{
|
||||
TEST_F(VideoProcessingModuleTest, FrameStats) {
|
||||
VideoProcessingModule::FrameStats stats;
|
||||
scoped_array<uint8_t> video_buffer(new uint8_t[_frame_length]);
|
||||
ASSERT_EQ(_frame_length, fread(video_buffer.get(), 1, _frame_length,
|
||||
_sourceFile));
|
||||
scoped_array<uint8_t> video_buffer(new uint8_t[frame_length_]);
|
||||
ASSERT_EQ(frame_length_, fread(video_buffer.get(), 1, frame_length_,
|
||||
source_file_));
|
||||
EXPECT_EQ(0, ConvertToI420(kI420, video_buffer.get(), 0, 0,
|
||||
_width, _height,
|
||||
0, kRotateNone, &_videoFrame));
|
||||
width_, height_,
|
||||
0, kRotateNone, &video_frame_));
|
||||
|
||||
EXPECT_FALSE(_vpm->ValidFrameStats(stats));
|
||||
EXPECT_EQ(0, _vpm->GetFrameStats(&stats, _videoFrame));
|
||||
EXPECT_TRUE(_vpm->ValidFrameStats(stats));
|
||||
EXPECT_FALSE(vpm_->ValidFrameStats(stats));
|
||||
EXPECT_EQ(0, vpm_->GetFrameStats(&stats, video_frame_));
|
||||
EXPECT_TRUE(vpm_->ValidFrameStats(stats));
|
||||
|
||||
printf("\nFrameStats\n");
|
||||
printf("mean: %u\nnumPixels: %u\nsubSamplWidth: "
|
||||
printf("mean: %u\nnum_pixels: %u\nsubSamplWidth: "
|
||||
"%u\nsumSamplHeight: %u\nsum: %u\n\n",
|
||||
static_cast<unsigned int>(stats.mean),
|
||||
static_cast<unsigned int>(stats.numPixels),
|
||||
static_cast<unsigned int>(stats.num_pixels),
|
||||
static_cast<unsigned int>(stats.subSamplHeight),
|
||||
static_cast<unsigned int>(stats.subSamplWidth),
|
||||
static_cast<unsigned int>(stats.sum));
|
||||
|
||||
_vpm->ClearFrameStats(&stats);
|
||||
EXPECT_FALSE(_vpm->ValidFrameStats(stats));
|
||||
vpm_->ClearFrameStats(&stats);
|
||||
EXPECT_FALSE(vpm_->ValidFrameStats(stats));
|
||||
}
|
||||
|
||||
TEST_F(VideoProcessingModuleTest, PreprocessorLogic)
|
||||
{
|
||||
TEST_F(VideoProcessingModuleTest, PreprocessorLogic) {
|
||||
// Disable temporal sampling (frame dropping).
|
||||
_vpm->EnableTemporalDecimation(false);
|
||||
vpm_->EnableTemporalDecimation(false);
|
||||
int resolution = 100;
|
||||
EXPECT_EQ(VPM_OK, _vpm->SetMaxFrameRate(30));
|
||||
EXPECT_EQ(VPM_OK, _vpm->SetTargetResolution(resolution, resolution, 15));
|
||||
EXPECT_EQ(VPM_OK, _vpm->SetTargetResolution(resolution, resolution, 30));
|
||||
EXPECT_EQ(VPM_OK, vpm_->SetMaxFramerate(30));
|
||||
EXPECT_EQ(VPM_OK, vpm_->SetTargetResolution(resolution, resolution, 15));
|
||||
EXPECT_EQ(VPM_OK, vpm_->SetTargetResolution(resolution, resolution, 30));
|
||||
// Disable spatial sampling.
|
||||
_vpm->SetInputFrameResampleMode(kNoRescaling);
|
||||
EXPECT_EQ(VPM_OK, _vpm->SetTargetResolution(resolution, resolution, 30));
|
||||
I420VideoFrame* outFrame = NULL;
|
||||
vpm_->SetInputFrameResampleMode(kNoRescaling);
|
||||
EXPECT_EQ(VPM_OK, vpm_->SetTargetResolution(resolution, resolution, 30));
|
||||
I420VideoFrame* out_frame = NULL;
|
||||
// Set rescaling => output frame != NULL.
|
||||
_vpm->SetInputFrameResampleMode(kFastRescaling);
|
||||
EXPECT_EQ(VPM_OK, _vpm->SetTargetResolution(resolution, resolution, 30));
|
||||
EXPECT_EQ(VPM_OK, _vpm->PreprocessFrame(_videoFrame, &outFrame));
|
||||
EXPECT_FALSE(outFrame == NULL);
|
||||
if (outFrame) {
|
||||
EXPECT_EQ(resolution, outFrame->width());
|
||||
EXPECT_EQ(resolution, outFrame->height());
|
||||
vpm_->SetInputFrameResampleMode(kFastRescaling);
|
||||
EXPECT_EQ(VPM_OK, vpm_->SetTargetResolution(resolution, resolution, 30));
|
||||
EXPECT_EQ(VPM_OK, vpm_->PreprocessFrame(video_frame_, &out_frame));
|
||||
EXPECT_FALSE(out_frame == NULL);
|
||||
if (out_frame) {
|
||||
EXPECT_EQ(resolution, out_frame->width());
|
||||
EXPECT_EQ(resolution, out_frame->height());
|
||||
}
|
||||
// No rescaling=> output frame = NULL.
|
||||
_vpm->SetInputFrameResampleMode(kNoRescaling);
|
||||
EXPECT_EQ(VPM_OK, _vpm->PreprocessFrame(_videoFrame, &outFrame));
|
||||
EXPECT_TRUE(outFrame == NULL);
|
||||
vpm_->SetInputFrameResampleMode(kNoRescaling);
|
||||
EXPECT_EQ(VPM_OK, vpm_->PreprocessFrame(video_frame_, &out_frame));
|
||||
EXPECT_TRUE(out_frame == NULL);
|
||||
}
|
||||
|
||||
TEST_F(VideoProcessingModuleTest, Resampler)
|
||||
{
|
||||
TEST_F(VideoProcessingModuleTest, Resampler) {
|
||||
enum { NumRuns = 1 };
|
||||
|
||||
int64_t minRuntime = 0;
|
||||
int64_t avgRuntime = 0;
|
||||
int64_t min_runtime = 0;
|
||||
int64_t avg_runtime = 0;
|
||||
|
||||
TickTime t0;
|
||||
TickTime t1;
|
||||
TickInterval accTicks;
|
||||
TickInterval acc_ticks;
|
||||
|
||||
rewind(_sourceFile);
|
||||
ASSERT_TRUE(_sourceFile != NULL) <<
|
||||
rewind(source_file_);
|
||||
ASSERT_TRUE(source_file_ != NULL) <<
|
||||
"Cannot read input file \n";
|
||||
|
||||
// CA not needed here
|
||||
_vpm->EnableContentAnalysis(false);
|
||||
vpm_->EnableContentAnalysis(false);
|
||||
// no temporal decimation
|
||||
_vpm->EnableTemporalDecimation(false);
|
||||
vpm_->EnableTemporalDecimation(false);
|
||||
|
||||
// Reading test frame
|
||||
scoped_array<uint8_t> video_buffer(new uint8_t[_frame_length]);
|
||||
ASSERT_EQ(_frame_length, fread(video_buffer.get(), 1, _frame_length,
|
||||
_sourceFile));
|
||||
scoped_array<uint8_t> video_buffer(new uint8_t[frame_length_]);
|
||||
ASSERT_EQ(frame_length_, fread(video_buffer.get(), 1, frame_length_,
|
||||
source_file_));
|
||||
// Using ConvertToI420 to add stride to the image.
|
||||
EXPECT_EQ(0, ConvertToI420(kI420, video_buffer.get(), 0, 0,
|
||||
_width, _height,
|
||||
0, kRotateNone, &_videoFrame));
|
||||
width_, height_,
|
||||
0, kRotateNone, &video_frame_));
|
||||
|
||||
for (uint32_t runIdx = 0; runIdx < NumRuns; runIdx++)
|
||||
{
|
||||
// initiate test timer
|
||||
for (uint32_t run_idx = 0; run_idx < NumRuns; run_idx++) {
|
||||
// Initiate test timer.
|
||||
t0 = TickTime::Now();
|
||||
|
||||
// Init the sourceFrame with a timestamp.
|
||||
_videoFrame.set_render_time_ms(t0.MillisecondTimestamp());
|
||||
_videoFrame.set_timestamp(t0.MillisecondTimestamp() * 90);
|
||||
video_frame_.set_render_time_ms(t0.MillisecondTimestamp());
|
||||
video_frame_.set_timestamp(t0.MillisecondTimestamp() * 90);
|
||||
|
||||
// Test scaling to different sizes: source is of |width|/|height| = 352/288.
|
||||
// Scaling mode in VPM is currently fixed to kScaleBox (mode = 3).
|
||||
TestSize(_videoFrame, 100, 50, 3, 24.0, _vpm);
|
||||
TestSize(_videoFrame, 352/4, 288/4, 3, 25.2, _vpm);
|
||||
TestSize(_videoFrame, 352/2, 288/2, 3, 28.1, _vpm);
|
||||
TestSize(_videoFrame, 352, 288, 3, -1, _vpm); // no resampling.
|
||||
TestSize(_videoFrame, 2*352, 2*288, 3, 32.2, _vpm);
|
||||
TestSize(_videoFrame, 400, 256, 3, 31.3, _vpm);
|
||||
TestSize(_videoFrame, 480, 640, 3, 32.15, _vpm);
|
||||
TestSize(_videoFrame, 960, 720, 3, 32.2, _vpm);
|
||||
TestSize(_videoFrame, 1280, 720, 3, 32.15, _vpm);
|
||||
TestSize(video_frame_, 100, 50, 3, 24.0, vpm_);
|
||||
TestSize(video_frame_, 352/4, 288/4, 3, 25.2, vpm_);
|
||||
TestSize(video_frame_, 352/2, 288/2, 3, 28.1, vpm_);
|
||||
TestSize(video_frame_, 352, 288, 3, -1, vpm_); // no resampling.
|
||||
TestSize(video_frame_, 2*352, 2*288, 3, 32.2, vpm_);
|
||||
TestSize(video_frame_, 400, 256, 3, 31.3, vpm_);
|
||||
TestSize(video_frame_, 480, 640, 3, 32.15, vpm_);
|
||||
TestSize(video_frame_, 960, 720, 3, 32.2, vpm_);
|
||||
TestSize(video_frame_, 1280, 720, 3, 32.15, vpm_);
|
||||
// Upsampling to odd size.
|
||||
TestSize(_videoFrame, 501, 333, 3, 32.05, _vpm);
|
||||
TestSize(video_frame_, 501, 333, 3, 32.05, vpm_);
|
||||
// Downsample to odd size.
|
||||
TestSize(_videoFrame, 281, 175, 3, 29.3, _vpm);
|
||||
TestSize(video_frame_, 281, 175, 3, 29.3, vpm_);
|
||||
|
||||
// stop timer
|
||||
t1 = TickTime::Now();
|
||||
accTicks += (t1 - t0);
|
||||
acc_ticks += (t1 - t0);
|
||||
|
||||
if (accTicks.Microseconds() < minRuntime || runIdx == 0) {
|
||||
minRuntime = accTicks.Microseconds();
|
||||
if (acc_ticks.Microseconds() < min_runtime || run_idx == 0) {
|
||||
min_runtime = acc_ticks.Microseconds();
|
||||
}
|
||||
avgRuntime += accTicks.Microseconds();
|
||||
avg_runtime += acc_ticks.Microseconds();
|
||||
}
|
||||
|
||||
printf("\nAverage run time = %d us / frame\n",
|
||||
//static_cast<int>(avgRuntime / frameNum / NumRuns));
|
||||
static_cast<int>(avgRuntime));
|
||||
//static_cast<int>(avg_runtime / frameNum / NumRuns));
|
||||
static_cast<int>(avg_runtime));
|
||||
printf("Min run time = %d us / frame\n\n",
|
||||
//static_cast<int>(minRuntime / frameNum));
|
||||
static_cast<int>(minRuntime));
|
||||
//static_cast<int>(min_runtime / frameNum));
|
||||
static_cast<int>(min_runtime));
|
||||
}
|
||||
|
||||
void TestSize(const I420VideoFrame& source_frame, int target_width,
|
||||
int target_height, int mode, double expected_psnr,
|
||||
void TestSize(const I420VideoFrame& source_frame, int targetwidth_,
|
||||
int targetheight_, int mode, double expected_psnr,
|
||||
VideoProcessingModule* vpm) {
|
||||
int source_width = source_frame.width();
|
||||
int source_height = source_frame.height();
|
||||
int sourcewidth_ = source_frame.width();
|
||||
int sourceheight_ = source_frame.height();
|
||||
I420VideoFrame* out_frame = NULL;
|
||||
|
||||
ASSERT_EQ(VPM_OK, vpm->SetTargetResolution(target_width, target_height, 30));
|
||||
ASSERT_EQ(VPM_OK, vpm->SetTargetResolution(targetwidth_, targetheight_, 30));
|
||||
ASSERT_EQ(VPM_OK, vpm->PreprocessFrame(source_frame, &out_frame));
|
||||
|
||||
if (out_frame) {
|
||||
@ -321,19 +309,19 @@ void TestSize(const I420VideoFrame& source_frame, int target_width,
|
||||
// (2) scale the resampled frame (|out_frame|) back to the original size and
|
||||
// compute PSNR relative to |source_frame| (for automatic verification).
|
||||
// (3) write out the processed frame for viewing.
|
||||
if (target_width != static_cast<int>(source_width) ||
|
||||
target_height != static_cast<int>(source_height)) {
|
||||
if (targetwidth_ != static_cast<int>(sourcewidth_) ||
|
||||
targetheight_ != static_cast<int>(sourceheight_)) {
|
||||
// Write the processed frame to file for visual inspection.
|
||||
std::ostringstream filename;
|
||||
filename << webrtc::test::OutputPath() << "Resampler_"<< mode << "_" <<
|
||||
"from_" << source_width << "x" << source_height << "_to_" <<
|
||||
target_width << "x" << target_height << "_30Hz_P420.yuv";
|
||||
"from_" << sourcewidth_ << "x" << sourceheight_ << "_to_" <<
|
||||
targetwidth_ << "x" << targetheight_ << "_30Hz_P420.yuv";
|
||||
std::cout << "Watch " << filename.str() << " and verify that it is okay."
|
||||
<< std::endl;
|
||||
FILE* stand_alone_file = fopen(filename.str().c_str(), "wb");
|
||||
if (PrintI420VideoFrame(*out_frame, stand_alone_file) < 0) {
|
||||
fprintf(stderr, "Failed to write frame for scaling to width/height: "
|
||||
" %d %d \n", target_width, target_height);
|
||||
" %d %d \n", targetwidth_, targetheight_);
|
||||
return;
|
||||
}
|
||||
fclose(stand_alone_file);
|
||||
@ -342,8 +330,8 @@ void TestSize(const I420VideoFrame& source_frame, int target_width,
|
||||
resampled_source_frame.CopyFrame(*out_frame);
|
||||
|
||||
// Scale |resampled_source_frame| back to original/source size.
|
||||
ASSERT_EQ(VPM_OK, vpm->SetTargetResolution(source_width,
|
||||
source_height,
|
||||
ASSERT_EQ(VPM_OK, vpm->SetTargetResolution(sourcewidth_,
|
||||
sourceheight_,
|
||||
30));
|
||||
ASSERT_EQ(VPM_OK, vpm->PreprocessFrame(resampled_source_frame,
|
||||
&out_frame));
|
||||
@ -351,14 +339,14 @@ void TestSize(const I420VideoFrame& source_frame, int target_width,
|
||||
// Write the processed frame to file for visual inspection.
|
||||
std::ostringstream filename2;
|
||||
filename2 << webrtc::test::OutputPath() << "Resampler_"<< mode << "_" <<
|
||||
"from_" << target_width << "x" << target_height << "_to_" <<
|
||||
source_width << "x" << source_height << "_30Hz_P420.yuv";
|
||||
"from_" << targetwidth_ << "x" << targetheight_ << "_to_" <<
|
||||
sourcewidth_ << "x" << sourceheight_ << "_30Hz_P420.yuv";
|
||||
std::cout << "Watch " << filename2.str() << " and verify that it is okay."
|
||||
<< std::endl;
|
||||
stand_alone_file = fopen(filename2.str().c_str(), "wb");
|
||||
if (PrintI420VideoFrame(*out_frame, stand_alone_file) < 0) {
|
||||
fprintf(stderr, "Failed to write frame for scaling to width/height "
|
||||
"%d %d \n", source_width, source_height);
|
||||
"%d %d \n", sourcewidth_, sourceheight_);
|
||||
return;
|
||||
}
|
||||
fclose(stand_alone_file);
|
||||
@ -368,7 +356,7 @@ void TestSize(const I420VideoFrame& source_frame, int target_width,
|
||||
EXPECT_GT(psnr, expected_psnr);
|
||||
printf("PSNR: %f. PSNR is between source of size %d %d, and a modified "
|
||||
"source which is scaled down/up to: %d %d, and back to source size \n",
|
||||
psnr, source_width, source_height, target_width, target_height);
|
||||
psnr, sourcewidth_, sourceheight_, targetwidth_, targetheight_);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -18,31 +18,28 @@
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class VideoProcessingModuleTest : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
VideoProcessingModuleTest();
|
||||
virtual void SetUp();
|
||||
virtual void TearDown();
|
||||
static void SetUpTestCase()
|
||||
{
|
||||
Trace::CreateTrace();
|
||||
std::string trace_file = webrtc::test::OutputPath() + "VPMTrace.txt";
|
||||
ASSERT_EQ(0, Trace::SetTraceFile(trace_file.c_str()));
|
||||
}
|
||||
static void TearDownTestCase()
|
||||
{
|
||||
Trace::ReturnTrace();
|
||||
}
|
||||
VideoProcessingModule* _vpm;
|
||||
FILE* _sourceFile;
|
||||
I420VideoFrame _videoFrame;
|
||||
const int _width;
|
||||
const int _half_width;
|
||||
const int _height;
|
||||
const int _size_y;
|
||||
const int _size_uv;
|
||||
const unsigned int _frame_length;
|
||||
class VideoProcessingModuleTest : public ::testing::Test {
|
||||
protected:
|
||||
VideoProcessingModuleTest();
|
||||
virtual void SetUp();
|
||||
virtual void TearDown();
|
||||
static void SetUpTestCase() {
|
||||
Trace::CreateTrace();
|
||||
std::string trace_file = webrtc::test::OutputPath() + "VPMTrace.txt";
|
||||
ASSERT_EQ(0, Trace::SetTraceFile(trace_file.c_str()));
|
||||
}
|
||||
static void TearDownTestCase() {
|
||||
Trace::ReturnTrace();
|
||||
}
|
||||
VideoProcessingModule* vpm_;
|
||||
FILE* source_file_;
|
||||
I420VideoFrame video_frame_;
|
||||
const int width_;
|
||||
const int half_width_;
|
||||
const int height_;
|
||||
const int size_y_;
|
||||
const int size_uv_;
|
||||
const unsigned int frame_length_;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
Loading…
Reference in New Issue
Block a user