Merge "Refactor predictor finding" into main

This commit is contained in:
Vincent Rabaud 2024-09-18 08:38:57 +00:00 committed by Gerrit Code Review
commit bc49176355

View File

@ -14,17 +14,24 @@
// Urvang Joshi (urvang@google.com)
// Vincent Rabaud (vrabaud@google.com)
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "src/dsp/lossless.h"
#include "src/dsp/lossless_common.h"
#include "src/enc/vp8i_enc.h"
#include "src/enc/vp8li_enc.h"
#include "src/utils/utils.h"
#include "src/webp/encode.h"
#include "src/webp/format_constants.h"
#include "src/webp/types.h"
#define HISTO_SIZE (4 * 256)
static const int64_t kSpatialPredictorBias = 15ll << LOG_2_PRECISION_BITS;
static const int kPredLowEffort = 11;
static const uint32_t kMaskAlpha = 0xff000000;
static const int kNumPredModes = 14;
// Mostly used to reduce code size + readability
static WEBP_INLINE int GetMin(int a, int b) { return (a > b) ? b : a; }
@ -305,17 +312,61 @@ static WEBP_INLINE void GetResidual(
}
}
// Returns best predictor and updates the accumulated histogram.
// Accessors to residual histograms.
static WEBP_INLINE uint32_t* GetHistoArgb(uint32_t* const all_histos,
int mode) {
return &all_histos[mode * HISTO_SIZE];
}
static WEBP_INLINE const uint32_t* GetHistoArgbConst(
const uint32_t* const all_histos, int mode) {
return &all_histos[mode * HISTO_SIZE];
}
// Find and store the best predictor.
static void GetBestPredictorForTile(const uint32_t* const all_argb, int tile_x,
int tile_y, int tiles_per_row,
uint32_t* accumulated_argb,
uint32_t* const modes) {
// Prediction modes of the left and above neighbor tiles.
const int left_mode =
(tile_x > 0) ? (modes[tile_y * tiles_per_row + tile_x - 1] >> 8) & 0xff
: 0xff;
const int above_mode =
(tile_y > 0) ? (modes[(tile_y - 1) * tiles_per_row + tile_x] >> 8) & 0xff
: 0xff;
int mode;
int64_t best_diff = WEBP_INT64_MAX;
uint32_t best_mode = 0;
const uint32_t* best_histo = GetHistoArgbConst(all_argb, best_mode);
for (mode = 0; mode < kNumPredModes; ++mode) {
const uint32_t* const histo_argb = GetHistoArgbConst(all_argb, mode);
const int64_t cur_diff = PredictionCostSpatialHistogram(
accumulated_argb, histo_argb, mode, left_mode, above_mode);
if (cur_diff < best_diff) {
best_histo = histo_argb;
best_diff = cur_diff;
best_mode = mode;
}
}
// Update the accumulated histogram.
VP8LAddVectorEq(best_histo, accumulated_argb, HISTO_SIZE);
modes[tile_y * tiles_per_row + tile_x] = ARGB_BLACK | (best_mode << 8);
}
// Computes the residuals for the different predictors.
// If max_quantization > 1, assumes that near lossless processing will be
// applied, quantizing residuals to multiples of quantization levels up to
// max_quantization (the actual quantization level depends on smoothness near
// the given pixel).
static int GetBestPredictorForTile(
int width, int height, int tile_x, int tile_y, int bits,
uint32_t accumulated[HISTO_SIZE], uint32_t* const argb_scratch,
const uint32_t* const argb, int max_quantization, int exact,
int used_subtract_green, const uint32_t* const modes) {
const int kNumPredModes = 14;
static void ComputeResidualsForTile(int width, int height, int tile_x,
int tile_y, int bits,
uint32_t* const all_argb,
uint32_t* const argb_scratch,
const uint32_t* const argb,
int max_quantization, int exact,
int used_subtract_green) {
const int start_x = tile_x << bits;
const int start_y = tile_y << bits;
const int tile_size = 1 << bits;
@ -329,34 +380,19 @@ static int GetBestPredictorForTile(
#if (WEBP_NEAR_LOSSLESS == 1)
const int context_width = max_x + have_left + (max_x < width - start_x);
#endif
const int tiles_per_row = VP8LSubSampleSize(width, bits);
// Prediction modes of the left and above neighbor tiles.
const int left_mode = (tile_x > 0) ?
(modes[tile_y * tiles_per_row + tile_x - 1] >> 8) & 0xff : 0xff;
const int above_mode = (tile_y > 0) ?
(modes[(tile_y - 1) * tiles_per_row + tile_x] >> 8) & 0xff : 0xff;
// The width of upper_row and current_row is one pixel larger than image width
// to allow the top right pixel to point to the leftmost pixel of the next row
// when at the right edge.
uint32_t* upper_row = argb_scratch;
uint32_t* current_row = upper_row + width + 1;
uint8_t* const max_diffs = (uint8_t*)(current_row + width + 1);
int64_t best_diff = WEBP_INT64_MAX;
int best_mode = 0;
int mode;
uint32_t histo_stack_1[HISTO_SIZE];
uint32_t histo_stack_2[HISTO_SIZE];
// Need pointers to be able to swap arrays.
uint32_t* histo_argb = histo_stack_1;
uint32_t* best_histo = histo_stack_2;
uint32_t residuals[1 << MAX_TRANSFORM_BITS];
assert(bits <= MAX_TRANSFORM_BITS);
assert(max_x <= (1 << MAX_TRANSFORM_BITS));
for (mode = 0; mode < kNumPredModes; ++mode) {
int64_t cur_diff;
int relative_y;
memset(histo_argb, 0, sizeof(histo_stack_1));
uint32_t* const histo_argb = GetHistoArgb(all_argb, mode);
if (start_y > 0) {
// Read the row above the tile which will become the first upper_row.
// Include a pixel to the left if it exists; include a pixel to the right
@ -393,20 +429,7 @@ static int GetBestPredictorForTile(
UpdateHisto(histo_argb, residuals[relative_x]);
}
}
cur_diff = PredictionCostSpatialHistogram(accumulated, histo_argb, mode,
left_mode, above_mode);
if (cur_diff < best_diff) {
uint32_t* tmp = histo_argb;
histo_argb = best_histo;
best_histo = tmp;
best_diff = cur_diff;
best_mode = mode;
}
}
VP8LAddVectorEq(best_histo, accumulated, HISTO_SIZE);
return best_mode;
}
// Converts pixels of the image to residuals with respect to predictions.
@ -536,6 +559,59 @@ static void OptimizeSampling(uint32_t* const image, int full_width,
*best_bits_out = best_bits;
}
// Computes the best predictor image.
// Finds the best predictors per tile. Once done, finds the best predictor image
// sampling.
// best_bits is set to 0 in case of error.
static void GetBestPredictorsAndSampling(
int width, int height, const int bits, uint32_t* const argb_scratch,
const uint32_t* const argb, int max_quantization, int exact,
int used_subtract_green, const WebPPicture* const pic, int percent_range,
int* const percent, uint32_t* const all_modes, int* best_bits) {
const int tiles_per_row = VP8LSubSampleSize(width, bits);
const int tiles_per_col = VP8LSubSampleSize(height, bits);
// Compute the needed memory size for residual histograms and accumulated
// residual histograms.
const int num_argb = kNumPredModes * HISTO_SIZE;
const int num_accumulated_argb = HISTO_SIZE;
uint32_t* const raw_data = (uint32_t*)WebPSafeCalloc(
num_argb + num_accumulated_argb, sizeof(*raw_data));
uint32_t* const all_argb = raw_data;
uint32_t* const all_accumulated_argb = all_argb + num_argb;
const int percent_start = *percent;
int tile_x = 0, tile_y = 0;
*best_bits = 0;
if (raw_data == NULL) return;
while (tile_y < tiles_per_col) {
ComputeResidualsForTile(width, height, tile_x, tile_y, bits, all_argb,
argb_scratch, argb, max_quantization, exact,
used_subtract_green);
GetBestPredictorForTile(all_argb, tile_x, tile_y, tiles_per_row,
all_accumulated_argb, all_modes);
// Reset the residuals.
memset(all_argb, 0, HISTO_SIZE * kNumPredModes * sizeof(*all_argb));
if (tile_x == (tiles_per_row - 1)) {
tile_x = 0;
++tile_y;
} else {
++tile_x;
}
if (tile_x == 0 &&
!WebPReportProgress(
pic, percent_start + percent_range * tile_y / tiles_per_col,
percent)) {
WebPSafeFree(raw_data);
return;
}
}
WebPSafeFree(raw_data);
OptimizeSampling(all_modes, width, height, bits, best_bits);
}
// Finds the best predictor for each tile, and converts the image to residuals
// with respect to predictions. If near_lossless_quality < 100, applies
// near lossless processing, shaving off more bits of residuals for lower
@ -557,24 +633,10 @@ int VP8LResidualImage(int width, int height, int bits, int low_effort,
}
*best_bits = bits;
} else {
int tile_y;
uint32_t histo[HISTO_SIZE] = { 0 };
for (tile_y = 0; tile_y < tiles_per_col; ++tile_y) {
int tile_x;
for (tile_x = 0; tile_x < tiles_per_row; ++tile_x) {
const int pred = GetBestPredictorForTile(
width, height, tile_x, tile_y, bits, histo, argb_scratch, argb,
max_quantization, exact, used_subtract_green, image);
image[tile_y * tiles_per_row + tile_x] = ARGB_BLACK | (pred << 8);
}
if (!WebPReportProgress(
pic, percent_start + percent_range * tile_y / tiles_per_col,
percent)) {
return 0;
}
}
OptimizeSampling(image, width, height, bits, best_bits);
GetBestPredictorsAndSampling(width, height, bits, argb_scratch, argb,
max_quantization, exact, used_subtract_green,
pic, percent_range, percent, image, best_bits);
if (*best_bits == 0) return 0;
}
CopyImageWithPrediction(width, height, *best_bits, image, argb_scratch, argb,