Average motion vector tutorial
The previous post gave a simple tutorial on how to glitch motion vectors. This post is a follow-up, with an average tutorial on how to glitch motion vectors by using an average of motion vectors.
The test video which I’ll use is one of my cat, Trompette the Ungrateful, eating her food which I placed somewhere that is not its usual place, just so that I could try to get a good shot of her eating.
As usual, we generate an MPEG-2 input file using ffgac
, the modified
build of ffmpeg
that comes with ffglitch
, with the following command:
$ ffgac -i trompette_eating.mp4 -an -mpv_flags +nopimb+forcemv -qscale:v 0 -g max -sc_threshold max -vcodec mpeg2video -f rawvideo -y trompette_eating.mpg
The script file we will use this time is a little more elaborate than
the previous one. It uses global variables and calls another function.
Save it as mv_average.js
:
// global variable holding forward motion vectors from previous frames
var prev_fwd_mvs = [ ];
// change this value to use a smaller or greater number of frames to
// perform the average of motion vectors
var tail_length = 10;
// calculate average of previous motion vectors
function average_mv(mv, i, j, n, k)
{
let sum = 0;
for ( let t = 0; t < n; t++ )
sum += prev_fwd_mvs[t][i][j][k];
let val = Math.round(sum / n);
val = Math.max(val, -64);
val = Math.min(val, 63);
return val;
}
function glitch_frame(frame)
{
// bail out if we have no motion vectors
let mvs = frame["mv"];
if ( !mvs )
return;
// bail out if we have no forward motion vectors
let fwd_mvs = mvs["forward"];
if ( !fwd_mvs )
return;
// update variable holding forward motion vectors from previous
// frames. note that we perform a deep copy of the clean motion
// vector values before modifying them.
let json_str = JSON.stringify(fwd_mvs);
let deep_copy = JSON.parse(json_str);
// push to the end of array
prev_fwd_mvs.push(deep_copy);
// drop values from earliest frames to always keep the same tail
// length
if ( prev_fwd_mvs.length > tail_length )
prev_fwd_mvs = prev_fwd_mvs.slice(1);
// bail out if we still don't have enough frames
if ( prev_fwd_mvs.length != tail_length )
return;
// replace all motion vectors of current frame with an average
// of the motion vectors from the previous 10 frames
for ( let i = 0; i < fwd_mvs.length; i++ )
{
// loop through all rows
let row = fwd_mvs[i];
for ( let j = 0; j < row.length; j++ )
{
// loop through all macroblocks
let mv = row[j];
// THIS IS WHERE THE MAGIC HAPPENS
mv[0] = average_mv(mv, i, j, tail_length, 0);
mv[1] = average_mv(mv, i, j, tail_length, 1);
}
}
}
Now run ffedit
with mv_average.js
to glitch the file:
$ ./ffedit -i trompette_eating.mpg -f mv -s mv_average.js -o trompette_eating_glitched.mpg
And this is the resulting trompette_eating_glitched.mpg
: