1/* 2 * Copyright (c) 2015 The WebM project authors. All Rights Reserved. 3 * 4 * Use of this source code is governed by a BSD-style license 5 * that can be found in the LICENSE file in the root of the source 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11#include <limits.h> 12#include <math.h> 13 14#include "vp9/common/vp9_blockd.h" 15#include "vp9/encoder/vp9_encoder.h" 16#include "vp9/encoder/vp9_skin_detection.h" 17 18int vp9_compute_skin_block(const uint8_t *y, const uint8_t *u, const uint8_t *v, 19 int stride, int strideuv, int bsize, 20 int consec_zeromv, int curr_motion_magn) { 21 // No skin if block has been zero/small motion for long consecutive time. 22 if (consec_zeromv > 60 && curr_motion_magn == 0) { 23 return 0; 24 } else { 25 int motion = 1; 26 // Take center pixel in block to determine is_skin. 27 const int y_width_shift = (4 << b_width_log2_lookup[bsize]) >> 1; 28 const int y_height_shift = (4 << b_height_log2_lookup[bsize]) >> 1; 29 const int uv_width_shift = y_width_shift >> 1; 30 const int uv_height_shift = y_height_shift >> 1; 31 const uint8_t ysource = y[y_height_shift * stride + y_width_shift]; 32 const uint8_t usource = u[uv_height_shift * strideuv + uv_width_shift]; 33 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 } 38} 39 40void vp9_compute_skin_sb(VP9_COMP *const cpi, BLOCK_SIZE bsize, int mi_row, 41 int mi_col) { 42 int i, j, num_bl; 43 VP9_COMMON *const cm = &cpi->common; 44 const uint8_t *src_y = cpi->Source->y_buffer; 45 const uint8_t *src_u = cpi->Source->u_buffer; 46 const uint8_t *src_v = cpi->Source->v_buffer; 47 const int src_ystride = cpi->Source->y_stride; 48 const int src_uvstride = cpi->Source->uv_stride; 49 const int y_bsize = 4 << b_width_log2_lookup[bsize]; 50 const int uv_bsize = y_bsize >> 1; 51 const int shy = (y_bsize == 8) ? 3 : 4; 52 const int shuv = shy - 1; 53 const int fac = y_bsize / 8; 54 const int y_shift = src_ystride * (mi_row << 3) + (mi_col << 3); 55 const int uv_shift = src_uvstride * (mi_row << 2) + (mi_col << 2); 56 const int mi_row_limit = VPXMIN(mi_row + 8, cm->mi_rows - 2); 57 const int mi_col_limit = VPXMIN(mi_col + 8, cm->mi_cols - 2); 58 src_y += y_shift; 59 src_u += uv_shift; 60 src_v += uv_shift; 61 62 for (i = mi_row; i < mi_row_limit; i += fac) { 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 int bl_index1 = bl_index + 1; 68 int bl_index2 = bl_index + cm->mi_cols; 69 int bl_index3 = bl_index2 + 1; 70 // Don't detect skin on the boundary. 71 if (i == 0 || j == 0) continue; 72 if (bsize == BLOCK_8X8) 73 consec_zeromv = cpi->consec_zero_mv[bl_index]; 74 else 75 consec_zeromv = VPXMIN(cpi->consec_zero_mv[bl_index], 76 VPXMIN(cpi->consec_zero_mv[bl_index1], 77 VPXMIN(cpi->consec_zero_mv[bl_index2], 78 cpi->consec_zero_mv[bl_index3]))); 79 cpi->skin_map[bl_index] = 80 vp9_compute_skin_block(src_y, src_u, src_v, src_ystride, src_uvstride, 81 bsize, consec_zeromv, 0); 82 num_bl++; 83 src_y += y_bsize; 84 src_u += uv_bsize; 85 src_v += uv_bsize; 86 } 87 src_y += (src_ystride << shy) - (num_bl << shy); 88 src_u += (src_uvstride << shuv) - (num_bl << shuv); 89 src_v += (src_uvstride << shuv) - (num_bl << shuv); 90 } 91 92 // Remove isolated skin blocks (none of its neighbors are skin) and isolated 93 // non-skin blocks (all of its neighbors are skin). 94 // Skip 4 corner blocks which have only 3 neighbors to remove isolated skin 95 // blocks. Skip superblock borders to remove isolated non-skin blocks. 96 for (i = mi_row; i < mi_row_limit; i += fac) { 97 for (j = mi_col; j < mi_col_limit; j += fac) { 98 int bl_index = i * cm->mi_cols + j; 99 int num_neighbor = 0; 100 int mi, mj; 101 int non_skin_threshold = 8; 102 // Skip 4 corners. 103 if ((i == mi_row && (j == mi_col || j == mi_col_limit - fac)) || 104 (i == mi_row_limit - fac && (j == mi_col || j == mi_col_limit - fac))) 105 continue; 106 // There are only 5 neighbors for non-skin blocks on the border. 107 if (i == mi_row || i == mi_row_limit - fac || j == mi_col || 108 j == mi_col_limit - fac) 109 non_skin_threshold = 5; 110 111 for (mi = -fac; mi <= fac; mi += fac) { 112 for (mj = -fac; mj <= fac; mj += fac) { 113 if (i + mi >= mi_row && i + mi < mi_row_limit && j + mj >= mi_col && 114 j + mj < mi_col_limit) { 115 int bl_neighbor_index = (i + mi) * cm->mi_cols + j + mj; 116 if (cpi->skin_map[bl_neighbor_index]) num_neighbor++; 117 } 118 } 119 } 120 121 if (cpi->skin_map[bl_index] && num_neighbor < 2) 122 cpi->skin_map[bl_index] = 0; 123 if (!cpi->skin_map[bl_index] && num_neighbor == non_skin_threshold) 124 cpi->skin_map[bl_index] = 1; 125 } 126 } 127} 128 129#ifdef OUTPUT_YUV_SKINMAP 130// For viewing skin map on input source. 131void vp9_output_skin_map(VP9_COMP *const cpi, FILE *yuv_skinmap_file) { 132 int i, j, mi_row, mi_col, num_bl; 133 VP9_COMMON *const cm = &cpi->common; 134 uint8_t *y; 135 const uint8_t *src_y = cpi->Source->y_buffer; 136 const int src_ystride = cpi->Source->y_stride; 137 const int y_bsize = 16; // Use 8x8 or 16x16. 138 const int shy = (y_bsize == 8) ? 3 : 4; 139 const int fac = y_bsize / 8; 140 141 YV12_BUFFER_CONFIG skinmap; 142 memset(&skinmap, 0, sizeof(YV12_BUFFER_CONFIG)); 143 if (vpx_alloc_frame_buffer(&skinmap, cm->width, cm->height, cm->subsampling_x, 144 cm->subsampling_y, VP9_ENC_BORDER_IN_PIXELS, 145 cm->byte_alignment)) { 146 vpx_free_frame_buffer(&skinmap); 147 return; 148 } 149 memset(skinmap.buffer_alloc, 128, skinmap.frame_size); 150 y = skinmap.y_buffer; 151 // Loop through blocks and set skin map based on center pixel of block. 152 // Set y to white for skin block, otherwise set to source with gray scale. 153 // Ignore rightmost/bottom boundary blocks. 154 for (mi_row = 0; mi_row < cm->mi_rows - 1; mi_row += fac) { 155 num_bl = 0; 156 for (mi_col = 0; mi_col < cm->mi_cols - 1; mi_col += fac) { 157 const int block_index = mi_row * cm->mi_cols + mi_col; 158 const int is_skin = cpi->skin_map[block_index]; 159 for (i = 0; i < y_bsize; i++) { 160 for (j = 0; j < y_bsize; j++) { 161 y[i * src_ystride + j] = is_skin ? 255 : src_y[i * src_ystride + j]; 162 } 163 } 164 num_bl++; 165 y += y_bsize; 166 src_y += y_bsize; 167 } 168 y += (src_ystride << shy) - (num_bl << shy); 169 src_y += (src_ystride << shy) - (num_bl << shy); 170 } 171 vpx_write_yuv_frame(yuv_skinmap_file, &skinmap); 172 vpx_free_frame_buffer(&skinmap); 173} 174#endif 175