Skip to content

Commit 1422eb1

Browse files
committed
Move avifProcessAOMOptionsPostInit() after tune
To take sharpness into account with tune_iq. Add aviftunetest.
1 parent 065b5b8 commit 1422eb1

File tree

4 files changed

+146
-15
lines changed

4 files changed

+146
-15
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ The changes are relative to the previous release, unless the baseline is specifi
2929
* Associate transformative properties with alpha auxiliary image items.
3030
* Use AOM_TUNE_IQ by default when encoding luma and chroma with libaom.
3131
* Use AOM_TUNE_PSNR by default when encoding alpha with libaom.
32+
* Set tuning before applying the user-provided specific aom codec options.
3233

3334
### Removed since 1.3.0
3435

src/codec_aom.c

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,6 @@ struct avifCodecInternal
6464
avifPixelFormatInfo formatInfo;
6565
aom_img_fmt_t aomFormat;
6666
avifBool monochromeEnabled;
67-
// Whether 'tuning' (of the specified distortion metric) was set with an
68-
// avifEncoderSetCodecSpecificOption(encoder, "tune", value) call.
69-
avifBool tuningSet;
7067
uint32_t currentLayer;
7168
#endif
7269
};
@@ -359,6 +356,17 @@ static avifBool avifKeyEqualsName(const char * key, const char * name, avifBool
359356
(!strncmp(key, shortPrefix, shortPrefixLen) && !strcmp(key + shortPrefixLen, name));
360357
}
361358

359+
static avifBool avifAOMOptionsSetTuning(const avifCodec * codec, avifBool alpha)
360+
{
361+
for (uint32_t i = 0; i < codec->csOptions->count; ++i) {
362+
const avifCodecSpecificOption * entry = &codec->csOptions->entries[i];
363+
if (avifKeyEqualsName(entry->key, "tune", alpha)) {
364+
return AVIF_TRUE;
365+
}
366+
}
367+
return AVIF_FALSE;
368+
}
369+
362370
static avifBool avifProcessAOMOptionsPreInit(avifCodec * codec, avifBool alpha, struct aom_codec_enc_cfg * cfg)
363371
{
364372
for (uint32_t i = 0; i < codec->csOptions->count; ++i) {
@@ -459,9 +467,6 @@ static avifBool avifProcessAOMOptionsPostInit(avifCodec * codec, avifBool alpha)
459467
aom_codec_error_detail(&codec->internal->encoder));
460468
return AVIF_FALSE;
461469
}
462-
if (!strcmp(key, "tune")) {
463-
codec->internal->tuningSet = AVIF_TRUE;
464-
}
465470
#else // !defined(HAVE_AOM_CODEC_SET_OPTION)
466471
avifBool match = AVIF_FALSE;
467472
for (int j = 0; aomOptionDefs[j].name; ++j) {
@@ -493,9 +498,6 @@ static avifBool avifProcessAOMOptionsPostInit(avifCodec * codec, avifBool alpha)
493498
if (!success) {
494499
return AVIF_FALSE;
495500
}
496-
if (aomOptionDefs[j].controlId == AOME_SET_TUNING) {
497-
codec->internal->tuningSet = AVIF_TRUE;
498-
}
499501
break;
500502
}
501503
}
@@ -883,10 +885,7 @@ static avifResult aomCodecEncodeImage(avifCodec * codec,
883885
}
884886
#endif
885887

