Removed firstpass motion map

The firstpass motion map consists of an 8-bit flag for
each MB indicating how strongly the firstpass code
believes it should be filtered during the second pass
ARNR filtering.

For long or large format material the motion map can
become extremely large and hamper the operation of
the encoding process.

This change removes the motion map altogether, leaving
the second pass to rely on the magnitude of the motion
compensated error to determine the filter weight to
use for the MB during ARNR filtering.

Tests on the derf set indicate that the effect of this
change is neutral, with some small wins and losses. The
motion map has therefore been removed based on
a cost/benefit evaluation.

Change-Id: I53e07d236f5ce09a6f0c54e7c4ffbb490fb870f6
This commit is contained in:
Adrian Grange 2011-03-10 11:32:48 -08:00
parent a0306ea660
commit ed40ff9e2d
5 changed files with 27 additions and 219 deletions

View File

@ -281,21 +281,6 @@ int frame_max_bits(VP8_COMP *cpi)
}
extern size_t vp8_firstpass_stats_sz(unsigned int mb_count)
{
/* Calculate the size of a stats packet, which is dependent on the frame
* resolution. The FIRSTPASS_STATS struct has a single element array,
* motion_map, which is virtually expanded to have one element per
* macroblock.
*/
size_t stats_sz;
stats_sz = sizeof(FIRSTPASS_STATS) + mb_count;
stats_sz = (stats_sz + 7) & ~7;
return stats_sz;
}
void vp8_output_stats(const VP8_COMP *cpi,
struct vpx_codec_pkt_list *pktlist,
FIRSTPASS_STATS *stats)
@ -303,7 +288,7 @@ void vp8_output_stats(const VP8_COMP *cpi,
struct vpx_codec_cx_pkt pkt;
pkt.kind = VPX_CODEC_STATS_PKT;
pkt.data.twopass_stats.buf = stats;
pkt.data.twopass_stats.sz = vp8_firstpass_stats_sz(cpi->common.MBs);
pkt.data.twopass_stats.sz = sizeof(FIRSTPASS_STATS);
vpx_codec_pkt_list_add(pktlist, &pkt);
// TEMP debug code
@ -333,24 +318,17 @@ void vp8_output_stats(const VP8_COMP *cpi,
stats->count,
stats->duration);
fclose(fpfile);
fpfile = fopen("fpmotionmap.stt", "a");
if(fwrite(cpi->fp_motion_map, 1, cpi->common.MBs, fpfile));
fclose(fpfile);
}
#endif
}
int vp8_input_stats(VP8_COMP *cpi, FIRSTPASS_STATS *fps)
{
size_t stats_sz = vp8_firstpass_stats_sz(cpi->common.MBs);
if (cpi->stats_in >= cpi->stats_in_end)
return EOF;
*fps = *cpi->stats_in;
cpi->stats_in = (void*)((char *)cpi->stats_in + stats_sz);
cpi->stats_in = (void*)((char *)cpi->stats_in + sizeof(FIRSTPASS_STATS));
return 1;
}
@ -416,57 +394,9 @@ void vp8_avg_stats(FIRSTPASS_STATS *section)
section->duration /= section->count;
}
unsigned char *vp8_fpmm_get_pos(VP8_COMP *cpi)
{
return cpi->fp_motion_map_stats;
}
void vp8_fpmm_reset_pos(VP8_COMP *cpi, unsigned char *target_pos)
{
cpi->fp_motion_map_stats = target_pos;
}
void vp8_advance_fpmm(VP8_COMP *cpi, int count)
{
cpi->fp_motion_map_stats = (void*)((char*)cpi->fp_motion_map_stats +
count * vp8_firstpass_stats_sz(cpi->common.MBs));
}
void vp8_input_fpmm(VP8_COMP *cpi)
{
unsigned char *fpmm = cpi->fp_motion_map;
int MBs = cpi->common.MBs;
int max_frames = cpi->active_arnr_frames;
int i;
for (i=0; i<max_frames; i++)
{
char *motion_map = (char*)cpi->fp_motion_map_stats
+ sizeof(FIRSTPASS_STATS);
memcpy(fpmm, motion_map, MBs);
fpmm += MBs;
vp8_advance_fpmm(cpi, 1);
}
// Flag the use of weights in the temporal filter
cpi->use_weighted_temporal_filter = 1;
}
void vp8_init_first_pass(VP8_COMP *cpi)
{
vp8_zero_stats(cpi->total_stats);
// TEMP debug code
#ifdef OUTPUT_FPF
{
FILE *fpfile;
fpfile = fopen("firstpass.stt", "w");
fclose(fpfile);
fpfile = fopen("fpmotionmap.stt", "wb");
fclose(fpfile);
}
#endif
}
void vp8_end_first_pass(VP8_COMP *cpi)
@ -583,8 +513,6 @@ void vp8_first_pass(VP8_COMP *cpi)
MV zero_ref_mv = {0, 0};
unsigned char *fp_motion_map_ptr = cpi->fp_motion_map;
vp8_clear_system_state(); //__asm emms;
x->src = * cpi->Source;
@ -636,7 +564,6 @@ void vp8_first_pass(VP8_COMP *cpi)
for (mb_col = 0; mb_col < cm->mb_cols; mb_col++)
{
int this_error;
int zero_error;
int zz_to_best_ratio;
int gf_motion_error = INT_MAX;
int use_dc_pred = (mb_col || mb_row) && (!mb_col || !mb_row);
@ -658,9 +585,6 @@ void vp8_first_pass(VP8_COMP *cpi)
// Cumulative intra error total
intra_error += (long long)this_error;
// Indicate default assumption of intra in the motion map
*fp_motion_map_ptr = 0;
// Set up limit values for motion vectors to prevent them extending outside the UMV borders
x->mv_col_min = -((mb_col * 16) + (VP8BORDERINPIXELS - 16));
x->mv_col_max = ((cm->mb_cols - 1 - mb_col) * 16) + (VP8BORDERINPIXELS - 16);
@ -679,9 +603,6 @@ void vp8_first_pass(VP8_COMP *cpi)
d->bmi.mv.as_mv.row = 0;
d->bmi.mv.as_mv.col = 0;
// Save (0,0) error for later use
zero_error = motion_error;
// Test last reference frame using the previous best mv as the
// starting point (best reference) for the search
vp8_first_pass_motion_search(cpi, x, &best_ref_mv.as_mv,
@ -796,25 +717,6 @@ void vp8_first_pass(VP8_COMP *cpi)
else if (d->bmi.mv.as_mv.col < 0)
sum_in_vectors--;
}
// Compute how close (0,0) predictor is to best
// predictor in terms of their prediction error
zz_to_best_ratio = (10*zero_error + this_error/2)
/ (this_error+!this_error);
if ((zero_error < 50000) &&
(zz_to_best_ratio <= 11) )
*fp_motion_map_ptr = 1;
else
*fp_motion_map_ptr = 0;
}
else
{
// 0,0 mv was best
if( zero_error<50000 )
*fp_motion_map_ptr = 2;
else
*fp_motion_map_ptr = 1;
}
}
}
@ -828,9 +730,6 @@ void vp8_first_pass(VP8_COMP *cpi)
recon_yoffset += 16;
recon_uvoffset += 8;
// Update the motion map
fp_motion_map_ptr++;
}
// adjust to the next row of mbs
@ -892,13 +791,10 @@ void vp8_first_pass(VP8_COMP *cpi)
// than the full time between subsequent cpi->source_time_stamp s .
fps.duration = cpi->source_end_time_stamp - cpi->source_time_stamp;
// don't want to do outputstats with a stack variable!
// don't want to do output stats with a stack variable!
memcpy(cpi->this_frame_stats,
&fps,
sizeof(FIRSTPASS_STATS));
memcpy((char*)cpi->this_frame_stats + sizeof(FIRSTPASS_STATS),
cpi->fp_motion_map,
sizeof(cpi->fp_motion_map[0]) * cpi->common.MBs);
vp8_output_stats(cpi, cpi->output_pkt_list, cpi->this_frame_stats);
vp8_accumulate_stats(cpi->total_stats, &fps);
}
@ -1351,8 +1247,6 @@ void vp8_init_second_pass(VP8_COMP *cpi)
cpi->clip_bpe = cpi->bits_left /
DOUBLE_DIVIDE_CHECK(cpi->modified_error_total);
cpi->observed_bpe = cpi->clip_bpe;
cpi->fp_motion_map_stats = (unsigned char *)cpi->stats_in;
}
void vp8_end_second_pass(VP8_COMP *cpi)
@ -1360,7 +1254,7 @@ void vp8_end_second_pass(VP8_COMP *cpi)
}
// This function gives and estimate of how badly we believe
// the predicition quality is decaying from frame to frame.
// the prediction quality is decaying from frame to frame.
double get_prediction_decay_rate(VP8_COMP *cpi, FIRSTPASS_STATS *next_frame)
{
double prediction_decay_rate;
@ -1472,8 +1366,6 @@ static void define_gf_group(VP8_COMP *cpi, FIRSTPASS_STATS *this_frame)
int max_bits = frame_max_bits(cpi); // Max for a single frame
unsigned char *fpmm_pos;
unsigned int allow_alt_ref =
cpi->oxcf.play_alternate && cpi->oxcf.lag_in_frames;
@ -1482,8 +1374,6 @@ static void define_gf_group(VP8_COMP *cpi, FIRSTPASS_STATS *this_frame)
vp8_clear_system_state(); //__asm emms;
fpmm_pos = vp8_fpmm_get_pos(cpi);
start_pos = cpi->stats_in;
vpx_memset(&next_frame, 0, sizeof(next_frame)); // assure clean
@ -1780,20 +1670,6 @@ static void define_gf_group(VP8_COMP *cpi, FIRSTPASS_STATS *this_frame)
}
cpi->active_arnr_frames = frames_bwd + 1 + frames_fwd;
{
// Advance to & read in the motion map for those frames
// to be considered for filtering based on the position
// of the ARF
vp8_fpmm_reset_pos(cpi, cpi->fp_motion_map_stats_save);
// Position at the 'earliest' frame to be filtered
vp8_advance_fpmm(cpi,
cpi->baseline_gf_interval - frames_bwd);
// Read / create a motion map for the region of interest
vp8_input_fpmm(cpi);
}
}
else
{
@ -2023,9 +1899,6 @@ static void define_gf_group(VP8_COMP *cpi, FIRSTPASS_STATS *this_frame)
reset_fpf_position(cpi, start_pos);
}
// Reset the First pass motion map file position
vp8_fpmm_reset_pos(cpi, fpmm_pos);
}
// Allocate bits to a normal frame that is neither a gf an arf or a key frame.
@ -2107,13 +1980,6 @@ void vp8_second_pass(VP8_COMP *cpi)
if (EOF == vp8_input_stats(cpi, &this_frame))
return;
vpx_memset(cpi->fp_motion_map, 0,
cpi->oxcf.arnr_max_frames*cpi->common.MBs);
cpi->fp_motion_map_stats_save = vp8_fpmm_get_pos(cpi);
// Step over this frame's first pass motion map
vp8_advance_fpmm(cpi, 1);
this_frame_error = this_frame.ssim_weighted_pred_err;
this_frame_intra_error = this_frame.intra_error;
this_frame_coded_error = this_frame.coded_error;

