Files
vpx/av1/encoder/pickrst.c
Yaowu Xu f883b42cab Port renaming changes from AOMedia
Cherry-Picked the following commits:
0defd8f Changed "WebM" to "AOMedia" & "webm" to "aomedia"
54e6676 Replace "VPx" by "AVx"
5082a36 Change "Vpx" to "Avx"
7df44f1 Replace "Vp9" w/ "Av1"
967f722 Remove kVp9CodecId
828f30c Change "Vp8" to "AOM"
030b5ff AUTHORS regenerated
2524cae Add ref-mv experimental flag
016762b Change copyright notice to AOMedia form
81e5526 Replace vp9 w/ av1
9b94565 Add missing files
fa8ca9f Change "vp9" to "av1"
ec838b7  Convert "vp8" to "aom"
80edfa0 Change "VP9" to "AV1"
d1a11fb Change "vp8" to "aom"
7b58251 Point to WebM test data
dd1a5c8 Replace "VP8" with "AOM"
ff00fc0 Change "VPX" to "AOM"
01dee0b Change "vp10" to "av1" in source code
cebe6f0 Convert "vpx" to "aom"
17b0567 rename vp10*.mk to av1_*.mk
fe5f8a8 rename files vp10_* to av1_*

Change-Id: I6fc3d18eb11fc171e46140c836ad5339cf6c9419
2016-08-31 18:19:03 -07:00

809 lines
28 KiB
C