886-
if (!avifProcessAOMOptionsPostInit(codec, alpha)) {
887-
return AVIF_RESULT_INVALID_CODEC_SPECIFIC_OPTION;
888-
}
889-
if (!lossless && !codec->internal->tuningSet) {
888+
if (!lossless && !avifAOMOptionsSetTuning(codec, alpha)) {
890889
aom_tune_metric tuneMetric = AOM_TUNE_SSIM;
891890
if (alpha) {
892891
// Minimize ringing for alpha.
@@ -903,6 +902,9 @@ static avifResult aomCodecEncodeImage(avifCodec * codec,
903902
return AVIF_RESULT_UNKNOWN_ERROR;
904903
}
905904
}
905+
if (!avifProcessAOMOptionsPostInit(codec, alpha)) {
906+
return AVIF_RESULT_INVALID_CODEC_SPECIFIC_OPTION;
907+
}
906908

907909
if (image->depth == 12) {
908910
// The encoder may produce integer overflows with 12-bit input when loop restoration is enabled. See crbug.com/aomedia/42302587.

tests/CMakeLists.txt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ if(AVIF_GTEST)
142142

143143
add_avif_gtest(aviftilingtest)
144144
add_avif_gtest_with_data(aviftransformtest)
145+
add_avif_gtest_with_data(aviftunetest)
145146
add_avif_gtest(avifutilstest)
146147
add_avif_gtest(avify4mtest)
147148

@@ -150,8 +151,8 @@ if(AVIF_GTEST)
150151
# these tests are disabled because other codecs may not implement all the necessary features.
151152
# For example, SVT-AV1 requires 4:2:0 images with even dimensions of at least 64x64 px.
152153
set_tests_properties(
153-
avifallocationtest avifgridapitest avifincrtest aviflosslesstest avifmetadatatest avifpixitest PROPERTIES DISABLED
154-
True
154+
avifallocationtest avifgridapitest avifincrtest aviflosslesstest avifmetadatatest avifpixitest aviftunetest
155+
PROPERTIES DISABLED True
155156
)
156157

157158
message(STATUS "Some tests are disabled because aom is unavailable for encoding.")
@@ -345,6 +346,7 @@ if(AVIF_CODEC_AVM_ENABLED)
345346
avifprogressivetest
346347
avifpropertytest
347348
avifrangetest
349+
aviftunetest
348350
avify4mtest
349351
PROPERTIES DISABLED True
350352
)

tests/gtest/aviftunetest.cc

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
// Copyright 2025 Google LLC
2+
// SPDX-License-Identifier: BSD-2-Clause
3+
4+
#include <vector>
5+
6+
#include "avif/avif.h"
7+
#include "aviftest_helpers.h"
8+
#include "gtest/gtest.h"
9+
10+
namespace avif {
11+
namespace {
12+
13+
// Used to pass the data folder path to the GoogleTest suites.
14+
const char* data_path = nullptr;
15+
16+
//------------------------------------------------------------------------------
17+
18+
TEST(AomTuneMetricTest, GenerateDifferentBitstreams) {
19+
if (avifCodecName(AVIF_CODEC_CHOICE_AOM, AVIF_CODEC_FLAG_CAN_ENCODE) ==
20+
nullptr ||
21+
!testutil::Av1DecoderAvailable()) {
22+
GTEST_SKIP() << "Codec unavailable, skip test.";
23+
}
24+
25+
const ImagePtr image =
26+
testutil::ReadImage(data_path, "paris_exif_xmp_icc.jpg");
27+
ASSERT_NE(image, nullptr);
28+
// Speed up the test.
29+
image->width = 64;
30+
image->height = 64;
31+
32+
std::vector<std::vector<uint8_t>> encoded_bitstreams;
33+
for (const char* tune : {"psnr", "ssim", "iq"}) {
34+
EncoderPtr encoder(avifEncoderCreate());
35+
ASSERT_NE(encoder, nullptr);
36+
encoder->codecChoice = AVIF_CODEC_CHOICE_AOM;
37+
ASSERT_EQ(avifEncoderSetCodecSpecificOption(encoder.get(), "tune", tune),
38+
AVIF_RESULT_OK);
39+
testutil::AvifRwData encoded;
40+
ASSERT_EQ(avifEncoderWrite(encoder.get(), image.get(), &encoded),
41+
AVIF_RESULT_OK);
42+
for (const std::vector<uint8_t>& encoded_with_another_tune :
43+
encoded_bitstreams) {
44+
const bool is_same = encoded.size == encoded_with_another_tune.size() &&
45+
std::equal(encoded.data, encoded.data + encoded.size,
46+
encoded_with_another_tune.data());
47+
ASSERT_FALSE(is_same);
48+
}
49+
50+
DecoderPtr decoder(avifDecoderCreate());
51+
ASSERT_NE(decoder, nullptr);
52+
ImagePtr decoded(avifImageCreateEmpty());
53+
ASSERT_NE(decoded, nullptr);
54+
ASSERT_EQ(avifDecoderReadMemory(decoder.get(), decoded.get(), encoded.data,
55+
encoded.size),
56+
AVIF_RESULT_OK);
57+
ASSERT_GT(testutil::GetPsnr(*image, *decoded), 32.0);
58+
59+
encoded_bitstreams.emplace_back(encoded.data, encoded.data + encoded.size);
60+
}
61+
}
62+
63+
TEST(AomSharpnessTest, GenerateDifferentBitstreams) {
64+
if (avifCodecName(AVIF_CODEC_CHOICE_AOM, AVIF_CODEC_FLAG_CAN_ENCODE) ==
65+
nullptr ||
66+
!testutil::Av1DecoderAvailable()) {
67+
GTEST_SKIP() << "Codec unavailable, skip test.";
68+
}
69+
70+
const ImagePtr image =
71+
testutil::ReadImage(data_path, "paris_exif_xmp_icc.jpg");
72+
ASSERT_NE(image, nullptr);
73+
// Speed up the test.
74+
image->width = 64;
75+
image->height = 64;
76+
77+
std::vector<std::vector<uint8_t>> encoded_bitstreams;
78+
for (bool set_sharpness_to_0 : {false, true}) {
79+
EncoderPtr encoder(avifEncoderCreate());
80+
ASSERT_NE(encoder, nullptr);
81+
encoder->codecChoice = AVIF_CODEC_CHOICE_AOM;
82+
if (set_sharpness_to_0) {
83+
ASSERT_EQ(
84+
avifEncoderSetCodecSpecificOption(encoder.get(), "sharpness", "0"),
85+
AVIF_RESULT_OK);
86+
}
87+
testutil::AvifRwData encoded;
88+
ASSERT_EQ(avifEncoderWrite(encoder.get(), image.get(), &encoded),
89+
AVIF_RESULT_OK);
90+
for (const std::vector<uint8_t>& encoded_with_another_tune :
91+
encoded_bitstreams) {
92+
const bool is_same = encoded.size == encoded_with_another_tune.size() &&
93+
std::equal(encoded.data, encoded.data + encoded.size,
94+
encoded_with_another_tune.data());
95+
ASSERT_FALSE(is_same);
96+
}
97+
98+
DecoderPtr decoder(avifDecoderCreate());
99+
ASSERT_NE(decoder, nullptr);
100+
ImagePtr decoded(avifImageCreateEmpty());
101+
ASSERT_NE(decoded, nullptr);
102+
ASSERT_EQ(avifDecoderReadMemory(decoder.get(), decoded.get(), encoded.data,
103+
encoded.size),
104+
AVIF_RESULT_OK);
105+
ASSERT_GT(testutil::GetPsnr(*image, *decoded), 32.0);
106+
107+
encoded_bitstreams.emplace_back(encoded.data, encoded.data + encoded.size);
108+
}
109+
}
110+
111+
//------------------------------------------------------------------------------
112+
113+
} // namespace
114+
} // namespace avif
115+
116+
int main(int argc, char** argv) {
117+
::testing::InitGoogleTest(&argc, argv);
118+
if (argc != 2) {
119+
std::cerr << "There must be exactly one argument containing the path to "
120+
"the test data folder"
121+
<< std::endl;
122+
return 1;
123+
}
124+
avif::data_path = argv[1];
125+
return RUN_ALL_TESTS();
126+
}

0 commit comments

Comments
 (0)