FFmpeg: CRT Screen Effect


A simple attempt at creating a [stylised] 'CRT screen' effect with FFmpeg. Loaded with the common CRT effect tropes and clich├ęs; interlaced lines, noise, chromatic aberration, bloom etc.

The filterchains were constructed to be modular; allowing them to be included or removed, as desired. The ideas included in these filterchains might be of more use in general, than the whole effect itself.

#!/bin/bash

# A collection of FFmpeg filterchains which can be used to create a stylised
# 'CRT screen' effect on given input.
#
# The filter-chains have been split apart to increase modularity at the cost of
# sacrificing simplicity and increasing redundant code. Filter-chains can be
# added or removed in various orders, but special attention must be paid to
# selecting the correct termination syntax for each stage.
#
# Includes basic demonstration FFmpeg command which takes "$1" input file.
#
# Version: 2019.04.06_02.49.13
# Source https://oioiiooixiii.blogspot.com

### FILTERCHAINS #############################################################

# Reduce input to 25% PAL resolution
shrink144="scale=-2:144"

# Crop to 4:3 aspect ratio at 25% PAL resolution
crop43="crop=180:144"

# Create RGB chromatic aberration
rgbFX="split=3[red][green][blue];
      [red] lutrgb=g=0:b=0,
            scale=188x144,
            crop=180:144 [red];
      [green] lutrgb=r=0:b=0,
              scale=184x144,
              crop=180:144 [green];
      [blue] lutrgb=r=0:g=0,
             scale=180x144,
             crop=180:144 [blue];
      [red][blue] blend=all_mode='addition' [rb];
      [rb][green] blend=all_mode='addition',
                  format=gbrp"

# Create YUV chromatic aberration
yuvFX="split=3[y][u][v];
      [y] lutyuv=u=0:v=0,
          scale=192x144,
          crop=180:144 [y];
      [u] lutyuv=v=0:y=0,
          scale=188x144,
          crop=180:144 [u];
      [v] lutyuv=u=0:y=0,
          scale=180x144,
          crop=180:144 [v];
      [y][v] blend=all_mode='lighten' [yv];
      [yv][u] blend=all_mode='lighten'"

# Create edge contour effect
edgeFX="edgedetect=mode=colormix:high=0"

# Add noise to each frame of input
noiseFX="noise=c0s=7:allf=t"

# Add interlaced fields effect to input
interlaceFX="split[a][b];
             [a] curves=darker [a];
             [a][b] blend=all_expr='if(eq(0,mod(Y,2)),A,B)':shortest=1"

# Re-scale input to full PAL resolution
scale2PAL="scale=720:576"

# Re-scale input to full PAL resolution with linear pixel
scale2PALpix="scale=720:576:flags=neighbor"

# Add magnetic damage effect to input [crt screen]
screenGauss="[base];
             nullsrc=size=720x576,
                drawtext=
                   fontfile=/usr/share/fonts/truetype/freefont/FreeSerif.ttf:
                   text='@':
                   x=600:
                   y=30:
                   fontsize=170:
                   fontcolor=red@1.0,
             boxblur=80 [gauss];
             [gauss][base] blend=all_mode=screen:shortest=1"

# Add reflections to input [crt screen]
reflections="[base];
             nullsrc=size=720x576,
             format=gbrp,
             drawtext=
               fontfile=/usr/share/fonts/truetype/freefont/FreeSerif.ttf:
               text='€':
               x=50:
               y=50:
               fontsize=150:
               fontcolor=white,
             drawtext=
               fontfile=/usr/share/fonts/truetype/freefont/FreeSerif.ttf:
               text='J':
               x=600:
               y=460:
               fontsize=120:
               fontcolor=white,
             boxblur=25 [lights];
             [lights][base] blend=all_mode=screen:shortest=1"

# Add more detailed highlight to input [crt screen]
highlight="[base];
             nullsrc=size=720x576,
             format=gbrp,
             drawtext=
               fontfile=/usr/share/fonts/truetype/freefont/FreeSerif.ttf:
               text='¡':
               x=80:
               y=60:
               fontsize=90:
               fontcolor=white,
             boxblur=7 [lights];
             [lights][base] blend=all_mode=screen:shortest=1"

# Curve input to mimic curve of crt screen
curveImage="vignette,
            format=gbrp,
            lenscorrection=k1=0.2:k2=0.2"

# Add bloom effect to input [crt screen]
bloomEffect="split [a][b];
             [b] boxblur=26,
                    format=gbrp [b];
             [b][a] blend=all_mode=screen:shortest=1"

### FFMPEG COMMAND ###########################################################

ffmpeg \
   -i "$1" \
   -vf "
         ${shrink144},
         ${crop43},
         ${rgbFX},
         ${yuvFX},
         ${noiseFX},
         ${interlaceFX},
         ${scale2PAL}
         ${screenGauss}
         ${reflections}
         ${highlight},
         ${curveImage},
         ${bloomEffect}
      " \
   "${1}__crtTV.mkv"

exit 0
download script: ffmpeg_CRT-effect.sh

A bank of 'screens' displaying different inputs.



Some alternate choices of filterchains.



source video: https://www.youtube.com/watch?v=8SPUHGRXQUY