Issue #2101 - Part 3: Update libyuv

Updated to version 1861, git revision 88b050f337cc0ca2a51800fe7bf4737222c87344 from
https://chromium.googlesource.com/libyuv/libyuv/
This commit is contained in:
u3shit 2023-02-24 21:56:43 +01:00 • committed by roytam1
commit a4d1f57b9e
222 changed files with 111006 additions and 37106 deletions

View file

@ -16,11 +16,11 @@
extern "C" {
#endif
typedef unsigned int uint32; // NOLINT
typedef unsigned short uint16; // NOLINT
typedef unsigned int uint32_t; // NOLINT
typedef unsigned short uint16_t; // NOLINT
#if !defined(LIBYUV_DISABLE_X86) && !defined(__SSE2__) && \
(defined(_M_X64) || (defined(_M_IX86_FP) && (_M_IX86_FP >= 2)))
(defined(_M_X64) || (defined(_M_IX86_FP) && (_M_IX86_FP >= 2)))
#define __SSE2__
#endif
#if !defined(LIBYUV_DISABLE_X86) && defined(__SSE2__)
@ -38,56 +38,69 @@ enum { KERNEL = 3, KERNEL_SIZE = 2 * KERNEL + 1 };
// The maximum value (11 x 11) must be less than 128 to avoid sign
// problems during the calls to _mm_mullo_epi16().
static const int K[KERNEL_SIZE] = {
1, 3, 7, 11, 7, 3, 1 // ~11 * exp(-0.3 * i * i)
1, 3, 7, 11, 7, 3, 1 // ~11 * exp(-0.3 * i * i)
};
static const double kiW[KERNEL + 1 + 1] = {
1. / 1089., // 1 / sum(i:0..6, j..6) K[i]*K[j]
1. / 1089., // 1 / sum(i:0..6, j..6) K[i]*K[j]
1. / 1056., // 1 / sum(i:0..5, j..6) K[i]*K[j]
1. / 957., // 1 / sum(i:0..4, j..6) K[i]*K[j]
1. / 726., // 1 / sum(i:0..3, j..6) K[i]*K[j]
1. / 1089., // 1 / sum(i:0..6, j..6) K[i]*K[j]
1. / 1089., // 1 / sum(i:0..6, j..6) K[i]*K[j]
1. / 1056., // 1 / sum(i:0..5, j..6) K[i]*K[j]
1. / 957., // 1 / sum(i:0..4, j..6) K[i]*K[j]
1. / 726., // 1 / sum(i:0..3, j..6) K[i]*K[j]
};
#if !defined(LIBYUV_DISABLE_X86) && defined(__SSE2__)
#define PWEIGHT(A, B) static_cast<uint16>(K[(A)] * K[(B)]) // weight product
#define MAKE_WEIGHT(L) \
{ { { PWEIGHT(L, 0), PWEIGHT(L, 1), PWEIGHT(L, 2), PWEIGHT(L, 3), \
PWEIGHT(L, 4), PWEIGHT(L, 5), PWEIGHT(L, 6), 0 } } }
#define PWEIGHT(A, B) static_cast<uint16_t>(K[(A)] * K[(B)]) // weight product
#define MAKE_WEIGHT(L) \
{ \
{ \
{ \
PWEIGHT(L, 0) \
, PWEIGHT(L, 1), PWEIGHT(L, 2), PWEIGHT(L, 3), PWEIGHT(L, 4), \
PWEIGHT(L, 5), PWEIGHT(L, 6), 0 \
} \
} \
}
// We need this union trick to be able to initialize constant static __m128i
// values. We can't call _mm_set_epi16() for static compile-time initialization.
static const struct {
union {
uint16 i16_[8];
uint16_t i16_[8];
__m128i m_;
} values_;
} W0 = MAKE_WEIGHT(0),
W1 = MAKE_WEIGHT(1),
W2 = MAKE_WEIGHT(2),
} W0 = MAKE_WEIGHT(0), W1 = MAKE_WEIGHT(1), W2 = MAKE_WEIGHT(2),
W3 = MAKE_WEIGHT(3);
// ... the rest is symmetric.
// ... the rest is symmetric.
#undef MAKE_WEIGHT
#undef PWEIGHT
#endif
// Common final expression for SSIM, once the weighted sums are known.
static double FinalizeSSIM(double iw, double xm, double ym,
double xxm, double xym, double yym) {
static double FinalizeSSIM(double iw,
double xm,
double ym,
double xxm,
double xym,
double yym) {
const double iwx = xm * iw;
const double iwy = ym * iw;
double sxx = xxm * iw - iwx * iwx;
double syy = yym * iw - iwy * iwy;
// small errors are possible, due to rounding. Clamp to zero.
if (sxx < 0.) sxx = 0.;
if (syy < 0.) syy = 0.;
if (sxx < 0.) {
sxx = 0.;
}
if (syy < 0.) {
syy = 0.;
}
const double sxsy = sqrt(sxx * syy);
const double sxy = xym * iw - iwx * iwy;
static const double C11 = (0.01 * 0.01) * (255 * 255);
static const double C22 = (0.03 * 0.03) * (255 * 255);
static const double C33 = (0.015 * 0.015) * (255 * 255);
const double l = (2. * iwx * iwy + C11) / (iwx * iwx + iwy * iwy + C11);
const double c = (2. * sxsy + C22) / (sxx + syy + C22);
const double c = (2. * sxsy + C22) / (sxx + syy + C22);
const double s = (sxy + C33) / (sxsy + C33);
return l * c * s;
}
@ -98,15 +111,22 @@ static double FinalizeSSIM(double iw, double xm, double ym,
// Note: worst case of accumulation is a weight of 33 = 11 + 2 * (7 + 3 + 1)
// with a diff of 255, squared. The maximum error is thus 0x4388241,
// which fits into 32 bits integers.
double GetSSIM(const uint8 *org, const uint8 *rec,
int xo, int yo, int W, int H, int stride) {
uint32 ws = 0, xm = 0, ym = 0, xxm = 0, xym = 0, yym = 0;
double GetSSIM(const uint8_t* org,
const uint8_t* rec,
int xo,
int yo,
int W,
int H,
int stride) {
uint32_t ws = 0, xm = 0, ym = 0, xxm = 0, xym = 0, yym = 0;
org += (yo - KERNEL) * stride;
org += (xo - KERNEL);
rec += (yo - KERNEL) * stride;
rec += (xo - KERNEL);
for (int y_ = 0; y_ < KERNEL_SIZE; ++y_, org += stride, rec += stride) {
if (((yo - KERNEL + y_) < 0) || ((yo - KERNEL + y_) >= H)) continue;
if (((yo - KERNEL + y_) < 0) || ((yo - KERNEL + y_) >= H)) {
continue;
}
const int Wy = K[y_];
for (int x_ = 0; x_ < KERNEL_SIZE; ++x_) {
const int Wxy = Wy * K[x_];
@ -114,8 +134,8 @@ double GetSSIM(const uint8 *org, const uint8 *rec,
const int org_x = org[x_];
const int rec_x = rec[x_];
ws += Wxy;
xm += Wxy * org_x;
ym += Wxy * rec_x;
xm += Wxy * org_x;
ym += Wxy * rec_x;
xxm += Wxy * org_x * org_x;
xym += Wxy * org_x * rec_x;
yym += Wxy * rec_x * rec_x;
@ -125,10 +145,13 @@ double GetSSIM(const uint8 *org, const uint8 *rec,
return FinalizeSSIM(1. / ws, xm, ym, xxm, xym, yym);
}
double GetSSIMFullKernel(const uint8 *org, const uint8 *rec,
int xo, int yo, int stride,
double GetSSIMFullKernel(const uint8_t* org,
const uint8_t* rec,
int xo,
int yo,
int stride,
double area_weight) {
uint32 xm = 0, ym = 0, xxm = 0, xym = 0, yym = 0;
uint32_t xm = 0, ym = 0, xxm = 0, xym = 0, yym = 0;
#if defined(LIBYUV_DISABLE_X86) || !defined(__SSE2__)
@ -161,8 +184,8 @@ double GetSSIMFullKernel(const uint8 *org, const uint8 *rec,
const int ll2 = rec[dy2 - x];
const int lr2 = rec[dy2 + x];
xm += Wxy * (ul1 + ur1 + ll1 + lr1);
ym += Wxy * (ul2 + ur2 + ll2 + lr2);
xm += Wxy * (ul1 + ur1 + ll1 + lr1);
ym += Wxy * (ul2 + ur2 + ll2 + lr2);
xxm += Wxy * (ul1 * ul1 + ur1 * ur1 + ll1 * ll1 + lr1 * lr1);
xym += Wxy * (ul1 * ul2 + ur1 * ur2 + ll1 * ll2 + lr1 * lr2);
yym += Wxy * (ul2 * ul2 + ur2 * ur2 + ll2 * ll2 + lr2 * lr2);
@ -189,8 +212,8 @@ double GetSSIMFullKernel(const uint8 *org, const uint8 *rec,
const int l2 = rec[-y];
const int r2 = rec[y];
xm += Wxy * (u1 + d1 + l1 + r1);
ym += Wxy * (u2 + d2 + l2 + r2);
xm += Wxy * (u1 + d1 + l1 + r1);
ym += Wxy * (u2 + d2 + l2 + r2);
xxm += Wxy * (u1 * u1 + d1 * d1 + l1 * l1 + r1 * r1);
xym += Wxy * (u1 * u2 + d1 * d2 + l1 * l2 + r1 * r2);
yym += Wxy * (u2 * u2 + d2 * d2 + l2 * l2 + r2 * r2);
@ -201,13 +224,13 @@ double GetSSIMFullKernel(const uint8 *org, const uint8 *rec,
const int s1 = org[0];
const int s2 = rec[0];
xm += Wxy * s1;
ym += Wxy * s2;
xm += Wxy * s1;
ym += Wxy * s2;
xxm += Wxy * s1 * s1;
xym += Wxy * s1 * s2;
yym += Wxy * s2 * s2;
#else // __SSE2__
#else // __SSE2__
org += (yo - KERNEL) * stride + (xo - KERNEL);
rec += (yo - KERNEL) * stride + (xo - KERNEL);
@ -221,29 +244,31 @@ double GetSSIMFullKernel(const uint8 *org, const uint8 *rec,
// Read 8 pixels at line #L, and convert to 16bit, perform weighting
// and acccumulate.
#define LOAD_LINE_PAIR(L, WEIGHT) do { \
const __m128i v0 = \
_mm_loadl_epi64(reinterpret_cast<const __m128i*>(org + (L) * stride)); \
const __m128i v1 = \
_mm_loadl_epi64(reinterpret_cast<const __m128i*>(rec + (L) * stride)); \
const __m128i w0 = _mm_unpacklo_epi8(v0, zero); \
const __m128i w1 = _mm_unpacklo_epi8(v1, zero); \
const __m128i ww0 = _mm_mullo_epi16(w0, (WEIGHT).values_.m_); \
const __m128i ww1 = _mm_mullo_epi16(w1, (WEIGHT).values_.m_); \
x = _mm_add_epi32(x, _mm_unpacklo_epi16(ww0, zero)); \
y = _mm_add_epi32(y, _mm_unpacklo_epi16(ww1, zero)); \
x = _mm_add_epi32(x, _mm_unpackhi_epi16(ww0, zero)); \
y = _mm_add_epi32(y, _mm_unpackhi_epi16(ww1, zero)); \
xx = _mm_add_epi32(xx, _mm_madd_epi16(ww0, w0)); \
xy = _mm_add_epi32(xy, _mm_madd_epi16(ww0, w1)); \
yy = _mm_add_epi32(yy, _mm_madd_epi16(ww1, w1)); \
} while (0)
#define LOAD_LINE_PAIR(L, WEIGHT) \
do { \
const __m128i v0 = \
_mm_loadl_epi64(reinterpret_cast<const __m128i*>(org + (L)*stride)); \
const __m128i v1 = \
_mm_loadl_epi64(reinterpret_cast<const __m128i*>(rec + (L)*stride)); \
const __m128i w0 = _mm_unpacklo_epi8(v0, zero); \
const __m128i w1 = _mm_unpacklo_epi8(v1, zero); \
const __m128i ww0 = _mm_mullo_epi16(w0, (WEIGHT).values_.m_); \
const __m128i ww1 = _mm_mullo_epi16(w1, (WEIGHT).values_.m_); \
x = _mm_add_epi32(x, _mm_unpacklo_epi16(ww0, zero)); \
y = _mm_add_epi32(y, _mm_unpacklo_epi16(ww1, zero)); \
x = _mm_add_epi32(x, _mm_unpackhi_epi16(ww0, zero)); \
y = _mm_add_epi32(y, _mm_unpackhi_epi16(ww1, zero)); \
xx = _mm_add_epi32(xx, _mm_madd_epi16(ww0, w0)); \
xy = _mm_add_epi32(xy, _mm_madd_epi16(ww0, w1)); \
yy = _mm_add_epi32(yy, _mm_madd_epi16(ww1, w1)); \
} while (0)
#define ADD_AND_STORE_FOUR_EPI32(M, OUT) do { \
uint32 tmp[4]; \
_mm_storeu_si128(reinterpret_cast<__m128i*>(tmp), (M)); \
(OUT) = tmp[3] + tmp[2] + tmp[1] + tmp[0]; \
} while (0)
#define ADD_AND_STORE_FOUR_EPI32(M, OUT) \
do { \
uint32_t tmp[4]; \
_mm_storeu_si128(reinterpret_cast<__m128i*>(tmp), (M)); \
(OUT) = tmp[3] + tmp[2] + tmp[1] + tmp[0]; \
} while (0)
LOAD_LINE_PAIR(0, W0);
LOAD_LINE_PAIR(1, W1);
@ -266,10 +291,14 @@ double GetSSIMFullKernel(const uint8 *org, const uint8 *rec,
return FinalizeSSIM(area_weight, xm, ym, xxm, xym, yym);
}
static int start_max(int x, int y) { return (x > y) ? x : y; }
static int start_max(int x, int y) {
return (x > y) ? x : y;
}
double CalcSSIM(const uint8 *org, const uint8 *rec,
const int image_width, const int image_height) {
double CalcSSIM(const uint8_t* org,
const uint8_t* rec,
const int image_width,
const int image_height) {
double SSIM = 0.;
const int KERNEL_Y = (image_height < KERNEL) ? image_height : KERNEL;
const int KERNEL_X = (image_width < KERNEL) ? image_width : KERNEL;
@ -284,7 +313,7 @@ double CalcSSIM(const uint8 *org, const uint8 *rec,
}
#ifdef _OPENMP
#pragma omp parallel for reduction(+: SSIM)
#pragma omp parallel for reduction(+ : SSIM)
#endif
for (int j = KERNEL_Y; j < image_height - KERNEL_Y; ++j) {
for (int i = 0; i < KERNEL_X; ++i) {
@ -302,8 +331,8 @@ double CalcSSIM(const uint8 *org, const uint8 *rec,
// NOTE: we could use similar method for the left-most pixels too.
const int kScratchWidth = 8;
const int kScratchStride = kScratchWidth + KERNEL + 1;
uint8 scratch_org[KERNEL_SIZE * kScratchStride] = { 0 };
uint8 scratch_rec[KERNEL_SIZE * kScratchStride] = { 0 };
uint8_t scratch_org[KERNEL_SIZE * kScratchStride] = {0};
uint8_t scratch_rec[KERNEL_SIZE * kScratchStride] = {0};
for (int k = 0; k < KERNEL_SIZE; ++k) {
const int offset =
@ -311,9 +340,9 @@ double CalcSSIM(const uint8 *org, const uint8 *rec,
memcpy(scratch_org + k * kScratchStride, org + offset, kScratchWidth);
memcpy(scratch_rec + k * kScratchStride, rec + offset, kScratchWidth);
}
for (int k = 0; k <= KERNEL_X + 1; ++k) {
SSIM += GetSSIMFullKernel(scratch_org, scratch_rec,
KERNEL + k, KERNEL, kScratchStride, kiW[k]);
for (int k = 0; k <= KERNEL_X + 1; ++k) {
SSIM += GetSSIMFullKernel(scratch_org, scratch_rec, KERNEL + k, KERNEL,
kScratchStride, kiW[k]);
}
}
}
@ -333,4 +362,3 @@ double CalcLSSIM(double ssim) {
#ifdef __cplusplus
} // extern "C"
#endif