Skip to content

Commit

Permalink
Fix assert failure when the gainmap has a constant values. (#2578)
Browse files Browse the repository at this point in the history
  • Loading branch information
maryla-uc authored Jan 22, 2025
1 parent 54e94f5 commit 7ae69db
Show file tree
Hide file tree
Showing 2 changed files with 90 additions and 12 deletions.
29 changes: 19 additions & 10 deletions src/gainmap.c
Original file line number Diff line number Diff line change
Expand Up @@ -704,17 +704,26 @@ avifResult avifRGBImageComputeGainMap(const avifRGBImage * baseRgbImage,

// Scale the gain map values to map [min, max] range to [0, 1].
for (int c = 0; c < numGainMapChannels; ++c) {
const float range = gainMapMaxLog2[c] - gainMapMinLog2[c];
if (range <= 0.0f) {
continue;
}
const float gainMapGamma = avifUnsignedFractionToFloat(gainMap->gainMapGamma[c]);
const float range = AVIF_MAX(gainMapMaxLog2[c] - gainMapMinLog2[c], 0.0f);

for (int j = 0; j < height; ++j) {
for (int i = 0; i < width; ++i) {
// Remap [min; max] range to [0; 1]
const float v = AVIF_CLAMP(gainMapF[c][j * width + i], gainMapMinLog2[c], gainMapMaxLog2[c]);
gainMapF[c][j * width + i] = powf((v - gainMapMinLog2[c]) / range, gainMapGamma);
if (range == 0.0f) {
for (int j = 0; j < height; ++j) {
for (int i = 0; i < width; ++i) {
// If the range is 0, the gain map values will be multiplied by zero when tonemapping so the values
// don't matter, but we still need to make sure that gainMapF is in [0,1].
gainMapF[c][j * width + i] = 0.0f;
}
}
} else {
// Remap [min; max] range to [0; 1]
const float gainMapGamma = avifUnsignedFractionToFloat(gainMap->gainMapGamma[c]);
for (int j = 0; j < height; ++j) {
for (int i = 0; i < width; ++i) {
float v = gainMapF[c][j * width + i];
v = AVIF_CLAMP(v, gainMapMinLog2[c], gainMapMaxLog2[c]);
v = powf((v - gainMapMinLog2[c]) / range, gainMapGamma);
gainMapF[c][j * width + i] = AVIF_CLAMP(v, 0.0f, 1.0f);
}
}
}
}
Expand Down
73 changes: 71 additions & 2 deletions tests/gtest/avifgainmaptest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1292,8 +1292,7 @@ TEST_P(CreateGainMapTest, Create) {
(uint32_t)std::round((float)image1->width / downscaling), 1u);
const uint32_t gain_map_height = std::max<uint32_t>(
(uint32_t)std::round((float)image1->height / downscaling), 1u);
std::unique_ptr<avifGainMap, decltype(&avifGainMapDestroy)> gain_map(
avifGainMapCreate(), avifGainMapDestroy);
GainMapPtr gain_map(avifGainMapCreate());
gain_map->image = avifImageCreate(gain_map_width, gain_map_height,
gain_map_depth, gain_map_format);

Expand Down Expand Up @@ -1441,10 +1440,32 @@ INSTANTIATE_TEST_SUITE_P(
/*gain_map_format=*/AVIF_PIXEL_FORMAT_YUV444,
/*min_psnr=*/55.0f, /*max_psnr=*/80.0f)));

TEST(GainMapTest, CreateGainMapConstantFactor) {
// Used only to initialize rgb images.
ImagePtr yuv(avifImageCreate(10, 10, 8, AVIF_PIXEL_FORMAT_YUV444));
testutil::AvifRgbImage base_image(yuv.get(), 8, AVIF_RGB_FORMAT_RGB);
testutil::AvifRgbImage alt_image(yuv.get(), 8, AVIF_RGB_FORMAT_RGB);
for (uint32_t i = 0; i < base_image.width * base_image.height * 3; ++i) {
base_image.pixels[i] = 10;
alt_image.pixels[i] = 200; // 20x factor compared to the base image.
}
GainMapPtr gain_map(avifGainMapCreate());
gain_map->image = avifImageCreate(5, 5, 8, AVIF_PIXEL_FORMAT_YUV444);
avifDiagnostics diag;
avifResult result = avifRGBImageComputeGainMap(
&base_image, AVIF_COLOR_PRIMARIES_SRGB,
AVIF_TRANSFER_CHARACTERISTICS_SRGB, &alt_image, AVIF_COLOR_PRIMARIES_SRGB,
AVIF_TRANSFER_CHARACTERISTICS_SRGB, gain_map.get(), &diag);

ASSERT_EQ(result, AVIF_RESULT_OK)
<< avifResultToString(result) << " " << diag.error;
}

TEST(FindMinMaxWithoutOutliers, AllSame) {
constexpr int kNumValues = 10000;

for (float v : {0.0f, 42.f, -12.f, 1.52f}) {
SCOPED_TRACE(v);
std::vector<float> values(kNumValues, v);

float min, max;
Expand All @@ -1456,10 +1477,58 @@ TEST(FindMinMaxWithoutOutliers, AllSame) {
}
}

TEST(FindMinMaxWithoutOutliers, AllSameExceptOne) {
constexpr int kNumValues = 10000;

for (float v : {42.f, -12.f, 1.52f}) {
SCOPED_TRACE(v);
std::vector<float> values(kNumValues, v);
values[42] = v * 2;

float min, max;
ASSERT_EQ(
avifFindMinMaxWithoutOutliers(values.data(), kNumValues, &min, &max),
AVIF_RESULT_OK);
constexpr float kBucketSize = 0.01f; // Should match the value in gainmap.c
const float kEpsilon = 0.00001f;
if (v > 0) {
EXPECT_NEAR(min, v, kEpsilon);
EXPECT_NEAR(max, v + kBucketSize, kEpsilon);
} else {
EXPECT_NEAR(min, v - kBucketSize, kEpsilon);
EXPECT_NEAR(max, v, kEpsilon);
}
}
}

TEST(FindMinMaxWithoutOutliers, AllSameExceptOneFewValues) {
constexpr int kNumValues = 100; // Not enough values to remove outliers.

for (float v : {42.f, -12.f, 1.52f}) {
SCOPED_TRACE(v);
std::vector<float> values(kNumValues, v);
values[42] = v * 2;

float min, max;
ASSERT_EQ(
avifFindMinMaxWithoutOutliers(values.data(), kNumValues, &min, &max),
AVIF_RESULT_OK);
const float kEpsilon = 0.00001f;
if (v > 0) {
EXPECT_NEAR(min, v, kEpsilon);
EXPECT_NEAR(max, v * 2, kEpsilon); // Outlier is kept.
} else {
EXPECT_NEAR(min, v * 2, kEpsilon); // Outlier is kept.
EXPECT_NEAR(max, v, kEpsilon);
}
}
}

TEST(FindMinMaxWithoutOutliers, Test) {
constexpr int kNumValues = 10000;

for (const float value_shift : {0.0f, -20.0f, 20.0f}) {
SCOPED_TRACE(value_shift);
SCOPED_TRACE("value_shift: " + std::to_string(value_shift));
std::vector<float> values(kNumValues, value_shift + 2.0f);
int k = 0;
Expand Down

0 comments on commit 7ae69db

Please sign in to comment.