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

@ -4,3 +4,6 @@ ifeq ($(CXX),icl)
else
$(CXX) -msse2 -O3 -fopenmp -static -o psnr psnr.cc ssim.cc psnr_main.cc -Wl,--strip-all
endif
# for MacOS
# /usr/local/bin/g++-7 -msse2 -O3 -fopenmp -Bstatic -o psnr psnr.cc ssim.cc psnr_main.cc

View file

@ -1,37 +0,0 @@
#!/usr/bin/env python
# Copyright 2014 The LibYuv 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.
"""
Runs tests on Android devices.
This script exists to avoid Libyuv being broken by changes in the Chrome Android
test execution toolchain. It also conveniently sets the CHECKOUT_SOURCE_ROOT
environment variable.
"""
import os
import sys
SCRIPT_DIR = os.path.dirname(__file__)
ROOT_DIR = os.path.abspath(os.path.join(SCRIPT_DIR, os.pardir, os.pardir))
CHROMIUM_BUILD_ANDROID_DIR = os.path.join(ROOT_DIR, 'build', 'android')
sys.path.insert(0, CHROMIUM_BUILD_ANDROID_DIR)
import test_runner # pylint: disable=W0406
def main():
# Override environment variable to make it possible for the scripts to find
# the root directory (our symlinking of the Chromium build toolchain would
# otherwise make them fail to do so).
os.environ['CHECKOUT_SOURCE_ROOT'] = ROOT_DIR
return test_runner.main()
if __name__ == '__main__':
sys.exit(main())

120
media/libyuv/util/color.cc Normal file
View file

@ -0,0 +1,120 @@
/*
* Copyright 2021 The LibYuv 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
// This utility computes values needed to generate yuvconstants based on
// white point values.
// The yuv formulas are tuned for 8 bit YUV channels.
// For those MCs that can be represented as kr and kb:
// Full range
// float M[3][3]
// {{1,0,2*(1-kr)},{1,-((2*kb)/((2-kb)*(1-kb-kr))),-((2*kr)/((2-kr)*(1-kb-kr)))},{1,2*(1-kb),0}};
// float B[3]
// {1+(256*(1-kr))/255,1-(256*kb)/(255*(2-kb)*(1-kb-kr))-(256*kr)/(255*(2-kr)*(1-kb-kr)),1+(256*(1-kb))/255};
// Limited range
// float M[3][3]
// {{85/73,0,255/112-(255*kr)/112},{85/73,-((255*kb)/(112*(2-kb)*(1-kb-kr))),-((255*kr)/(112*(2-kr)*(1-kb-kr)))},{85/73,255/112-(255*kb)/112,0}};
// float B[3]
// {77662/43435-(1537*kr)/1785,203/219-(1537*kb)/(1785*(2-kb)*(1-kb-kr))-(1537*kr)/(1785*(2-kr)*(1-kb-kr)),77662/43435-(1537*kb)/1785};
// mc bt
// 1 bt.709 KR = 0.2126; KB = 0.0722
// 4 fcc KR = 0.30; KB = 0.11
// 6 bt.601 KR = 0.299; KB = 0.114
// 7 SMPTE 240M KR = 0.212; KB = 0.087
// 10 bt2020 KR = 0.2627; KB = 0.0593
// BT.709 full range YUV to RGB reference
// R = Y + V * 1.5748
// G = Y - U * 0.18732 - V * 0.46812
// B = Y + U * 1.8556
// KR = 0.2126
// KB = 0.0722
// https://mymusing.co/bt601-yuv-to-rgb-conversion-color/
// // Y contribution to R,G,B. Scale and bias.
// #define YG 16320 /* round(1.000 * 64 * 256 * 256 / 257) */
// #define YB 32 /* 64 / 2 */
//
// // U and V contributions to R,G,B.
// #define UB 113 /* round(1.77200 * 64) */
// #define UG 22 /* round(0.34414 * 64) */
// #define VG 46 /* round(0.71414 * 64) */
// #define VR 90 /* round(1.40200 * 64) */
//
// // Bias values to round, and subtract 128 from U and V.
// #define BB (-UB * 128 + YB)
// #define BG (UG * 128 + VG * 128 + YB)
// #define BR (-VR * 128 + YB)
int round(float v) {
return (int)(v + 0.5);
}
int main(int argc, const char* argv[]) {
if (argc < 2) {
printf("color kr kb\n");
return -1;
}
float kr = atof(argv[1]);
float kb = atof(argv[2]);
float kg = 1 - kr - kb;
float vr = 2 * (1 - kr);
float ug = 2 * ((1 - kb) * kb / kg);
float vg = 2 * ((1 - kr) * kr / kg);
float ub = 2 * (1 - kb);
printf("Full range\n");
printf("R = Y + V * %5f\n", vr);
printf("G = Y - U * %6f - V * %6f\n", ug, vg);
printf("B = Y + U * %5f\n", ub);
printf("KR = %4f; ", kr);
printf("KB = %4f\n", kb);
// printf("KG = %4f\n", kg);
// #define YG 16320 /* round(1.000 * 64 * 256 * 256 / 257) */
// #define YB 32 /* 64 / 2 */
//
// // U and V contributions to R,G,B.
printf("UB %-3d /* round(%f * 64) */\n", round(ub * 64), ub);
printf("UG %-3d /* round(%f * 64) */\n", round(ug * 64), ug);
printf("VG %-3d /* round(%f * 64) */\n", round(vg * 64), vg);
printf("VR %-3d /* round(%f * 64) */\n", round(vr * 64), vr);
vr = 255.f / 224.f * 2 * (1 - kr);
ug = 255.f / 224.f * 2 * ((1 - kb) * kb / kg);
vg = 255.f / 224.f * 2 * ((1 - kr) * kr / kg);
ub = 255.f / 224.f * 2 * (1 - kb);
printf("Limited range\n");
printf("R = (Y - 16) * 1.164 + V * %5f\n", vr);
printf("G = (Y - 16) * 1.164 - U * %6f - V * %6f\n", ug, vg);
printf("B = (Y - 16) * 1.164 + U * %5f\n", ub);
// printf("KG = %4f\n", kg);
// #define YG 16320 /* round(1.000 * 64 * 256 * 256 / 257) */
// #define YB 32 /* 64 / 2 */
//
// // U and V contributions to R,G,B.
printf("UB %-3d /* round(%f * 64) */\n", round(ub * 64), ub);
printf("UG %-3d /* round(%f * 64) */\n", round(ug * 64), ug);
printf("VG %-3d /* round(%f * 64) */\n", round(vg * 64), vg);
printf("VR %-3d /* round(%f * 64) */\n", round(vr * 64), vr);
return 0;
}

View file