View File

@ -281,12 +281,6 @@ void vp8_dealloc_compressor_data(VP8_COMP *cpi)
vpx_free(cpi->active_map);
cpi->active_map = 0;
#if !(CONFIG_REALTIME_ONLY)
// Delete first pass motion map
vpx_free(cpi->fp_motion_map);
cpi->fp_motion_map = 0;
#endif
vp8_de_alloc_frame_buffers(&cpi->common);
vp8_yv12_de_alloc_frame_buffer(&cpi->last_frame_uf);
@ -1360,11 +1354,11 @@ void vp8_alloc_compressor_data(VP8_COMP *cpi)
#if !(CONFIG_REALTIME_ONLY)
vpx_free(cpi->total_stats);
cpi->total_stats = vpx_calloc(1, vp8_firstpass_stats_sz(cpi->common.MBs));
cpi->total_stats = vpx_calloc(1, sizeof(FIRSTPASS_STATS));
vpx_free(cpi->this_frame_stats);
cpi->this_frame_stats = vpx_calloc(1, vp8_firstpass_stats_sz(cpi->common.MBs));
cpi->this_frame_stats = vpx_calloc(1, sizeof(FIRSTPASS_STATS));
if(!cpi->total_stats || !cpi->this_frame_stats)
vpx_internal_error(&cpi->common.error, VPX_CODEC_MEM_ERROR,
@ -1543,8 +1537,6 @@ void vp8_init_config(VP8_PTR ptr, VP8_CONFIG *oxcf)
#if VP8_TEMPORAL_ALT_REF
cpi->use_weighted_temporal_filter = 0;
{
int i;
@ -1927,12 +1919,6 @@ VP8_PTR vp8_create_compressor(VP8_CONFIG *oxcf)
vpx_memset(cpi->active_map , 1, (cpi->common.mb_rows * cpi->common.mb_cols));
cpi->active_map_enabled = 0;
#if !(CONFIG_REALTIME_ONLY)
// Create the first pass motion map structure and set to 0
// Allocate space for maximum of 15 buffers
CHECK_MEM_ERROR(cpi->fp_motion_map, vpx_calloc(15*cpi->common.MBs, 1));
#endif
#if 0
// Experimental code for lagged and one pass
// Initialise one_pass GF frames stats
@ -2082,7 +2068,7 @@ VP8_PTR vp8_create_compressor(VP8_CONFIG *oxcf)
}
else if (cpi->pass == 2)
{
size_t packet_sz = vp8_firstpass_stats_sz(cpi->common.MBs);
size_t packet_sz = sizeof(FIRSTPASS_STATS);
int packets = oxcf->two_pass_stats_in.sz / packet_sz;
cpi->stats_in = oxcf->two_pass_stats_in.buf;

View File

@ -496,11 +496,6 @@ typedef struct
struct vpx_codec_pkt_list *output_pkt_list;
int first_pass_done;
#if !(CONFIG_REALTIME_ONLY)
unsigned char *fp_motion_map;
unsigned char *fp_motion_map_stats, *fp_motion_map_stats_save;
#endif
#if 0
// Experimental code for lagged and one pass
ONEPASS_FRAMESTATS one_pass_frame_stats[MAX_LAG_BUFFERS];
@ -642,8 +637,6 @@ typedef struct
YV12_BUFFER_CONFIG *frames[MAX_LAG_BUFFERS];
int fixed_divide[512];
#endif
// Flag to indicate temporal filter method
int use_weighted_temporal_filter;
#if CONFIG_PSNR
int count;

View File

@ -287,8 +287,7 @@ static void vp8_temporal_filter_iterate_c
int byte;
int frame;
int mb_col, mb_row;
unsigned int filter_weight[MAX_LAG_BUFFERS];
unsigned char *mm_ptr = cpi->fp_motion_map;
unsigned int filter_weight;
int mb_cols = cpi->common.mb_cols;
int mb_rows = cpi->common.mb_rows;
int MBs = cpi->common.MBs;
@ -306,13 +305,6 @@ static void vp8_temporal_filter_iterate_c
unsigned char *u_buffer = mbd->pre.u_buffer;
unsigned char *v_buffer = mbd->pre.v_buffer;
if (!cpi->use_weighted_temporal_filter)
{
// Temporal filtering is unweighted
for (frame = 0; frame < frame_count; frame++)
filter_weight[frame] = 1;
}
for (mb_row = 0; mb_row < mb_rows; mb_row++)
{
#if ALT_REF_MC_ENABLED
@ -338,34 +330,9 @@ static void vp8_temporal_filter_iterate_c
+ (VP8BORDERINPIXELS - 19);
#endif
// Read & process macroblock weights from motion map
if (cpi->use_weighted_temporal_filter)
{
weight_cap = 2;
for (frame = alt_ref_index-1; frame >= 0; frame--)
{
w = *(mm_ptr + (frame+1)*MBs);
filter_weight[frame] = w < weight_cap ? w : weight_cap;
weight_cap = w;
}
filter_weight[alt_ref_index] = 2;
weight_cap = 2;
for (frame = alt_ref_index+1; frame < frame_count; frame++)
{
w = *(mm_ptr + frame*MBs);
filter_weight[frame] = w < weight_cap ? w : weight_cap;
weight_cap = w;
}
}
for (frame = 0; frame < frame_count; frame++)
{
int err;
int err = 0;
if (cpi->frames[frame] == NULL)
continue;
@ -374,28 +341,25 @@ static void vp8_temporal_filter_iterate_c
mbd->block[0].bmi.mv.as_mv.col = 0;
#if ALT_REF_MC_ENABLED
//if (filter_weight[frame] == 0)
{
#define THRESH_LOW 10000
#define THRESH_HIGH 20000
// Correlation has been lost try MC
err = vp8_temporal_filter_find_matching_mb_c
(cpi,
cpi->frames[alt_ref_index],
cpi->frames[frame],
mb_y_offset,
THRESH_LOW);
// Find best match in this frame by MC
err = vp8_temporal_filter_find_matching_mb_c
(cpi,
cpi->frames[alt_ref_index],
cpi->frames[frame],
mb_y_offset,
THRESH_LOW);
if (filter_weight[frame] < 2)
{
// Set weight depending on error
filter_weight[frame] = err<THRESH_LOW
? 2 : err<THRESH_HIGH ? 1 : 0;
}
}
#endif
if (filter_weight[frame] != 0)
// Assign higher weight to matching MB if it's error
// score is lower. If not applying MC default behavior
// is to weight all MBs equal.
filter_weight = err<THRESH_LOW
? 2 : err<THRESH_HIGH ? 1 : 0;
if (filter_weight != 0)
{
// Construct the predictors
vp8_temporal_filter_predictors_mb_c
@ -415,7 +379,7 @@ static void vp8_temporal_filter_iterate_c
predictor,
16,
strength,
filter_weight[frame],
filter_weight,
accumulator,
count);
@ -425,7 +389,7 @@ static void vp8_temporal_filter_iterate_c
predictor + 256,
8,
strength,
filter_weight[frame],
filter_weight,
accumulator + 256,
count + 256);
@ -435,7 +399,7 @@ static void vp8_temporal_filter_iterate_c
predictor + 320,
8,
strength,
filter_weight[frame],
filter_weight,
accumulator + 320,
count + 320);
}
@ -491,7 +455,6 @@ static void vp8_temporal_filter_iterate_c
byte += stride - 8;
}
mm_ptr++;
mb_y_offset += 16;
mb_uv_offset += 8;
}

View File

@ -199,7 +199,7 @@ static vpx_codec_err_t validate_config(vpx_codec_alg_priv_t *ctx,
{
int mb_r = (cfg->g_h + 15) / 16;
int mb_c = (cfg->g_w + 15) / 16;
size_t packet_sz = vp8_firstpass_stats_sz(mb_r * mb_c);
size_t packet_sz = sizeof(FIRSTPASS_STATS);
int n_packets = cfg->rc_twopass_stats_in.sz / packet_sz;
FIRSTPASS_STATS *stats;