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: