From 23701f4f877ba7bdb6c3b308c1fdd3bbcf3c91dd Mon Sep 17 00:00:00 2001 From: Paul Wilkins Date: Fri, 7 Oct 2011 16:58:28 +0100 Subject: [PATCH] Segmentation Features; Only encode sign bit for feature data that can have a sign. Tweaks to the test segmentation rules so that it now actually gives a net benefit on the derf set of about 0.4% though much higher on some clips at the low end. Change-Id: I8e61f1aebf41c9037db7e67e2f8975aa18a0c986 --- vp8/common/seg_common.c | 14 +++++ vp8/common/seg_common.h | 4 ++ vp8/decoder/decodframe.c | 32 +++++++---- vp8/encoder/bitstream.c | 30 +++++++--- vp8/encoder/encodeframe.c | 2 +- vp8/encoder/mbgraph.c | 79 +++++++++++++++++++++---- vp8/encoder/onyx_if.c | 118 ++++++++++++++++++++++++++++---------- 7 files changed, 217 insertions(+), 62 deletions(-) diff --git a/vp8/common/seg_common.c b/vp8/common/seg_common.c index 292a2fd75..ef77dfc53 100644 --- a/vp8/common/seg_common.c +++ b/vp8/common/seg_common.c @@ -10,6 +10,9 @@ #include "vp8/common/seg_common.h" +const int segfeaturedata_signed[SEG_LVL_MAX] = {1, 1, 0, 0, 0, 0}; + + // These functions provide access to new segment level features. // Eventually these function may be "optimized out" but for the moment, // the coding mechanism is still subject to change so these provide a @@ -25,6 +28,12 @@ int segfeature_active( MACROBLOCKD *xd, (0x01 << feature_id) ) ); } +void clearall_segfeatures( MACROBLOCKD *xd ) +{ + vpx_memset(xd->segment_feature_data, 0, sizeof(xd->segment_feature_data)); + vpx_memset(xd->segment_feature_mask, 0, sizeof(xd->segment_feature_mask)); +} + void enable_segfeature( MACROBLOCKD *xd, int segment_id, SEG_LVL_FEATURES feature_id ) @@ -38,4 +47,9 @@ void disable_segfeature( MACROBLOCKD *xd, xd->segment_feature_mask[segment_id] &= ~(1 << feature_id); } +int is_segfeature_signed( SEG_LVL_FEATURES feature_id ) +{ + return ( segfeaturedata_signed[feature_id] ); +} + // TBD? Functions to read and write segment data with range / validity checking \ No newline at end of file diff --git a/vp8/common/seg_common.h b/vp8/common/seg_common.h index 2a9d3dc8f..cea16c885 100644 --- a/vp8/common/seg_common.h +++ b/vp8/common/seg_common.h @@ -18,6 +18,8 @@ int segfeature_active( MACROBLOCKD *xd, int segment_id, SEG_LVL_FEATURES feature_id ); +void clearall_segfeatures( MACROBLOCKD *xd ); + void enable_segfeature( MACROBLOCKD *xd, int segment_id, SEG_LVL_FEATURES feature_id ); @@ -26,5 +28,7 @@ void disable_segfeature( MACROBLOCKD *xd, int segment_id, SEG_LVL_FEATURES feature_id ); +int is_segfeature_signed( SEG_LVL_FEATURES feature_id ); + #endif /* __INC_SEG_COMMON_H__ */ diff --git a/vp8/decoder/decodframe.c b/vp8/decoder/decodframe.c index 585413b86..bfa13caec 100644 --- a/vp8/decoder/decodframe.c +++ b/vp8/decoder/decodframe.c @@ -1030,14 +1030,8 @@ int vp8_decode_frame(VP8D_COMP *pbi) { xd->mb_segement_abs_delta = (unsigned char)vp8_read_bit(bc); - // Clear down feature data structure - vpx_memset(xd->segment_feature_data, 0, - sizeof(xd->segment_feature_data)); - #if CONFIG_SEGFEATURES - // Clear down feature enabled masks - vpx_memset(xd->segment_feature_mask, 0, - sizeof(xd->segment_feature_mask)); + clearall_segfeatures( xd ); // For each segmentation... for (j = 0; j < MAX_MB_SEGMENTS; j++) @@ -1045,8 +1039,11 @@ int vp8_decode_frame(VP8D_COMP *pbi) // For each of the segments features... for (i = 0; i < SEG_LVL_MAX; i++) { - #else + // Clear down feature data structure + vpx_memset(xd->segment_feature_data, 0, + sizeof(xd->segment_feature_data)); + // For each segmentation feature... for (i = 0; i < SEG_LVL_MAX; i++) { @@ -1061,10 +1058,23 @@ int vp8_decode_frame(VP8D_COMP *pbi) // Update the feature data and mask enable_segfeature(xd, j, i); #endif - xd->segment_feature_data[j][i] = (signed char)vp8_read_literal(bc, mb_feature_data_bits[i]); + xd->segment_feature_data[j][i] = + (signed char)vp8_read_literal( + bc, mb_feature_data_bits[i]); - if (vp8_read_bit(bc)) - xd->segment_feature_data[j][i] = -xd->segment_feature_data[j][i]; +#if CONFIG_SEGFEATURES + // Is the segment data signed.. + if ( is_segfeature_signed(i) ) +#else + if ( 1 ) +#endif + { + if (vp8_read_bit(bc)) + { + xd->segment_feature_data[j][i] = + -xd->segment_feature_data[j][i]; + } + } } else { diff --git a/vp8/encoder/bitstream.c b/vp8/encoder/bitstream.c index 6b30a59af..395ffa79d 100644 --- a/vp8/encoder/bitstream.c +++ b/vp8/encoder/bitstream.c @@ -1987,20 +1987,32 @@ void vp8_pack_bitstream(VP8_COMP *cpi, unsigned char *dest, unsigned long *size) { vp8_write_bit(bc, 1); - // Encode the relevant feature data - if (Data < 0) +#if CONFIG_SEGFEATURES + // Is the segment data signed.. + if ( is_segfeature_signed(i) ) +#else + if ( 1 ) +#endif { - Data = - Data; - vp8_write_literal(bc, Data, - mb_feature_data_bits[i]); - vp8_write_bit(bc, 1); + // Encode the relevant feature data + if (Data < 0) + { + Data = - Data; + vp8_write_literal(bc, Data, + mb_feature_data_bits[i]); + vp8_write_bit(bc, 1); + } + else + { + vp8_write_literal(bc, Data, + mb_feature_data_bits[i]); + vp8_write_bit(bc, 0); + } } + // Unsigned data element so no sign bit needed else - { vp8_write_literal(bc, Data, mb_feature_data_bits[i]); - vp8_write_bit(bc, 0); - } } else vp8_write_bit(bc, 0); diff --git a/vp8/encoder/encodeframe.c b/vp8/encoder/encodeframe.c index ca3cb6efb..a6e47f240 100644 --- a/vp8/encoder/encodeframe.c +++ b/vp8/encoder/encodeframe.c @@ -969,7 +969,7 @@ void vp8_encode_frame(VP8_COMP *cpi) #if CONFIG_SEGFEATURES // debug output -#if 0 +#if DBG_PRNT_SEGMAP { FILE *statsfile; statsfile = fopen("segmap2.stt", "a"); diff --git a/vp8/encoder/mbgraph.c b/vp8/encoder/mbgraph.c index 1a9612d39..8a3f55f33 100644 --- a/vp8/encoder/mbgraph.c +++ b/vp8/encoder/mbgraph.c @@ -143,6 +143,47 @@ static int do_16x16_motion_search return err; } +static int do_16x16_zerozero_search +( + VP8_COMP *cpi, + int_mv *dst_mv, + YV12_BUFFER_CONFIG *buf, + int buf_mb_y_offset, + YV12_BUFFER_CONFIG *ref, + int mb_y_offset +) +{ + MACROBLOCK * const x = &cpi->mb; + MACROBLOCKD * const xd = &x->e_mbd; + unsigned int err, tmp_err; + int_mv tmp_mv; + int n; + + for (n = 0; n < 16; n++) { + BLOCKD *d = &xd->block[n]; + BLOCK *b = &x->block[n]; + + b->base_src = &buf->y_buffer; + b->src_stride = buf->y_stride; + b->src = buf->y_stride * (n & 12) + (n & 3) * 4 + buf_mb_y_offset; + + d->base_pre = &ref->y_buffer; + d->pre_stride = ref->y_stride; + d->pre = ref->y_stride * (n & 12) + (n & 3) * 4 + mb_y_offset; + } + + // Try zero MV first + // FIXME should really use something like near/nearest MV and/or MV prediction + xd->pre.y_buffer = ref->y_buffer + mb_y_offset; + xd->pre.y_stride = ref->y_stride; + VARIANCE_INVOKE(&cpi->rtcd.variance, satd16x16) + (ref->y_buffer + mb_y_offset, + ref->y_stride, xd->dst.y_buffer, + xd->dst.y_stride, &err); + dst_mv->as_int = 0; + + return err; +} static int find_best_16x16_intra ( VP8_COMP *cpi, @@ -227,10 +268,17 @@ static void update_mbgraph_mb_stats // Alt-ref frame MV search, if it exists and is different than last/golden frame if (alt_ref) { - int a_motion_error = do_16x16_motion_search(cpi, prev_alt_ref_mv, - &stats->ref[ALTREF_FRAME].m.mv, - buf, mb_y_offset, - alt_ref, arf_y_offset); + //int a_motion_error = do_16x16_motion_search(cpi, prev_alt_ref_mv, + // &stats->ref[ALTREF_FRAME].m.mv, + // buf, mb_y_offset, + // alt_ref, arf_y_offset); + + int a_motion_error = + do_16x16_zerozero_search( cpi, + &stats->ref[ALTREF_FRAME].m.mv, + buf, mb_y_offset, + alt_ref, arf_y_offset); + stats->ref[ALTREF_FRAME].err = a_motion_error; } else @@ -312,6 +360,15 @@ static void update_mbgraph_frame_stats } } +// Test for small magnitude (<= 1 pel mvs) +int small_mv( MV mv ) +{ + if ( (abs( (int)mv.col ) > 2) || (abs( (int)mv.row ) > 2) ) + return FALSE; + else + return TRUE; +} + //void separate_arf_mbs_byzz void separate_arf_mbs ( @@ -350,16 +407,15 @@ void separate_arf_mbs int altref_err = mb_stats->ref[ALTREF_FRAME].err; int intra_err = - ((mb_stats->ref[INTRA_FRAME ].err * 9) >> 3); + mb_stats->ref[INTRA_FRAME ].err + 250; int golden_err = - 250 + ((mb_stats->ref[GOLDEN_FRAME].err * 9) >> 3); + mb_stats->ref[GOLDEN_FRAME].err + 250; // Test for altref vs intra and gf and that its mv was 0,0. - if ( mb_stats->ref[ALTREF_FRAME].m.mv.as_int || - ( (altref_err > 500) && - ( (altref_err > (intra_err >> 2)) || - (altref_err > golden_err) ) ) ) + if ( (altref_err > 1000) || + (altref_err > intra_err) || + (altref_err > golden_err) ) { arf_not_zz[offset + mb_col]++; } @@ -389,7 +445,8 @@ void separate_arf_mbs } // Only bother with segmentation if over 10% of the MBs in static segment - if ( ncnt[1] && (ncnt[0] / ncnt[1] < 10) ) + //if ( ncnt[1] && (ncnt[0] / ncnt[1] < 10) ) + if ( 1 ) { cpi->mbgraph_use_arf_segmentation = ncnt[1]; vp8_enable_segmentation((VP8_PTR) cpi); diff --git a/vp8/encoder/onyx_if.c b/vp8/encoder/onyx_if.c index 26cbbf011..c6547cf4a 100644 --- a/vp8/encoder/onyx_if.c +++ b/vp8/encoder/onyx_if.c @@ -489,8 +489,8 @@ static void init_seg_features(VP8_COMP *cpi) return; } - // Disable and clear down for KF,ARF and low Q - if ( cm->frame_type == KEY_FRAME || cm->refresh_alt_ref_frame ) + // Disable and clear down for KF + if ( cm->frame_type == KEY_FRAME ) { // Clear down the global segmentation map vpx_memset( cpi->segmentation_map, 0, (cm->mb_rows * cm->mb_cols)); @@ -499,59 +499,117 @@ static void init_seg_features(VP8_COMP *cpi) // Disable segmentation vp8_disable_segmentation((VP8_PTR)cpi); + + // Clear down the segment features. + clearall_segfeatures(xd); } - // First normal frame in a valid alt ref group and we dont have low Q - else if ( cpi->source_alt_ref_active && - (cpi->common.frames_since_golden == 1) ) + // If this is an alt ref frame + else if ( cm->refresh_alt_ref_frame ) { - // Low Q test (only use segmentation at high q) - if ( ( (cpi->oxcf.end_usage == USAGE_CONSTRAINED_QUALITY) && - (cpi->cq_target_quality > 56 ) ) || - (cpi->ni_av_qi > 64) ) + // Clear down the global segmentation map + vpx_memset( cpi->segmentation_map, 0, (cm->mb_rows * cm->mb_cols)); + xd->update_mb_segmentation_map = 0; + xd->update_mb_segmentation_data = 0; + + // Disable segmentation and individual segment features by default + vp8_disable_segmentation((VP8_PTR)cpi); + clearall_segfeatures(xd); + + // Scan frames from current to arf frame. + // This function re-enables segmentation if appropriate. + vp8_update_mbgraph_stats(cpi); + + // If segmentation was enabled set those features needed for the + // arf itself. + if ( xd->segmentation_enabled ) { - xd->segment_feature_data[1][SEG_LVL_REF_FRAME] = LAST_FRAME; - xd->segment_feature_data[1][SEG_LVL_MODE] = ZEROMV; - xd->segment_feature_data[1][SEG_LVL_EOB] = 10; - xd->segment_feature_data[1][SEG_LVL_ALT_Q] = 10; - xd->segment_feature_data[1][SEG_LVL_ALT_LF] = -5; - - // Enable target features is the segment feature mask - enable_segfeature(xd, 1, SEG_LVL_REF_FRAME); - enable_segfeature(xd, 1, SEG_LVL_MODE); - enable_segfeature(xd, 1, SEG_LVL_EOB); + xd->update_mb_segmentation_map = 1; + xd->update_mb_segmentation_data = 1; + + xd->segment_feature_data[1][SEG_LVL_ALT_Q] = -3; + xd->segment_feature_data[1][SEG_LVL_ALT_LF] = -2; + enable_segfeature(xd, 1, SEG_LVL_ALT_Q); enable_segfeature(xd, 1, SEG_LVL_ALT_LF); // Where relevant assume segment data is delta data xd->mb_segement_abs_delta = SEGMENT_DELTADATA; - - // Scan frames from current to arf frame and define segmentation - vp8_update_mbgraph_stats(cpi); } } - // Normal frames if segmentation got enabled. + + // All other frames if segmentation has been enabled else if ( xd->segmentation_enabled ) { - // Special case where we are coding over the top of a previous - // alt ref frame - if ( cpi->is_src_frame_alt_ref ) + // First normal frame in a valid gf or alt ref group + if ( cpi->common.frames_since_golden == 0 ) { - if ( cpi->source_alt_ref_pending ) + // Set up segment features for normal frames in an af group + if ( cpi->source_alt_ref_active ) { + xd->update_mb_segmentation_map = 0; xd->update_mb_segmentation_data = 1; - xd->segment_feature_data[1][SEG_LVL_REF_FRAME] = ALTREF_FRAME; + xd->mb_segement_abs_delta = SEGMENT_DELTADATA; + + xd->segment_feature_data[1][SEG_LVL_ALT_Q] = 5; + xd->segment_feature_data[1][SEG_LVL_ALT_LF] = -2; + + enable_segfeature(xd, 1, SEG_LVL_ALT_Q); + enable_segfeature(xd, 1, SEG_LVL_ALT_LF); + + if ( ( (cpi->oxcf.end_usage == USAGE_CONSTRAINED_QUALITY) && + (cpi->cq_target_quality > 56 ) ) || + (cpi->ni_av_qi > 64) ) + { + xd->segment_feature_data[1] + [SEG_LVL_REF_FRAME] = LAST_FRAME; + xd->segment_feature_data[1][SEG_LVL_MODE] = ZEROMV; + xd->segment_feature_data[1][SEG_LVL_EOB] = 15; + + enable_segfeature(xd, 1, SEG_LVL_REF_FRAME); + enable_segfeature(xd, 1, SEG_LVL_MODE); + enable_segfeature(xd, 1, SEG_LVL_EOB); + } } + + // Disable segmentation and clear down features if alt ref + // is not active for this group else { + vp8_disable_segmentation((VP8_PTR)cpi); + vpx_memset( cpi->segmentation_map, 0, (cm->mb_rows * cm->mb_cols)); - xd->update_mb_segmentation_map = 1; - xd->update_mb_segmentation_data = 1; + + xd->update_mb_segmentation_map = 0; + xd->update_mb_segmentation_data = 0; + + clearall_segfeatures(xd); } } + + // Special case where we are coding over the top of a previous + // alt ref frame + else if ( cpi->is_src_frame_alt_ref ) + { + // Enable mode and ref frame features for segment 0 as well + enable_segfeature(xd, 0, SEG_LVL_REF_FRAME); + enable_segfeature(xd, 0, SEG_LVL_MODE); + + // All mbs should use ALTREF_FRAME, ZEROMV + xd->segment_feature_data[0][SEG_LVL_REF_FRAME] = ALTREF_FRAME; + xd->segment_feature_data[0][SEG_LVL_MODE] = ZEROMV; + xd->segment_feature_data[1][SEG_LVL_REF_FRAME] = ALTREF_FRAME; + xd->segment_feature_data[1][SEG_LVL_MODE] = ZEROMV; + + // Enable data udpate + xd->update_mb_segmentation_data = 1; + } + // All other frames. else { + // No updeates.. leave things as they are. + xd->update_mb_segmentation_map = 0; xd->update_mb_segmentation_data = 0; } } -- 2.40.0