/*
* Copyright (c) 2010 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 <assert.h>
#include <float.h>
#include <limits.h>
#include <math.h>
#include "./aom_scale_rtcd.h"
#include "aom_dsp/psnr.h"
#include "aom_dsp/aom_dsp_common.h"
#include "aom_mem/aom_mem.h"
#include "aom_ports/mem.h"
#include "av1/common/onyxc_int.h"
#include "av1/common/quant_common.h"
#include "av1/encoder/encoder.h"
#include "av1/encoder/picklpf.h"
#include "av1/encoder/pickrst.h"
#include "av1/encoder/quantize.h"
static int64_t try_restoration_frame(const YV12_BUFFER_CONFIG *sd,
AV1_COMP *const cpi, RestorationInfo *rsi,
int partial_frame) {
AV1_COMMON *const cm = &cpi->common;
int64_t filt_err;
av1_loop_restoration_frame(cm->frame_to_show, cm, rsi, 1, partial_frame);
#if CONFIG_AOM_HIGHBITDEPTH
if (cm->use_highbitdepth) {
filt_err = aom_highbd_get_y_sse(sd, cm->frame_to_show);
} else {
filt_err = aom_get_y_sse(sd, cm->frame_to_show);
}
#else
filt_err = aom_get_y_sse(sd, cm->frame_to_show);
#endif // CONFIG_AOM_HIGHBITDEPTH
// Re-instate the unfiltered frame
aom_yv12_copy_y(&cpi->last_frame_db, cm->frame_to_show);
return filt_err;
}
static int search_bilateral_level(const YV12_BUFFER_CONFIG *sd, AV1_COMP *cpi,
int filter_level, int partial_frame,
int *bilateral_level, double *best_cost_ret) {
AV1_COMMON *const cm = &cpi->common;
int i, j, tile_idx;
int64_t err;
int bits;
double cost, best_cost, cost_norestore, cost_bilateral;
const int bilateral_level_bits = av1_bilateral_level_bits(&cpi->common);
const int bilateral_levels = 1 << bilateral_level_bits;
MACROBLOCK *x = &cpi->td.mb;
RestorationInfo rsi;
const int ntiles =
av1_get_restoration_ntiles(BILATERAL_TILESIZE, cm->width, cm->height);
// Make a copy of the unfiltered / processed recon buffer
aom_yv12_copy_y(cm->frame_to_show, &cpi->last_frame_uf);
av1_loop_filter_frame(cm->frame_to_show, cm, &cpi->td.mb.e_mbd, filter_level,
1, partial_frame);
aom_yv12_copy_y(cm->frame_to_show, &cpi->last_frame_db);
// RD cost associated with no restoration
rsi.restoration_type = RESTORE_NONE;
err = try_restoration_frame(sd, cpi, &rsi, partial_frame);
bits = 0;
cost_norestore =
RDCOST_DBL(x->rdmult, x->rddiv, (bits << (AV1_PROB_COST_SHIFT - 4)), err);
best_cost = cost_norestore;
// RD cost associated with bilateral filtering
rsi.restoration_type = RESTORE_BILATERAL;
rsi.bilateral_level =
(int *)aom_malloc(sizeof(*rsi.bilateral_level) * ntiles);
assert(rsi.bilateral_level != NULL);
for (j = 0; j < ntiles; ++j) bilateral_level[j] = -1;
// Find best filter for each tile
for (tile_idx = 0; tile_idx < ntiles; ++tile_idx) {
for (j = 0; j < ntiles; ++j) rsi.bilateral_level[j] = -1;
best_cost = cost_norestore;
for (i = 0; i < bilateral_levels; ++i) {
rsi.bilateral_level[tile_idx] = i;
err = try_restoration_frame(sd, cpi, &rsi, partial_frame);
bits = bilateral_level_bits + 1;
// Normally the rate is rate in bits * 256 and dist is sum sq err * 64
// when RDCOST is used. However below we just scale both in the correct
// ratios appropriately but not exactly by these values.
cost = RDCOST_DBL(x->rdmult, x->rddiv,
(bits << (AV1_PROB_COST_SHIFT - 4)), err);
if (cost < best_cost) {
bilateral_level[tile_idx] = i;
best_cost = cost;
}
}
}
// Find cost for combined configuration
bits = 0;
for (j = 0; j < ntiles; ++j) {
rsi.bilateral_level[j] = bilateral_level[j];
if (rsi.bilateral_level[j] >= 0) {
bits += (bilateral_level_bits + 1);
} else {
bits += 1;
}
}
err = try_restoration_frame(sd, cpi, &rsi, partial_frame);
cost_bilateral =
RDCOST_DBL(x->rdmult, x->rddiv, (bits << (AV1_PROB_COST_SHIFT - 4)), err);
aom_free(rsi.bilateral_level);
aom_yv12_copy_y(&cpi->last_frame_uf, cm->frame_to_show);
if (cost_bilateral < cost_norestore) {
if (best_cost_ret) *best_cost_ret = cost_bilateral;
return 1;
} else {
if (best_cost_ret) *best_cost_ret = cost_norestore;
return 0;
}
}
static int search_filter_bilateral_level(const YV12_BUFFER_CONFIG *sd,
AV1_COMP *cpi, int partial_frame,
int *filter_best, int *bilateral_level,
double *best_cost_ret) {
const AV1_COMMON *const cm = &cpi->common;
const struct loopfilter *const lf = &cm->lf;
const int min_filter_level = 0;
const int max_filter_level = av1_get_max_filter_level(cpi);
int filt_direction = 0;
int filt_best;
double best_err;
int i, j;
int *tmp_level;
int bilateral_success[MAX_LOOP_FILTER + 1];
const int ntiles =
av1_get_restoration_ntiles(BILATERAL_TILESIZE, cm->width, cm->height);
// Start the search at the previous frame filter level unless it is now out of
// range.
int filt_mid = clamp(lf->filter_level, min_filter_level, max_filter_level);
int filter_step = filt_mid < 16 ? 4 : filt_mid / 4;
double ss_err[MAX_LOOP_FILTER + 1];
// Set each entry to -1
for (i = 0; i <= MAX_LOOP_FILTER; ++i) ss_err[i] = -1.0;
tmp_level = (int *)aom_malloc(sizeof(*tmp_level) * ntiles);
bilateral_success[filt_mid] = search_bilateral_level(
sd, cpi, filt_mid, partial_frame, tmp_level, &best_err);
filt_best = filt_mid;
ss_err[filt_mid] = best_err;
for (j = 0; j < ntiles; ++j) {
bilateral_level[j] = tmp_level[j];
}
while (filter_step > 0) {
const int filt_high = AOMMIN(filt_mid + filter_step, max_filter_level);
const int filt_low = AOMMAX(filt_mid - filter_step, min_filter_level);
// Bias against raising loop filter in favor of lowering it.
double bias = (best_err / (1 << (15 - (filt_mid / 8)))) * filter_step;
if ((cpi->oxcf.pass == 2) && (cpi->twopass.section_intra_rating < 20))
bias = (bias * cpi->twopass.section_intra_rating) / 20;
// yx, bias less for large block size
if (cm->tx_mode != ONLY_4X4) bias /= 2;
if (filt_direction <= 0 && filt_low != filt_mid) {
// Get Low filter error score
if (ss_err[filt_low] < 0) {
bilateral_success[filt_low] = search_bilateral_level(
sd, cpi, filt_low, partial_frame, tmp_level, &ss_err[filt_low]);
}
// If value is close to the best so far then bias towards a lower loop
// filter value.
if (ss_err[filt_low] < (best_err + bias)) {
// Was it actually better than the previous best?
if (ss_err[filt_low] < best_err) {
best_err = ss_err[filt_low];
}
filt_best = filt_low;
for (j = 0; j < ntiles; ++j) {
bilateral_level[j] = tmp_level[j];
}
}
}
// Now look at filt_high
if (filt_direction >= 0 && filt_high != filt_mid) {
if (ss_err[filt_high] < 0) {
bilateral_success[filt_high] = search_bilateral_level(
sd, cpi, filt_high, partial_frame, tmp_level, &ss_err[filt_high]);
}
// If value is significantly better than previous best, bias added against
// raising filter value
if (ss_err[filt_high] < (best_err - bias)) {
best_err = ss_err[filt_high];
filt_best = filt_high;
for (j = 0; j < ntiles; ++j) {
bilateral_level[j] = tmp_level[j];
}
}
}
// Half the step distance if the best filter value was the same as last time
if (filt_best == filt_mid) {
filter_step /= 2;
filt_direction = 0;
} else {
filt_direction = (filt_best < filt_mid) ? -1 : 1;
filt_mid = filt_best;
}
}
aom_free(tmp_level);
// Update best error
best_err = ss_err[filt_best];
if (best_cost_ret) *best_cost_ret = best_err;
if (filter_best) *filter_best = filt_best;
return bilateral_success[filt_best];
}
static double find_average(uint8_t *src, int h_start, int h_end, int v_start,
int v_end, int stride) {
uint64_t sum = 0;
double avg = 0;
int i, j;
for (i = v_start; i < v_end; i++)
for (j = h_start; j < h_end; j++) sum += src[i * stride + j];
avg = (double)sum / ((v_end - v_start) * (h_end - h_start));
return avg;
}
static void compute_stats(uint8_t *dgd, uint8_t *src, int h_start, int h_end,
int v_start, int v_end, int dgd_stride,
int src_stride, double *M, double *H) {
int i, j, k, l;
double Y[RESTORATION_WIN2];
const double avg =
find_average(dgd, h_start, h_end, v_start, v_end, dgd_stride);
memset(M, 0, sizeof(*M) * RESTORATION_WIN2);
memset(H, 0, sizeof(*H) * RESTORATION_WIN2 * RESTORATION_WIN2);
for (i = v_start; i < v_end; i++) {
for (j = h_start; j < h_end; j++) {
const double X = (double)src[i * src_stride + j] - avg;
int idx = 0;
for (k = -RESTORATION_HALFWIN; k <= RESTORATION_HALFWIN; k++) {
for (l = -RESTORATION_HALFWIN; l <= RESTORATION_HALFWIN; l++) {
Y[idx] = (double)dgd[(i + l) * dgd_stride + (j + k)] - avg;
idx++;
}
}
for (k = 0; k < RESTORATION_WIN2; ++k) {
M[k] += Y[k] * X;
H[k * RESTORATION_WIN2 + k] += Y[k] * Y[k];
for (l = k + 1; l < RESTORATION_WIN2; ++l) {
double value = Y[k] * Y[l];
H[k * RESTORATION_WIN2 + l] += value;
H[l * RESTORATION_WIN2 + k] += value;
}
}
}
}
}
#if CONFIG_AOM_HIGHBITDEPTH
static double find_average_highbd(uint16_t *src, int h_start, int h_end,
int v_start, int v_end, int stride) {
uint64_t sum = 0;
double avg = 0;
int i, j;
for (i = v_start; i < v_end; i++)
for (j = h_start; j < h_end; j++) sum += src[i * stride + j];
avg = (double)sum / ((v_end - v_start) * (h_end - h_start));
return avg;
}
static void compute_stats_highbd(uint8_t *dgd8, uint8_t *src8, int h_start,
int h_end, int v_start, int v_end,
int dgd_stride, int src_stride, double *M,
double *H) {
int i, j, k, l;
double Y[RESTORATION_WIN2];
uint16_t *src = CONVERT_TO_SHORTPTR(src8);
uint16_t *dgd = CONVERT_TO_SHORTPTR(dgd8);
const double avg =
find_average_highbd(dgd, h_start, h_end, v_start, v_end, dgd_stride);
memset(M, 0, sizeof(*M) * RESTORATION_WIN2);
memset(H, 0, sizeof(*H) * RESTORATION_WIN2 * RESTORATION_WIN2);
for (i = v_start; i < v_end; i++) {
for (j = h_start; j < h_end; j++) {
const double X = (double)src[i * src_stride + j] - avg;
int idx = 0;
for (k = -RESTORATION_HALFWIN; k <= RESTORATION_HALFWIN; k++) {
for (l = -RESTORATION_HALFWIN; l <= RESTORATION_HALFWIN; l++) {
Y[idx] = (double)dgd[(i + l) * dgd_stride + (j + k)] - avg;
idx++;
}
}
for (k = 0; k < RESTORATION_WIN2; ++k) {
M[k] += Y[k] * X;
H[k * RESTORATION_WIN2 + k] += Y[k] * Y[k];
for (l = k + 1; l < RESTORATION_WIN2; ++l) {
double value = Y[k] * Y[l];
H[k * RESTORATION_WIN2 + l] += value;
H[l * RESTORATION_WIN2 + k] += value;
}
}
}
}
}
#endif // CONFIG_AOM_HIGHBITDEPTH
// Solves Ax = b, where x and b are column vectors
static int linsolve(int n, double *A, int stride, double *b, double *x) {
int i, j, k;
double c;
// Partial pivoting
for (i = n - 1; i > 0; i--) {
if (A[(i - 1) * stride] < A[i * stride]) {
for (j = 0; j < n; j++) {
c = A[i * stride + j];
A[i * stride + j] = A[(i - 1) * stride + j];
A[(i - 1) * stride + j] = c;
}
c = b[i];
b[i] = b[i - 1];
b[i - 1] = c;
}
}
// Forward elimination
for (k = 0; k < n - 1; k++) {
for (i = k; i < n - 1; i++) {
c = A[(i + 1) * stride + k] / A[k * stride + k];
for (j = 0; j < n; j++) A[(i + 1) * stride + j] -= c * A[k * stride + j];
b[i + 1] -= c * b[k];
}
}
// Backward substitution
for (i = n - 1; i >= 0; i--) {
if (fabs(A[i * stride + i]) < 1e-10) return 0;
c = 0;
for (j = i + 1; j <= n - 1; j++) c += A[i * stride + j] * x[j];
x[i] = (b[i] - c) / A[i * stride + i];
}
return 1;
}
static INLINE int wrap_index(int i) {
return (i >= RESTORATION_HALFWIN1 ? RESTORATION_WIN - 1 - i : i);
}
// Fix vector b, update vector a
static void update_a_sep_sym(double **Mc, double **Hc, double *a, double *b) {
int i, j;
double S[RESTORATION_WIN];
double A[RESTORATION_WIN], B[RESTORATION_WIN2];
int w, w2;
memset(A, 0, sizeof(A));
memset(B, 0, sizeof(B));
for (i = 0; i < RESTORATION_WIN; i++) {
int j;
for (j = 0; j < RESTORATION_WIN; ++j) {
const int jj = wrap_index(j);
A[jj] += Mc[i][j] * b[i];
}
}
for (i = 0; i < RESTORATION_WIN; i++) {
for (j = 0; j < RESTORATION_WIN; j++) {
int k, l;
for (k = 0; k < RESTORATION_WIN; ++k)
for (l = 0; l < RESTORATION_WIN; ++l) {
const int kk = wrap_index(k);
const int ll = wrap_index(l);
B[ll * RESTORATION_HALFWIN1 + kk] +=
Hc[j * RESTORATION_WIN + i][k * RESTORATION_WIN2 + l] * b[i] *
b[j];
}
}
}
// Normalization enforcement in the system of equations itself
w = RESTORATION_WIN;
w2 = (w >> 1) + 1;
for (i = 0; i < w2 - 1; ++i)
A[i] -=
A[w2 - 1] * 2 + B[i * w2 + w2 - 1] - 2 * B[(w2 - 1) * w2 + (w2 - 1)];
for (i = 0; i < w2 - 1; ++i)
for (j = 0; j < w2 - 1; ++j)
B[i * w2 + j] -= 2 * (B[i * w2 + (w2 - 1)] + B[(w2 - 1) * w2 + j] -
2 * B[(w2 - 1) * w2 + (w2 - 1)]);
if (linsolve(w2 - 1, B, w2, A, S)) {
S[w2 - 1] = 1.0;
for (i = w2; i < w; ++i) {
S[i] = S[w - 1 - i];
S[w2 - 1] -= 2 * S[i];
}
memcpy(a, S, w * sizeof(*a));
}
}
// Fix vector a, update vector b
static void update_b_sep_sym(double **Mc, double **Hc, double *a, double *b) {
int i, j;
double S[RESTORATION_WIN];
double A[RESTORATION_WIN], B[RESTORATION_WIN2];
int w, w2;
memset(A, 0, sizeof(A));
memset(B, 0, sizeof(B));
for (i = 0; i < RESTORATION_WIN; i++) {
int j;
const int ii = wrap_index(i);
for (j = 0; j < RESTORATION_WIN; j++) A[ii] += Mc[i][j] * a[j];
}
for (i = 0; i < RESTORATION_WIN; i++) {
for (j = 0; j < RESTORATION_WIN; j++) {
const int ii = wrap_index(i);
const int jj = wrap_index(j);
int k, l;
for (k = 0; k < RESTORATION_WIN; ++k)
for (l = 0; l < RESTORATION_WIN; ++l)
B[jj * RESTORATION_HALFWIN1 + ii] +=
Hc[i * RESTORATION_WIN + j][k * RESTORATION_WIN2 + l] * a[k] *
a[l];
}
}
// Normalization enforcement in the system of equations itself
w = RESTORATION_WIN;
w2 = RESTORATION_HALFWIN1;
for (i = 0; i < w2 - 1; ++i)
A[i] -=
A[w2 - 1] * 2 + B[i * w2 + w2 - 1] - 2 * B[(w2 - 1) * w2 + (w2 - 1)];
for (i = 0; i < w2 - 1; ++i)
for (j = 0; j < w2 - 1; ++j)
B[i * w2 + j] -= 2 * (B[i * w2 + (w2 - 1)] + B[(w2 - 1) * w2 + j] -
2 * B[(w2 - 1) * w2 + (w2 - 1)]);
if (linsolve(w2 - 1, B, w2, A, S)) {
S[w2 - 1] = 1.0;
for (i = w2; i < w; ++i) {
S[i] = S[w - 1 - i];
S[w2 - 1] -= 2 * S[i];
}
memcpy(b, S, w * sizeof(*b));
}
}
static int wiener_decompose_sep_sym(double *M, double *H, double *a,
double *b) {
static const double init_filt[RESTORATION_WIN] = {
0.035623, -0.127154, 0.211436, 0.760190, 0.211436, -0.127154, 0.035623,
};
int i, j, iter;
double *Hc[RESTORATION_WIN2];
double *Mc[RESTORATION_WIN];
for (i = 0; i < RESTORATION_WIN; i++) {
Mc[i] = M + i * RESTORATION_WIN;
for (j = 0; j < RESTORATION_WIN; j++) {
Hc[i * RESTORATION_WIN + j] =
H + i * RESTORATION_WIN * RESTORATION_WIN2 + j * RESTORATION_WIN;
}
}
memcpy(a, init_filt, sizeof(*a) * RESTORATION_WIN);
memcpy(b, init_filt, sizeof(*b) * RESTORATION_WIN);
iter = 1;
while (iter < 10) {
update_a_sep_sym(Mc, Hc, a, b);
update_b_sep_sym(Mc, Hc, a, b);
iter++;
}
return 1;
}
// Computes the function x'*A*x - x'*b for the learned filters, and compares
// against identity filters; Final score is defined as the difference between
// the function values
static double compute_score(double *M, double *H, int *vfilt, int *hfilt) {
double ab[RESTORATION_WIN * RESTORATION_WIN];
int i, k, l;
double P = 0, Q = 0;
double iP = 0, iQ = 0;
double Score, iScore;
int w;
double a[RESTORATION_WIN], b[RESTORATION_WIN];
w = RESTORATION_WIN;
a[RESTORATION_HALFWIN] = b[RESTORATION_HALFWIN] = 1.0;
for (i = 0; i < RESTORATION_HALFWIN; ++i) {
a[i] = a[RESTORATION_WIN - i - 1] =
(double)vfilt[i] / RESTORATION_FILT_STEP;
b[i] = b[RESTORATION_WIN - i - 1] =
(double)hfilt[i] / RESTORATION_FILT_STEP;
a[RESTORATION_HALFWIN] -= 2 * a[i];
b[RESTORATION_HALFWIN] -= 2 * b[i];
}
for (k = 0; k < w; ++k) {
for (l = 0; l < w; ++l) {
ab[k * w + l] = a[l] * b[k];
}
}
for (k = 0; k < w * w; ++k) {
P += ab[k] * M[k];
for (l = 0; l < w * w; ++l) Q += ab[k] * H[k * w * w + l] * ab[l];
}
Score = Q - 2 * P;
iP = M[(w * w) >> 1];
iQ = H[((w * w) >> 1) * w * w + ((w * w) >> 1)];
iScore = iQ - 2 * iP;
return Score - iScore;
}
#define CLIP(x, lo, hi) ((x) < (lo) ? (lo) : (x) > (hi) ? (hi) : (x))
#define RINT(x) ((x) < 0 ? (int)((x)-0.5) : (int)((x) + 0.5))
static void quantize_sym_filter(double *f, int *fi) {
int i;
for (i = 0; i < RESTORATION_HALFWIN; ++i) {
fi[i] = RINT(f[i] * RESTORATION_FILT_STEP);
}
// Specialize for 7-tap filter
fi[0] = CLIP(fi[0], WIENER_FILT_TAP0_MINV, WIENER_FILT_TAP0_MAXV);
fi[1] = CLIP(fi[1], WIENER_FILT_TAP1_MINV, WIENER_FILT_TAP1_MAXV);
fi[2] = CLIP(fi[2], WIENER_FILT_TAP2_MINV, WIENER_FILT_TAP2_MAXV);
}
static int search_wiener_filter(const YV12_BUFFER_CONFIG *src, AV1_COMP *cpi,
int filter_level, int partial_frame,
int (*vfilter)[RESTORATION_HALFWIN],
int (*hfilter)[RESTORATION_HALFWIN],
int *process_tile, double *best_cost_ret) {
AV1_COMMON *const cm = &cpi->common;
RestorationInfo rsi;
int64_t err;
int bits;
double cost_wiener, cost_norestore;
MACROBLOCK *x = &cpi->td.mb;
double M[RESTORATION_WIN2];
double H[RESTORATION_WIN2 * RESTORATION_WIN2];
double vfilterd[RESTORATION_WIN], hfilterd[RESTORATION_WIN];
const YV12_BUFFER_CONFIG *dgd = cm->frame_to_show;
const int width = cm->width;
const int height = cm->height;
const int src_stride = src->y_stride;
const int dgd_stride = dgd->y_stride;
double score;
int tile_idx, htile_idx, vtile_idx, tile_width, tile_height, nhtiles, nvtiles;
int h_start, h_end, v_start, v_end;
int i, j;
const int tilesize = WIENER_TILESIZE;
const int ntiles = av1_get_restoration_ntiles(tilesize, width, height);
assert(width == dgd->y_crop_width);
assert(height == dgd->y_crop_height);
assert(width == src->y_crop_width);
assert(height == src->y_crop_height);
av1_get_restoration_tile_size(tilesize, width, height, &tile_width,
&tile_height, &nhtiles, &nvtiles);
// Make a copy of the unfiltered / processed recon buffer
aom_yv12_copy_y(cm->frame_to_show, &cpi->last_frame_uf);
av1_loop_filter_frame(cm->frame_to_show, cm, &cpi->td.mb.e_mbd, filter_level,
1, partial_frame);
aom_yv12_copy_y(cm->frame_to_show, &cpi->last_frame_db);
rsi.restoration_type = RESTORE_NONE;
err = try_restoration_frame(src, cpi, &rsi, partial_frame);
bits = 0;
cost_norestore =
RDCOST_DBL(x->rdmult, x->rddiv, (bits << (AV1_PROB_COST_SHIFT - 4)), err);
rsi.restoration_type = RESTORE_WIENER;
rsi.vfilter =
(int(*)[RESTORATION_HALFWIN])aom_malloc(sizeof(*rsi.vfilter) * ntiles);
assert(rsi.vfilter != NULL);
rsi.hfilter =
(int(*)[RESTORATION_HALFWIN])aom_malloc(sizeof(*rsi.hfilter) * ntiles);
assert(rsi.hfilter != NULL);
rsi.wiener_level = (int *)aom_malloc(sizeof(*rsi.wiener_level) * ntiles);
assert(rsi.wiener_level != NULL);
// Compute best Wiener filters for each tile
for (tile_idx = 0; tile_idx < ntiles; ++tile_idx) {
htile_idx = tile_idx % nhtiles;
vtile_idx = tile_idx / nhtiles;
h_start =
htile_idx * tile_width + ((htile_idx > 0) ? 0 : RESTORATION_HALFWIN);
h_end = (htile_idx < nhtiles - 1) ? ((htile_idx + 1) * tile_width)
: (width - RESTORATION_HALFWIN);
v_start =
vtile_idx * tile_height + ((vtile_idx > 0) ? 0 : RESTORATION_HALFWIN);
v_end = (vtile_idx < nvtiles - 1) ? ((vtile_idx + 1) * tile_height)
: (height - RESTORATION_HALFWIN);
#if CONFIG_AOM_HIGHBITDEPTH
if (cm->use_highbitdepth)
compute_stats_highbd(dgd->y_buffer, src->y_buffer, h_start, h_end,
v_start, v_end, dgd_stride, src_stride, M, H);
else
#endif // CONFIG_AOM_HIGHBITDEPTH
compute_stats(dgd->y_buffer, src->y_buffer, h_start, h_end, v_start,
v_end, dgd_stride, src_stride, M, H);
if (!wiener_decompose_sep_sym(M, H, vfilterd, hfilterd)) {
for (i = 0; i < RESTORATION_HALFWIN; ++i)
rsi.vfilter[tile_idx][i] = rsi.hfilter[tile_idx][i] = 0;
process_tile[tile_idx] = 0;
continue;
}
quantize_sym_filter(vfilterd, rsi.vfilter[tile_idx]);
quantize_sym_filter(hfilterd, rsi.hfilter[tile_idx]);
process_tile[tile_idx] = 1;
// Filter score computes the value of the function x'*A*x - x'*b for the
// learned filter and compares it against identity filer. If there is no
// reduction in the function, the filter is reverted back to identity
score = compute_score(M, H, rsi.vfilter[tile_idx], rsi.hfilter[tile_idx]);
if (score > 0.0) {
for (i = 0; i < RESTORATION_HALFWIN; ++i)
rsi.vfilter[tile_idx][i] = rsi.hfilter[tile_idx][i] = 0;
process_tile[tile_idx] = 0;
continue;
}
for (j = 0; j < ntiles; ++j) rsi.wiener_level[j] = 0;
rsi.wiener_level[tile_idx] = 1;
err = try_restoration_frame(src, cpi, &rsi, partial_frame);
bits = 1 + WIENER_FILT_BITS;
cost_wiener = RDCOST_DBL(x->rdmult, x->rddiv,
(bits << (AV1_PROB_COST_SHIFT - 4)), err);
if (cost_wiener >= cost_norestore) process_tile[tile_idx] = 0;
}
// Cost for Wiener filtering
bits = 0;
for (tile_idx = 0; tile_idx < ntiles; ++tile_idx) {
bits += (process_tile[tile_idx] ? (WIENER_FILT_BITS + 1) : 1);
rsi.wiener_level[tile_idx] = process_tile[tile_idx];
}
err = try_restoration_frame(src, cpi, &rsi, partial_frame);
cost_wiener =
RDCOST_DBL(x->rdmult, x->rddiv, (bits << (AV1_PROB_COST_SHIFT - 4)), err);
for (tile_idx = 0; tile_idx < ntiles; ++tile_idx) {
if (process_tile[tile_idx] == 0) continue;
for (i = 0; i < RESTORATION_HALFWIN; ++i) {
vfilter[tile_idx][i] = rsi.vfilter[tile_idx][i];
hfilter[tile_idx][i] = rsi.hfilter[tile_idx][i];
}
}
aom_free(rsi.vfilter);
aom_free(rsi.hfilter);
aom_free(rsi.wiener_level);
aom_yv12_copy_y(&cpi->last_frame_uf, cm->frame_to_show);
if (cost_wiener < cost_norestore) {
if (best_cost_ret) *best_cost_ret = cost_wiener;
return 1;
} else {
if (best_cost_ret) *best_cost_ret = cost_norestore;
return 0;
}
}
void av1_pick_filter_restoration(const YV12_BUFFER_CONFIG *sd, AV1_COMP *cpi,
LPF_PICK_METHOD method) {
AV1_COMMON *const cm = &cpi->common;
struct loopfilter *const lf = &cm->lf;
int wiener_success = 0;
int bilateral_success = 0;
double cost_bilateral = DBL_MAX;
double cost_wiener = DBL_MAX;
double cost_norestore = DBL_MAX;
int ntiles;
ntiles =
av1_get_restoration_ntiles(BILATERAL_TILESIZE, cm->width, cm->height);
cm->rst_info.bilateral_level =
(int *)aom_realloc(cm->rst_info.bilateral_level,
sizeof(*cm->rst_info.bilateral_level) * ntiles);
assert(cm->rst_info.bilateral_level != NULL);
ntiles = av1_get_restoration_ntiles(WIENER_TILESIZE, cm->width, cm->height);
cm->rst_info.wiener_level = (int *)aom_realloc(
cm->rst_info.wiener_level, sizeof(*cm->rst_info.wiener_level) * ntiles);
assert(cm->rst_info.wiener_level != NULL);
cm->rst_info.vfilter = (int(*)[RESTORATION_HALFWIN])aom_realloc(
cm->rst_info.vfilter, sizeof(*cm->rst_info.vfilter) * ntiles);
assert(cm->rst_info.vfilter != NULL);
cm->rst_info.hfilter = (int(*)[RESTORATION_HALFWIN])aom_realloc(
cm->rst_info.hfilter, sizeof(*cm->rst_info.hfilter) * ntiles);
assert(cm->rst_info.hfilter != NULL);
lf->sharpness_level = cm->frame_type == KEY_FRAME ? 0 : cpi->oxcf.sharpness;
if (method == LPF_PICK_MINIMAL_LPF && lf->filter_level) {
lf->filter_level = 0;
cm->rst_info.restoration_type = RESTORE_NONE;
} else if (method >= LPF_PICK_FROM_Q) {
const int min_filter_level = 0;
const int max_filter_level = av1_get_max_filter_level(cpi);
const int q = av1_ac_quant(cm->base_qindex, 0, cm->bit_depth);
// These values were determined by linear fitting the result of the
// searched level, filt_guess = q * 0.316206 + 3.87252
#if CONFIG_AOM_HIGHBITDEPTH
int filt_guess;
switch (cm->bit_depth) {
case AOM_BITS_8:
filt_guess = ROUND_POWER_OF_TWO(q * 20723 + 1015158, 18);
break;
case AOM_BITS_10:
filt_guess = ROUND_POWER_OF_TWO(q * 20723 + 4060632, 20);
break;
case AOM_BITS_12:
filt_guess = ROUND_POWER_OF_TWO(q * 20723 + 16242526, 22);
break;
default:
assert(0 &&
"bit_depth should be AOM_BITS_8, AOM_BITS_10 "
"or AOM_BITS_12");
return;
}
#else
int filt_guess = ROUND_POWER_OF_TWO(q * 20723 + 1015158, 18);
#endif // CONFIG_AOM_HIGHBITDEPTH
if (cm->frame_type == KEY_FRAME) filt_guess -= 4;
lf->filter_level = clamp(filt_guess, min_filter_level, max_filter_level);
bilateral_success = search_bilateral_level(
sd, cpi, lf->filter_level, method == LPF_PICK_FROM_SUBIMAGE,
cm->rst_info.bilateral_level, &cost_bilateral);
wiener_success = search_wiener_filter(
sd, cpi, lf->filter_level, method == LPF_PICK_FROM_SUBIMAGE,
cm->rst_info.vfilter, cm->rst_info.hfilter, cm->rst_info.wiener_level,
&cost_wiener);
if (cost_bilateral < cost_wiener) {
if (bilateral_success)
cm->rst_info.restoration_type = RESTORE_BILATERAL;
else
cm->rst_info.restoration_type = RESTORE_NONE;
} else {
if (wiener_success)
cm->rst_info.restoration_type = RESTORE_WIENER;
else
cm->rst_info.restoration_type = RESTORE_NONE;
}
} else {
int blf_filter_level = -1;
bilateral_success = search_filter_bilateral_level(
sd, cpi, method == LPF_PICK_FROM_SUBIMAGE, &blf_filter_level,
cm->rst_info.bilateral_level, &cost_bilateral);
lf->filter_level = av1_search_filter_level(
sd, cpi, method == LPF_PICK_FROM_SUBIMAGE, &cost_norestore);
wiener_success = search_wiener_filter(
sd, cpi, lf->filter_level, method == LPF_PICK_FROM_SUBIMAGE,
cm->rst_info.vfilter, cm->rst_info.hfilter, cm->rst_info.wiener_level,
&cost_wiener);
if (cost_bilateral < cost_wiener) {
lf->filter_level = blf_filter_level;
if (bilateral_success)
cm->rst_info.restoration_type = RESTORE_BILATERAL;
else
cm->rst_info.restoration_type = RESTORE_NONE;
} else {
if (wiener_success)
cm->rst_info.restoration_type = RESTORE_WIENER;
else
cm->rst_info.restoration_type = RESTORE_NONE;
}
// printf("[%d] Costs %g %g (%d) %g (%d)\n", cm->rst_info.restoration_type,
// cost_norestore, cost_bilateral, lf->filter_level, cost_wiener,
// wiener_success);
}
if (cm->rst_info.restoration_type != RESTORE_BILATERAL) {
aom_free(cm->rst_info.bilateral_level);
cm->rst_info.bilateral_level = NULL;
}
if (cm->rst_info.restoration_type != RESTORE_WIENER) {
aom_free(cm->rst_info.vfilter);
cm->rst_info.vfilter = NULL;
aom_free(cm->rst_info.hfilter);
cm->rst_info.hfilter = NULL;
aom_free(cm->rst_info.wiener_level);
cm->rst_info.wiener_level = NULL;
}
}