diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 01:13:27 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 01:13:27 +0000 |
commit | 40a355a42d4a9444dc753c04c6608dade2f06a23 (patch) | |
tree | 871fc667d2de662f171103ce5ec067014ef85e61 /third_party/jpeg-xl/lib/jxl/butteraugli | |
parent | Adding upstream version 124.0.1. (diff) | |
download | firefox-adbda400be353e676059e335c3c0aaf99e719475.tar.xz firefox-adbda400be353e676059e335c3c0aaf99e719475.zip |
Adding upstream version 125.0.1.upstream/125.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/jpeg-xl/lib/jxl/butteraugli')
3 files changed, 221 insertions, 170 deletions
diff --git a/third_party/jpeg-xl/lib/jxl/butteraugli/butteraugli.cc b/third_party/jpeg-xl/lib/jxl/butteraugli/butteraugli.cc index 66dde9afb1..2e06d79eba 100644 --- a/third_party/jpeg-xl/lib/jxl/butteraugli/butteraugli.cc +++ b/third_party/jpeg-xl/lib/jxl/butteraugli/butteraugli.cc @@ -28,11 +28,12 @@ #include <string.h> #include <algorithm> -#include <array> #include <cmath> -#include <new> +#include <memory> #include <vector> +#include "lib/jxl/image.h" + #undef HWY_TARGET_INCLUDE #define HWY_TARGET_INCLUDE "lib/jxl/butteraugli/butteraugli.cc" #include <hwy/foreach_target.h> @@ -222,8 +223,8 @@ void ConvolutionWithTranspose(const ImageF& in, // We retain a special case for 5x5 kernels (even faster than gauss_blur), // optionally use gauss_blur followed by fixup of the borders for large images, // or fall back to the previous truncated FIR followed by a transpose. -void Blur(const ImageF& in, float sigma, const ButteraugliParams& params, - BlurTemp* temp, ImageF* out) { +Status Blur(const ImageF& in, float sigma, const ButteraugliParams& params, + BlurTemp* temp, ImageF* out) { std::vector<float> kernel = ComputeKernel(sigma); // Separable5 does an in-place convolution, so this fast path is not safe if // in aliases out. @@ -241,12 +242,14 @@ void Blur(const ImageF& in, float sigma, const ButteraugliParams& params, {HWY_REP4(w0), HWY_REP4(w1), HWY_REP4(w2)}, }; Separable5(in, Rect(in), weights, /*pool=*/nullptr, out); - return; + return true; } - ImageF* JXL_RESTRICT temp_t = temp->GetTransposed(in); + ImageF* temp_t; + JXL_RETURN_IF_ERROR(temp->GetTransposed(in, &temp_t)); ConvolutionWithTranspose(in, kernel, temp_t); ConvolutionWithTranspose(*temp_t, kernel, out); + return true; } // Allows PaddedMaltaUnit to call either function via overloading. @@ -386,29 +389,32 @@ void Subtract(const ImageF& a, const ImageF& b, ImageF* c) { } } -void SeparateLFAndMF(const ButteraugliParams& params, const Image3F& xyb, - Image3F* lf, Image3F* mf, BlurTemp* blur_temp) { +Status SeparateLFAndMF(const ButteraugliParams& params, const Image3F& xyb, + Image3F* lf, Image3F* mf, BlurTemp* blur_temp) { static const double kSigmaLf = 7.15593339443; for (int i = 0; i < 3; ++i) { // Extract lf ... - Blur(xyb.Plane(i), kSigmaLf, params, blur_temp, &lf->Plane(i)); + JXL_RETURN_IF_ERROR( + Blur(xyb.Plane(i), kSigmaLf, params, blur_temp, &lf->Plane(i))); // ... and keep everything else in mf. Subtract(xyb.Plane(i), lf->Plane(i), &mf->Plane(i)); } XybLowFreqToVals(lf); + return true; } -void SeparateMFAndHF(const ButteraugliParams& params, Image3F* mf, ImageF* hf, - BlurTemp* blur_temp) { +Status SeparateMFAndHF(const ButteraugliParams& params, Image3F* mf, ImageF* hf, + BlurTemp* blur_temp) { const HWY_FULL(float) d; static const double kSigmaHf = 3.22489901262; const size_t xsize = mf->xsize(); const size_t ysize = mf->ysize(); - hf[0] = ImageF(xsize, ysize); - hf[1] = ImageF(xsize, ysize); + JXL_ASSIGN_OR_RETURN(hf[0], ImageF::Create(xsize, ysize)); + JXL_ASSIGN_OR_RETURN(hf[1], ImageF::Create(xsize, ysize)); for (int i = 0; i < 3; ++i) { if (i == 2) { - Blur(mf->Plane(i), kSigmaHf, params, blur_temp, &mf->Plane(i)); + JXL_RETURN_IF_ERROR( + Blur(mf->Plane(i), kSigmaHf, params, blur_temp, &mf->Plane(i))); break; } for (size_t y = 0; y < ysize; ++y) { @@ -418,7 +424,8 @@ void SeparateMFAndHF(const ButteraugliParams& params, Image3F* mf, ImageF* hf, Store(Load(d, row_mf + x), d, row_hf + x); } } - Blur(mf->Plane(i), kSigmaHf, params, blur_temp, &mf->Plane(i)); + JXL_RETURN_IF_ERROR( + Blur(mf->Plane(i), kSigmaHf, params, blur_temp, &mf->Plane(i))); static const double kRemoveMfRange = 0.29; static const double kAddMfRange = 0.1; if (i == 0) { @@ -450,16 +457,17 @@ void SeparateMFAndHF(const ButteraugliParams& params, Image3F* mf, ImageF* hf, } // Suppress red-green by intensity change in the high freq channels. SuppressXByY(hf[1], &hf[0]); + return true; } -void SeparateHFAndUHF(const ButteraugliParams& params, ImageF* hf, ImageF* uhf, - BlurTemp* blur_temp) { +Status SeparateHFAndUHF(const ButteraugliParams& params, ImageF* hf, + ImageF* uhf, BlurTemp* blur_temp) { const HWY_FULL(float) d; const size_t xsize = hf[0].xsize(); const size_t ysize = hf[0].ysize(); static const double kSigmaUhf = 1.56416327805; - uhf[0] = ImageF(xsize, ysize); - uhf[1] = ImageF(xsize, ysize); + JXL_ASSIGN_OR_RETURN(uhf[0], ImageF::Create(xsize, ysize)); + JXL_ASSIGN_OR_RETURN(uhf[1], ImageF::Create(xsize, ysize)); for (int i = 0; i < 2; ++i) { // Divide hf into hf and uhf. for (size_t y = 0; y < ysize; ++y) { @@ -469,7 +477,7 @@ void SeparateHFAndUHF(const ButteraugliParams& params, ImageF* hf, ImageF* uhf, row_uhf[x] = row_hf[x]; } } - Blur(hf[i], kSigmaUhf, params, blur_temp, &hf[i]); + JXL_RETURN_IF_ERROR(Blur(hf[i], kSigmaUhf, params, blur_temp, &hf[i])); static const double kRemoveHfRange = 1.5; static const double kAddHfRange = 0.132; static const double kRemoveUhfRange = 0.04; @@ -510,6 +518,7 @@ void SeparateHFAndUHF(const ButteraugliParams& params, ImageF* hf, ImageF* uhf, } } } + return true; } void DeallocateHFAndUHF(ImageF* hf, ImageF* uhf) { @@ -519,15 +528,16 @@ void DeallocateHFAndUHF(ImageF* hf, ImageF* uhf) { } } -static void SeparateFrequencies(size_t xsize, size_t ysize, - const ButteraugliParams& params, - BlurTemp* blur_temp, const Image3F& xyb, - PsychoImage& ps) { - ps.lf = Image3F(xyb.xsize(), xyb.ysize()); - ps.mf = Image3F(xyb.xsize(), xyb.ysize()); - SeparateLFAndMF(params, xyb, &ps.lf, &ps.mf, blur_temp); - SeparateMFAndHF(params, &ps.mf, &ps.hf[0], blur_temp); - SeparateHFAndUHF(params, &ps.hf[0], &ps.uhf[0], blur_temp); +Status SeparateFrequencies(size_t xsize, size_t ysize, + const ButteraugliParams& params, BlurTemp* blur_temp, + const Image3F& xyb, PsychoImage& ps) { + JXL_ASSIGN_OR_RETURN(ps.lf, Image3F::Create(xyb.xsize(), xyb.ysize())); + JXL_ASSIGN_OR_RETURN(ps.mf, Image3F::Create(xyb.xsize(), xyb.ysize())); + JXL_RETURN_IF_ERROR(SeparateLFAndMF(params, xyb, &ps.lf, &ps.mf, blur_temp)); + JXL_RETURN_IF_ERROR(SeparateMFAndHF(params, &ps.mf, &ps.hf[0], blur_temp)); + JXL_RETURN_IF_ERROR( + SeparateHFAndUHF(params, &ps.hf[0], &ps.uhf[0], blur_temp)); + return true; } namespace { @@ -1024,7 +1034,7 @@ static void MaltaDiffMapT(const Tag tag, const ImageF& lum0, const ImageF& lum1, } const HWY_FULL(float) df; - const size_t aligned_x = std::max(size_t(4), Lanes(df)); + const size_t aligned_x = std::max(static_cast<size_t>(4), Lanes(df)); const intptr_t stride = diffs->PixelsPerRow(); // Middle @@ -1097,7 +1107,7 @@ void CombineChannelsForMasking(const ImageF* hf, const ImageF* uhf, float xdiff = (row_x_uhf[x] + row_x_hf[x]) * muls[0]; float ydiff = row_y_uhf[x] * muls[1] + row_y_hf[x] * muls[2]; row[x] = xdiff * xdiff + ydiff * ydiff; - row[x] = sqrt(row[x]); + row[x] = std::sqrt(row[x]); } } } @@ -1106,13 +1116,13 @@ void DiffPrecompute(const ImageF& xyb, float mul, float bias_arg, ImageF* out) { const size_t xsize = xyb.xsize(); const size_t ysize = xyb.ysize(); const float bias = mul * bias_arg; - const float sqrt_bias = sqrt(bias); + const float sqrt_bias = std::sqrt(bias); for (size_t y = 0; y < ysize; ++y) { const float* BUTTERAUGLI_RESTRICT row_in = xyb.Row(y); float* BUTTERAUGLI_RESTRICT row_out = out->Row(y); for (size_t x = 0; x < xsize; ++x) { // kBias makes sqrt behave more linearly. - row_out[x] = sqrt(mul * std::abs(row_in[x]) + bias) - sqrt_bias; + row_out[x] = std::sqrt(mul * std::abs(row_in[x]) + bias) - sqrt_bias; } } } @@ -1188,25 +1198,25 @@ void FuzzyErosion(const ImageF& from, ImageF* to) { // Compute values of local frequency and dc masking based on the activity // in the two images. img_diff_ac may be null. -void Mask(const ImageF& mask0, const ImageF& mask1, - const ButteraugliParams& params, BlurTemp* blur_temp, - ImageF* BUTTERAUGLI_RESTRICT mask, - ImageF* BUTTERAUGLI_RESTRICT diff_ac) { +Status Mask(const ImageF& mask0, const ImageF& mask1, + const ButteraugliParams& params, BlurTemp* blur_temp, + ImageF* BUTTERAUGLI_RESTRICT mask, + ImageF* BUTTERAUGLI_RESTRICT diff_ac) { const size_t xsize = mask0.xsize(); const size_t ysize = mask0.ysize(); - *mask = ImageF(xsize, ysize); + JXL_ASSIGN_OR_RETURN(*mask, ImageF::Create(xsize, ysize)); static const float kMul = 6.19424080439; static const float kBias = 12.61050594197; static const float kRadius = 2.7; - ImageF diff0(xsize, ysize); - ImageF diff1(xsize, ysize); - ImageF blurred0(xsize, ysize); - ImageF blurred1(xsize, ysize); + JXL_ASSIGN_OR_RETURN(ImageF diff0, ImageF::Create(xsize, ysize)); + JXL_ASSIGN_OR_RETURN(ImageF diff1, ImageF::Create(xsize, ysize)); + JXL_ASSIGN_OR_RETURN(ImageF blurred0, ImageF::Create(xsize, ysize)); + JXL_ASSIGN_OR_RETURN(ImageF blurred1, ImageF::Create(xsize, ysize)); DiffPrecompute(mask0, kMul, kBias, &diff0); DiffPrecompute(mask1, kMul, kBias, &diff1); - Blur(diff0, kRadius, params, blur_temp, &blurred0); + JXL_RETURN_IF_ERROR(Blur(diff0, kRadius, params, blur_temp, &blurred0)); FuzzyErosion(blurred0, &diff0); - Blur(diff1, kRadius, params, blur_temp, &blurred1); + JXL_RETURN_IF_ERROR(Blur(diff1, kRadius, params, blur_temp, &blurred1)); for (size_t y = 0; y < ysize; ++y) { for (size_t x = 0; x < xsize; ++x) { mask->Row(y)[x] = diff0.Row(y)[x]; @@ -1217,19 +1227,21 @@ void Mask(const ImageF& mask0, const ImageF& mask1, } } } + return true; } // `diff_ac` may be null. -void MaskPsychoImage(const PsychoImage& pi0, const PsychoImage& pi1, - const size_t xsize, const size_t ysize, - const ButteraugliParams& params, BlurTemp* blur_temp, - ImageF* BUTTERAUGLI_RESTRICT mask, - ImageF* BUTTERAUGLI_RESTRICT diff_ac) { - ImageF mask0(xsize, ysize); - ImageF mask1(xsize, ysize); +Status MaskPsychoImage(const PsychoImage& pi0, const PsychoImage& pi1, + const size_t xsize, const size_t ysize, + const ButteraugliParams& params, BlurTemp* blur_temp, + ImageF* BUTTERAUGLI_RESTRICT mask, + ImageF* BUTTERAUGLI_RESTRICT diff_ac) { + JXL_ASSIGN_OR_RETURN(ImageF mask0, ImageF::Create(xsize, ysize)); + JXL_ASSIGN_OR_RETURN(ImageF mask1, ImageF::Create(xsize, ysize)); CombineChannelsForMasking(&pi0.hf[0], &pi0.uhf[0], &mask0); CombineChannelsForMasking(&pi1.hf[0], &pi1.uhf[0], &mask1); - Mask(mask0, mask1, params, blur_temp, mask, diff_ac); + JXL_RETURN_IF_ERROR(Mask(mask0, mask1, params, blur_temp, mask, diff_ac)); + return true; } double MaskY(double delta) { @@ -1275,8 +1287,8 @@ void CombineChannelsToDiffmap(const ImageF& mask, const Image3F& block_diff_dc, } diff_ac[0] *= xmul; diff_dc[0] *= xmul; - row_out[x] = - sqrt(MaskColor(diff_dc, dc_maskval) + MaskColor(diff_ac, maskval)); + row_out[x] = std::sqrt(MaskColor(diff_dc, dc_maskval) + + MaskColor(diff_ac, maskval)); } } } @@ -1430,12 +1442,15 @@ BUTTERAUGLI_INLINE void OpsinAbsorbance(const DF df, const V& in0, const V& in1, } // `blurred` is a temporary image used inside this function and not returned. -void OpsinDynamicsImage(const Image3F& rgb, const ButteraugliParams& params, - Image3F* blurred, BlurTemp* blur_temp, Image3F* xyb) { +Status OpsinDynamicsImage(const Image3F& rgb, const ButteraugliParams& params, + Image3F* blurred, BlurTemp* blur_temp, Image3F* xyb) { const double kSigma = 1.2; - Blur(rgb.Plane(0), kSigma, params, blur_temp, &blurred->Plane(0)); - Blur(rgb.Plane(1), kSigma, params, blur_temp, &blurred->Plane(1)); - Blur(rgb.Plane(2), kSigma, params, blur_temp, &blurred->Plane(2)); + JXL_RETURN_IF_ERROR( + Blur(rgb.Plane(0), kSigma, params, blur_temp, &blurred->Plane(0))); + JXL_RETURN_IF_ERROR( + Blur(rgb.Plane(1), kSigma, params, blur_temp, &blurred->Plane(1))); + JXL_RETURN_IF_ERROR( + Blur(rgb.Plane(2), kSigma, params, blur_temp, &blurred->Plane(2))); const HWY_FULL(float) df; const auto intensity_target_multiplier = Set(df, params.intensity_target); for (size_t y = 0; y < rgb.ysize(); ++y) { @@ -1497,31 +1512,36 @@ void OpsinDynamicsImage(const Image3F& rgb, const ButteraugliParams& params, Store(cur_mixed2, df, row_out_b + x); } } + return true; } -void ButteraugliDiffmapInPlace(Image3F& image0, Image3F& image1, - const ButteraugliParams& params, - ImageF& diffmap) { +Status ButteraugliDiffmapInPlace(Image3F& image0, Image3F& image1, + const ButteraugliParams& params, + ImageF& diffmap) { // image0 and image1 are in linear sRGB color space const size_t xsize = image0.xsize(); const size_t ysize = image0.ysize(); BlurTemp blur_temp; { // Convert image0 and image1 to XYB in-place - Image3F temp(xsize, ysize); - OpsinDynamicsImage(image0, params, &temp, &blur_temp, &image0); - OpsinDynamicsImage(image1, params, &temp, &blur_temp, &image1); + JXL_ASSIGN_OR_RETURN(Image3F temp, Image3F::Create(xsize, ysize)); + JXL_RETURN_IF_ERROR( + OpsinDynamicsImage(image0, params, &temp, &blur_temp, &image0)); + JXL_RETURN_IF_ERROR( + OpsinDynamicsImage(image1, params, &temp, &blur_temp, &image1)); } // image0 and image1 are in XYB color space - ImageF block_diff_dc(xsize, ysize); + JXL_ASSIGN_OR_RETURN(ImageF block_diff_dc, ImageF::Create(xsize, ysize)); ZeroFillImage(&block_diff_dc); { // separate out LF components from image0 and image1 and compute the dc // diff image from them - Image3F lf0 = Image3F(xsize, ysize); - Image3F lf1 = Image3F(xsize, ysize); - SeparateLFAndMF(params, image0, &lf0, &image0, &blur_temp); - SeparateLFAndMF(params, image1, &lf1, &image1, &blur_temp); + JXL_ASSIGN_OR_RETURN(Image3F lf0, Image3F::Create(xsize, ysize)); + JXL_ASSIGN_OR_RETURN(Image3F lf1, Image3F::Create(xsize, ysize)); + JXL_RETURN_IF_ERROR( + SeparateLFAndMF(params, image0, &lf0, &image0, &blur_temp)); + JXL_RETURN_IF_ERROR( + SeparateLFAndMF(params, image1, &lf1, &image1, &blur_temp)); for (size_t c = 0; c < 3; ++c) { L2Diff(lf0.Plane(c), lf1.Plane(c), wmul[6 + c], &block_diff_dc); } @@ -1529,15 +1549,15 @@ void ButteraugliDiffmapInPlace(Image3F& image0, Image3F& image1, // image0 and image1 are MF residuals (before blurring) in XYB color space ImageF hf0[2]; ImageF hf1[2]; - SeparateMFAndHF(params, &image0, &hf0[0], &blur_temp); - SeparateMFAndHF(params, &image1, &hf1[0], &blur_temp); + JXL_RETURN_IF_ERROR(SeparateMFAndHF(params, &image0, &hf0[0], &blur_temp)); + JXL_RETURN_IF_ERROR(SeparateMFAndHF(params, &image1, &hf1[0], &blur_temp)); // image0 and image1 are MF-images in XYB color space - ImageF block_diff_ac(xsize, ysize); + JXL_ASSIGN_OR_RETURN(ImageF block_diff_ac, ImageF::Create(xsize, ysize)); ZeroFillImage(&block_diff_ac); // start accumulating ac diff image from MF images { - ImageF diffs(xsize, ysize); + JXL_ASSIGN_OR_RETURN(ImageF diffs, ImageF::Create(xsize, ysize)); MaltaDiffMapLF(image0.Plane(1), image1.Plane(1), wMfMalta, wMfMalta, norm1Mf, &diffs, &block_diff_ac); MaltaDiffMapLF(image0.Plane(0), image1.Plane(0), wMfMaltaX, wMfMaltaX, @@ -1553,13 +1573,13 @@ void ButteraugliDiffmapInPlace(Image3F& image0, Image3F& image1, ImageF uhf0[2]; ImageF uhf1[2]; - SeparateHFAndUHF(params, &hf0[0], &uhf0[0], &blur_temp); - SeparateHFAndUHF(params, &hf1[0], &uhf1[0], &blur_temp); + JXL_RETURN_IF_ERROR(SeparateHFAndUHF(params, &hf0[0], &uhf0[0], &blur_temp)); + JXL_RETURN_IF_ERROR(SeparateHFAndUHF(params, &hf1[0], &uhf1[0], &blur_temp)); // continue accumulating ac diff image from HF and UHF images const float hf_asymmetry = params.hf_asymmetry; { - ImageF diffs(xsize, ysize); + JXL_ASSIGN_OR_RETURN(ImageF diffs, ImageF::Create(xsize, ysize)); MaltaDiffMap(uhf0[1], uhf1[1], wUhfMalta * hf_asymmetry, wUhfMalta / hf_asymmetry, norm1Uhf, &diffs, &block_diff_ac); MaltaDiffMap(uhf0[0], uhf1[0], wUhfMaltaX * hf_asymmetry, @@ -1577,19 +1597,20 @@ void ButteraugliDiffmapInPlace(Image3F& image0, Image3F& image1, } // compute mask image from HF and UHF X and Y images - ImageF mask(xsize, ysize); + JXL_ASSIGN_OR_RETURN(ImageF mask, ImageF::Create(xsize, ysize)); { - ImageF mask0(xsize, ysize); - ImageF mask1(xsize, ysize); + JXL_ASSIGN_OR_RETURN(ImageF mask0, ImageF::Create(xsize, ysize)); + JXL_ASSIGN_OR_RETURN(ImageF mask1, ImageF::Create(xsize, ysize)); CombineChannelsForMasking(&hf0[0], &uhf0[0], &mask0); CombineChannelsForMasking(&hf1[0], &uhf1[0], &mask1); DeallocateHFAndUHF(&hf1[0], &uhf1[0]); DeallocateHFAndUHF(&hf0[0], &uhf0[0]); - Mask(mask0, mask1, params, &blur_temp, &mask, &block_diff_ac); + JXL_RETURN_IF_ERROR( + Mask(mask0, mask1, params, &blur_temp, &mask, &block_diff_ac)); } // compute final diffmap from mask image and ac and dc diff images - diffmap = ImageF(xsize, ysize); + JXL_ASSIGN_OR_RETURN(diffmap, ImageF::Create(xsize, ysize)); for (size_t y = 0; y < ysize; ++y) { const float* row_dc = block_diff_dc.Row(y); const float* row_ac = block_diff_ac.Row(y); @@ -1599,6 +1620,7 @@ void ButteraugliDiffmapInPlace(Image3F& image0, Image3F& image1, row_out[x] = sqrt(row_dc[x] * MaskDcY(val) + row_ac[x] * MaskY(val)); } } + return true; } // NOLINTNEXTLINE(google-readability-namespace-comments) @@ -1669,10 +1691,10 @@ static inline void CheckImage(const ImageF& image, const char* name) { // Calculate a 2x2 subsampled image for purposes of recursive butteraugli at // multiresolution. -static Image3F SubSample2x(const Image3F& in) { +static StatusOr<Image3F> SubSample2x(const Image3F& in) { size_t xs = (in.xsize() + 1) / 2; size_t ys = (in.ysize() + 1) / 2; - Image3F retval(xs, ys); + JXL_ASSIGN_OR_RETURN(Image3F retval, Image3F::Create(xs, ys)); for (size_t c = 0; c < 3; ++c) { for (size_t y = 0; y < ys; ++y) { for (size_t x = 0; x < xs; ++x) { @@ -1724,69 +1746,86 @@ Image3F* ButteraugliComparator::Temp() const { void ButteraugliComparator::ReleaseTemp() const { temp_in_use_.clear(); } -ButteraugliComparator::ButteraugliComparator(const Image3F& rgb0, +ButteraugliComparator::ButteraugliComparator(size_t xsize, size_t ysize, const ButteraugliParams& params) - : xsize_(rgb0.xsize()), - ysize_(rgb0.ysize()), - params_(params), - temp_(xsize_, ysize_) { - if (xsize_ < 8 || ysize_ < 8) { - return; + : xsize_(xsize), ysize_(ysize), params_(params) {} + +StatusOr<std::unique_ptr<ButteraugliComparator>> ButteraugliComparator::Make( + const Image3F& rgb0, const ButteraugliParams& params) { + size_t xsize = rgb0.xsize(); + size_t ysize = rgb0.ysize(); + std::unique_ptr<ButteraugliComparator> result = + std::unique_ptr<ButteraugliComparator>( + new ButteraugliComparator(xsize, ysize, params)); + JXL_ASSIGN_OR_RETURN(result->temp_, Image3F::Create(xsize, ysize)); + + if (xsize < 8 || ysize < 8) { + return result; } - Image3F xyb0(xsize_, ysize_); - HWY_DYNAMIC_DISPATCH(OpsinDynamicsImage) - (rgb0, params, Temp(), &blur_temp_, &xyb0); - ReleaseTemp(); - HWY_DYNAMIC_DISPATCH(SeparateFrequencies) - (xsize_, ysize_, params_, &blur_temp_, xyb0, pi0_); + JXL_ASSIGN_OR_RETURN(Image3F xyb0, Image3F::Create(xsize, ysize)); + JXL_RETURN_IF_ERROR(HWY_DYNAMIC_DISPATCH(OpsinDynamicsImage)( + rgb0, params, result->Temp(), &result->blur_temp_, &xyb0)); + result->ReleaseTemp(); + JXL_RETURN_IF_ERROR(HWY_DYNAMIC_DISPATCH(SeparateFrequencies)( + xsize, ysize, params, &result->blur_temp_, xyb0, result->pi0_)); // Awful recursive construction of samples of different resolution. // This is an after-thought and possibly somewhat parallel in // functionality with the PsychoImage multi-resolution approach. - sub_.reset(new ButteraugliComparator(SubSample2x(rgb0), params)); + JXL_ASSIGN_OR_RETURN(Image3F subsampledRgb0, SubSample2x(rgb0)); + StatusOr<std::unique_ptr<ButteraugliComparator>> sub = + ButteraugliComparator::Make(subsampledRgb0, params); + if (!sub.ok()) return sub.status(); + result->sub_ = std::move(sub).value(); + + return result; } -void ButteraugliComparator::Mask(ImageF* BUTTERAUGLI_RESTRICT mask) const { - HWY_DYNAMIC_DISPATCH(MaskPsychoImage) - (pi0_, pi0_, xsize_, ysize_, params_, &blur_temp_, mask, nullptr); +Status ButteraugliComparator::Mask(ImageF* BUTTERAUGLI_RESTRICT mask) const { + return HWY_DYNAMIC_DISPATCH(MaskPsychoImage)( + pi0_, pi0_, xsize_, ysize_, params_, &blur_temp_, mask, nullptr); } -void ButteraugliComparator::Diffmap(const Image3F& rgb1, ImageF& result) const { +Status ButteraugliComparator::Diffmap(const Image3F& rgb1, + ImageF& result) const { if (xsize_ < 8 || ysize_ < 8) { ZeroFillImage(&result); - return; + return true; } - Image3F xyb1(xsize_, ysize_); - HWY_DYNAMIC_DISPATCH(OpsinDynamicsImage) - (rgb1, params_, Temp(), &blur_temp_, &xyb1); + JXL_ASSIGN_OR_RETURN(Image3F xyb1, Image3F::Create(xsize_, ysize_)); + JXL_RETURN_IF_ERROR(HWY_DYNAMIC_DISPATCH(OpsinDynamicsImage)( + rgb1, params_, Temp(), &blur_temp_, &xyb1)); ReleaseTemp(); - DiffmapOpsinDynamicsImage(xyb1, result); + JXL_RETURN_IF_ERROR(DiffmapOpsinDynamicsImage(xyb1, result)); if (sub_) { if (sub_->xsize_ < 8 || sub_->ysize_ < 8) { - return; + return true; } - Image3F sub_xyb(sub_->xsize_, sub_->ysize_); - HWY_DYNAMIC_DISPATCH(OpsinDynamicsImage) - (SubSample2x(rgb1), params_, sub_->Temp(), &sub_->blur_temp_, &sub_xyb); + JXL_ASSIGN_OR_RETURN(Image3F sub_xyb, + Image3F::Create(sub_->xsize_, sub_->ysize_)); + JXL_ASSIGN_OR_RETURN(Image3F subsampledRgb1, SubSample2x(rgb1)); + JXL_RETURN_IF_ERROR(HWY_DYNAMIC_DISPATCH(OpsinDynamicsImage)( + subsampledRgb1, params_, sub_->Temp(), &sub_->blur_temp_, &sub_xyb)); sub_->ReleaseTemp(); ImageF subresult; - sub_->DiffmapOpsinDynamicsImage(sub_xyb, subresult); + JXL_RETURN_IF_ERROR(sub_->DiffmapOpsinDynamicsImage(sub_xyb, subresult)); AddSupersampled2x(subresult, 0.5, result); } + return true; } -void ButteraugliComparator::DiffmapOpsinDynamicsImage(const Image3F& xyb1, - ImageF& result) const { +Status ButteraugliComparator::DiffmapOpsinDynamicsImage(const Image3F& xyb1, + ImageF& result) const { if (xsize_ < 8 || ysize_ < 8) { ZeroFillImage(&result); - return; + return true; } PsychoImage pi1; - HWY_DYNAMIC_DISPATCH(SeparateFrequencies) - (xsize_, ysize_, params_, &blur_temp_, xyb1, pi1); - result = ImageF(xsize_, ysize_); - DiffmapPsychoImage(pi1, result); + JXL_RETURN_IF_ERROR(HWY_DYNAMIC_DISPATCH(SeparateFrequencies)( + xsize_, ysize_, params_, &blur_temp_, xyb1, pi1)); + JXL_ASSIGN_OR_RETURN(result, ImageF::Create(xsize_, ysize_)); + return DiffmapPsychoImage(pi1, result); } namespace { @@ -1809,18 +1848,18 @@ void MaltaDiffMapLF(const ImageF& lum0, const ImageF& lum1, const double w_0gt1, } // namespace -void ButteraugliComparator::DiffmapPsychoImage(const PsychoImage& pi1, - ImageF& diffmap) const { +Status ButteraugliComparator::DiffmapPsychoImage(const PsychoImage& pi1, + ImageF& diffmap) const { if (xsize_ < 8 || ysize_ < 8) { ZeroFillImage(&diffmap); - return; + return true; } const float hf_asymmetry_ = params_.hf_asymmetry; const float xmul_ = params_.xmul; - ImageF diffs(xsize_, ysize_); - Image3F block_diff_ac(xsize_, ysize_); + JXL_ASSIGN_OR_RETURN(ImageF diffs, ImageF::Create(xsize_, ysize_)); + JXL_ASSIGN_OR_RETURN(Image3F block_diff_ac, Image3F::Create(xsize_, ysize_)); ZeroFillImage(&block_diff_ac); MaltaDiffMap(pi0_.uhf[1], pi1.uhf[1], wUhfMalta * hf_asymmetry_, wUhfMalta / hf_asymmetry_, norm1Uhf, &diffs, &block_diff_ac, 1); @@ -1838,7 +1877,7 @@ void ButteraugliComparator::DiffmapPsychoImage(const PsychoImage& pi1, MaltaDiffMapLF(pi0_.mf.Plane(0), pi1.mf.Plane(0), wMfMaltaX, wMfMaltaX, norm1MfX, &diffs, &block_diff_ac, 0); - Image3F block_diff_dc(xsize_, ysize_); + JXL_ASSIGN_OR_RETURN(Image3F block_diff_dc, Image3F::Create(xsize_, ysize_)); for (size_t c = 0; c < 3; ++c) { if (c < 2) { // No blue channel error accumulated at HF. HWY_DYNAMIC_DISPATCH(L2DiffAsymmetric) @@ -1852,12 +1891,13 @@ void ButteraugliComparator::DiffmapPsychoImage(const PsychoImage& pi1, } ImageF mask; - HWY_DYNAMIC_DISPATCH(MaskPsychoImage) - (pi0_, pi1, xsize_, ysize_, params_, &blur_temp_, &mask, - &block_diff_ac.Plane(1)); + JXL_RETURN_IF_ERROR(HWY_DYNAMIC_DISPATCH(MaskPsychoImage)( + pi0_, pi1, xsize_, ysize_, params_, &blur_temp_, &mask, + &block_diff_ac.Plane(1))); HWY_DYNAMIC_DISPATCH(CombineChannelsToDiffmap) (mask, block_diff_dc, block_diff_ac, xmul_, &diffmap); + return true; } double ButteraugliScoreFromDiffmap(const ImageF& diffmap, @@ -1872,8 +1912,8 @@ double ButteraugliScoreFromDiffmap(const ImageF& diffmap, return retval; } -bool ButteraugliDiffmap(const Image3F& rgb0, const Image3F& rgb1, - double hf_asymmetry, double xmul, ImageF& diffmap) { +Status ButteraugliDiffmap(const Image3F& rgb0, const Image3F& rgb1, + double hf_asymmetry, double xmul, ImageF& diffmap) { ButteraugliParams params; params.hf_asymmetry = hf_asymmetry; params.xmul = xmul; @@ -1893,8 +1933,8 @@ bool ButteraugliDiffmapSmall(const Image3F& rgb0, const Image3F& rgb1, size_t yborder = ysize < kMax ? (kMax - ysize) / 2 : 0; size_t xscaled = std::max<size_t>(kMax, xsize); size_t yscaled = std::max<size_t>(kMax, ysize); - Image3F scaled0(xscaled, yscaled); - Image3F scaled1(xscaled, yscaled); + JXL_ASSIGN_OR_RETURN(Image3F scaled0, Image3F::Create(xscaled, yscaled)); + JXL_ASSIGN_OR_RETURN(Image3F scaled1, Image3F::Create(xscaled, yscaled)); for (int i = 0; i < 3; ++i) { for (size_t y = 0; y < yscaled; ++y) { for (size_t x = 0; x < xscaled; ++x) { @@ -1907,7 +1947,7 @@ bool ButteraugliDiffmapSmall(const Image3F& rgb0, const Image3F& rgb1, } ImageF diffmap_scaled; const bool ok = ButteraugliDiffmap(scaled0, scaled1, params, diffmap_scaled); - diffmap = ImageF(xsize, ysize); + JXL_ASSIGN_OR_RETURN(diffmap, ImageF::Create(xsize, ysize)); for (size_t y = 0; y < ysize; ++y) { for (size_t x = 0; x < xsize; ++x) { diffmap.Row(y)[x] = diffmap_scaled.Row(y + yborder)[x + xborder]; @@ -1916,8 +1956,8 @@ bool ButteraugliDiffmapSmall(const Image3F& rgb0, const Image3F& rgb1, return ok; } -bool ButteraugliDiffmap(const Image3F& rgb0, const Image3F& rgb1, - const ButteraugliParams& params, ImageF& diffmap) { +Status ButteraugliDiffmap(const Image3F& rgb0, const Image3F& rgb1, + const ButteraugliParams& params, ImageF& diffmap) { const size_t xsize = rgb0.xsize(); const size_t ysize = rgb0.ysize(); if (xsize < 1 || ysize < 1) { @@ -1930,8 +1970,9 @@ bool ButteraugliDiffmap(const Image3F& rgb0, const Image3F& rgb1, if (xsize < kMax || ysize < kMax) { return ButteraugliDiffmapSmall<kMax>(rgb0, rgb1, params, diffmap); } - ButteraugliComparator butteraugli(rgb0, params); - butteraugli.Diffmap(rgb1, diffmap); + JXL_ASSIGN_OR_RETURN(std::unique_ptr<ButteraugliComparator> butteraugli, + ButteraugliComparator::Make(rgb0, params)); + JXL_RETURN_IF_ERROR(butteraugli->Diffmap(rgb1, diffmap)); return true; } @@ -1954,9 +1995,9 @@ bool ButteraugliInterface(const Image3F& rgb0, const Image3F& rgb1, return true; } -bool ButteraugliInterfaceInPlace(Image3F&& rgb0, Image3F&& rgb1, - const ButteraugliParams& params, - ImageF& diffmap, double& diffvalue) { +Status ButteraugliInterfaceInPlace(Image3F&& rgb0, Image3F&& rgb1, + const ButteraugliParams& params, + ImageF& diffmap, double& diffvalue) { const size_t xsize = rgb0.xsize(); const size_t ysize = rgb0.ysize(); if (xsize < 1 || ysize < 1) { @@ -1973,12 +2014,13 @@ bool ButteraugliInterfaceInPlace(Image3F&& rgb0, Image3F&& rgb1, } ImageF subdiffmap; if (xsize >= 15 && ysize >= 15) { - Image3F rgb0_sub = SubSample2x(rgb0); - Image3F rgb1_sub = SubSample2x(rgb1); - HWY_DYNAMIC_DISPATCH(ButteraugliDiffmapInPlace) - (rgb0_sub, rgb1_sub, params, subdiffmap); + JXL_ASSIGN_OR_RETURN(Image3F rgb0_sub, SubSample2x(rgb0)); + JXL_ASSIGN_OR_RETURN(Image3F rgb1_sub, SubSample2x(rgb1)); + JXL_RETURN_IF_ERROR(HWY_DYNAMIC_DISPATCH(ButteraugliDiffmapInPlace)( + rgb0_sub, rgb1_sub, params, subdiffmap)); } - HWY_DYNAMIC_DISPATCH(ButteraugliDiffmapInPlace)(rgb0, rgb1, params, diffmap); + JXL_RETURN_IF_ERROR(HWY_DYNAMIC_DISPATCH(ButteraugliDiffmapInPlace)( + rgb0, rgb1, params, diffmap)); if (xsize >= 15 && ysize >= 15) { AddSupersampled2x(subdiffmap, 0.5, diffmap); } @@ -2066,9 +2108,11 @@ void ScoreToRgb(double score, double good_threshold, double bad_threshold, } // namespace -Image3F CreateHeatMapImage(const ImageF& distmap, double good_threshold, - double bad_threshold) { - Image3F heatmap(distmap.xsize(), distmap.ysize()); +StatusOr<Image3F> CreateHeatMapImage(const ImageF& distmap, + double good_threshold, + double bad_threshold) { + JXL_ASSIGN_OR_RETURN(Image3F heatmap, + Image3F::Create(distmap.xsize(), distmap.ysize())); for (size_t y = 0; y < distmap.ysize(); ++y) { const float* BUTTERAUGLI_RESTRICT row_distmap = distmap.ConstRow(y); float* BUTTERAUGLI_RESTRICT row_h0 = heatmap.PlaneRow(0, y); diff --git a/third_party/jpeg-xl/lib/jxl/butteraugli/butteraugli.h b/third_party/jpeg-xl/lib/jxl/butteraugli/butteraugli.h index 29130e8768..e0bfd354e1 100644 --- a/third_party/jpeg-xl/lib/jxl/butteraugli/butteraugli.h +++ b/third_party/jpeg-xl/lib/jxl/butteraugli/butteraugli.h @@ -14,12 +14,12 @@ #include <atomic> #include <cmath> +#include <cstddef> #include <memory> -#include <vector> #include "lib/jxl/base/compiler_specific.h" +#include "lib/jxl/base/status.h" #include "lib/jxl/image.h" -#include "lib/jxl/image_ops.h" #define BUTTERAUGLI_ENABLE_CHECKS 0 #define BUTTERAUGLI_RESTRICT JXL_RESTRICT @@ -87,9 +87,9 @@ bool ButteraugliInterface(const Image3F &rgb0, const Image3F &rgb1, // Same as ButteraugliInterface, but reuses rgb0 and rgb1 for other purposes // inside the function after they are not needed any more, and it ignores // params.xmul. -bool ButteraugliInterfaceInPlace(Image3F &&rgb0, Image3F &&rgb1, - const ButteraugliParams ¶ms, - ImageF &diffmap, double &diffvalue); +Status ButteraugliInterfaceInPlace(Image3F &&rgb0, Image3F &&rgb1, + const ButteraugliParams ¶ms, + ImageF &diffmap, double &diffvalue); // Converts the butteraugli score into fuzzy class values that are continuous // at the class boundary. The class boundary location is based on human @@ -147,11 +147,13 @@ struct PsychoImage { // Blur needs a transposed image. // Hold it here and only allocate on demand to reduce memory usage. struct BlurTemp { - ImageF *GetTransposed(const ImageF &in) { + Status GetTransposed(const ImageF &in, ImageF **out) { if (transposed_temp.xsize() == 0) { - transposed_temp = ImageF(in.ysize(), in.xsize()); + JXL_ASSIGN_OR_RETURN(transposed_temp, + ImageF::Create(in.ysize(), in.xsize())); } - return &transposed_temp; + *out = &transposed_temp; + return true; } ImageF transposed_temp; @@ -162,22 +164,26 @@ class ButteraugliComparator { // Butteraugli is calibrated at xmul = 1.0. We add a multiplier here so that // we can test the hypothesis that a higher weighing of the X channel would // improve results at higher Butteraugli values. - ButteraugliComparator(const Image3F &rgb0, const ButteraugliParams ¶ms); virtual ~ButteraugliComparator() = default; + static StatusOr<std::unique_ptr<ButteraugliComparator>> Make( + const Image3F &rgb0, const ButteraugliParams ¶ms); + // Computes the butteraugli map between the original image given in the // constructor and the distorted image give here. - void Diffmap(const Image3F &rgb1, ImageF &result) const; + Status Diffmap(const Image3F &rgb1, ImageF &result) const; // Same as above, but OpsinDynamicsImage() was already applied. - void DiffmapOpsinDynamicsImage(const Image3F &xyb1, ImageF &result) const; + Status DiffmapOpsinDynamicsImage(const Image3F &xyb1, ImageF &result) const; // Same as above, but the frequency decomposition was already applied. - void DiffmapPsychoImage(const PsychoImage &pi1, ImageF &diffmap) const; + Status DiffmapPsychoImage(const PsychoImage &pi1, ImageF &diffmap) const; - void Mask(ImageF *BUTTERAUGLI_RESTRICT mask) const; + Status Mask(ImageF *BUTTERAUGLI_RESTRICT mask) const; private: + ButteraugliComparator(size_t xsize, size_t ysize, + const ButteraugliParams ¶ms); Image3F *Temp() const; void ReleaseTemp() const; @@ -196,18 +202,19 @@ class ButteraugliComparator { }; // Deprecated. -bool ButteraugliDiffmap(const Image3F &rgb0, const Image3F &rgb1, - double hf_asymmetry, double xmul, ImageF &diffmap); +Status ButteraugliDiffmap(const Image3F &rgb0, const Image3F &rgb1, + double hf_asymmetry, double xmul, ImageF &diffmap); -bool ButteraugliDiffmap(const Image3F &rgb0, const Image3F &rgb1, - const ButteraugliParams ¶ms, ImageF &diffmap); +Status ButteraugliDiffmap(const Image3F &rgb0, const Image3F &rgb1, + const ButteraugliParams ¶ms, ImageF &diffmap); double ButteraugliScoreFromDiffmap(const ImageF &diffmap, const ButteraugliParams *params = nullptr); // Generate rgb-representation of the distance between two images. -Image3F CreateHeatMapImage(const ImageF &distmap, double good_threshold, - double bad_threshold); +StatusOr<Image3F> CreateHeatMapImage(const ImageF &distmap, + double good_threshold, + double bad_threshold); } // namespace jxl diff --git a/third_party/jpeg-xl/lib/jxl/butteraugli/butteraugli_test.cc b/third_party/jpeg-xl/lib/jxl/butteraugli/butteraugli_test.cc index c2ccf56175..39df4bd473 100644 --- a/third_party/jpeg-xl/lib/jxl/butteraugli/butteraugli_test.cc +++ b/third_party/jpeg-xl/lib/jxl/butteraugli/butteraugli_test.cc @@ -30,7 +30,7 @@ using extras::PackedPixelFile; using test::TestImage; Image3F SinglePixelImage(float red, float green, float blue) { - Image3F img(1, 1); + JXL_ASSIGN_OR_DIE(Image3F img, Image3F::Create(1, 1)); img.PlaneRow(0, 0)[0] = red; img.PlaneRow(1, 0)[0] = green; img.PlaneRow(2, 0)[0] = blue; @@ -42,7 +42,7 @@ Image3F GetColorImage(const PackedPixelFile& ppf) { const PackedImage& image = ppf.frames[0].color; const JxlPixelFormat& format = image.format; const uint8_t* pixels = reinterpret_cast<const uint8_t*>(image.pixels()); - Image3F color(image.xsize, image.ysize); + JXL_ASSIGN_OR_DIE(Image3F color, Image3F::Create(image.xsize, image.ysize)); for (size_t c = 0; c < format.num_channels; ++c) { JXL_CHECK(ConvertFromExternal(pixels, image.pixels_size, image.xsize, image.ysize, ppf.info.bits_per_sample, format, @@ -93,7 +93,7 @@ TEST(ButteraugliInPlaceTest, LargeImage) { TestImage img; img.SetDimensions(xsize, ysize).AddFrame().RandomFill(777); Image3F rgb0 = GetColorImage(img.ppf()); - Image3F rgb1(xsize, ysize); + JXL_ASSIGN_OR_DIE(Image3F rgb1, Image3F::Create(xsize, ysize)); CopyImageTo(rgb0, &rgb1); AddUniformNoise(&rgb1, 0.02f, 7777); AddEdge(&rgb1, 0.1f, xsize / 2, xsize / 2); |