Blame src/enc/filter_enc.c

Packit 9c6abc
// Copyright 2011 Google Inc. All Rights Reserved.
Packit 9c6abc
//
Packit 9c6abc
// Use of this source code is governed by a BSD-style license
Packit 9c6abc
// that can be found in the COPYING file in the root of the source
Packit 9c6abc
// tree. An additional intellectual property rights grant can be found
Packit 9c6abc
// in the file PATENTS. All contributing project authors may
Packit 9c6abc
// be found in the AUTHORS file in the root of the source tree.
Packit 9c6abc
// -----------------------------------------------------------------------------
Packit 9c6abc
//
Packit 9c6abc
// Selecting filter level
Packit 9c6abc
//
Packit 9c6abc
// Author: somnath@google.com (Somnath Banerjee)
Packit 9c6abc
Packit 9c6abc
#include <assert.h>
Packit 9c6abc
#include "src/enc/vp8i_enc.h"
Packit 9c6abc
#include "src/dsp/dsp.h"
Packit 9c6abc
Packit 9c6abc
// This table gives, for a given sharpness, the filtering strength to be
Packit 9c6abc
// used (at least) in order to filter a given edge step delta.
Packit 9c6abc
// This is constructed by brute force inspection: for all delta, we iterate
Packit 9c6abc
// over all possible filtering strength / thresh until needs_filter() returns
Packit 9c6abc
// true.
Packit 9c6abc
#define MAX_DELTA_SIZE 64
Packit 9c6abc
static const uint8_t kLevelsFromDelta[8][MAX_DELTA_SIZE] = {
Packit 9c6abc
  { 0,   1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15,
Packit 9c6abc
    16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
Packit 9c6abc
    32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
Packit 9c6abc
    48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63 },
Packit 9c6abc
  { 0,  1,  2,  3,  5,  6,  7,  8,  9, 11, 12, 13, 14, 15, 17, 18,
Packit 9c6abc
    20, 21, 23, 24, 26, 27, 29, 30, 32, 33, 35, 36, 38, 39, 41, 42,
Packit 9c6abc
    44, 45, 47, 48, 50, 51, 53, 54, 56, 57, 59, 60, 62, 63, 63, 63,
Packit 9c6abc
    63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63 },
Packit 9c6abc
  {  0,  1,  2,  3,  5,  6,  7,  8,  9, 11, 12, 13, 14, 16, 17, 19,
Packit 9c6abc
    20, 22, 23, 25, 26, 28, 29, 31, 32, 34, 35, 37, 38, 40, 41, 43,
Packit 9c6abc
    44, 46, 47, 49, 50, 52, 53, 55, 56, 58, 59, 61, 62, 63, 63, 63,
Packit 9c6abc
    63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63 },
Packit 9c6abc
  {  0,  1,  2,  3,  5,  6,  7,  8,  9, 11, 12, 13, 15, 16, 18, 19,
Packit 9c6abc
    21, 22, 24, 25, 27, 28, 30, 31, 33, 34, 36, 37, 39, 40, 42, 43,
Packit 9c6abc
    45, 46, 48, 49, 51, 52, 54, 55, 57, 58, 60, 61, 63, 63, 63, 63,
Packit 9c6abc
    63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63 },
Packit 9c6abc
  {  0,  1,  2,  3,  5,  6,  7,  8,  9, 11, 12, 14, 15, 17, 18, 20,
Packit 9c6abc
    21, 23, 24, 26, 27, 29, 30, 32, 33, 35, 36, 38, 39, 41, 42, 44,
Packit 9c6abc
    45, 47, 48, 50, 51, 53, 54, 56, 57, 59, 60, 62, 63, 63, 63, 63,
Packit 9c6abc
    63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63 },
Packit 9c6abc
  {  0,  1,  2,  4,  5,  7,  8,  9, 11, 12, 13, 15, 16, 17, 19, 20,
Packit 9c6abc
    22, 23, 25, 26, 28, 29, 31, 32, 34, 35, 37, 38, 40, 41, 43, 44,
Packit 9c6abc
    46, 47, 49, 50, 52, 53, 55, 56, 58, 59, 61, 62, 63, 63, 63, 63,
Packit 9c6abc
    63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63 },
Packit 9c6abc
  {  0,  1,  2,  4,  5,  7,  8,  9, 11, 12, 13, 15, 16, 18, 19, 21,
Packit 9c6abc
    22, 24, 25, 27, 28, 30, 31, 33, 34, 36, 37, 39, 40, 42, 43, 45,
Packit 9c6abc
    46, 48, 49, 51, 52, 54, 55, 57, 58, 60, 61, 63, 63, 63, 63, 63,
Packit 9c6abc
    63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63 },
Packit 9c6abc
  {  0,  1,  2,  4,  5,  7,  8,  9, 11, 12, 14, 15, 17, 18, 20, 21,
Packit 9c6abc
    23, 24, 26, 27, 29, 30, 32, 33, 35, 36, 38, 39, 41, 42, 44, 45,
Packit 9c6abc
    47, 48, 50, 51, 53, 54, 56, 57, 59, 60, 62, 63, 63, 63, 63, 63,
Packit 9c6abc
    63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63 }
Packit 9c6abc
};
Packit 9c6abc
Packit 9c6abc
int VP8FilterStrengthFromDelta(int sharpness, int delta) {
Packit 9c6abc
  const int pos = (delta < MAX_DELTA_SIZE) ? delta : MAX_DELTA_SIZE - 1;
Packit 9c6abc
  assert(sharpness >= 0 && sharpness <= 7);
Packit 9c6abc
  return kLevelsFromDelta[sharpness][pos];
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
//------------------------------------------------------------------------------
Packit 9c6abc
// Paragraph 15.4: compute the inner-edge filtering strength
Packit 9c6abc
Packit 9c6abc
#if !defined(WEBP_REDUCE_SIZE)
Packit 9c6abc
Packit 9c6abc
static int GetILevel(int sharpness, int level) {
Packit 9c6abc
  if (sharpness > 0) {
Packit 9c6abc
    if (sharpness > 4) {
Packit 9c6abc
      level >>= 2;
Packit 9c6abc
    } else {
Packit 9c6abc
      level >>= 1;
Packit 9c6abc
    }
Packit 9c6abc
    if (level > 9 - sharpness) {
Packit 9c6abc
      level = 9 - sharpness;
Packit 9c6abc
    }
Packit 9c6abc
  }
Packit 9c6abc
  if (level < 1) level = 1;
Packit 9c6abc
  return level;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
static void DoFilter(const VP8EncIterator* const it, int level) {
Packit 9c6abc
  const VP8Encoder* const enc = it->enc_;
Packit 9c6abc
  const int ilevel = GetILevel(enc->config_->filter_sharpness, level);
Packit 9c6abc
  const int limit = 2 * level + ilevel;
Packit 9c6abc
Packit 9c6abc
  uint8_t* const y_dst = it->yuv_out2_ + Y_OFF_ENC;
Packit 9c6abc
  uint8_t* const u_dst = it->yuv_out2_ + U_OFF_ENC;
Packit 9c6abc
  uint8_t* const v_dst = it->yuv_out2_ + V_OFF_ENC;
Packit 9c6abc
Packit 9c6abc
  // copy current block to yuv_out2_
Packit 9c6abc
  memcpy(y_dst, it->yuv_out_, YUV_SIZE_ENC * sizeof(uint8_t));
Packit 9c6abc
Packit 9c6abc
  if (enc->filter_hdr_.simple_ == 1) {   // simple
Packit 9c6abc
    VP8SimpleHFilter16i(y_dst, BPS, limit);
Packit 9c6abc
    VP8SimpleVFilter16i(y_dst, BPS, limit);
Packit 9c6abc
  } else {    // complex
Packit 9c6abc
    const int hev_thresh = (level >= 40) ? 2 : (level >= 15) ? 1 : 0;
Packit 9c6abc
    VP8HFilter16i(y_dst, BPS, limit, ilevel, hev_thresh);
Packit 9c6abc
    VP8HFilter8i(u_dst, v_dst, BPS, limit, ilevel, hev_thresh);
Packit 9c6abc
    VP8VFilter16i(y_dst, BPS, limit, ilevel, hev_thresh);
Packit 9c6abc
    VP8VFilter8i(u_dst, v_dst, BPS, limit, ilevel, hev_thresh);
Packit 9c6abc
  }
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
//------------------------------------------------------------------------------
Packit 9c6abc
// SSIM metric for one macroblock
Packit 9c6abc
Packit 9c6abc
static double GetMBSSIM(const uint8_t* yuv1, const uint8_t* yuv2) {
Packit 9c6abc
  int x, y;
Packit 9c6abc
  double sum = 0.;
Packit 9c6abc
Packit 9c6abc
  // compute SSIM in a 10 x 10 window
Packit 9c6abc
  for (y = VP8_SSIM_KERNEL; y < 16 - VP8_SSIM_KERNEL; y++) {
Packit 9c6abc
    for (x = VP8_SSIM_KERNEL; x < 16 - VP8_SSIM_KERNEL; x++) {
Packit 9c6abc
      sum += VP8SSIMGetClipped(yuv1 + Y_OFF_ENC, BPS, yuv2 + Y_OFF_ENC, BPS,
Packit 9c6abc
                               x, y, 16, 16);
Packit 9c6abc
    }
Packit 9c6abc
  }
Packit 9c6abc
  for (x = 1; x < 7; x++) {
Packit 9c6abc
    for (y = 1; y < 7; y++) {
Packit 9c6abc
      sum += VP8SSIMGetClipped(yuv1 + U_OFF_ENC, BPS, yuv2 + U_OFF_ENC, BPS,
Packit 9c6abc
                               x, y, 8, 8);
Packit 9c6abc
      sum += VP8SSIMGetClipped(yuv1 + V_OFF_ENC, BPS, yuv2 + V_OFF_ENC, BPS,
Packit 9c6abc
                               x, y, 8, 8);
Packit 9c6abc
    }
Packit 9c6abc
  }
Packit 9c6abc
  return sum;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
#endif  // !defined(WEBP_REDUCE_SIZE)
Packit 9c6abc
Packit 9c6abc
//------------------------------------------------------------------------------
Packit 9c6abc
// Exposed APIs: Encoder should call the following 3 functions to adjust
Packit 9c6abc
// loop filter strength
Packit 9c6abc
Packit 9c6abc
void VP8InitFilter(VP8EncIterator* const it) {
Packit 9c6abc
#if !defined(WEBP_REDUCE_SIZE)
Packit 9c6abc
  if (it->lf_stats_ != NULL) {
Packit 9c6abc
    int s, i;
Packit 9c6abc
    for (s = 0; s < NUM_MB_SEGMENTS; s++) {
Packit 9c6abc
      for (i = 0; i < MAX_LF_LEVELS; i++) {
Packit 9c6abc
        (*it->lf_stats_)[s][i] = 0;
Packit 9c6abc
      }
Packit 9c6abc
    }
Packit 9c6abc
    VP8SSIMDspInit();
Packit 9c6abc
  }
Packit 9c6abc
#else
Packit 9c6abc
  (void)it;
Packit 9c6abc
#endif
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
void VP8StoreFilterStats(VP8EncIterator* const it) {
Packit 9c6abc
#if !defined(WEBP_REDUCE_SIZE)
Packit 9c6abc
  int d;
Packit 9c6abc
  VP8Encoder* const enc = it->enc_;
Packit 9c6abc
  const int s = it->mb_->segment_;
Packit 9c6abc
  const int level0 = enc->dqm_[s].fstrength_;
Packit 9c6abc
Packit 9c6abc
  // explore +/-quant range of values around level0
Packit 9c6abc
  const int delta_min = -enc->dqm_[s].quant_;
Packit 9c6abc
  const int delta_max = enc->dqm_[s].quant_;
Packit 9c6abc
  const int step_size = (delta_max - delta_min >= 4) ? 4 : 1;
Packit 9c6abc
Packit 9c6abc
  if (it->lf_stats_ == NULL) return;
Packit 9c6abc
Packit 9c6abc
  // NOTE: Currently we are applying filter only across the sublock edges
Packit 9c6abc
  // There are two reasons for that.
Packit 9c6abc
  // 1. Applying filter on macro block edges will change the pixels in
Packit 9c6abc
  // the left and top macro blocks. That will be hard to restore
Packit 9c6abc
  // 2. Macro Blocks on the bottom and right are not yet compressed. So we
Packit 9c6abc
  // cannot apply filter on the right and bottom macro block edges.
Packit 9c6abc
  if (it->mb_->type_ == 1 && it->mb_->skip_) return;
Packit 9c6abc
Packit 9c6abc
  // Always try filter level  zero
Packit 9c6abc
  (*it->lf_stats_)[s][0] += GetMBSSIM(it->yuv_in_, it->yuv_out_);
Packit 9c6abc
Packit 9c6abc
  for (d = delta_min; d <= delta_max; d += step_size) {
Packit 9c6abc
    const int level = level0 + d;
Packit 9c6abc
    if (level <= 0 || level >= MAX_LF_LEVELS) {
Packit 9c6abc
      continue;
Packit 9c6abc
    }
Packit 9c6abc
    DoFilter(it, level);
Packit 9c6abc
    (*it->lf_stats_)[s][level] += GetMBSSIM(it->yuv_in_, it->yuv_out2_);
Packit 9c6abc
  }
Packit 9c6abc
#else  // defined(WEBP_REDUCE_SIZE)
Packit 9c6abc
  (void)it;
Packit 9c6abc
#endif  // !defined(WEBP_REDUCE_SIZE)
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
void VP8AdjustFilterStrength(VP8EncIterator* const it) {
Packit 9c6abc
  VP8Encoder* const enc = it->enc_;
Packit 9c6abc
#if !defined(WEBP_REDUCE_SIZE)
Packit 9c6abc
  if (it->lf_stats_ != NULL) {
Packit 9c6abc
    int s;
Packit 9c6abc
    for (s = 0; s < NUM_MB_SEGMENTS; s++) {
Packit 9c6abc
      int i, best_level = 0;
Packit 9c6abc
      // Improvement over filter level 0 should be at least 1e-5 (relatively)
Packit 9c6abc
      double best_v = 1.00001 * (*it->lf_stats_)[s][0];
Packit 9c6abc
      for (i = 1; i < MAX_LF_LEVELS; i++) {
Packit 9c6abc
        const double v = (*it->lf_stats_)[s][i];
Packit 9c6abc
        if (v > best_v) {
Packit 9c6abc
          best_v = v;
Packit 9c6abc
          best_level = i;
Packit 9c6abc
        }
Packit 9c6abc
      }
Packit 9c6abc
      enc->dqm_[s].fstrength_ = best_level;
Packit 9c6abc
    }
Packit 9c6abc
    return;
Packit 9c6abc
  }
Packit 9c6abc
#endif  // !defined(WEBP_REDUCE_SIZE)
Packit 9c6abc
  if (enc->config_->filter_strength > 0) {
Packit 9c6abc
    int max_level = 0;
Packit 9c6abc
    int s;
Packit 9c6abc
    for (s = 0; s < NUM_MB_SEGMENTS; s++) {
Packit 9c6abc
      VP8SegmentInfo* const dqm = &enc->dqm_[s];
Packit 9c6abc
      // this '>> 3' accounts for some inverse WHT scaling
Packit 9c6abc
      const int delta = (dqm->max_edge_ * dqm->y2_.q_[1]) >> 3;
Packit 9c6abc
      const int level =
Packit 9c6abc
          VP8FilterStrengthFromDelta(enc->filter_hdr_.sharpness_, delta);
Packit 9c6abc
      if (level > dqm->fstrength_) {
Packit 9c6abc
        dqm->fstrength_ = level;
Packit 9c6abc
      }
Packit 9c6abc
      if (max_level < dqm->fstrength_) {
Packit 9c6abc
        max_level = dqm->fstrength_;
Packit 9c6abc
      }
Packit 9c6abc
    }
Packit 9c6abc
    enc->filter_hdr_.level_ = max_level;
Packit 9c6abc
  }
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
// -----------------------------------------------------------------------------