Remove remaining quality-analysis (QM).
This was never turned on, contains a lot of complexity and somehow manages triggering a bug in a downstream project. BUG=webrtc:5066 R=marpan@webrtc.org TBR=mflodman@webrtc.org Review URL: https://codereview.webrtc.org/1917323002 . Cr-Commit-Position: refs/heads/master@{#12692}
This commit is contained in:
@ -13,8 +13,6 @@ build_video_processing_sse2 = current_cpu == "x86" || current_cpu == "x64"
|
||||
|
||||
source_set("video_processing") {
|
||||
sources = [
|
||||
"content_analysis.cc",
|
||||
"content_analysis.h",
|
||||
"frame_preprocessor.cc",
|
||||
"frame_preprocessor.h",
|
||||
"include/video_processing.h",
|
||||
@ -63,7 +61,6 @@ source_set("video_processing") {
|
||||
if (build_video_processing_sse2) {
|
||||
source_set("video_processing_sse2") {
|
||||
sources = [
|
||||
"content_analysis_sse2.cc",
|
||||
"util/denoiser_filter_sse2.cc",
|
||||
"util/denoiser_filter_sse2.h",
|
||||
]
|
||||
|
||||
@ -1,280 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
#include "webrtc/modules/video_processing/content_analysis.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "webrtc/system_wrappers/include/cpu_features_wrapper.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
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 defined(WEBRTC_ARCH_X86_FAMILY)
|
||||
if (WebRtc_GetCPUInfo(kSSE2)) {
|
||||
ComputeSpatialMetrics = &VPMContentAnalysis::ComputeSpatialMetrics_SSE2;
|
||||
TemporalDiffMetric = &VPMContentAnalysis::TemporalDiffMetric_SSE2;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
Release();
|
||||
}
|
||||
|
||||
VPMContentAnalysis::~VPMContentAnalysis() {
|
||||
Release();
|
||||
}
|
||||
|
||||
VideoContentMetrics* VPMContentAnalysis::ComputeContentMetrics(
|
||||
const VideoFrame& 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.
|
||||
orig_frame_ = inputFrame.buffer(kYPlane);
|
||||
|
||||
// Compute spatial metrics: 3 spatial prediction errors.
|
||||
(this->*ComputeSpatialMetrics)();
|
||||
|
||||
// Compute motion metrics
|
||||
if (first_frame_ == false)
|
||||
ComputeMotionMetrics();
|
||||
|
||||
// Saving current frame as previous one: Y only.
|
||||
memcpy(prev_frame_, orig_frame_, width_ * height_);
|
||||
|
||||
first_frame_ = false;
|
||||
ca_Init_ = true;
|
||||
|
||||
return ContentMetrics();
|
||||
}
|
||||
|
||||
int32_t VPMContentAnalysis::Release() {
|
||||
if (content_metrics_ != NULL) {
|
||||
delete content_metrics_;
|
||||
content_metrics_ = NULL;
|
||||
}
|
||||
|
||||
if (prev_frame_ != NULL) {
|
||||
delete[] prev_frame_;
|
||||
prev_frame_ = NULL;
|
||||
}
|
||||
|
||||
width_ = 0;
|
||||
height_ = 0;
|
||||
first_frame_ = true;
|
||||
|
||||
return VPM_OK;
|
||||
}
|
||||
|
||||
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.
|
||||
skip_num_ = 1;
|
||||
|
||||
// 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 (content_metrics_ != NULL) {
|
||||
delete content_metrics_;
|
||||
}
|
||||
|
||||
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) {
|
||||
ca_Init_ = false;
|
||||
return VPM_PARAMETER_ERROR;
|
||||
}
|
||||
|
||||
content_metrics_ = new VideoContentMetrics();
|
||||
if (content_metrics_ == NULL) {
|
||||
return VPM_MEMORY;
|
||||
}
|
||||
|
||||
prev_frame_ = new uint8_t[width_ * height_]; // Y only.
|
||||
if (prev_frame_ == NULL)
|
||||
return VPM_MEMORY;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// 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_;
|
||||
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_;
|
||||
|
||||
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;
|
||||
|
||||
uint8_t currPixel = orig_frame_[ssn];
|
||||
uint8_t prevPixel = prev_frame_[ssn];
|
||||
|
||||
tempDiffSum +=
|
||||
static_cast<uint32_t>(abs((int16_t)(currPixel - prevPixel)));
|
||||
pixelSum += static_cast<uint32_t>(currPixel);
|
||||
pixelSqSum += static_cast<uint64_t>(currPixel * currPixel);
|
||||
}
|
||||
}
|
||||
|
||||
// Default.
|
||||
motion_magnitude_ = 0.0f;
|
||||
|
||||
if (tempDiffSum == 0)
|
||||
return VPM_OK;
|
||||
|
||||
// Normalize over all pixels.
|
||||
float const tempDiffAvg =
|
||||
static_cast<float>(tempDiffSum) / static_cast<float>(num_pixels);
|
||||
float const pixelSumAvg =
|
||||
static_cast<float>(pixelSum) / static_cast<float>(num_pixels);
|
||||
float const pixelSqSumAvg =
|
||||
static_cast<float>(pixelSqSum) / static_cast<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:
|
||||
// To reduce complexity, we compute the metric for a reduced set of points.
|
||||
// The spatial metrics are rough estimates of the prediction error cost for
|
||||
// each QM spatial mode: 2x2,1x2,2x1
|
||||
// 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() {
|
||||
const int sizei = height_;
|
||||
const int sizej = width_;
|
||||
|
||||
// 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;
|
||||
|
||||
// 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 += 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
|
||||
|
||||
uint16_t refPixel1 = orig_frame_[ssn1] << 1;
|
||||
uint16_t refPixel2 = orig_frame_[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];
|
||||
|
||||
spatialErrSum += static_cast<uint32_t>(abs(static_cast<int16_t>(
|
||||
refPixel2 - static_cast<uint16_t>(bottPixel + topPixel + leftPixel +
|
||||
rightPixel))));
|
||||
spatialErrVSum += static_cast<uint32_t>(abs(static_cast<int16_t>(
|
||||
refPixel1 - static_cast<uint16_t>(bottPixel + topPixel))));
|
||||
spatialErrHSum += static_cast<uint32_t>(abs(static_cast<int16_t>(
|
||||
refPixel1 - static_cast<uint16_t>(leftPixel + rightPixel))));
|
||||
pixelMSA += orig_frame_[ssn1];
|
||||
}
|
||||
}
|
||||
|
||||
// Normalize over all pixels.
|
||||
const float spatialErr = static_cast<float>(spatialErrSum >> 2);
|
||||
const float spatialErrH = static_cast<float>(spatialErrHSum >> 1);
|
||||
const float spatialErrV = static_cast<float>(spatialErrVSum >> 1);
|
||||
const float norm = static_cast<float>(pixelMSA);
|
||||
|
||||
// 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 (ca_Init_ == false)
|
||||
return NULL;
|
||||
|
||||
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 webrtc
|
||||
@ -1,87 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2012 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 WEBRTC_MODULES_VIDEO_PROCESSING_CONTENT_ANALYSIS_H_
|
||||
#define WEBRTC_MODULES_VIDEO_PROCESSING_CONTENT_ANALYSIS_H_
|
||||
|
||||
#include "webrtc/modules/include/module_common_types.h"
|
||||
#include "webrtc/modules/video_processing/include/video_processing_defines.h"
|
||||
#include "webrtc/typedefs.h"
|
||||
#include "webrtc/video_frame.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
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);
|
||||
|
||||
// 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 VideoFrame& inputFrame);
|
||||
|
||||
// Release all allocated memory
|
||||
// Output: 0 if OK, negative value upon error
|
||||
int32_t Release();
|
||||
|
||||
private:
|
||||
// return motion metrics
|
||||
VideoContentMetrics* ContentMetrics();
|
||||
|
||||
// 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();
|
||||
|
||||
// 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();
|
||||
#endif
|
||||
|
||||
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 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* content_metrics_;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // WEBRTC_MODULES_VIDEO_PROCESSING_CONTENT_ANALYSIS_H_
|
||||
@ -1,271 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "webrtc/modules/video_processing/content_analysis.h"
|
||||
|
||||
#include <emmintrin.h>
|
||||
#include <math.h>
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
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 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();
|
||||
|
||||
for (uint16_t i = 0; i < (height_ - 2 * border_); i += skip_num_) {
|
||||
__m128i sqsum_32 = _mm_setzero_si128();
|
||||
|
||||
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));
|
||||
|
||||
lineO += 16;
|
||||
lineP += 16;
|
||||
|
||||
// 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));
|
||||
|
||||
// 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);
|
||||
|
||||
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_ * skip_num_;
|
||||
imgBufP += width_ * skip_num_;
|
||||
num_pixels += (width_end - border_);
|
||||
}
|
||||
|
||||
__m128i sad_final_128;
|
||||
__m128i sum_final_128;
|
||||
__m128i sqsum_final_128;
|
||||
|
||||
// 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);
|
||||
|
||||
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);
|
||||
|
||||
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];
|
||||
|
||||
// Default.
|
||||
motion_magnitude_ = 0.0f;
|
||||
|
||||
if (tempDiffSum == 0)
|
||||
return VPM_OK;
|
||||
|
||||
// Normalize over all pixels.
|
||||
const float tempDiffAvg =
|
||||
static_cast<float>(tempDiffSum) / static_cast<float>(num_pixels);
|
||||
const float pixelSumAvg =
|
||||
static_cast<float>(pixelSum) / static_cast<float>(num_pixels);
|
||||
const float pixelSqSumAvg =
|
||||
static_cast<float>(pixelSqSum) / static_cast<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 = 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();
|
||||
|
||||
// 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_;
|
||||
|
||||
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;
|
||||
|
||||
// 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));
|
||||
|
||||
// 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));
|
||||
|
||||
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));
|
||||
|
||||
// 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_ * skip_num_;
|
||||
}
|
||||
|
||||
__m128i se_128;
|
||||
__m128i sev_128;
|
||||
__m128i seh_128;
|
||||
__m128i msa_128;
|
||||
|
||||
// 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)));
|
||||
|
||||
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);
|
||||
|
||||
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];
|
||||
|
||||
// Normalize over all pixels.
|
||||
const float spatialErr = static_cast<float>(spatialErrSum >> 2);
|
||||
const float spatialErrH = static_cast<float>(spatialErrHSum >> 1);
|
||||
const float spatialErrV = static_cast<float>(spatialErrVSum >> 1);
|
||||
const float norm = static_cast<float>(pixelMSA);
|
||||
|
||||
// 2X2:
|
||||
spatial_pred_err_ = spatialErr / norm;
|
||||
|
||||
// 1X2:
|
||||
spatial_pred_err_h_ = spatialErrH / norm;
|
||||
|
||||
// 2X1:
|
||||
spatial_pred_err_v_ = spatialErrV / norm;
|
||||
|
||||
return VPM_OK;
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
@ -15,12 +15,8 @@
|
||||
namespace webrtc {
|
||||
|
||||
VPMFramePreprocessor::VPMFramePreprocessor()
|
||||
: content_metrics_(nullptr),
|
||||
resampled_frame_(),
|
||||
enable_ca_(false),
|
||||
frame_cnt_(0) {
|
||||
: resampled_frame_(), frame_cnt_(0) {
|
||||
spatial_resampler_ = new VPMSimpleSpatialResampler();
|
||||
ca_ = new VPMContentAnalysis(true);
|
||||
vd_ = new VPMVideoDecimator();
|
||||
EnableDenoising(false);
|
||||
denoised_frame_toggle_ = 0;
|
||||
@ -28,17 +24,13 @@ VPMFramePreprocessor::VPMFramePreprocessor()
|
||||
|
||||
VPMFramePreprocessor::~VPMFramePreprocessor() {
|
||||
Reset();
|
||||
delete ca_;
|
||||
delete vd_;
|
||||
delete spatial_resampler_;
|
||||
}
|
||||
|
||||
void VPMFramePreprocessor::Reset() {
|
||||
ca_->Release();
|
||||
vd_->Reset();
|
||||
content_metrics_ = nullptr;
|
||||
spatial_resampler_->Reset();
|
||||
enable_ca_ = false;
|
||||
frame_cnt_ = 0;
|
||||
}
|
||||
|
||||
@ -46,10 +38,6 @@ void VPMFramePreprocessor::EnableTemporalDecimation(bool enable) {
|
||||
vd_->EnableTemporalDecimation(enable);
|
||||
}
|
||||
|
||||
void VPMFramePreprocessor::EnableContentAnalysis(bool enable) {
|
||||
enable_ca_ = enable;
|
||||
}
|
||||
|
||||
void VPMFramePreprocessor::SetInputFrameResampleMode(
|
||||
VideoFrameResampling resampling_mode) {
|
||||
spatial_resampler_->SetInputFrameResampleMode(resampling_mode);
|
||||
@ -131,18 +119,8 @@ const VideoFrame* VPMFramePreprocessor::PreprocessFrame(
|
||||
current_frame = &resampled_frame_;
|
||||
}
|
||||
|
||||
// Perform content analysis on the frame to be encoded.
|
||||
if (enable_ca_ && frame_cnt_ % kSkipFrameCA == 0) {
|
||||
// Compute new metrics every |kSkipFramesCA| frames, starting with
|
||||
// the first frame.
|
||||
content_metrics_ = ca_->ComputeContentMetrics(*current_frame);
|
||||
}
|
||||
++frame_cnt_;
|
||||
return current_frame;
|
||||
}
|
||||
|
||||
VideoContentMetrics* VPMFramePreprocessor::GetContentMetrics() const {
|
||||
return content_metrics_;
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
@ -14,7 +14,6 @@
|
||||
#include <memory>
|
||||
|
||||
#include "webrtc/modules/video_processing/include/video_processing.h"
|
||||
#include "webrtc/modules/video_processing/content_analysis.h"
|
||||
#include "webrtc/modules/video_processing/spatial_resampler.h"
|
||||
#include "webrtc/modules/video_processing/video_decimator.h"
|
||||
#include "webrtc/typedefs.h"
|
||||
@ -38,9 +37,6 @@ class VPMFramePreprocessor {
|
||||
|
||||
void SetInputFrameResampleMode(VideoFrameResampling resampling_mode);
|
||||
|
||||
// Enable content analysis.
|
||||
void EnableContentAnalysis(bool enable);
|
||||
|
||||
// Set target resolution: frame rate and dimension.
|
||||
int32_t SetTargetResolution(uint32_t width,
|
||||
uint32_t height,
|
||||
@ -59,21 +55,17 @@ class VPMFramePreprocessor {
|
||||
// Preprocess output:
|
||||
void EnableDenoising(bool enable);
|
||||
const VideoFrame* PreprocessFrame(const VideoFrame& frame);
|
||||
VideoContentMetrics* GetContentMetrics() 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 };
|
||||
|
||||
VideoContentMetrics* content_metrics_;
|
||||
VideoFrame denoised_frame_[2];
|
||||
VideoFrame resampled_frame_;
|
||||
VPMSpatialResampler* spatial_resampler_;
|
||||
VPMContentAnalysis* ca_;
|
||||
VPMVideoDecimator* vd_;
|
||||
std::unique_ptr<VideoDenoiser> denoiser_;
|
||||
bool enable_ca_;
|
||||
uint8_t denoised_frame_toggle_;
|
||||
uint32_t frame_cnt_;
|
||||
};
|
||||
|
||||
@ -53,9 +53,6 @@ class VideoProcessing {
|
||||
|
||||
virtual void EnableDenoising(bool enable) = 0;
|
||||
virtual const VideoFrame* PreprocessFrame(const VideoFrame& frame) = 0;
|
||||
|
||||
virtual VideoContentMetrics* GetContentMetrics() const = 0;
|
||||
virtual void EnableContentAnalysis(bool enable) = 0;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
@ -1,50 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "webrtc/common_video/libyuv/include/webrtc_libyuv.h"
|
||||
#include "webrtc/modules/video_processing/include/video_processing.h"
|
||||
#include "webrtc/modules/video_processing/content_analysis.h"
|
||||
#include "webrtc/modules/video_processing/test/video_processing_unittest.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
#if defined(WEBRTC_IOS)
|
||||
TEST_F(VideoProcessingTest, DISABLED_ContentAnalysis) {
|
||||
#else
|
||||
TEST_F(VideoProcessingTest, ContentAnalysis) {
|
||||
#endif
|
||||
VPMContentAnalysis ca__c(false);
|
||||
VPMContentAnalysis ca__sse(true);
|
||||
VideoContentMetrics* _cM_c;
|
||||
VideoContentMetrics* _cM_SSE;
|
||||
|
||||
ca__c.Initialize(width_, height_);
|
||||
ca__sse.Initialize(width_, height_);
|
||||
|
||||
std::unique_ptr<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, kVideoRotation_0, &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(source_file_)) << "Error reading source file";
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
@ -126,8 +126,6 @@ TEST_F(VideoProcessingTest, Resampler) {
|
||||
rewind(source_file_);
|
||||
ASSERT_TRUE(source_file_ != NULL) << "Cannot read input file \n";
|
||||
|
||||
// CA not needed here
|
||||
vp_->EnableContentAnalysis(false);
|
||||
// no temporal decimation
|
||||
vp_->EnableTemporalDecimation(false);
|
||||
|
||||
|
||||
@ -20,8 +20,6 @@
|
||||
'sources': [
|
||||
'include/video_processing.h',
|
||||
'include/video_processing_defines.h',
|
||||
'content_analysis.cc',
|
||||
'content_analysis.h',
|
||||
'frame_preprocessor.cc',
|
||||
'frame_preprocessor.h',
|
||||
'spatial_resampler.cc',
|
||||
@ -58,7 +56,6 @@
|
||||
'target_name': 'video_processing_sse2',
|
||||
'type': 'static_library',
|
||||
'sources': [
|
||||
'content_analysis_sse2.cc',
|
||||
'util/denoiser_filter_sse2.cc',
|
||||
'util/denoiser_filter_sse2.h',
|
||||
],
|
||||
|
||||
@ -69,14 +69,4 @@ const VideoFrame* VideoProcessingImpl::PreprocessFrame(
|
||||
return frame_pre_processor_.PreprocessFrame(frame);
|
||||
}
|
||||
|
||||
VideoContentMetrics* VideoProcessingImpl::GetContentMetrics() const {
|
||||
rtc::CritScope mutex(&mutex_);
|
||||
return frame_pre_processor_.GetContentMetrics();
|
||||
}
|
||||
|
||||
void VideoProcessingImpl::EnableContentAnalysis(bool enable) {
|
||||
rtc::CritScope mutex(&mutex_);
|
||||
frame_pre_processor_.EnableContentAnalysis(enable);
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
@ -26,7 +26,6 @@ class VideoProcessingImpl : public VideoProcessing {
|
||||
// Implements VideoProcessing.
|
||||
void EnableTemporalDecimation(bool enable) override;
|
||||
void SetInputFrameResampleMode(VideoFrameResampling resampling_mode) override;
|
||||
void EnableContentAnalysis(bool enable) override;
|
||||
int32_t SetTargetResolution(uint32_t width,
|
||||
uint32_t height,
|
||||
uint32_t frame_rate) override;
|
||||
@ -35,7 +34,6 @@ class VideoProcessingImpl : public VideoProcessing {
|
||||
uint32_t GetDecimatedHeight() const override;
|
||||
void EnableDenoising(bool enable) override;
|
||||
const VideoFrame* PreprocessFrame(const VideoFrame& frame) override;
|
||||
VideoContentMetrics* GetContentMetrics() const override;
|
||||
|
||||
private:
|
||||
rtc::CriticalSection mutex_;
|
||||
|
||||
Reference in New Issue
Block a user