Unverified Commit 83fcc030 authored by fbossen's avatar fbossen Committed by GitHub

Add basic support for nonzero motion vectors (#485)

* Add motion vector paramater to inter prediction function

Motion vector is read from added data field in Block structure

* Add reference frame and motion vector fields/parameters

Reference frame and motion vector are added fields to the
RDOOutput structure and added parameters to the block
encoding function

* Fix inter prediction function for nonzero MVs

* Add call to encode new motion vector

* Import default cdfs for MV coding from libaom

* Fix handling of MV precision in MV coding function

* Add coding of DRL mode

* Add motion vector stack

* Add new MV counter to correctly determine context

* Use motion vectors in MV stack for prediction

* Sort MV stack according to weights

Also update weights after near search

* Fix log2() function

* Compute correct context for DRL flag

* Store MVs with 1/8 pel precision and fix MV generation

* Add some basic motion estimation

Motion estimation is full search (+/-16 pel range in 2-pel increments) and
is done independently for each block

* Fix chroma motion compensation for small blocks

Chroma motion compensation sometimes happens on 2x2 basis
parent a66ae61b
This diff is collapsed.
......@@ -340,7 +340,7 @@ impl FrameInvariants {
showable_frame: true,
error_resilient: true,
intra_only: false,
allow_high_precision_mv: true,
allow_high_precision_mv: false,
frame_type: FrameType::KEY,
show_existing_frame: false,
use_reduced_tx_set,
......@@ -1198,6 +1198,7 @@ pub fn encode_block_a(seq: &Sequence,
pub fn encode_block_b(fi: &FrameInvariants, fs: &mut FrameState,
cw: &mut ContextWriter, w: &mut dyn Writer,
luma_mode: PredictionMode, chroma_mode: PredictionMode,
ref_frame: usize, mv: MotionVector,
bsize: BlockSize, bo: &BlockOffset, skip: bool, bit_depth: usize) {
let is_inter = !luma_mode.is_intra();
if is_inter { assert!(luma_mode == chroma_mode); };
......@@ -1208,15 +1209,46 @@ pub fn encode_block_b(fi: &FrameInvariants, fs: &mut FrameState,
if fi.frame_type == FrameType::INTER {
cw.write_is_inter(w, bo, is_inter);
if is_inter {
let ref_frame = LAST_FRAME;
cw.fill_neighbours_ref_counts(bo);
cw.bc.set_ref_frame(bo, bsize, ref_frame);
cw.bc.set_motion_vector(bo, bsize, mv);
cw.write_ref_frames(w, bo);
let mode_context = cw.find_mvrefs(bo, ref_frame);
let mut mv_stack = Vec::new();
let mode_context = cw.find_mvrefs(bo, ref_frame, &mut mv_stack);
//let mode_context = if bo.x == 0 && bo.y == 0 { 0 } else if bo.x ==0 || bo.y == 0 { 51 } else { 85 };
// NOTE: Until rav1e supports other inter modes than GLOBALMV
assert!(luma_mode == PredictionMode::GLOBALMV);
cw.write_inter_mode(w, luma_mode, mode_context);
if luma_mode == PredictionMode::NEWMV || luma_mode == PredictionMode::NEW_NEWMV {
let ref_mv_idx = 0;
let num_mv_found = mv_stack.len();
for idx in 0..2 {
if num_mv_found > idx + 1 {
let drl_mode = ref_mv_idx > idx;
let ctx: usize = (mv_stack[idx].weight < REF_CAT_LEVEL) as usize
+ (mv_stack[idx + 1].weight < REF_CAT_LEVEL) as usize;
cw.write_drl_mode(w, drl_mode, ctx);
if !drl_mode { break; }
}
}
let ref_mv = if num_mv_found > 0 {
mv_stack[ref_mv_idx].this_mv
} else {
MotionVector{ row: 0, col: 0 }
};
let mv_precision = if fi.force_integer_mv != 0 {
MvSubpelPrecision::MV_SUBPEL_NONE
} else if fi.allow_high_precision_mv {
MvSubpelPrecision::MV_SUBPEL_HIGH_PRECISION
} else {
MvSubpelPrecision::MV_SUBPEL_LOW_PRECISION
};
cw.write_mv(w, &mv, &ref_mv, mv_precision);
}
} else {
cw.write_intra_mode(w, bsize, luma_mode);
}
......@@ -1267,6 +1299,9 @@ pub fn encode_block_b(fi: &FrameInvariants, fs: &mut FrameState,
};
if is_inter {
{
let ref_frame = cw.bc.at(bo).ref_frames[0];
let mv = &cw.bc.at(bo).mv[0];
// Inter mode prediction can take place once for a whole partition,
// instead of each tx-block.
let num_planes = 1 + if has_chroma(bo, bsize, xdec, ydec) { 2 } else { 0 };
......@@ -1278,11 +1313,38 @@ pub fn encode_block_b(fi: &FrameInvariants, fs: &mut FrameState,
let rec = &mut fs.rec.planes[p];
luma_mode.predict_inter(fi, p, &po, &mut rec.mut_slice(&po), plane_bsize);
// TODO: make more generic to handle 2xN and Nx2 MC
if p > 0 && bsize == BlockSize::BLOCK_4X4 {
let mv0 = &cw.bc.at(&bo.with_offset(-1,-1)).mv[0];
let mv1 = &cw.bc.at(&bo.with_offset(0,-1)).mv[0];
let po1 = PlaneOffset { x: po.x+2, y: po.y };
let mv2 = &cw.bc.at(&bo.with_offset(-1,0)).mv[0];
let po2 = PlaneOffset { x: po.x, y: po.y+2 };
let po3 = PlaneOffset { x: po.x+2, y: po.y+2 };
let some_use_intra = cw.bc.at(&bo.with_offset(-1,-1)).mode.is_intra()
|| cw.bc.at(&bo.with_offset(0,-1)).mode.is_intra()
|| cw.bc.at(&bo.with_offset(-1,0)).mode.is_intra();
if some_use_intra {
luma_mode.predict_inter(fi, p, &po, &mut rec.mut_slice(&po), plane_bsize.width(),
plane_bsize.height(), ref_frame, mv);
} else {
luma_mode.predict_inter(fi, p, &po, &mut rec.mut_slice(&po), 2, 2, ref_frame, mv0);
luma_mode.predict_inter(fi, p, &po1, &mut rec.mut_slice(&po1), 2, 2, ref_frame, mv1);
luma_mode.predict_inter(fi, p, &po2, &mut rec.mut_slice(&po2), 2, 2, ref_frame, mv2);
luma_mode.predict_inter(fi, p, &po3, &mut rec.mut_slice(&po3), 2, 2, ref_frame, mv);
}
}
else
{
luma_mode.predict_inter(fi, p, &po, &mut rec.mut_slice(&po), plane_bsize.width(),
plane_bsize.height(), ref_frame, mv);
}
}
write_tx_tree(fi, fs, cw, w, luma_mode, bo, bsize, tx_size, tx_type, skip, bit_depth); // i.e. var-tx if inter mode
}
write_tx_tree(fi, fs, cw, w, luma_mode, bo, bsize, tx_size, tx_type, skip, bit_depth); // i.e. var-tx if inter mode
} else {
write_tx_blocks(fi, fs, cw, w, luma_mode, chroma_mode, bo, bsize, tx_size, tx_type, skip, bit_depth);
write_tx_blocks(fi, fs, cw, w, luma_mode, chroma_mode, bo, bsize, tx_size, tx_type, skip, bit_depth);
}
}
......@@ -1437,6 +1499,8 @@ fn encode_partition_bottomup(seq: &Sequence, fi: &FrameInvariants, fs: &mut Fram
bo: bo.clone(),
pred_mode_luma: PredictionMode::DC_PRED,
pred_mode_chroma: PredictionMode::DC_PRED,
ref_frame: INTRA_FRAME,
mv: MotionVector { row: 0, col: 0},
skip: false
}; // Best decision that is not PARTITION_SPLIT
......@@ -1457,6 +1521,8 @@ fn encode_partition_bottomup(seq: &Sequence, fi: &FrameInvariants, fs: &mut Fram
}
let mode_decision = rdo_mode_decision(seq, fi, fs, cw, bsize, bo).part_modes[0].clone();
let (mode_luma, mode_chroma) = (mode_decision.pred_mode_luma, mode_decision.pred_mode_chroma);
let ref_frame = mode_decision.ref_frame;
let mv = mode_decision.mv;
let skip = mode_decision.skip;
let mut cdef_coded = cw.bc.cdef_coded;
rd_cost = mode_decision.rd_cost;
......@@ -1464,7 +1530,7 @@ fn encode_partition_bottomup(seq: &Sequence, fi: &FrameInvariants, fs: &mut Fram
cdef_coded = encode_block_a(seq, cw, if cdef_coded {w_post_cdef} else {w_pre_cdef},
bsize, bo, skip);
encode_block_b(fi, fs, cw, if cdef_coded {w_post_cdef} else {w_pre_cdef},
mode_luma, mode_chroma, bsize, bo, skip, seq.bit_depth);
mode_luma, mode_chroma, ref_frame, mv, bsize, bo, skip, seq.bit_depth);
best_decision = mode_decision;
}
......@@ -1509,12 +1575,14 @@ fn encode_partition_bottomup(seq: &Sequence, fi: &FrameInvariants, fs: &mut Fram
// FIXME: redundant block re-encode
let (mode_luma, mode_chroma) = (best_decision.pred_mode_luma, best_decision.pred_mode_chroma);
let ref_frame = best_decision.ref_frame;
let mv = best_decision.mv;
let skip = best_decision.skip;
let mut cdef_coded = cw.bc.cdef_coded;
cdef_coded = encode_block_a(seq, cw, if cdef_coded {w_post_cdef} else {w_pre_cdef},
bsize, bo, skip);
encode_block_b(fi, fs, cw, if cdef_coded {w_post_cdef} else {w_pre_cdef},
mode_luma, mode_chroma, bsize, bo, skip, seq.bit_depth);
mode_luma, mode_chroma, ref_frame, mv, bsize, bo, skip, seq.bit_depth);
}
}
......@@ -1586,13 +1654,15 @@ fn encode_partition_topdown(seq: &Sequence, fi: &FrameInvariants, fs: &mut Frame
let (mode_luma, mode_chroma) = (part_decision.pred_mode_luma, part_decision.pred_mode_chroma);
let skip = part_decision.skip;
let ref_frame = part_decision.ref_frame;
let mv = part_decision.mv;
let mut cdef_coded = cw.bc.cdef_coded;
// FIXME: every final block that has gone through the RDO decision process is encoded twice
cdef_coded = encode_block_a(seq, cw, if cdef_coded {w_post_cdef} else {w_pre_cdef},
bsize, bo, skip);
encode_block_b(fi, fs, cw, if cdef_coded {w_post_cdef} else {w_pre_cdef},
mode_luma, mode_chroma, bsize, bo, skip, seq.bit_depth);
mode_luma, mode_chroma, ref_frame, mv, bsize, bo, skip, seq.bit_depth);
},
PartitionType::PARTITION_SPLIT => {
if rdo_output.part_modes.len() >= 4 {
......
......@@ -29,6 +29,7 @@ pub mod rdo;
pub mod util;
pub mod cdef;
pub mod encoder;
pub mod me;
pub use encoder::*;
......
// Copyright (c) 2017-2018, The rav1e contributors. All rights reserved
//
// This source code is subject to the terms of the BSD 2 Clause License and
// the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
// was not distributed with this source code in the LICENSE file, you can
// obtain it at www.aomedia.org/license/software. If the Alliance for Open
// Media Patent License 1.0 was not distributed with this source code in the
// PATENTS file, you can obtain it at www.aomedia.org/license/patent.
use std::cmp;
use FrameInvariants;
use FrameState;
use partition::BlockSize;
use context::BlockOffset;
use partition::MotionVector;
use partition::LAST_FRAME;
use plane::PlaneOffset;
use context::BLOCK_TO_PLANE_SHIFT;
pub fn motion_estimation(fi: &FrameInvariants, fs: &mut FrameState, bsize: BlockSize,
bo: &BlockOffset, ref_frame: usize) -> MotionVector {
match fi.rec_buffer.frames[fi.ref_frames[ref_frame - LAST_FRAME]] {
Some(ref rec) => {
let po = PlaneOffset { x: bo.x << BLOCK_TO_PLANE_SHIFT, y: bo.y << BLOCK_TO_PLANE_SHIFT };
let range = 16 as usize;
let blk_w = bsize.width();
let blk_h = bsize.height();
let x_lo = cmp::max(0, po.x as isize - range as isize) as usize;
let x_hi = cmp::min(fs.input.planes[0].cfg.width - blk_w, po.x + range);
let y_lo = cmp::max(0, po.y as isize - range as isize) as usize;
let y_hi = cmp::min(fs.input.planes[0].cfg.height - blk_h, po.y + range);
let sorg = fs.input.planes[0].slice(&po);
let slice_org = sorg.as_slice();
let stride_org = fs.input.planes[0].cfg.stride;
let stride_ref = rec.planes[0].cfg.stride;
let mut lowest_sad = 128*128*4096 as usize;
let mut best_mv = MotionVector { row: 0, col: 0 };
for y in (y_lo..y_hi).step_by(2) {
for x in (x_lo..x_hi).step_by(2) {
let mut sad = 0;
let sref = rec.planes[0].slice(&PlaneOffset { x: x, y: y });
let slice_ref = sref.as_slice();
for r in 0..blk_h {
for c in 0..blk_w {
let org_index = r * stride_org + c;
let ref_index = r * stride_ref + c;
let a = slice_org[org_index];
let b = slice_ref[ref_index];
let delta = b as isize - a as isize;
sad += delta.abs() as usize;
}
}
if sad < lowest_sad {
lowest_sad = sad;
best_mv = MotionVector { row: 8*(y as i16 - po.y as i16), col: 8*(x as i16 - po.x as i16) }
}
}
}
best_mv
}
None => MotionVector { row: 0, col : 0 }
}
}
......@@ -10,6 +10,7 @@
#![allow(non_camel_case_types)]
#![allow(dead_code)]
use std::cmp;
use self::BlockSize::*;
use self::TxSize::*;
use encoder::FrameInvariants;
......@@ -348,6 +349,12 @@ pub enum PredictionMode {
NEW_NEWMV
}
#[derive(Copy, Clone)]
pub struct MotionVector {
pub row: i16,
pub col: i16,
}
pub const NEWMV_MODE_CONTEXTS: usize = 7;
pub const GLOBALMV_MODE_CONTEXTS: usize = 2;
pub const REFMV_MODE_CONTEXTS: usize = 9;
......@@ -529,24 +536,30 @@ impl PredictionMode {
}
pub fn predict_inter<'a>(self, fi: &FrameInvariants, p: usize, po: &PlaneOffset,
dst: &'a mut PlaneMutSlice<'a>, plane_size: BlockSize) {
dst: &'a mut PlaneMutSlice<'a>, width: usize, height: usize,
ref_frame: usize, mv: &MotionVector) {
assert!(!self.is_intra());
assert!(self == PredictionMode::GLOBALMV); // Other modes not implemented
let ref_frame_idx = LAST_FRAME;
assert!(ref_frame == LAST_FRAME);
match fi.rec_buffer.frames[fi.ref_frames[ref_frame_idx - LAST_FRAME]] {
match fi.rec_buffer.frames[fi.ref_frames[ref_frame - LAST_FRAME]] {
Some(ref rec) => {
let ref_stride = rec.planes[p].cfg.stride;
let src = rec.planes[p].slice(po);
let ref_slice = src.as_slice();
let rec_cfg = &rec.planes[p].cfg;
let shift_row = 3 + rec_cfg.ydec;
let shift_col = 3 + rec_cfg.xdec;
let row_offset = mv.row as i32 >> shift_row;
let col_offset = mv.col as i32 >> shift_col;
let ref_width = rec_cfg.width;
let ref_height = rec_cfg.height;
let stride = dst.plane.cfg.stride;
let slice = dst.as_mut_slice();
for r in 0..plane_size.height() {
for c in 0..plane_size.width() {
let input_index = r * ref_stride + c;
for r in 0..height {
for c in 0..width {
let rs = cmp::min(ref_height as i32 - 1, cmp::max(0, po.y as i32 + row_offset + r as i32)) as usize;
let cs = cmp::min(ref_width as i32 - 1, cmp::max(0, po.x as i32 + col_offset + c as i32)) as usize;
let output_index = r * stride + c;
slice[output_index] = ref_slice[input_index];
slice[output_index] = rec.planes[p].p(cs, rs);
}
}
},
......
......@@ -38,6 +38,7 @@ pub static RAV1E_INTRA_MODES_MINIMAL: &'static [PredictionMode] = &[
pub static RAV1E_INTER_MODES: &'static [PredictionMode] = &[
PredictionMode::GLOBALMV,
PredictionMode::NEWMV,
];
// Weights are quadratic from '1' to '1 / block_size', scaled by 2^sm_weight_log2_scale.
......
......@@ -12,6 +12,7 @@
#![cfg_attr(feature = "cargo-clippy", allow(cast_lossless))]
use context::*;
use me::*;
use ec::OD_BITRES;
use ec::Writer;
use ec::WriterCounter;
......@@ -48,6 +49,8 @@ pub struct RDOPartitionOutput {
pub bo: BlockOffset,
pub pred_mode_luma: PredictionMode,
pub pred_mode_chroma: PredictionMode,
pub ref_frame: usize,
pub mv: MotionVector,
pub skip: bool
}
......@@ -189,6 +192,8 @@ pub fn rdo_mode_decision(
let mut best_mode_chroma = PredictionMode::DC_PRED;
let mut best_skip = false;
let mut best_rd = std::f64::MAX;
let mut best_ref_frame = INTRA_FRAME;
let mut best_mv = MotionVector { row: 0, col: 0 };
// Get block luma and chroma dimensions
let w = bsize.width();
......@@ -223,6 +228,13 @@ pub fn rdo_mode_decision(
mode_set_chroma.push(PredictionMode::DC_PRED);
}
let ref_frame = if luma_mode.is_intra() { INTRA_FRAME } else { LAST_FRAME };
let mv = if luma_mode != PredictionMode::NEWMV {
MotionVector { row: 0, col: 0 }
} else {
motion_estimation(fi, fs, bsize, bo, ref_frame)
};
// Find the best chroma prediction mode for the current luma prediction mode
for &chroma_mode in &mode_set_chroma {
for &skip in &[false, true] {
......@@ -232,8 +244,9 @@ pub fn rdo_mode_decision(
let mut wr: &mut dyn Writer = &mut WriterCounter::new();
let tell = wr.tell_frac();
encode_block_a(seq, cw, wr, bsize, bo, skip);
encode_block_b(fi, fs, cw, wr, luma_mode, chroma_mode, bsize, bo, skip, seq.bit_depth);
encode_block_b(fi, fs, cw, wr, luma_mode, chroma_mode, ref_frame, mv, bsize, bo, skip, seq.bit_depth);
let cost = wr.tell_frac() - tell;
let rd = compute_rd_cost(
......@@ -251,6 +264,8 @@ pub fn rdo_mode_decision(
best_rd = rd;
best_mode_luma = luma_mode;
best_mode_chroma = chroma_mode;
best_ref_frame = ref_frame;
best_mv = mv;
best_skip = skip;
}
......@@ -270,6 +285,8 @@ pub fn rdo_mode_decision(
bo: bo.clone(),
pred_mode_luma: best_mode_luma,
pred_mode_chroma: best_mode_chroma,
ref_frame: best_ref_frame,
mv: best_mv,
rd_cost: best_rd,
skip: best_skip
}]
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment