s = x->plane[0].src.buf;
sp = x->plane[0].src.stride;
+ // Index for force_split: 0 for 64x64, 1-4 for 32x32 blocks,
+ // 5-20 for the 16x16 blocks.
+ force_split[0] = 0;
+
if (!is_key_frame) {
// In the case of spatial/temporal scalable coding, the assumption here is
// that the temporal reference frame will always be of type LAST_FRAME.
vp9_build_inter_predictors_sb(xd, mi_row, mi_col, BLOCK_64X64);
+ // Check if most of the superblock is skin content, and if so, force split
+ // to 32x32. Avoid checking superblocks on/near boundary for high resoln
+ // Note superblock may still pick 64X64 if y_sad is very small
+ // (i.e., y_sad < cpi->vbp_threshold_sad) below. For now leave this as is.
+ x->sb_is_skin = 0;
+ if (cpi->oxcf.content != VP9E_CONTENT_SCREEN && (low_res || (mi_col >= 8 &&
+ mi_col + 8 < cm->mi_cols && mi_row >= 8 && mi_row + 8 < cm->mi_rows))) {
+ int num_16x16_skin = 0;
+ int num_16x16_nonskin = 0;
+ uint8_t *ysignal = x->plane[0].src.buf;
+ uint8_t *usignal = x->plane[1].src.buf;
+ uint8_t *vsignal = x->plane[2].src.buf;
+ int spuv = x->plane[1].src.stride;
+ for (i = 0; i < 4; i++) {
+ for (j = 0; j < 4; j++) {
+ int is_skin = vp9_compute_skin_block(ysignal,
+ usignal,
+ vsignal,
+ sp,
+ spuv,
+ BLOCK_16X16);
+ num_16x16_skin += is_skin;
+ num_16x16_nonskin += (1 - is_skin);
+ if (num_16x16_nonskin > 3) {
+ // Exit loop if at least 4 of the 16x16 blocks are not skin.
+ i = 4;
+ j = 4;
+ }
+ ysignal += 16;
+ usignal += 8;
+ vsignal += 8;
+ }
+ ysignal += (sp << 4) - 64;
+ usignal += (spuv << 3) - 32;
+ vsignal += (spuv << 3) - 32;
+ }
+ if (num_16x16_skin > 12) {
+ x->sb_is_skin = 1;
+ force_split[0] = 1;
+ }
+ }
for (i = 1; i <= 2; ++i) {
struct macroblock_plane *p = &x->plane[i];
struct macroblockd_plane *pd = &xd->plane[i];
uv_sad = cpi->fn_ptr[bs].sdf(p->src.buf, p->src.stride,
pd->dst.buf, pd->dst.stride);
- x->color_sensitivity[i - 1] = uv_sad > (y_sad >> 2);
+ // TODO(marpan): Investigate if we should lower this threshold if
+ // superblock is detected as skin.
+ x->color_sensitivity[i - 1] = uv_sad > (y_sad >> 2);
}
d = xd->plane[0].dst.buf;
#endif // CONFIG_VP9_HIGHBITDEPTH
}
- // Index for force_split: 0 for 64x64, 1-4 for 32x32 blocks,
- // 5-20 for the 16x16 blocks.
- force_split[0] = 0;
// Fill in the entire tree of 8x8 (or 4x4 under some conditions) variances
// for splits.
for (i = 0; i < 4; i++) {
vp9_rd_cost_init(&dummy_rdc);
x->color_sensitivity[0] = 0;
x->color_sensitivity[1] = 0;
+ x->sb_is_skin = 0;
if (seg->enabled) {
const uint8_t *const map = seg->update_map ? cpi->segmentation_map
mi->mv[0].as_mv.col > 64 ||
mi->mv[0].as_mv.col < -64)
motion_low = 0;
- if (x->encode_breakout > 0 && motion_low == 1) {
+ if (x->encode_breakout > 0 && motion_low == 1 && !x->sb_is_skin) {
// Set a maximum for threshold to avoid big PSNR loss in low bit rate
// case. Use extreme low threshold for static frames to limit
// skipping.
this_rdc.rdcost = RDCOST(x->rdmult, x->rddiv, this_rdc.rate, this_rdc.dist);
if (cpi->oxcf.speed >= 5 &&
- cpi->oxcf.content != VP9E_CONTENT_SCREEN) {
+ cpi->oxcf.content != VP9E_CONTENT_SCREEN &&
+ !x->sb_is_skin) {
// Bias against non-zero (above some threshold) motion for large blocks.
// This is temporary fix to avoid selection of large mv for big blocks.
if (frame_mv[this_mode][ref_frame].as_mv.row > 64 ||