@ -29,20 +29,24 @@ int main(int argc, char** argv) {
FILE* fin2 = name2 ? fopen(name2, "rb") : NULL;
const int kBlockSize = 32768;
uint8 buf1[kBlockSize];
uint8 buf2[kBlockSize];
uint32 hash1 = 5381;
uint32 hash2 = 5381;
uint64 sum_square_err = 0;
uint64 size_min = 0;
uint8_t buf1[kBlockSize];
uint8_t buf2[kBlockSize];
uint32_t hash1 = 5381;
uint32_t hash2 = 5381;
uint64_t sum_square_err = 0;
uint64_t size_min = 0;
int amt1 = 0;
int amt2 = 0;
do {
amt1 = static_cast<int>(fread(buf1, 1, kBlockSize, fin1));
if (amt1 > 0) hash1 = libyuv::HashDjb2(buf1, amt1, hash1);
if (amt1 > 0) {
hash1 = libyuv::HashDjb2(buf1, amt1, hash1);
}
if (fin2) {
amt2 = static_cast<int>(fread(buf2, 1, kBlockSize, fin2));
if (amt2 > 0) hash2 = libyuv::HashDjb2(buf2, amt2, hash2);
if (amt2 > 0) {
hash2 = libyuv::HashDjb2(buf2, amt2, hash2);
}
int amt_min = (amt1 < amt2) ? amt1 : amt2;
size_min += amt_min;
sum_square_err += libyuv::ComputeSumSquareError(buf1, buf2, amt_min);
@ -52,8 +56,8 @@ int main(int argc, char** argv) {
printf("hash1 %x", hash1);
if (fin2) {
printf(", hash2 %x", hash2);
double mse = static_cast<double>(sum_square_err) /
static_cast<double>(size_min);
double mse =
static_cast<double>(sum_square_err) / static_cast<double>(size_min);
printf(", mse %.2f", mse);
double psnr = libyuv::SumSquareErrorToPsnr(sum_square_err, size_min);
printf(", psnr %.2f\n", psnr);

View file

@ -12,20 +12,25 @@
#include <stdlib.h>
#include <string.h>
#define INCLUDE_LIBYUV_COMPARE_H_
#include "libyuv.h"
#include "./psnr.h"
#include "./ssim.h"
#include "libyuv/cpu_id.h"
#ifdef __cplusplus
using namespace libyuv;
#endif
int main(int argc, const char* argv[]) {
int cpu_flags = TestCpuFlag(-1);
int has_arm = TestCpuFlag(kCpuHasARM);
int has_mips = TestCpuFlag(kCpuHasMIPS);
int has_x86 = TestCpuFlag(kCpuHasX86);
int has_loongarch = TestCpuFlag(kCpuHasLOONGARCH);
(void)argc;
(void)argv;
#if defined(__i386__) || defined(__x86_64__) || \
defined(_M_IX86) || defined(_M_X64)
if (has_x86) {
uint32 family, model, cpu_info[4];
int family, model, cpu_info[4];
// Vendor ID:
// AuthenticAMD AMD processor
// CentaurHauls Centaur processor
@ -61,13 +66,20 @@ int main(int argc, const char* argv[]) {
printf("Has ARM %x\n", has_arm);
printf("Has MIPS %x\n", has_mips);
printf("Has X86 %x\n", has_x86);
printf("Has LOONGARCH %x\n", has_loongarch);
if (has_arm) {
int has_neon = TestCpuFlag(kCpuHasNEON);
printf("Has NEON %x\n", has_neon);
}
if (has_mips) {
int has_dspr2 = TestCpuFlag(kCpuHasDSPR2);
printf("Has DSPR2 %x\n", has_dspr2);
int has_msa = TestCpuFlag(kCpuHasMSA);
printf("Has MSA %x\n", has_msa);
}
if (has_loongarch) {
int has_lsx = TestCpuFlag(kCpuHasLSX);
printf("Has LSX %x\n", has_lsx);
int has_lasx = TestCpuFlag(kCpuHasLASX);
printf("Has LASX %x\n", has_lasx);
}
if (has_x86) {
int has_sse2 = TestCpuFlag(kCpuHasSSE2);
@ -76,18 +88,34 @@ int main(int argc, const char* argv[]) {
int has_sse42 = TestCpuFlag(kCpuHasSSE42);
int has_avx = TestCpuFlag(kCpuHasAVX);
int has_avx2 = TestCpuFlag(kCpuHasAVX2);
int has_avx3 = TestCpuFlag(kCpuHasAVX3);
int has_erms = TestCpuFlag(kCpuHasERMS);
int has_fma3 = TestCpuFlag(kCpuHasFMA3);
int has_f16c = TestCpuFlag(kCpuHasF16C);
int has_gfni = TestCpuFlag(kCpuHasGFNI);
int has_avx512bw = TestCpuFlag(kCpuHasAVX512BW);
int has_avx512vl = TestCpuFlag(kCpuHasAVX512VL);
int has_avx512vnni = TestCpuFlag(kCpuHasAVX512VNNI);
int has_avx512vbmi = TestCpuFlag(kCpuHasAVX512VBMI);
int has_avx512vbmi2 = TestCpuFlag(kCpuHasAVX512VBMI2);
int has_avx512vbitalg = TestCpuFlag(kCpuHasAVX512VBITALG);
int has_avx512vpopcntdq = TestCpuFlag(kCpuHasAVX512VPOPCNTDQ);
printf("Has SSE2 %x\n", has_sse2);
printf("Has SSSE3 %x\n", has_ssse3);
printf("Has SSE4.1 %x\n", has_sse41);
printf("Has SSE4.2 %x\n", has_sse42);
printf("Has AVX %x\n", has_avx);
printf("Has AVX2 %x\n", has_avx2);
printf("Has AVX3 %x\n", has_avx3);
printf("Has ERMS %x\n", has_erms);
printf("Has FMA3 %x\n", has_fma3);
printf("Has F16C %x\n", has_f16c);
printf("Has GFNI %x\n", has_gfni);
printf("Has AVX512BW %x\n", has_avx512bw);
printf("Has AVX512VL %x\n", has_avx512vl);
printf("Has AVX512VNNI %x\n", has_avx512vnni);
printf("Has AVX512VBMI %x\n", has_avx512vbmi);
printf("Has AVX512VBMI2 %x\n", has_avx512vbmi2);
printf("Has AVX512VBITALG %x\n", has_avx512vbitalg);
printf("Has AVX512VPOPCNTDQ %x\n", has_avx512vpopcntdq);
}
return 0;
}

View file

@ -0,0 +1,28 @@
#include "libyuv/convert.h"
#include <stdio.h> // for printf
#include <string.h> // for memset
int main(int, char**) {
unsigned char src_i444[640 * 400 * 3];
unsigned char dst_nv12[640 * 400 * 3 / 2];
for (size_t i = 0; i < sizeof(src_i444); ++i) {
src_i444[i] = i & 255;
}
memset(dst_nv12, 0, sizeof(dst_nv12));
libyuv::I444ToNV12(&src_i444[0], 640, // source Y
&src_i444[640 * 400], 640, // source U
&src_i444[640 * 400 * 2], 640, // source V
&dst_nv12[0], 640, // dest Y
&dst_nv12[640 * 400], 640, // dest UV
640, 400); // width and height
int checksum = 0;
for (size_t i = 0; i < sizeof(dst_nv12); ++i) {
checksum += dst_nv12[i];
}
printf("checksum %x %s\n", checksum, checksum == 0x2ec0c00 ? "PASS" : "FAIL");
return 0;
}

View file

@ -21,14 +21,14 @@
extern "C" {
#endif
typedef unsigned int uint32; // NOLINT
typedef unsigned int uint32_t; // NOLINT
#ifdef _MSC_VER
typedef unsigned __int64 uint64;
typedef unsigned __int64 uint64_t;
#else // COMPILER_MSVC
#if defined(__LP64__) && !defined(__OpenBSD__) && !defined(__APPLE__)
typedef unsigned long uint64; // NOLINT
#else // defined(__LP64__) && !defined(__OpenBSD__) && !defined(__APPLE__)
typedef unsigned long long uint64; // NOLINT
typedef unsigned long uint64_t; // NOLINT
#else // defined(__LP64__) && !defined(__OpenBSD__) && !defined(__APPLE__)
typedef unsigned long long uint64_t; // NOLINT
#endif // __LP64__
#endif // _MSC_VER
@ -38,86 +38,82 @@ typedef unsigned long long uint64; // NOLINT
#if !defined(LIBYUV_DISABLE_NEON) && defined(__ARM_NEON__) && \
!defined(__aarch64__)
#define HAS_SUMSQUAREERROR_NEON
static uint32 SumSquareError_NEON(const uint8* src_a,
const uint8* src_b, int count) {
volatile uint32 sse;
asm volatile (
"vmov.u8 q7, #0 \n"
"vmov.u8 q9, #0 \n"
"vmov.u8 q8, #0 \n"
"vmov.u8 q10, #0 \n"
static uint32_t SumSquareError_NEON(const uint8_t* src_a,
const uint8_t* src_b,
int count) {
volatile uint32_t sse;
asm volatile(
"vmov.u8 q7, #0 \n"
"vmov.u8 q9, #0 \n"
"vmov.u8 q8, #0 \n"
"vmov.u8 q10, #0 \n"
"1: \n"
"vld1.u8 {q0}, [%0]! \n"
"vld1.u8 {q1}, [%1]! \n"
"vsubl.u8 q2, d0, d2 \n"
"vsubl.u8 q3, d1, d3 \n"
"vmlal.s16 q7, d4, d4 \n"
"vmlal.s16 q8, d6, d6 \n"
"vmlal.s16 q8, d5, d5 \n"
"vmlal.s16 q10, d7, d7 \n"
"subs %2, %2, #16 \n"
"bhi 1b \n"
"1: \n"
"vld1.u8 {q0}, [%0]! \n"
"vld1.u8 {q1}, [%1]! \n"
"vsubl.u8 q2, d0, d2 \n"
"vsubl.u8 q3, d1, d3 \n"
"vmlal.s16 q7, d4, d4 \n"
"vmlal.s16 q8, d6, d6 \n"
"vmlal.s16 q8, d5, d5 \n"
"vmlal.s16 q10, d7, d7 \n"
"subs %2, %2, #16 \n"
"bhi 1b \n"
"vadd.u32 q7, q7, q8 \n"
"vadd.u32 q9, q9, q10 \n"
"vadd.u32 q10, q7, q9 \n"
"vpaddl.u32 q1, q10 \n"
"vadd.u64 d0, d2, d3 \n"
"vmov.32 %3, d0[0] \n"
: "+r"(src_a),
"+r"(src_b),
"+r"(count),
"=r"(sse)
:
: "memory", "cc", "q0", "q1", "q2", "q3", "q7", "q8", "q9", "q10");
"vadd.u32 q7, q7, q8 \n"
"vadd.u32 q9, q9, q10 \n"
"vadd.u32 q10, q7, q9 \n"
"vpaddl.u32 q1, q10 \n"
"vadd.u64 d0, d2, d3 \n"
"vmov.32 %3, d0[0] \n"
: "+r"(src_a), "+r"(src_b), "+r"(count), "=r"(sse)
:
: "memory", "cc", "q0", "q1", "q2", "q3", "q7", "q8", "q9", "q10");
return sse;
}
#elif !defined(LIBYUV_DISABLE_NEON) && defined(__aarch64__)
#define HAS_SUMSQUAREERROR_NEON
static uint32 SumSquareError_NEON(const uint8* src_a,
const uint8* src_b, int count) {
volatile uint32 sse;
asm volatile (
"eor v16.16b, v16.16b, v16.16b \n"
"eor v18.16b, v18.16b, v18.16b \n"
"eor v17.16b, v17.16b, v17.16b \n"
"eor v19.16b, v19.16b, v19.16b \n"
static uint32_t SumSquareError_NEON(const uint8_t* src_a,
const uint8_t* src_b,
int count) {
volatile uint32_t sse;
asm volatile(
"eor v16.16b, v16.16b, v16.16b \n"
"eor v18.16b, v18.16b, v18.16b \n"
"eor v17.16b, v17.16b, v17.16b \n"
"eor v19.16b, v19.16b, v19.16b \n"
"1: \n"
"ld1 {v0.16b}, [%0], #16 \n"
"ld1 {v1.16b}, [%1], #16 \n"
"subs %w2, %w2, #16 \n"
"usubl v2.8h, v0.8b, v1.8b \n"
"usubl2 v3.8h, v0.16b, v1.16b \n"
"smlal v16.4s, v2.4h, v2.4h \n"
"smlal v17.4s, v3.4h, v3.4h \n"
"smlal2 v18.4s, v2.8h, v2.8h \n"
"smlal2 v19.4s, v3.8h, v3.8h \n"
"b.gt 1b \n"
"1: \n"
"ld1 {v0.16b}, [%0], #16 \n"
"ld1 {v1.16b}, [%1], #16 \n"
"subs %w2, %w2, #16 \n"
"usubl v2.8h, v0.8b, v1.8b \n"
"usubl2 v3.8h, v0.16b, v1.16b \n"
"smlal v16.4s, v2.4h, v2.4h \n"
"smlal v17.4s, v3.4h, v3.4h \n"
"smlal2 v18.4s, v2.8h, v2.8h \n"
"smlal2 v19.4s, v3.8h, v3.8h \n"
"b.gt 1b \n"
"add v16.4s, v16.4s, v17.4s \n"
"add v18.4s, v18.4s, v19.4s \n"
"add v19.4s, v16.4s, v18.4s \n"
"addv s0, v19.4s \n"
"fmov %w3, s0 \n"
: "+r"(src_a),
"+r"(src_b),
"+r"(count),
"=r"(sse)
:
: "cc", "v0", "v1", "v2", "v3", "v16", "v17", "v18", "v19");
"add v16.4s, v16.4s, v17.4s \n"
"add v18.4s, v18.4s, v19.4s \n"
"add v19.4s, v16.4s, v18.4s \n"
"addv s0, v19.4s \n"
"fmov %w3, s0 \n"
: "+r"(src_a), "+r"(src_b), "+r"(count), "=r"(sse)
:
: "cc", "v0", "v1", "v2", "v3", "v16", "v17", "v18", "v19");
return sse;
}
#elif !defined(LIBYUV_DISABLE_X86) && defined(_M_IX86) && defined(_MSC_VER)
#define HAS_SUMSQUAREERROR_SSE2
__declspec(naked)
static uint32 SumSquareError_SSE2(const uint8* /*src_a*/,
const uint8* /*src_b*/, int /*count*/) {
__declspec(naked) static uint32_t SumSquareError_SSE2(const uint8_t* /*src_a*/,
const uint8_t* /*src_b*/,
int /*count*/) {
__asm {
mov eax, [esp + 4] // src_a
mov edx, [esp + 8] // src_b
mov ecx, [esp + 12] // count
mov eax, [esp + 4] // src_a
mov edx, [esp + 8] // src_b
mov ecx, [esp + 12] // count
pxor xmm0, xmm0
pxor xmm5, xmm5
sub edx, eax
@ -150,46 +146,48 @@ static uint32 SumSquareError_SSE2(const uint8* /*src_a*/,
}
#elif !defined(LIBYUV_DISABLE_X86) && (defined(__x86_64__) || defined(__i386__))
#define HAS_SUMSQUAREERROR_SSE2
static uint32 SumSquareError_SSE2(const uint8* src_a,
const uint8* src_b, int count) {
uint32 sse;
asm volatile ( // NOLINT
"pxor %%xmm0,%%xmm0 \n"
"pxor %%xmm5,%%xmm5 \n"
"sub %0,%1 \n"
static uint32_t SumSquareError_SSE2(const uint8_t* src_a,
const uint8_t* src_b,
int count) {
uint32_t sse;
asm volatile( // NOLINT
"pxor %%xmm0,%%xmm0 \n"
"pxor %%xmm5,%%xmm5 \n"
"sub %0,%1 \n"
"1: \n"
"movdqu (%0),%%xmm1 \n"
"movdqu (%0,%1,1),%%xmm2 \n"
"lea 0x10(%0),%0 \n"
"movdqu %%xmm1,%%xmm3 \n"
"psubusb %%xmm2,%%xmm1 \n"
"psubusb %%xmm3,%%xmm2 \n"
"por %%xmm2,%%xmm1 \n"
"movdqu %%xmm1,%%xmm2 \n"
"punpcklbw %%xmm5,%%xmm1 \n"
"punpckhbw %%xmm5,%%xmm2 \n"
"pmaddwd %%xmm1,%%xmm1 \n"
"pmaddwd %%xmm2,%%xmm2 \n"
"paddd %%xmm1,%%xmm0 \n"
"paddd %%xmm2,%%xmm0 \n"
"sub $0x10,%2 \n"
"ja 1b \n"
"1: \n"
"movdqu (%0),%%xmm1 \n"
"movdqu (%0,%1,1),%%xmm2 \n"
"lea 0x10(%0),%0 \n"
"movdqu %%xmm1,%%xmm3 \n"
"psubusb %%xmm2,%%xmm1 \n"
"psubusb %%xmm3,%%xmm2 \n"
"por %%xmm2,%%xmm1 \n"
"movdqu %%xmm1,%%xmm2 \n"
"punpcklbw %%xmm5,%%xmm1 \n"
"punpckhbw %%xmm5,%%xmm2 \n"
"pmaddwd %%xmm1,%%xmm1 \n"
"pmaddwd %%xmm2,%%xmm2 \n"
"paddd %%xmm1,%%xmm0 \n"
"paddd %%xmm2,%%xmm0 \n"
"sub $0x10,%2 \n"
"ja 1b \n"
"pshufd $0xee,%%xmm0,%%xmm1 \n"
"paddd %%xmm1,%%xmm0 \n"
"pshufd $0x1,%%xmm0,%%xmm1 \n"
"paddd %%xmm1,%%xmm0 \n"
"movd %%xmm0,%3 \n"
"pshufd $0xee,%%xmm0,%%xmm1 \n"
"paddd %%xmm1,%%xmm0 \n"
"pshufd $0x1,%%xmm0,%%xmm1 \n"
"paddd %%xmm1,%%xmm0 \n"
"movd %%xmm0,%3 \n"
: "+r"(src_a), // %0
"+r"(src_b), // %1
"+r"(count), // %2
"=g"(sse) // %3
:
: "memory", "cc"
: "+r"(src_a), // %0
"+r"(src_b), // %1
"+r"(count), // %2
"=g"(sse) // %3
:
: "memory", "cc"
#if defined(__SSE2__)
, "xmm0", "xmm1", "xmm2", "xmm3", "xmm5"
,
"xmm0", "xmm1", "xmm2", "xmm3", "xmm5"
#endif
); // NOLINT
return sse;
@ -199,20 +197,22 @@ static uint32 SumSquareError_SSE2(const uint8* src_a,
#if defined(HAS_SUMSQUAREERROR_SSE2)
#if (defined(__pic__) || defined(__APPLE__)) && defined(__i386__)
static __inline void __cpuid(int cpu_info[4], int info_type) {
asm volatile ( // NOLINT
"mov %%ebx, %%edi \n"
"cpuid \n"
"xchg %%edi, %%ebx \n"
: "=a"(cpu_info[0]), "=D"(cpu_info[1]), "=c"(cpu_info[2]), "=d"(cpu_info[3])
: "a"(info_type));
asm volatile( // NOLINT
"mov %%ebx, %%edi \n"
"cpuid \n"
"xchg %%edi, %%ebx \n"
: "=a"(cpu_info[0]), "=D"(cpu_info[1]), "=c"(cpu_info[2]),
"=d"(cpu_info[3])
: "a"(info_type));
}
// For gcc/clang but not clangcl.
#elif (defined(__i386__) || defined(__x86_64__)) && !defined(_MSC_VER)
#elif !defined(_MSC_VER) && (defined(__i386__) || defined(__x86_64__))
static __inline void __cpuid(int cpu_info[4], int info_type) {
asm volatile ( // NOLINT
"cpuid \n"
: "=a"(cpu_info[0]), "=b"(cpu_info[1]), "=c"(cpu_info[2]), "=d"(cpu_info[3])
: "a"(info_type));
asm volatile( // NOLINT
"cpuid \n"
: "=a"(cpu_info[0]), "=b"(cpu_info[1]), "=c"(cpu_info[2]),
"=d"(cpu_info[3])
: "a"(info_type));
}
#endif
@ -228,20 +228,22 @@ static int CpuHasSSE2() {
}
#endif // HAS_SUMSQUAREERROR_SSE2
static uint32 SumSquareError_C(const uint8* src_a,
const uint8* src_b, int count) {
uint32 sse = 0u;
static uint32_t SumSquareError_C(const uint8_t* src_a,
const uint8_t* src_b,
int count) {
uint32_t sse = 0u;
for (int x = 0; x < count; ++x) {
int diff = src_a[x] - src_b[x];
sse += static_cast<uint32>(diff * diff);
sse += static_cast<uint32_t>(diff * diff);
}
return sse;
}
double ComputeSumSquareError(const uint8* src_a,
const uint8* src_b, int count) {
uint32 (*SumSquareError)(const uint8* src_a,
const uint8* src_b, int count) = SumSquareError_C;
double ComputeSumSquareError(const uint8_t* src_a,
const uint8_t* src_b,
int count) {
uint32_t (*SumSquareError)(const uint8_t* src_a, const uint8_t* src_b,
int count) = SumSquareError_C;
#if defined(HAS_SUMSQUAREERROR_NEON)
SumSquareError = SumSquareError_NEON;
#endif
@ -251,9 +253,9 @@ double ComputeSumSquareError(const uint8* src_a,
}
#endif
const int kBlockSize = 1 << 15;
uint64 sse = 0;
uint64_t sse = 0;
#ifdef _OPENMP
#pragma omp parallel for reduction(+: sse)
#pragma omp parallel for reduction(+ : sse)
#endif
for (int i = 0; i < (count - (kBlockSize - 1)); i += kBlockSize) {
sse += SumSquareError(src_a + i, src_b + i, kBlockSize);
@ -278,8 +280,9 @@ double ComputeSumSquareError(const uint8* src_a,
// Returns 128.0 (kMaxPSNR) if sse is 0 (perfect match).
double ComputePSNR(double sse, double size) {
const double kMINSSE = 255.0 * 255.0 * size / pow(10.0, kMaxPSNR / 10.0);
if (sse <= kMINSSE)
if (sse <= kMINSSE) {
sse = kMINSSE; // Produces max PSNR of 128
}
return 10.0 * log10(255.0 * 255.0 * size / sse);
}

View file

@ -20,7 +20,7 @@ extern "C" {
#endif
#if !defined(INT_TYPES_DEFINED) && !defined(UINT8_TYPE_DEFINED)
typedef unsigned char uint8;
typedef unsigned char uint8_t;
#define UINT8_TYPE_DEFINED
#endif
@ -31,7 +31,9 @@ static const double kMaxPSNR = 128.0;
#if !defined(HAVE_JPEG)
// Computer Sum of Squared Error (SSE).
// Pass this to ComputePSNR for final result.
double ComputeSumSquareError(const uint8* org, const uint8* rec, int size);
double ComputeSumSquareError(const uint8_t* src_a,
const uint8_t* src_b,
int count);
#endif
// PSNR formula: psnr = 10 * log10 (Peak Signal^2 * size / sse)

View file

@ -71,8 +71,8 @@ bool ExtractResolutionFromFilename(const char* name,
// Isolate the .width_height. section of the filename by searching for a
// dot or underscore followed by a digit.
for (int i = 0; name[i]; ++i) {
if ((name[i] == '.' || name[i] == '_') &&
name[i + 1] >= '0' && name[i + 1] <= '9') {
if ((name[i] == '.' || name[i] == '_') && name[i + 1] >= '0' &&
name[i + 1] <= '9') {
int n = sscanf(name + i + 1, "%dx%d", width_ptr, height_ptr); // NOLINT
if (2 == n) {
return true;
@ -88,11 +88,11 @@ bool ExtractResolutionFromFilename(const char* name,
return false;
}
fseek(file_org, 0, SEEK_END);
size_t total_size = ftell(file_org);
size_t total_size = ftell(file_org);
fseek(file_org, 0, SEEK_SET);
uint8* const ch_org = new uint8[total_size];
uint8_t* const ch_org = new uint8_t[total_size];
memset(ch_org, 0, total_size);
size_t bytes_org = fread(ch_org, sizeof(uint8), total_size, file_org);
size_t bytes_org = fread(ch_org, sizeof(uint8_t), total_size, file_org);
fclose(file_org);
if (bytes_org == total_size) {
if (0 == libyuv::MJPGSize(ch_org, total_size, width_ptr, height_ptr)) {
@ -107,11 +107,15 @@ bool ExtractResolutionFromFilename(const char* name,
// Scale Y channel from 16..240 to 0..255.
// This can be useful when comparing codecs that are inconsistant about Y
uint8 ScaleY(uint8 y) {
uint8_t ScaleY(uint8_t y) {
int ny = (y - 16) * 256 / 224;
if (ny < 0) ny = 0;
if (ny > 255) ny = 255;
return static_cast<uint8>(ny);
if (ny < 0) {
ny = 0;
}
if (ny > 255) {
ny = 255;
}
return static_cast<uint8_t>(ny);
}
// MSE = Mean Square Error
@ -119,16 +123,18 @@ double GetMSE(double sse, double size) {
return sse / size;
}
void PrintHelp(const char * program) {
void PrintHelp(const char* program) {
printf("%s [-options] org_seq rec_seq [rec_seq2.. etc]\n", program);
#ifdef HAVE_JPEG
printf("jpeg or raw YUV 420 supported.\n");
#endif
printf("options:\n");
printf(" -s <width> <height> .... specify YUV size, mandatory if none of the "
"sequences have the\n");
printf(" resolution embedded in their filename (ie. "
"name.1920x800_24Hz_P420.yuv)\n");
printf(
" -s <width> <height> .... specify YUV size, mandatory if none of the "
"sequences have the\n");
printf(
" resolution embedded in their filename (ie. "
"name.1920x800_24Hz_P420.yuv)\n");
printf(" -psnr .................. compute PSNR (default)\n");
printf(" -ssim .................. compute SSIM\n");
printf(" -mse ................... compute MSE\n");
@ -146,7 +152,9 @@ void PrintHelp(const char * program) {
}
void ParseOptions(int argc, const char* argv[]) {
if (argc <= 1) PrintHelp(argv[0]);
if (argc <= 1) {
PrintHelp(argv[0]);
}
for (int c = 1; c < argc; ++c) {
if (!strcmp(argv[c], "-v")) {
verbose = true;
@ -168,16 +176,16 @@ void ParseOptions(int argc, const char* argv[]) {
} else if (!strcmp(argv[c], "-h") || !strcmp(argv[c], "-help")) {
PrintHelp(argv[0]);
} else if (!strcmp(argv[c], "-s") && c + 2 < argc) {
image_width = atoi(argv[++c]); // NOLINT
image_height = atoi(argv[++c]); // NOLINT
image_width = atoi(argv[++c]); // NOLINT
image_height = atoi(argv[++c]); // NOLINT
} else if (!strcmp(argv[c], "-skip") && c + 2 < argc) {
num_skip_org = atoi(argv[++c]); // NOLINT
num_skip_rec = atoi(argv[++c]); // NOLINT
num_skip_org = atoi(argv[++c]); // NOLINT
num_skip_rec = atoi(argv[++c]); // NOLINT
} else if (!strcmp(argv[c], "-frames") && c + 1 < argc) {
num_frames = atoi(argv[++c]); // NOLINT
num_frames = atoi(argv[++c]); // NOLINT
#ifdef _OPENMP
} else if (!strcmp(argv[c], "-t") && c + 1 < argc) {
num_threads = atoi(argv[++c]); // NOLINT
num_threads = atoi(argv[++c]); // NOLINT
#endif
} else if (argv[c][0] == '-') {
fprintf(stderr, "Unknown option. %s\n", argv[c]);
@ -206,11 +214,9 @@ void ParseOptions(int argc, const char* argv[]) {
int org_width, org_height;
int rec_width, rec_height;
bool org_res_avail = ExtractResolutionFromFilename(argv[fileindex_org],
&org_width,
&org_height);
&org_width, &org_height);
bool rec_res_avail = ExtractResolutionFromFilename(argv[fileindex_rec],
&rec_width,
&rec_height);
&rec_width, &rec_height);
if (org_res_avail) {
if (rec_res_avail) {
if ((org_width == rec_width) && (org_height == rec_height)) {
@ -234,24 +240,28 @@ void ParseOptions(int argc, const char* argv[]) {
}
}
bool UpdateMetrics(uint8* ch_org, uint8* ch_rec,
const int y_size, const int uv_size, const size_t total_size,
bool UpdateMetrics(uint8_t* ch_org,
uint8_t* ch_rec,
const int y_size,
const int uv_size,
const size_t total_size,
int number_of_frames,
metric* cur_distortion_psnr,
metric* distorted_frame, bool do_psnr) {
metric* distorted_frame,
bool compute_psnr) {
const int uv_offset = (do_swap_uv ? uv_size : 0);
const uint8* const u_org = ch_org + y_size + uv_offset;
const uint8* const u_rec = ch_rec + y_size;
const uint8* const v_org = ch_org + y_size + (uv_size - uv_offset);
const uint8* const v_rec = ch_rec + y_size + uv_size;
if (do_psnr) {
const uint8_t* const u_org = ch_org + y_size + uv_offset;
const uint8_t* const u_rec = ch_rec + y_size;
const uint8_t* const v_org = ch_org + y_size + (uv_size - uv_offset);
const uint8_t* const v_rec = ch_rec + y_size + uv_size;
if (compute_psnr) {
#ifdef HAVE_JPEG
double y_err = static_cast<double>(
libyuv::ComputeSumSquareError(ch_org, ch_rec, y_size));
libyuv::ComputeSumSquareError(ch_org, ch_rec, y_size));
double u_err = static_cast<double>(
libyuv::ComputeSumSquareError(u_org, u_rec, uv_size));
libyuv::ComputeSumSquareError(u_org, u_rec, uv_size));
double v_err = static_cast<double>(
libyuv::ComputeSumSquareError(v_org, v_rec, uv_size));
libyuv::ComputeSumSquareError(v_org, v_rec, uv_size));
#else
double y_err = ComputeSumSquareError(ch_org, ch_rec, y_size);
double u_err = ComputeSumSquareError(u_org, u_rec, uv_size);
@ -265,17 +275,17 @@ bool UpdateMetrics(uint8* ch_org, uint8* ch_rec,
distorted_frame->y = ComputePSNR(y_err, static_cast<double>(y_size));
distorted_frame->u = ComputePSNR(u_err, static_cast<double>(uv_size));
distorted_frame->v = ComputePSNR(v_err, static_cast<double>(uv_size));
distorted_frame->all = ComputePSNR(total_err,
static_cast<double>(total_size));
distorted_frame->all =
ComputePSNR(total_err, static_cast<double>(total_size));
} else {
distorted_frame->y = CalcSSIM(ch_org, ch_rec, image_width, image_height);
distorted_frame->u = CalcSSIM(u_org, u_rec, (image_width + 1) / 2,
(image_height + 1) / 2);
distorted_frame->v = CalcSSIM(v_org, v_rec, (image_width + 1) / 2,
(image_height + 1) / 2);
distorted_frame->u =
CalcSSIM(u_org, u_rec, (image_width + 1) / 2, (image_height + 1) / 2);
distorted_frame->v =
CalcSSIM(v_org, v_rec, (image_width + 1) / 2, (image_height + 1) / 2);
distorted_frame->all =
(distorted_frame->y + distorted_frame->u + distorted_frame->v)
/ total_size;
(distorted_frame->y + distorted_frame->u + distorted_frame->v) /
total_size;
distorted_frame->y /= y_size;
distorted_frame->u /= uv_size;
distorted_frame->v /= uv_size;
@ -294,12 +304,15 @@ bool UpdateMetrics(uint8* ch_org, uint8* ch_rec,
cur_distortion_psnr->all += distorted_frame->all;
bool ismin = false;
if (distorted_frame->y < cur_distortion_psnr->min_y)
if (distorted_frame->y < cur_distortion_psnr->min_y) {
cur_distortion_psnr->min_y = distorted_frame->y;
if (distorted_frame->u < cur_distortion_psnr->min_u)
}
if (distorted_frame->u < cur_distortion_psnr->min_u) {
cur_distortion_psnr->min_u = distorted_frame->u;
if (distorted_frame->v < cur_distortion_psnr->min_v)
}
if (distorted_frame->v < cur_distortion_psnr->min_v) {
cur_distortion_psnr->min_v = distorted_frame->v;
}
if (distorted_frame->all < cur_distortion_psnr->min_all) {
cur_distortion_psnr->min_all = distorted_frame->all;
cur_distortion_psnr->min_frame = number_of_frames;
@ -330,8 +343,8 @@ int main(int argc, const char* argv[]) {
}
// Open all files to compare to
FILE** file_rec = new FILE* [num_rec];
memset(file_rec, 0, num_rec * sizeof(FILE*)); // NOLINT
FILE** file_rec = new FILE*[num_rec];
memset(file_rec, 0, num_rec * sizeof(FILE*)); // NOLINT
for (int cur_rec = 0; cur_rec < num_rec; ++cur_rec) {
file_rec[cur_rec] = fopen(argv[fileindex_rec + cur_rec], "rb");
if (file_rec[cur_rec] == NULL) {
@ -347,27 +360,28 @@ int main(int argc, const char* argv[]) {
const int y_size = image_width * image_height;
const int uv_size = ((image_width + 1) / 2) * ((image_height + 1) / 2);
const size_t total_size = y_size + 2 * uv_size; // NOLINT
const size_t total_size = y_size + 2 * uv_size; // NOLINT
#if defined(_MSC_VER)
_fseeki64(file_org,
static_cast<__int64>(num_skip_org) *
static_cast<__int64>(total_size), SEEK_SET);
_fseeki64(
file_org,
static_cast<__int64>(num_skip_org) * static_cast<__int64>(total_size),
SEEK_SET);
#else
fseek(file_org, num_skip_org * total_size, SEEK_SET);
#endif
for (int cur_rec = 0; cur_rec < num_rec; ++cur_rec) {
#if defined(_MSC_VER)
_fseeki64(file_rec[cur_rec],
static_cast<__int64>(num_skip_rec) *
static_cast<__int64>(total_size),
SEEK_SET);
_fseeki64(
file_rec[cur_rec],
static_cast<__int64>(num_skip_rec) * static_cast<__int64>(total_size),
SEEK_SET);
#else
fseek(file_rec[cur_rec], num_skip_rec * total_size, SEEK_SET);
#endif
}
uint8* const ch_org = new uint8[total_size];
uint8* const ch_rec = new uint8[total_size];
uint8_t* const ch_org = new uint8_t[total_size];
uint8_t* const ch_rec = new uint8_t[total_size];
if (ch_org == NULL || ch_rec == NULL) {
fprintf(stderr, "No memory available\n");
fclose(file_org);
@ -420,29 +434,24 @@ int main(int argc, const char* argv[]) {
}
int number_of_frames;
for (number_of_frames = 0; ; ++number_of_frames) {
if (num_frames && number_of_frames >= num_frames)
for (number_of_frames = 0;; ++number_of_frames) {
if (num_frames && number_of_frames >= num_frames) {
break;
}
size_t bytes_org = fread(ch_org, sizeof(uint8), total_size, file_org);
size_t bytes_org = fread(ch_org, sizeof(uint8_t), total_size, file_org);
if (bytes_org < total_size) {
#ifdef HAVE_JPEG
// Try parsing file as a jpeg.
uint8* const ch_jpeg = new uint8[bytes_org];
uint8_t* const ch_jpeg = new uint8_t[bytes_org];
memcpy(ch_jpeg, ch_org, bytes_org);
memset(ch_org, 0, total_size);
if (0 != libyuv::MJPGToI420(ch_jpeg, bytes_org,
ch_org,
image_width,
ch_org + y_size,
(image_width + 1) / 2,
if (0 != libyuv::MJPGToI420(ch_jpeg, bytes_org, ch_org, image_width,
ch_org + y_size, (image_width + 1) / 2,
ch_org + y_size + uv_size,
(image_width + 1) / 2,
image_width,
image_height,
image_width,
image_height)) {
(image_width + 1) / 2, image_width,
image_height, image_width, image_height)) {
delete[] ch_jpeg;
break;
}
@ -453,26 +462,20 @@ int main(int argc, const char* argv[]) {
}
for (int cur_rec = 0; cur_rec < num_rec; ++cur_rec) {
size_t bytes_rec = fread(ch_rec, sizeof(uint8),
total_size, file_rec[cur_rec]);
size_t bytes_rec =
fread(ch_rec, sizeof(uint8_t), total_size, file_rec[cur_rec]);
if (bytes_rec < total_size) {
#ifdef HAVE_JPEG
// Try parsing file as a jpeg.
uint8* const ch_jpeg = new uint8[bytes_rec];
uint8_t* const ch_jpeg = new uint8_t[bytes_rec];
memcpy(ch_jpeg, ch_rec, bytes_rec);
memset(ch_rec, 0, total_size);
if (0 != libyuv::MJPGToI420(ch_jpeg, bytes_rec,
ch_rec,
image_width,
ch_rec + y_size,
(image_width + 1) / 2,
if (0 != libyuv::MJPGToI420(ch_jpeg, bytes_rec, ch_rec, image_width,
ch_rec + y_size, (image_width + 1) / 2,
ch_rec + y_size + uv_size,
(image_width + 1) / 2,
image_width,
image_height,
image_width,
image_height)) {
(image_width + 1) / 2, image_width,
image_height, image_width, image_height)) {
delete[] ch_jpeg;
break;
}
@ -486,12 +489,10 @@ int main(int argc, const char* argv[]) {
printf("%5d", number_of_frames);
}
if (do_psnr) {
metric distorted_frame;
metric distorted_frame = {};
metric* cur_distortion_psnr = &distortion_psnr[cur_rec];
bool ismin = UpdateMetrics(ch_org, ch_rec,
y_size, uv_size, total_size,
number_of_frames,
cur_distortion_psnr,
bool ismin = UpdateMetrics(ch_org, ch_rec, y_size, uv_size, total_size,
number_of_frames, cur_distortion_psnr,
&distorted_frame, true);
if (verbose) {
printf("\t%10.6f", distorted_frame.y);
@ -502,12 +503,10 @@ int main(int argc, const char* argv[]) {
}
}
if (do_ssim) {
metric distorted_frame;
metric distorted_frame = {};
metric* cur_distortion_ssim = &distortion_ssim[cur_rec];
bool ismin = UpdateMetrics(ch_org, ch_rec,
y_size, uv_size, total_size,
number_of_frames,
cur_distortion_ssim,
bool ismin = UpdateMetrics(ch_org, ch_rec, y_size, uv_size, total_size,
number_of_frames, cur_distortion_ssim,
&distorted_frame, false);
if (verbose) {
printf("\t%10.6f", distorted_frame.y);
@ -543,24 +542,20 @@ int main(int argc, const char* argv[]) {
}
if (do_psnr) {
const double global_psnr_y = ComputePSNR(
cur_distortion_psnr->global_y,
static_cast<double>(y_size) * number_of_frames);
const double global_psnr_u = ComputePSNR(
cur_distortion_psnr->global_u,
static_cast<double>(uv_size) * number_of_frames);
const double global_psnr_v = ComputePSNR(
cur_distortion_psnr->global_v,
static_cast<double>(uv_size) * number_of_frames);
const double global_psnr_all = ComputePSNR(
cur_distortion_psnr->global_all,
static_cast<double>(total_size) * number_of_frames);
printf("Global:\t%10.6f\t%10.6f\t%10.6f\t%10.6f\t%5d",
global_psnr_y,
global_psnr_u,
global_psnr_v,
global_psnr_all,
number_of_frames);
const double global_psnr_y =
ComputePSNR(cur_distortion_psnr->global_y,
static_cast<double>(y_size) * number_of_frames);
const double global_psnr_u =
ComputePSNR(cur_distortion_psnr->global_u,
static_cast<double>(uv_size) * number_of_frames);
const double global_psnr_v =
ComputePSNR(cur_distortion_psnr->global_v,
static_cast<double>(uv_size) * number_of_frames);
const double global_psnr_all =
ComputePSNR(cur_distortion_psnr->global_all,
static_cast<double>(total_size) * number_of_frames);
printf("Global:\t%10.6f\t%10.6f\t%10.6f\t%10.6f\t%5d", global_psnr_y,
global_psnr_u, global_psnr_v, global_psnr_all, number_of_frames);
if (show_name) {
printf("\t%s", argv[fileindex_rec + cur_rec]);
}
@ -570,20 +565,14 @@ int main(int argc, const char* argv[]) {
if (!quiet) {
printf("Avg:");
if (do_psnr) {
printf("\t%10.6f\t%10.6f\t%10.6f\t%10.6f\t%5d",
cur_distortion_psnr->y,
cur_distortion_psnr->u,
cur_distortion_psnr->v,
cur_distortion_psnr->all,
number_of_frames);
printf("\t%10.6f\t%10.6f\t%10.6f\t%10.6f\t%5d", cur_distortion_psnr->y,
cur_distortion_psnr->u, cur_distortion_psnr->v,
cur_distortion_psnr->all, number_of_frames);
}
if (do_ssim) {
printf("\t%10.6f\t%10.6f\t%10.6f\t%10.6f\t%5d",
cur_distortion_ssim->y,
cur_distortion_ssim->u,
cur_distortion_ssim->v,
cur_distortion_ssim->all,
number_of_frames);
printf("\t%10.6f\t%10.6f\t%10.6f\t%10.6f\t%5d", cur_distortion_ssim->y,
cur_distortion_ssim->u, cur_distortion_ssim->v,
cur_distortion_ssim->all, number_of_frames);
}
if (show_name) {
printf("\t%s", argv[fileindex_rec + cur_rec]);
@ -594,19 +583,15 @@ int main(int argc, const char* argv[]) {
printf("Min:");
if (do_psnr) {
printf("\t%10.6f\t%10.6f\t%10.6f\t%10.6f\t%5d",
cur_distortion_psnr->min_y,
cur_distortion_psnr->min_u,
cur_distortion_psnr->min_v,
cur_distortion_psnr->min_all,
cur_distortion_psnr->min_frame);
cur_distortion_psnr->min_y, cur_distortion_psnr->min_u,
cur_distortion_psnr->min_v, cur_distortion_psnr->min_all,
cur_distortion_psnr->min_frame);
}
if (do_ssim) {
printf("\t%10.6f\t%10.6f\t%10.6f\t%10.6f\t%5d",
cur_distortion_ssim->min_y,
cur_distortion_ssim->min_u,
cur_distortion_ssim->min_v,
cur_distortion_ssim->min_all,
cur_distortion_ssim->min_frame);
cur_distortion_ssim->min_y, cur_distortion_ssim->min_u,
cur_distortion_ssim->min_v, cur_distortion_ssim->min_all,
cur_distortion_ssim->min_frame);
}
if (show_name) {
printf("\t%s", argv[fileindex_rec + cur_rec]);
@ -615,20 +600,20 @@ int main(int argc, const char* argv[]) {
}
if (do_mse) {
double global_mse_y = GetMSE(cur_distortion_psnr->global_y,
static_cast<double>(y_size) * number_of_frames);
double global_mse_u = GetMSE(cur_distortion_psnr->global_u,
static_cast<double>(uv_size) * number_of_frames);
double global_mse_v = GetMSE(cur_distortion_psnr->global_v,
static_cast<double>(uv_size) * number_of_frames);
double global_mse_all = GetMSE(cur_distortion_psnr->global_all,
static_cast<double>(total_size) * number_of_frames);
printf("MSE:\t%10.6f\t%10.6f\t%10.6f\t%10.6f\t%5d",
global_mse_y,
global_mse_u,
global_mse_v,
global_mse_all,
number_of_frames);
double global_mse_y =
GetMSE(cur_distortion_psnr->global_y,
static_cast<double>(y_size) * number_of_frames);
double global_mse_u =
GetMSE(cur_distortion_psnr->global_u,
static_cast<double>(uv_size) * number_of_frames);
double global_mse_v =
GetMSE(cur_distortion_psnr->global_v,
static_cast<double>(uv_size) * number_of_frames);
double global_mse_all =
GetMSE(cur_distortion_psnr->global_all,
static_cast<double>(total_size) * number_of_frames);
printf("MSE:\t%10.6f\t%10.6f\t%10.6f\t%10.6f\t%5d", global_mse_y,
global_mse_u, global_mse_v, global_mse_all, number_of_frames);
if (show_name) {
printf("\t%s", argv[fileindex_rec + cur_rec]);
}

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

View file

@ -10,7 +10,7 @@
// Get SSIM for video sequence. Assuming RAW 4:2:0 Y:Cb:Cr format
#ifndef UTIL_SSIM_H_ // NOLINT
#ifndef UTIL_SSIM_H_
#define UTIL_SSIM_H_
#include <math.h> // For log10()
@ -20,12 +20,14 @@ extern "C" {
#endif
#if !defined(INT_TYPES_DEFINED) && !defined(UINT8_TYPE_DEFINED)
typedef unsigned char uint8;
typedef unsigned char uint8_t;
#define UINT8_TYPE_DEFINED
#endif
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 CalcLSSIM(double ssim);
@ -33,4 +35,4 @@ double CalcLSSIM(double ssim);
} // extern "C"
#endif
#endif // UTIL_SSIM_H_ // NOLINT
#endif // UTIL_SSIM_H_

View file

@ -0,0 +1,106 @@
/*
* Copyright 2021 The LibYuv 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 <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// This utility computes values needed to generate yuvconstants based on
// white point values.
// The yuv formulas are tuned for 8 bit YUV channels.
// See Also
// https://mymusing.co/bt601-yuv-to-rgb-conversion-color/
// BT.709 full range YUV to RGB reference
// R = Y + V * 1.5748
// G = Y - U * 0.18732 - V * 0.46812
// B = Y + U * 1.8556
// KR = 0.2126
// KB = 0.0722
// // Y contribution to R,G,B. Scale and bias.
// #define YG 16320 /* round(1.000 * 64 * 256 * 256 / 257) */
// #define YB 32 /* 64 / 2 */
//
// // U and V contributions to R,G,B.
// #define UB 113 /* round(1.77200 * 64) */
// #define UG 22 /* round(0.34414 * 64) */
// #define VG 46 /* round(0.71414 * 64) */
// #define VR 90 /* round(1.40200 * 64) */
//
// // Bias values to round, and subtract 128 from U and V.
// #define BB (-UB * 128 + YB)
// #define BG (UG * 128 + VG * 128 + YB)
// #define BR (-VR * 128 + YB)
int main(int argc, const char* argv[]) {
if (argc < 3) {
printf("yuvconstants [KR] [KB]\n");
printf(" e.g. yuvconstants 0.2126 0.0722\n");
printf(" MC BT KR KB\n");
printf(" 1 BT.709 KR = 0.2126; KB = 0.0722\n");
printf(" 4 FCC KR = 0.30; KB = 0.11\n");
printf(" 6 BT.601 KR = 0.299; KB = 0.114\n");
printf(" 7 SMPTE 240M KR = 0.212; KB = 0.087\n");
printf(" 9 BT.2020 KR = 0.2627; KB = 0.0593\n");
return -1;
}
float kr = atof(argv[1]);
float kb = atof(argv[2]);
float kg = 1 - kr - kb;
float vr = 2 * (1 - kr);
float ug = 2 * ((1 - kb) * kb / kg);
float vg = 2 * ((1 - kr) * kr / kg);
float ub = 2 * (1 - kb);
printf("Full range\n");
printf("R = Y + V * %5f\n", vr);
printf("G = Y - U * %6f - V * %6f\n", ug, vg);
printf("B = Y + U * %5f\n", ub);
printf("KR = %4f; ", kr);
printf("KB = %4f\n", kb);
// printf("KG = %4f\n", kg);
// #define YG 16320 /* round(1.000 * 64 * 256 * 256 / 257) */
// #define YB 32 /* 64 / 2 */
//
// // U and V contributions to R,G,B.
printf("UB %-3.0f /* round(%f * 64 = %8.4f) */\n", round(ub * 64), ub, ub * 64);
printf("UG %-3.0f /* round(%f * 64 = %8.4f) */\n", round(ug * 64), ug, ug * 64);
printf("VG %-3.0f /* round(%f * 64 = %8.4f) */\n", round(vg * 64), vg, vg * 64);
printf("VR %-3.0f /* round(%f * 64 = %8.4f) */\n", round(vr * 64), vr, vr * 64);
vr = 255.f / 224.f * 2 * (1 - kr);
ug = 255.f / 224.f * 2 * ((1 - kb) * kb / kg);
vg = 255.f / 224.f * 2 * ((1 - kr) * kr / kg);
ub = 255.f / 224.f * 2 * (1 - kb);
printf("\nLimited range\n");
printf("R = (Y - 16) * 1.164 + V * %5f\n", vr);
printf("G = (Y - 16) * 1.164 - U * %6f - V * %6f\n", ug, vg);
printf("B = (Y - 16) * 1.164 + U * %5f\n", ub);
// printf("KG = %4f\n", kg);
// #define YG 16320 /* round(1.000 * 64 * 256 * 256 / 257) */
// #define YB 32 /* 64 / 2 */
//
// // U and V contributions to R,G,B.
printf("UB %-3.0f /* round(%f * 64 = %8.4f) */\n", round(ub * 64), ub, ub * 64);
printf("UG %-3.0f /* round(%f * 64 = %8.4f) */\n", round(ug * 64), ug, ug * 64);
printf("VG %-3.0f /* round(%f * 64 = %8.4f) */\n", round(vg * 64), vg, vg * 64);
printf("VR %-3.0f /* round(%f * 64 = %8.4f) */\n", round(vr * 64), vr, vr * 64);
return 0;
}

View file

@ -9,7 +9,7 @@
*/
// Convert an ARGB image to YUV.
// Usage: convert src_argb.raw dst_yuv.raw
// Usage: yuvconvert src_argb.raw dst_yuv.raw
#ifndef _CRT_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
@ -29,27 +29,27 @@ bool verbose = false;
bool attenuate = false;
bool unattenuate = false;
int image_width = 0, image_height = 0; // original width and height
int dst_width = 0, dst_height = 0; // new width and height
int dst_width = 0, dst_height = 0; // new width and height
int fileindex_org = 0; // argv argument contains the original file name.
int fileindex_rec = 0; // argv argument contains the reconstructed file name.
int num_rec = 0; // Number of reconstructed images.
int num_skip_org = 0; // Number of frames to skip in original.
int num_frames = 0; // Number of frames to convert.
int filter = 1; // Bilinear filter for scaling.
int num_rec = 0; // Number of reconstructed images.
int num_skip_org = 0; // Number of frames to skip in original.
int num_frames = 0; // Number of frames to convert.
int filter = 1; // Bilinear filter for scaling.
static __inline uint32 Abs(int32 v) {
static __inline uint32_t Abs(int32_t v) {
return v >= 0 ? v : -v;
}
// Parse PYUV format. ie name.1920x800_24Hz_P420.yuv
bool ExtractResolutionFromFilename(const char* name,
int* width_ptr,
int* height_ptr) {
static bool ExtractResolutionFromFilename(const char* name,
int* width_ptr,
int* height_ptr) {
// Isolate the .width_height. section of the filename by searching for a
// dot or underscore followed by a digit.
for (int i = 0; name[i]; ++i) {
if ((name[i] == '.' || name[i] == '_') &&
name[i + 1] >= '0' && name[i + 1] <= '9') {
if ((name[i] == '.' || name[i] == '_') && name[i + 1] >= '0' &&
name[i + 1] <= '9') {
int n = sscanf(name + i + 1, "%dx%d", width_ptr, height_ptr); // NOLINT
if (2 == n) {
return true;
@ -59,13 +59,14 @@ bool ExtractResolutionFromFilename(const char* name,
return false;
}
void PrintHelp(const char * program) {
static void PrintHelp(const char* program) {
printf("%s [-options] src_argb.raw dst_yuv.raw\n", program);
printf(" -s <width> <height> .... specify source resolution. "
"Optional if name contains\n"
" resolution (ie. "
"name.1920x800_24Hz_P420.yuv)\n"
" Negative value mirrors.\n");
printf(
" -s <width> <height> .... specify source resolution. "
"Optional if name contains\n"
" resolution (ie. "
"name.1920x800_24Hz_P420.yuv)\n"
" Negative value mirrors.\n");
printf(" -d <width> <height> .... specify destination resolution.\n");
printf(" -f <filter> ............ 0 = point, 1 = bilinear (default).\n");
printf(" -skip <src_argb> ....... Number of frame to skip of src_argb\n");
@ -77,8 +78,10 @@ void PrintHelp(const char * program) {
exit(0);
}
void ParseOptions(int argc, const char* argv[]) {
if (argc <= 1) PrintHelp(argv[0]);
static void ParseOptions(int argc, const char* argv[]) {
if (argc <= 1) {
PrintHelp(argv[0]);
}
for (int c = 1; c < argc; ++c) {
if (!strcmp(argv[c], "-v")) {
verbose = true;
@ -89,17 +92,17 @@ void ParseOptions(int argc, const char* argv[]) {
} else if (!strcmp(argv[c], "-h") || !strcmp(argv[c], "-help")) {
PrintHelp(argv[0]);
} else if (!strcmp(argv[c], "-s") && c + 2 < argc) {
image_width = atoi(argv[++c]); // NOLINT
image_height = atoi(argv[++c]); // NOLINT
image_width = atoi(argv[++c]); // NOLINT
image_height = atoi(argv[++c]); // NOLINT
} else if (!strcmp(argv[c], "-d") && c + 2 < argc) {
dst_width = atoi(argv[++c]); // NOLINT
dst_height = atoi(argv[++c]); // NOLINT
dst_width = atoi(argv[++c]); // NOLINT
dst_height = atoi(argv[++c]); // NOLINT
} else if (!strcmp(argv[c], "-skip") && c + 1 < argc) {
num_skip_org = atoi(argv[++c]); // NOLINT
num_skip_org = atoi(argv[++c]); // NOLINT
} else if (!strcmp(argv[c], "-frames") && c + 1 < argc) {
num_frames = atoi(argv[++c]); // NOLINT
num_frames = atoi(argv[++c]); // NOLINT
} else if (!strcmp(argv[c], "-f") && c + 1 < argc) {
filter = atoi(argv[++c]); // NOLINT
filter = atoi(argv[++c]); // NOLINT
} else if (argv[c][0] == '-') {
fprintf(stderr, "Unknown option. %s\n", argv[c]);
} else if (fileindex_org == 0) {
@ -127,11 +130,9 @@ void ParseOptions(int argc, const char* argv[]) {
int org_width, org_height;
int rec_width, rec_height;
bool org_res_avail = ExtractResolutionFromFilename(argv[fileindex_org],
&org_width,
&org_height);
&org_width, &org_height);
bool rec_res_avail = ExtractResolutionFromFilename(argv[fileindex_rec],
&rec_width,
&rec_height);
&rec_width, &rec_height);
if (image_width == 0 || image_height == 0) {
if (org_res_avail) {
image_width = org_width;
@ -158,26 +159,29 @@ void ParseOptions(int argc, const char* argv[]) {
static const int kTileX = 32;
static const int kTileY = 32;
static int TileARGBScale(const uint8* src_argb, int src_stride_argb,
int src_width, int src_height,
uint8* dst_argb, int dst_stride_argb,
int dst_width, int dst_height,
static int TileARGBScale(const uint8_t* src_argb,
int src_stride_argb,
int src_width,
int src_height,
uint8_t* dst_argb,
int dst_stride_argb,
int destination_width,
int destination_height,
libyuv::FilterMode filtering) {
for (int y = 0; y < dst_height; y += kTileY) {
for (int x = 0; x < dst_width; x += kTileX) {
for (int y = 0; y < destination_height; y += kTileY) {
for (int x = 0; x < destination_width; x += kTileX) {
int clip_width = kTileX;
if (x + clip_width > dst_width) {
clip_width = dst_width - x;
if (x + clip_width > destination_width) {
clip_width = destination_width - x;
}
int clip_height = kTileY;
if (y + clip_height > dst_height) {
clip_height = dst_height - y;
if (y + clip_height > destination_height) {
clip_height = destination_height - y;
}
int r = libyuv::ARGBScaleClip(src_argb, src_stride_argb,
src_width, src_height,
dst_argb, dst_stride_argb,
dst_width, dst_height,
x, y, clip_width, clip_height, filtering);
int r = libyuv::ARGBScaleClip(src_argb, src_stride_argb, src_width,
src_height, dst_argb, dst_stride_argb,
destination_width, destination_height, x, y,
clip_width, clip_height, filtering);
if (r) {
return r;
}
@ -197,8 +201,8 @@ int main(int argc, const char* argv[]) {
}
// Open all files to convert to
FILE** file_rec = new FILE* [num_rec];
memset(file_rec, 0, num_rec * sizeof(FILE*)); // NOLINT
FILE** file_rec = new FILE*[num_rec];
memset(file_rec, 0, num_rec * sizeof(FILE*)); // NOLINT
for (int cur_rec = 0; cur_rec < num_rec; ++cur_rec) {
file_rec[cur_rec] = fopen(argv[fileindex_rec + cur_rec], "wb");
if (file_rec[cur_rec] == NULL) {
@ -222,8 +226,8 @@ int main(int argc, const char* argv[]) {
// Input is YUV
if (org_is_yuv) {
const int y_size = Abs(image_width) * Abs(image_height);
const int uv_size = ((Abs(image_width) + 1) / 2) *
((Abs(image_height) + 1) / 2);
const int uv_size =
((Abs(image_width) + 1) / 2) * ((Abs(image_height) + 1) / 2);
org_size = y_size + 2 * uv_size; // YUV original.
}
@ -233,15 +237,15 @@ int main(int argc, const char* argv[]) {
const size_t total_size = y_size + 2 * uv_size;
#if defined(_MSC_VER)
_fseeki64(file_org,
static_cast<__int64>(num_skip_org) *
static_cast<__int64>(org_size), SEEK_SET);
static_cast<__int64>(num_skip_org) * static_cast<__int64>(org_size),
SEEK_SET);
#else
fseek(file_org, num_skip_org * total_size, SEEK_SET);
#endif
uint8* const ch_org = new uint8[org_size];
uint8* const ch_dst = new uint8[dst_size];
uint8* const ch_rec = new uint8[total_size];
uint8_t* const ch_org = new uint8_t[org_size];
uint8_t* const ch_dst = new uint8_t[dst_size];
uint8_t* const ch_rec = new uint8_t[total_size];
if (ch_org == NULL || ch_rec == NULL) {
fprintf(stderr, "No memory available\n");
fclose(file_org);
@ -256,20 +260,22 @@ int main(int argc, const char* argv[]) {
}
if (verbose) {
printf("Size: %dx%d to %dx%d\n", image_width, image_height,
dst_width, dst_height);
printf("Size: %dx%d to %dx%d\n", image_width, image_height, dst_width,
dst_height);
}
int number_of_frames;
for (number_of_frames = 0; ; ++number_of_frames) {
if (num_frames && number_of_frames >= num_frames)
for (number_of_frames = 0;; ++number_of_frames) {
if (num_frames && number_of_frames >= num_frames) {
break;
}
// Load original YUV or ARGB frame.
size_t bytes_org = fread(ch_org, sizeof(uint8),
static_cast<size_t>(org_size), file_org);
if (bytes_org < static_cast<size_t>(org_size))
size_t bytes_org =
fread(ch_org, sizeof(uint8_t), static_cast<size_t>(org_size), file_org);
if (bytes_org < static_cast<size_t>(org_size)) {
break;
}
// TODO(fbarchard): Attenuate doesnt need to know dimensions.
// ARGB attenuate frame
@ -290,22 +296,17 @@ int main(int argc, const char* argv[]) {
int half_src_height = (src_height + 1) / 2;
int half_dst_width = (dst_width + 1) / 2;
int half_dst_height = (dst_height + 1) / 2;
I420Scale(ch_org, src_width,
ch_org + src_width * src_height, half_src_width,
ch_org + src_width * src_height +
half_src_width * half_src_height, half_src_width,
image_width, image_height,
ch_rec, dst_width,
ch_rec + dst_width * dst_height, half_dst_width,
ch_rec + dst_width * dst_height +
half_dst_width * half_dst_height, half_dst_width,
dst_width, dst_height,
static_cast<libyuv::FilterMode>(filter));
I420Scale(
ch_org, src_width, ch_org + src_width * src_height, half_src_width,
ch_org + src_width * src_height + half_src_width * half_src_height,
half_src_width, image_width, image_height, ch_rec, dst_width,
ch_rec + dst_width * dst_height, half_dst_width,
ch_rec + dst_width * dst_height + half_dst_width * half_dst_height,
half_dst_width, dst_width, dst_height,
static_cast<libyuv::FilterMode>(filter));
} else {
TileARGBScale(ch_org, Abs(image_width) * 4,
image_width, image_height,
ch_dst, dst_width * 4,
dst_width, dst_height,
TileARGBScale(ch_org, Abs(image_width) * 4, image_width, image_height,
ch_dst, dst_width * 4, dst_width, dst_height,
static_cast<libyuv::FilterMode>(filter));
}
bool rec_is_yuv = strstr(argv[fileindex_rec + cur_rec], "_P420.") != NULL;
@ -321,27 +322,28 @@ int main(int argc, const char* argv[]) {
if (!org_is_yuv && rec_is_yuv) {
int half_width = (dst_width + 1) / 2;
int half_height = (dst_height + 1) / 2;
libyuv::ARGBToI420(ch_dst, dst_width * 4,
ch_rec, dst_width,
ch_rec + dst_width * dst_height, half_width,
ch_rec + dst_width * dst_height +
half_width * half_height, half_width,
dst_width, dst_height);
libyuv::ARGBToI420(
ch_dst, dst_width * 4, ch_rec, dst_width,
ch_rec + dst_width * dst_height, half_width,
ch_rec + dst_width * dst_height + half_width * half_height,
half_width, dst_width, dst_height);
}
// Output YUV or ARGB frame.
if (rec_is_yuv) {
size_t bytes_rec = fwrite(ch_rec, sizeof(uint8),
static_cast<size_t>(total_size),
file_rec[cur_rec]);
if (bytes_rec < static_cast<size_t>(total_size))
size_t bytes_rec =
fwrite(ch_rec, sizeof(uint8_t), static_cast<size_t>(total_size),
file_rec[cur_rec]);
if (bytes_rec < static_cast<size_t>(total_size)) {
break;
}
} else {
size_t bytes_rec = fwrite(ch_dst, sizeof(uint8),
static_cast<size_t>(dst_size),
file_rec[cur_rec]);
if (bytes_rec < static_cast<size_t>(dst_size))
size_t bytes_rec =
fwrite(ch_dst, sizeof(uint8_t), static_cast<size_t>(dst_size),
file_rec[cur_rec]);
if (bytes_rec < static_cast<size_t>(dst_size)) {
break;
}
}
if (verbose) {
printf("%5d", number_of_frames);