Skip to content
GitLab
Menu
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
Xiph.Org
aom-rav1e
Commits
03829f2f
Commit
03829f2f
authored
Apr 17, 2015
by
Jim Bankoski
Committed by
Gerrit Code Review
Apr 17, 2015
Browse files
Merge "Adds a blockiness metric to internal stats."
parents
3d2f037a
1777413a
Changes
6
Hide whitespace changes
Inline
Side-by-side
test/blockiness_test.cc
0 → 100644
View file @
03829f2f
/*
* Copyright (c) 2012 The WebM 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
<string.h>
#include
<limits.h>
#include
<stdio.h>
#include
"./vpx_config.h"
#if CONFIG_VP9_ENCODER
#include
"./vp9_rtcd.h"
#endif
#include
"test/acm_random.h"
#include
"test/clear_system_state.h"
#include
"test/register_state_check.h"
#include
"test/util.h"
#include
"third_party/googletest/src/include/gtest/gtest.h"
#include
"vpx_mem/vpx_mem.h"
extern
"C"
double
vp9_get_blockiness
(
const
unsigned
char
*
img1
,
int
img1_pitch
,
const
unsigned
char
*
img2
,
int
img2_pitch
,
int
width
,
int
height
);
using
libvpx_test
::
ACMRandom
;
namespace
{
class
BlockinessTestBase
:
public
::
testing
::
Test
{
public:
BlockinessTestBase
(
int
width
,
int
height
)
:
width_
(
width
),
height_
(
height
)
{}
static
void
SetUpTestCase
()
{
source_data_
=
reinterpret_cast
<
uint8_t
*>
(
vpx_memalign
(
kDataAlignment
,
kDataBufferSize
));
reference_data_
=
reinterpret_cast
<
uint8_t
*>
(
vpx_memalign
(
kDataAlignment
,
kDataBufferSize
));
}
static
void
TearDownTestCase
()
{
vpx_free
(
source_data_
);
source_data_
=
NULL
;
vpx_free
(
reference_data_
);
reference_data_
=
NULL
;
}
virtual
void
TearDown
()
{
libvpx_test
::
ClearSystemState
();
}
protected:
// Handle frames up to 640x480
static
const
int
kDataAlignment
=
16
;
static
const
int
kDataBufferSize
=
640
*
480
;
virtual
void
SetUp
()
{
source_stride_
=
(
width_
+
31
)
&
~
31
;
reference_stride_
=
width_
*
2
;
rnd_
.
Reset
(
ACMRandom
::
DeterministicSeed
());
}
void
FillConstant
(
uint8_t
*
data
,
int
stride
,
uint8_t
fill_constant
,
int
width
,
int
height
)
{
for
(
int
h
=
0
;
h
<
height
;
++
h
)
{
for
(
int
w
=
0
;
w
<
width
;
++
w
)
{
data
[
h
*
stride
+
w
]
=
fill_constant
;
}
}
}
void
FillConstant
(
uint8_t
*
data
,
int
stride
,
uint8_t
fill_constant
)
{
FillConstant
(
data
,
stride
,
fill_constant
,
width_
,
height_
);
}
void
FillRandom
(
uint8_t
*
data
,
int
stride
,
int
width
,
int
height
)
{
for
(
int
h
=
0
;
h
<
height
;
++
h
)
{
for
(
int
w
=
0
;
w
<
width
;
++
w
)
{
data
[
h
*
stride
+
w
]
=
rnd_
.
Rand8
();
}
}
}
void
FillRandom
(
uint8_t
*
data
,
int
stride
)
{
FillRandom
(
data
,
stride
,
width_
,
height_
);
}
void
FillRandomBlocky
(
uint8_t
*
data
,
int
stride
)
{
for
(
int
h
=
0
;
h
<
height_
;
h
+=
4
)
{
for
(
int
w
=
0
;
w
<
width_
;
w
+=
4
)
{
FillRandom
(
data
+
h
*
stride
+
w
,
stride
,
4
,
4
);
}
}
}
void
FillCheckerboard
(
uint8_t
*
data
,
int
stride
)
{
for
(
int
h
=
0
;
h
<
height_
;
h
+=
4
)
{
for
(
int
w
=
0
;
w
<
width_
;
w
+=
4
)
{
if
(((
h
/
4
)
^
(
w
/
4
))
&
1
)
FillConstant
(
data
+
h
*
stride
+
w
,
stride
,
255
,
4
,
4
);
else
FillConstant
(
data
+
h
*
stride
+
w
,
stride
,
0
,
4
,
4
);
}
}
}
void
Blur
(
uint8_t
*
data
,
int
stride
,
int
taps
)
{
int
sum
=
0
;
int
half_taps
=
taps
/
2
;
for
(
int
h
=
0
;
h
<
height_
;
++
h
)
{
for
(
int
w
=
0
;
w
<
taps
;
++
w
)
{
sum
+=
data
[
w
+
h
*
stride
];
}
for
(
int
w
=
taps
;
w
<
width_
;
++
w
)
{
sum
+=
data
[
w
+
h
*
stride
]
-
data
[
w
-
taps
+
h
*
stride
];
data
[
w
-
half_taps
+
h
*
stride
]
=
(
sum
+
half_taps
)
/
taps
;
}
}
for
(
int
w
=
0
;
w
<
width_
;
++
w
)
{
for
(
int
h
=
0
;
h
<
taps
;
++
h
)
{
sum
+=
data
[
h
+
w
*
stride
];
}
for
(
int
h
=
taps
;
h
<
height_
;
++
h
)
{
sum
+=
data
[
w
+
h
*
stride
]
-
data
[(
h
-
taps
)
*
stride
+
w
];
data
[(
h
-
half_taps
)
*
stride
+
w
]
=
(
sum
+
half_taps
)
/
taps
;
}
}
}
int
width_
,
height_
;
static
uint8_t
*
source_data_
;
int
source_stride_
;
static
uint8_t
*
reference_data_
;
int
reference_stride_
;
ACMRandom
rnd_
;
};
#if CONFIG_VP9_ENCODER
typedef
std
::
tr1
::
tuple
<
int
,
int
>
BlockinessParam
;
class
BlockinessVP9Test
:
public
BlockinessTestBase
,
public
::
testing
::
WithParamInterface
<
BlockinessParam
>
{
public:
BlockinessVP9Test
()
:
BlockinessTestBase
(
GET_PARAM
(
0
),
GET_PARAM
(
1
))
{}
protected:
int
CheckBlockiness
()
{
return
vp9_get_blockiness
(
source_data_
,
source_stride_
,
reference_data_
,
reference_stride_
,
width_
,
height_
);
}
};
#endif // CONFIG_VP9_ENCODER
uint8_t
*
BlockinessTestBase
::
source_data_
=
NULL
;
uint8_t
*
BlockinessTestBase
::
reference_data_
=
NULL
;
#if CONFIG_VP9_ENCODER
TEST_P
(
BlockinessVP9Test
,
SourceBlockierThanReference
)
{
// Source is blockier than reference.
FillRandomBlocky
(
source_data_
,
source_stride_
);
FillConstant
(
reference_data_
,
reference_stride_
,
128
);
int
super_blocky
=
CheckBlockiness
();
EXPECT_EQ
(
0
,
super_blocky
)
<<
"Blocky source should produce 0 blockiness."
;
}
TEST_P
(
BlockinessVP9Test
,
ReferenceBlockierThanSource
)
{
// Source is blockier than reference.
FillConstant
(
source_data_
,
source_stride_
,
128
);
FillRandomBlocky
(
reference_data_
,
reference_stride_
);
int
super_blocky
=
CheckBlockiness
();
EXPECT_GT
(
super_blocky
,
0.0
)
<<
"Blocky reference should score high for blockiness."
;
}
TEST_P
(
BlockinessVP9Test
,
BlurringDecreasesBlockiness
)
{
// Source is blockier than reference.
FillConstant
(
source_data_
,
source_stride_
,
128
);
FillRandomBlocky
(
reference_data_
,
reference_stride_
);
int
super_blocky
=
CheckBlockiness
();
Blur
(
reference_data_
,
reference_stride_
,
4
);
int
less_blocky
=
CheckBlockiness
();
EXPECT_GT
(
super_blocky
,
less_blocky
)
<<
"A straight blur should decrease blockiness."
;
}
TEST_P
(
BlockinessVP9Test
,
WorstCaseBlockiness
)
{
// Source is blockier than reference.
FillConstant
(
source_data_
,
source_stride_
,
128
);
FillCheckerboard
(
reference_data_
,
reference_stride_
);
int
super_blocky
=
CheckBlockiness
();
Blur
(
reference_data_
,
reference_stride_
,
4
);
int
less_blocky
=
CheckBlockiness
();
EXPECT_GT
(
super_blocky
,
less_blocky
)
<<
"A straight blur should decrease blockiness."
;
}
#endif // CONFIG_VP9_ENCODER
using
std
::
tr1
::
make_tuple
;
//------------------------------------------------------------------------------
// C functions
#if CONFIG_VP9_ENCODER
const
BlockinessParam
c_vp9_tests
[]
=
{
make_tuple
(
320
,
240
),
make_tuple
(
318
,
242
),
make_tuple
(
318
,
238
),
};
INSTANTIATE_TEST_CASE_P
(
C
,
BlockinessVP9Test
,
::
testing
::
ValuesIn
(
c_vp9_tests
));
#endif
}
// namespace
test/test.mk
View file @
03829f2f
...
...
@@ -150,6 +150,7 @@ LIBVPX_TEST_SRCS-$(CONFIG_VP9) += vp9_intrapred_test.cc
ifeq
($(CONFIG_VP9_ENCODER),yes)
LIBVPX_TEST_SRCS-$(CONFIG_SPATIAL_SVC)
+=
svc_test.cc
LIBVPX_TEST_SRCS-$(CONFIG_INTERNAL_STATS)
+=
blockiness_test.cc
endif
ifeq
($(CONFIG_VP9_ENCODER)$(CONFIG_VP9_TEMPORAL_DENOISING),yesyes)
...
...
vp9/encoder/vp9_blockiness.c
0 → 100644
View file @
03829f2f
/*
* Copyright (c) 2014 The WebM 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
"./vpx_config.h"
#include
"./vp9_rtcd.h"
#include
"vp9/common/vp9_common.h"
#include
"vp9/common/vp9_convolve.h"
#include
"vp9/common/vp9_filter.h"
#include
"vpx/vpx_integer.h"
#include
"vpx_ports/mem.h"
static
int
horizontal_filter
(
const
uint8_t
*
s
)
{
return
(
s
[
1
]
-
s
[
-
2
])
*
2
+
(
s
[
-
1
]
-
s
[
0
])
*
6
;
}
static
int
vertical_filter
(
const
uint8_t
*
s
,
int
p
)
{
return
(
s
[
p
]
-
s
[
-
2
*
p
])
*
2
+
(
s
[
-
p
]
-
s
[
0
])
*
6
;
}
static
int
variance
(
int
sum
,
int
sum_squared
,
int
size
)
{
return
sum_squared
/
size
-
(
sum
/
size
)
*
(
sum
/
size
);
}
// Calculate a blockiness level for a vertical block edge.
// This function returns a new blockiness metric that's defined as
// p0 p1 p2 p3
// q0 q1 q2 q3
// block edge ->
// r0 r1 r2 r3
// s0 s1 s2 s3
// blockiness = p0*-2+q0*6+r0*-6+s0*2 +
// p1*-2+q1*6+r1*-6+s1*2 +
// p2*-2+q2*6+r2*-6+s2*2 +
// p3*-2+q3*6+r3*-6+s3*2 ;
// reconstructed_blockiness = abs(blockiness from reconstructed buffer -
// blockiness from source buffer,0)
//
// I make the assumption that flat blocks are much more visible than high
// contrast blocks. As such, I scale the result of the blockiness calc
// by dividing the blockiness by the variance of the pixels on either side
// of the edge as follows:
// var_0 = (q0^2+q1^2+q2^2+q3^2) - ((q0 + q1 + q2 + q3) / 4 )^2
// var_1 = (r0^2+r1^2+r2^2+r3^2) - ((r0 + r1 + r2 + r3) / 4 )^2
// The returned blockiness is the scaled value
// Reconstructed blockiness / ( 1 + var_0 + var_1 ) ;
int
blockiness_vertical
(
const
uint8_t
*
s
,
int
sp
,
const
uint8_t
*
r
,
int
rp
,
int
size
)
{
int
s_blockiness
=
0
;
int
r_blockiness
=
0
;
int
sum_0
=
0
;
int
sum_sq_0
=
0
;
int
sum_1
=
0
;
int
sum_sq_1
=
0
;
int
i
;
int
var_0
;
int
var_1
;
for
(
i
=
0
;
i
<
size
;
++
i
,
s
+=
sp
,
r
+=
rp
)
{
s_blockiness
+=
horizontal_filter
(
s
);
r_blockiness
+=
horizontal_filter
(
r
);
sum_0
+=
s
[
0
];
sum_sq_0
+=
s
[
0
]
*
s
[
0
];
sum_1
+=
s
[
-
1
];
sum_sq_1
+=
s
[
-
1
]
*
s
[
-
1
];
}
var_0
=
variance
(
sum_0
,
sum_sq_0
,
size
);
var_1
=
variance
(
sum_1
,
sum_sq_1
,
size
);
r_blockiness
=
abs
(
r_blockiness
);
s_blockiness
=
abs
(
s_blockiness
);
if
(
r_blockiness
>
s_blockiness
)
return
(
r_blockiness
-
s_blockiness
)
/
(
1
+
var_0
+
var_1
);
else
return
0
;
}
// Calculate a blockiness level for a horizontal block edge
// same as above.
int
blockiness_horizontal
(
const
uint8_t
*
s
,
int
sp
,
const
uint8_t
*
r
,
int
rp
,
int
size
)
{
int
s_blockiness
=
0
;
int
r_blockiness
=
0
;
int
sum_0
=
0
;
int
sum_sq_0
=
0
;
int
sum_1
=
0
;
int
sum_sq_1
=
0
;
int
i
;
int
var_0
;
int
var_1
;
for
(
i
=
0
;
i
<
size
;
++
i
,
++
s
,
++
r
)
{
s_blockiness
+=
vertical_filter
(
s
,
sp
);
r_blockiness
+=
vertical_filter
(
r
,
rp
);
sum_0
+=
s
[
0
];
sum_sq_0
+=
s
[
0
]
*
s
[
0
];
sum_1
+=
s
[
-
sp
];
sum_sq_1
+=
s
[
-
sp
]
*
s
[
-
sp
];
}
var_0
=
variance
(
sum_0
,
sum_sq_0
,
size
);
var_1
=
variance
(
sum_1
,
sum_sq_1
,
size
);
r_blockiness
=
abs
(
r_blockiness
);
s_blockiness
=
abs
(
s_blockiness
);
if
(
r_blockiness
>
s_blockiness
)
return
(
r_blockiness
-
s_blockiness
)
/
(
1
+
var_0
+
var_1
);
else
return
0
;
}
// This function returns the blockiness for the entire frame currently by
// looking at all borders in steps of 4.
double
vp9_get_blockiness
(
const
unsigned
char
*
img1
,
int
img1_pitch
,
const
unsigned
char
*
img2
,
int
img2_pitch
,
int
width
,
int
height
)
{
double
blockiness
=
0
;
int
i
,
j
;
vp9_clear_system_state
();
for
(
i
=
0
;
i
<
height
;
i
+=
4
,
img1
+=
img1_pitch
*
4
,
img2
+=
img2_pitch
*
4
)
{
for
(
j
=
0
;
j
<
width
;
j
+=
4
)
{
if
(
i
>
0
&&
i
<
height
&&
j
>
0
&&
j
<
width
)
{
blockiness
+=
blockiness_vertical
(
img1
+
j
,
img1_pitch
,
img2
+
j
,
img2_pitch
,
4
);
blockiness
+=
blockiness_horizontal
(
img1
+
j
,
img1_pitch
,
img2
+
j
,
img2_pitch
,
4
);
}
}
}
blockiness
/=
width
*
height
/
16
;
return
blockiness
;
}
vp9/encoder/vp9_encoder.c
View file @
03829f2f
...
...
@@ -1617,6 +1617,8 @@ VP9_COMP *vp9_create_compressor(VP9EncoderConfig *oxcf,
cpi
->
b_calculate_psnr
=
CONFIG_INTERNAL_STATS
;
#if CONFIG_INTERNAL_STATS
cpi
->
b_calculate_ssimg
=
0
;
cpi
->
b_calculate_blockiness
=
1
;
cpi
->
count
=
0
;
cpi
->
bytes
=
0
;
...
...
@@ -1658,6 +1660,15 @@ VP9_COMP *vp9_create_compressor(VP9EncoderConfig *oxcf,
cpi
->
total_psnrhvs_u
=
0
;
cpi
->
total_psnrhvs_v
=
0
;
cpi
->
total_psnrhvs_all
=
0
;
if
(
cpi
->
b_calculate_blockiness
)
{
cpi
->
total_blockiness
=
0
;
}
if
(
cpi
->
b_calculate_blockiness
)
{
cpi
->
total_blockiness
=
0
;
}
#endif
cpi
->
first_time_stamp_ever
=
INT64_MAX
;
...
...
@@ -1865,7 +1876,6 @@ void vp9_remove_compressor(VP9_COMP *cpi) {
if
(
cpi
&&
(
cm
->
current_video_frame
>
0
))
{
#if CONFIG_INTERNAL_STATS
vp9_clear_system_state
();
// printf("\n8x8-4x4:%d-%d\n", cpi->t8x8_count, cpi->t4x4_count);
...
...
@@ -1888,19 +1898,28 @@ void vp9_remove_compressor(VP9_COMP *cpi) {
(
double
)
cpi
->
totalp_sq_error
);
const
double
total_ssim
=
100
*
pow
(
cpi
->
summed_quality
/
cpi
->
summed_weights
,
8
.
0
);
const
double
totalp_ssim
=
100
*
pow
(
cpi
->
summedp_quality
/
cpi
->
summedp_weights
,
8
.
0
);
fprintf
(
f
,
"Bitrate
\t
AVGPsnr
\t
GLBPsnr
\t
AVPsnrP
\t
GLPsnrP
\t
"
if
(
cpi
->
b_calculate_blockiness
)
{
fprintf
(
f
,
"Bitrate
\t
AVGPsnr
\t
GLBPsnr
\t
AVPsnrP
\t
GLPsnrP
\t
"
"VPXSSIM
\t
VPSSIMP
\t
FASTSSIM
\t
PSNRHVS
\t
Time(ms)
\n
"
);
fprintf
(
f
,
"%7.2f
\t
%7.3f
\t
%7.3f
\t
%7.3f
\t
%7.3f
\t
%7.3f
\t
"
"%7.3f
\t
%7.3f
\t
%
7.3f
\t
%
8.0f
\n
"
,
dr
,
cpi
->
total
/
cpi
->
count
,
total_psnr
,
cpi
->
totalp
/
cpi
->
count
,
totalp_psnr
,
total_ssim
,
totalp_ssim
,
fprintf
(
f
,
"%7.2f
\t
%7.3f
\t
%7.3f
\t
%7.3f
\t
%7.3f
\t
%7.3f
\t
"
"%7.3f
\t
%7.3f
\t
%8.0f
\n
"
,
dr
,
cpi
->
total
/
cpi
->
count
,
total_psnr
,
cpi
->
totalp
/
cpi
->
count
,
totalp_psnr
,
total_ssim
,
cpi
->
total_fastssim_all
/
cpi
->
count
,
cpi
->
total_psnrhvs_all
/
cpi
->
count
,
total_encode_time
);
}
else
{
fprintf
(
f
,
"Bitrate
\t
AVGPsnr
\t
GLBPsnr
\t
AVPsnrP
\t
GLPsnrP
\t
"
"VPXSSIM
\t
VPSSIMP
\t
Blockiness
\t
FASTSSIM
\t
PSNRHVS
\t
Time(ms)
\n
"
);
fprintf
(
f
,
"%7.2f
\t
%7.3f
\t
%7.3f
\t
%7.3f
\t
%7.3f
\t
%7.3f
\t
"
"%7.3f
\t
%7.3f
\t
%7.3f
\t
%8.0f
\n
"
,
dr
,
cpi
->
total
/
cpi
->
count
,
total_psnr
,
cpi
->
totalp
/
cpi
->
count
,
totalp_psnr
,
total_ssim
,
cpi
->
total_blockiness
/
cpi
->
count
,
cpi
->
total_fastssim_all
/
cpi
->
count
,
cpi
->
total_psnrhvs_all
/
cpi
->
count
,
total_encode_time
);
}
}
...
...
@@ -3820,6 +3839,12 @@ static void check_src_altref(VP9_COMP *cpi,
}
}
#if CONFIG_INTERNAL_STATS
extern
double
vp9_get_blockiness
(
const
unsigned
char
*
img1
,
int
img1_pitch
,
const
unsigned
char
*
img2
,
int
img2_pitch
,
int
width
,
int
height
);
#endif
int
vp9_get_compressed_data
(
VP9_COMP
*
cpi
,
unsigned
int
*
frame_flags
,
size_t
*
size
,
uint8_t
*
dest
,
int64_t
*
time_stamp
,
int64_t
*
time_end
,
int
flush
)
{
...
...
@@ -4169,7 +4194,12 @@ int vp9_get_compressed_data(VP9_COMP *cpi, unsigned int *frame_flags,
#endif
}
}
if
(
cpi
->
b_calculate_blockiness
)
cpi
->
total_blockiness
+=
vp9_get_blockiness
(
cpi
->
Source
->
y_buffer
,
cpi
->
Source
->
y_stride
,
cm
->
frame_to_show
->
y_buffer
,
cm
->
frame_to_show
->
y_stride
,
cpi
->
Source
->
y_width
,
cpi
->
Source
->
y_height
);
if
(
cpi
->
b_calculate_ssimg
)
{
double
y
,
u
,
v
,
frame_all
;
...
...
vp9/encoder/vp9_encoder.h
View file @
03829f2f
...
...
@@ -402,6 +402,8 @@ typedef struct VP9_COMP {
uint64_t
totalp_sq_error
;
uint64_t
totalp_samples
;
double
total_blockiness
;
int
bytes
;
double
summed_quality
;
double
summed_weights
;
...
...
@@ -426,6 +428,7 @@ typedef struct VP9_COMP {
double
total_psnrhvs_all
;
int
b_calculate_ssimg
;
int
b_calculate_blockiness
;
#endif
int
b_calculate_psnr
;
...
...
vp9/vp9cx.mk
View file @
03829f2f
...
...
@@ -81,6 +81,8 @@ VP9_CX_SRCS-yes += encoder/vp9_resize.c
VP9_CX_SRCS-yes
+=
encoder/vp9_resize.h
VP9_CX_SRCS-$(CONFIG_INTERNAL_STATS)
+=
encoder/vp9_ssim.c
VP9_CX_SRCS-$(CONFIG_INTERNAL_STATS)
+=
encoder/vp9_ssim.h
VP9_CX_SRCS-$(CONFIG_INTERNAL_STATS)
+=
encoder/vp9_blockiness.c
VP9_CX_SRCS-yes
+=
encoder/vp9_tokenize.c
VP9_CX_SRCS-yes
+=
encoder/vp9_treewriter.c
VP9_CX_SRCS-yes
+=
encoder/vp9_variance.c
...
...
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment