vp9_skin_detection.c 6.87 KB
Newer Older
Marco's avatar
Marco committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/*
 *  Copyright (c) 2015 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 <limits.h>
#include <math.h>

#include "vp9/common/vp9_blockd.h"
#include "vp9/encoder/vp9_encoder.h"
#include "vp9/encoder/vp9_skin_detection.h"

Marco's avatar
Marco committed
18
int vp9_compute_skin_block(const uint8_t *y, const uint8_t *u, const uint8_t *v,
19 20 21
                           int stride, int strideuv, int bsize,
                           int consec_zeromv, int curr_motion_magn) {
  // No skin if block has been zero/small motion for long consecutive time.
22
  if (consec_zeromv > 60 && curr_motion_magn == 0) {
23 24 25 26 27 28 29 30 31 32 33
    return 0;
  } else {
    int motion = 1;
    // Take center pixel in block to determine is_skin.
    const int y_width_shift = (4 << b_width_log2_lookup[bsize]) >> 1;
    const int y_height_shift = (4 << b_height_log2_lookup[bsize]) >> 1;
    const int uv_width_shift = y_width_shift >> 1;
    const int uv_height_shift = y_height_shift >> 1;
    const uint8_t ysource = y[y_height_shift * stride + y_width_shift];
    const uint8_t usource = u[uv_height_shift * strideuv + uv_width_shift];
    const uint8_t vsource = v[uv_height_shift * strideuv + uv_width_shift];
34

35
    if (consec_zeromv > 25 && curr_motion_magn == 0) motion = 0;
36
    return vpx_skin_pixel(ysource, usource, vsource, motion);
37
  }
Marco's avatar
Marco committed
38 39
}

40 41 42
void vp9_compute_skin_sb(VP9_COMP *const cpi, BLOCK_SIZE bsize, int mi_row,
                         int mi_col) {
  int i, j, num_bl;
Marco's avatar
Marco committed
43 44 45 46 47 48
  VP9_COMMON *const cm = &cpi->common;
  const uint8_t *src_y = cpi->Source->y_buffer;
  const uint8_t *src_u = cpi->Source->u_buffer;
  const uint8_t *src_v = cpi->Source->v_buffer;
  const int src_ystride = cpi->Source->y_stride;
  const int src_uvstride = cpi->Source->uv_stride;
49 50 51 52 53
  const int y_bsize = 4 << b_width_log2_lookup[bsize];
  const int uv_bsize = y_bsize >> 1;
  const int shy = (y_bsize == 8) ? 3 : 4;
  const int shuv = shy - 1;
  const int fac = y_bsize / 8;
54 55
  const int y_shift = src_ystride * (mi_row << 3) + (mi_col << 3);
  const int uv_shift = src_uvstride * (mi_row << 2) + (mi_col << 2);
56 57
  const int mi_row_limit = VPXMIN(mi_row + 8, cm->mi_rows - 2);
  const int mi_col_limit = VPXMIN(mi_col + 8, cm->mi_cols - 2);
58 59 60 61
  src_y += y_shift;
  src_u += uv_shift;
  src_v += uv_shift;

62
  for (i = mi_row; i < mi_row_limit; i += fac) {
Marco's avatar
Marco committed
63
    num_bl = 0;
64
    for (j = mi_col; j < mi_col_limit; j += fac) {
65
      int consec_zeromv = 0;
66
      int bl_index = i * cm->mi_cols + j;
67 68 69
      int bl_index1 = bl_index + 1;
      int bl_index2 = bl_index + cm->mi_cols;
      int bl_index3 = bl_index2 + 1;
70 71
      // Don't detect skin on the boundary.
      if (i == 0 || j == 0) continue;
72 73 74 75 76 77 78
      if (bsize == BLOCK_8X8)
        consec_zeromv = cpi->consec_zero_mv[bl_index];
      else
        consec_zeromv = VPXMIN(cpi->consec_zero_mv[bl_index],
                               VPXMIN(cpi->consec_zero_mv[bl_index1],
                                      VPXMIN(cpi->consec_zero_mv[bl_index2],
                                             cpi->consec_zero_mv[bl_index3])));
79
      cpi->skin_map[bl_index] =
80 81
          vp9_compute_skin_block(src_y, src_u, src_v, src_ystride, src_uvstride,
                                 bsize, consec_zeromv, 0);
82 83 84 85 86 87 88 89 90
      num_bl++;
      src_y += y_bsize;
      src_u += uv_bsize;
      src_v += uv_bsize;
    }
    src_y += (src_ystride << shy) - (num_bl << shy);
    src_u += (src_uvstride << shuv) - (num_bl << shuv);
    src_v += (src_uvstride << shuv) - (num_bl << shuv);
  }
91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126

  // Remove isolated skin blocks (none of its neighbors are skin) and isolated
  // non-skin blocks (all of its neighbors are skin).
  // Skip 4 corner blocks which have only 3 neighbors to remove isolated skin
  // blocks. Skip superblock borders to remove isolated non-skin blocks.
  for (i = mi_row; i < mi_row_limit; i += fac) {
    for (j = mi_col; j < mi_col_limit; j += fac) {
      int bl_index = i * cm->mi_cols + j;
      int num_neighbor = 0;
      int mi, mj;
      int non_skin_threshold = 8;
      // Skip 4 corners.
      if ((i == mi_row && (j == mi_col || j == mi_col_limit - fac)) ||
          (i == mi_row_limit - fac && (j == mi_col || j == mi_col_limit - fac)))
        continue;
      // There are only 5 neighbors for non-skin blocks on the border.
      if (i == mi_row || i == mi_row_limit - fac || j == mi_col ||
          j == mi_col_limit - fac)
        non_skin_threshold = 5;

      for (mi = -fac; mi <= fac; mi += fac) {
        for (mj = -fac; mj <= fac; mj += fac) {
          if (i + mi >= mi_row && i + mi < mi_row_limit && j + mj >= mi_col &&
              j + mj < mi_col_limit) {
            int bl_neighbor_index = (i + mi) * cm->mi_cols + j + mj;
            if (cpi->skin_map[bl_neighbor_index]) num_neighbor++;
          }
        }
      }

      if (cpi->skin_map[bl_index] && num_neighbor < 2)
        cpi->skin_map[bl_index] = 0;
      if (!cpi->skin_map[bl_index] && num_neighbor == non_skin_threshold)
        cpi->skin_map[bl_index] = 1;
    }
  }
127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
}

#ifdef OUTPUT_YUV_SKINMAP
// For viewing skin map on input source.
void vp9_output_skin_map(VP9_COMP *const cpi, FILE *yuv_skinmap_file) {
  int i, j, mi_row, mi_col, num_bl;
  VP9_COMMON *const cm = &cpi->common;
  uint8_t *y;
  const uint8_t *src_y = cpi->Source->y_buffer;
  const int src_ystride = cpi->Source->y_stride;
  const int y_bsize = 16;  // Use 8x8 or 16x16.
  const int shy = (y_bsize == 8) ? 3 : 4;
  const int fac = y_bsize / 8;

  YV12_BUFFER_CONFIG skinmap;
  memset(&skinmap, 0, sizeof(YV12_BUFFER_CONFIG));
  if (vpx_alloc_frame_buffer(&skinmap, cm->width, cm->height, cm->subsampling_x,
                             cm->subsampling_y, VP9_ENC_BORDER_IN_PIXELS,
                             cm->byte_alignment)) {
    vpx_free_frame_buffer(&skinmap);
    return;
  }
  memset(skinmap.buffer_alloc, 128, skinmap.frame_size);
  y = skinmap.y_buffer;
  // Loop through blocks and set skin map based on center pixel of block.
  // Set y to white for skin block, otherwise set to source with gray scale.
  // Ignore rightmost/bottom boundary blocks.
  for (mi_row = 0; mi_row < cm->mi_rows - 1; mi_row += fac) {
    num_bl = 0;
    for (mi_col = 0; mi_col < cm->mi_cols - 1; mi_col += fac) {
      const int block_index = mi_row * cm->mi_cols + mi_col;
      const int is_skin = cpi->skin_map[block_index];
Marco's avatar
Marco committed
159 160
      for (i = 0; i < y_bsize; i++) {
        for (j = 0; j < y_bsize; j++) {
161
          y[i * src_ystride + j] = is_skin ? 255 : src_y[i * src_ystride + j];
Marco's avatar
Marco committed
162 163
        }
      }
Marco's avatar
Marco committed
164 165 166
      num_bl++;
      y += y_bsize;
      src_y += y_bsize;
Marco's avatar
Marco committed
167
    }
Marco's avatar
Marco committed
168 169
    y += (src_ystride << shy) - (num_bl << shy);
    src_y += (src_ystride << shy) - (num_bl << shy);
Marco's avatar
Marco committed
170
  }
171
  vpx_write_yuv_frame(yuv_skinmap_file, &skinmap);
172
  vpx_free_frame_buffer(&skinmap);
Marco's avatar
Marco committed
173 174
}
#endif