Highlight Video Differences Using FFmpeg lut2

This article explains how to visual inspect and highlight pixel-level differences between two similar video files using FFmpeg's powerful lut2 filter. You will learn the mechanics behind the dual-input lookup table, the command-line syntax required to compute differences, and techniques to amplify subtle compression artifacts or color shifts so they become clearly visible.


Understanding the lut2 Filter

The lut2 filter takes two video inputs of the exact same dimensions and pixel format, evaluates an expression for each corresponding pixel component, and outputs a single composite video.

In lut2 expressions:

  • x represents the pixel value from the first input (stream 0).
  • y represents the pixel value from the second input (stream 1).
  • c0, c1, c2, and c3 correspond to each plane of the video format (e.g., Y, U, V in YUV, or R, G, B in RGB).

To highlight differences, you compute the absolute difference between x and y using the mathematical function abs(x-y).


Basic Difference Command (RGB Space)

When comparing two videos in the RGB color space, calculate the absolute difference across all three color channels:

ffmpeg -i original.mp4 -i encoded.mp4 -filter_complex \
"[0:v]format=rgb24[v0]; \
 [1:v]format=rgb24[v1]; \
 [v0][v1]lut2=c0='abs(x-y)':c1='abs(x-y)':c2='abs(x-y)'" \
-c:v libx264 -crf 18 diff.mp4

In this output:

  • Identical pixels result in 0 (completely black).
  • Differing pixels appear brighter depending on the magnitude of the discrepancy.

Amplifying Subtle Differences

Compression artifacts or minor color grades often result in differences of only 1 to 5 values per pixel, which are almost invisible to the human eye on a standard monitor. To make these variations immediately apparent, multiply the difference by an amplification factor (for example, 10 or 20):

ffmpeg -i original.mp4 -i encoded.mp4 -filter_complex \
"[0:v]format=rgb24[v0]; \
 [1:v]format=rgb24[v1]; \
 [v0][v1]lut2=c0='clip(abs(x-y)*10,0,255)':c1='clip(abs(x-y)*10,0,255)':c2='clip(abs(x-y)*10,0,255)'" \
-c:v libx264 -crf 18 amplified_diff.mp4

The clip(val, min, max) function prevents values from overflowing beyond standard 8-bit limits (0 to 255).


Highlighting Differences in YUV Space

If you want to keep the process in standard YUV video format (such as yuv420p), evaluate luminance (Y) and chrominance (U/V) independently.

For standard 8-bit YUV:

  • Luma (Y / c0) ranges from 0 to 255.
  • Chroma (U / c1, V / c2) centers at 128 (neutral/gray).

To show luma differences on a neutral gray canvas:

ffmpeg -i original.mp4 -i encoded.mp4 -filter_complex \
"[0:v][1:v]lut2=c0='clip(abs(x-y)*10,0,255)':c1='128':c2='128'" \
-c:v libx264 -crf 18 yuv_diff.mp4

Binary Masking (Thresholding)

If you only want to know whether a difference exists regardless of magnitude, apply a threshold condition:

ffmpeg -i original.mp4 -i encoded.mp4 -filter_complex \
"[0:v]format=rgb24[v0]; \
 [1:v]format=rgb24[v1]; \
 [v0][v1]lut2=c0='if(gt(abs(x-y),2),255,0)':c1='if(gt(abs(x-y),2),255,0)':c2='if(gt(abs(x-y),2),255,0)'" \
-c:v libx264 -crf 18 mask_diff.mp4

This expression evaluates whether the absolute difference between x and y is greater than 2. If true, it outputs full white (255); otherwise, it outputs full black (0).