0

I want to add a film grain effect using FFMPEG if possible.

Taking a nice clean computer rendered scene and filter for a gritty black and white 16mm film look. As an example something like Clerks https://www.youtube.com/watch?v=Mlfn5n-E2WE

According to Simulating TV noise Ishould be able to use the following filter

-filter_complex "geq=random(1)*255:128:128;aevalsrc=-2+random(0)"

but when I add it to my ffmpeg command

ffmpeg.exe -framerate 30 -i XYZ%05d.PNG -vf format=yuv420p -dst_range 1 -color_range 2 -c:v libxvid -vtag xvid -q:v 1 -y OUTPUT.AVI

so the command is now

ffmpeg.exe -framerate 30 -i XYZ%05d.PNG -vf format=yuv420p -dst_range 1 -color_range 2 -c:v libxvid -vtag xvid -q:v 1 -y -filter_complex "geq=random(1)*255:128:128;aevalsrc=-2+random(0)" OUTPUT.AVI

I get the message

Filtergraph 'format=yuv420p' was specified through the -vf/-af/-filter option for output stream 0:0, which is fed from a complex filtergraph. -vf/-af/-filter and -filter_complex cannot be used together for the same stream.

How can I change my ffmpeg command line so the grain filter works? Additionally, can I add a slight blur too? The old 16mm looks more like blurred then grainy.

Thanks for any tips.

Some1Else
  • 715
  • 11
  • 26

2 Answers2

6

I just needed to make a film grain and wanted something "neater" than just randomizing every pixel. Here's what I came up with: FFmpeg film grain.

It starts with white noise:

white noise

Then it uses the "deflate" and "dilation" filters to cause certain features to expand out to multiple pixels:

made clumpy with deflate and dilation

The effect is pretty subtle but you can see that there are a few larger "blobs" of white and black in amongst the noise. This means that the features of the noise aren't just straight-up single pixels any more. Then, that image gets halved in resolution, because it was being rendered at twice the resolution of the target video.

scaled down

The highest-resolution detail is now softened, and the clumps of pixels are reduced in size to be 1-2 pixels in size. So, this is the noise plane.

Then, I take the source video and do some processing on it.

original frame

Desaturate:

desaturated

Filter luminance so that the closer an input pixel was to luminance level 75 (arrived at experimentally), the brighter the pixel is. If the input pixel was darker or brighter, the output pixel is uniformly darker. This creates "bands" of brightness where the luminance level is close to 75.

luminance band filter

This is then scaled down, and this is where the level of noise is "tuned". This band selection means that we will be adding noise specifically in the areas of the frame where it will be most noticed. Not adding noise in other areas leaves more bits to encode the noise.

luminance band filter, scaled

This scaled mask is then applied to the previously-computed noise. In this screenshot, I've removed the tuning so that the noise is easily visible:

masked noise, unscaled

The areas not selected by the band filter are greatly scaled down and are essentially black; the noise variation fades to nothing.

Here's what it looks like with a scaling factor of 0.32 -- pretty subtle:

masked noise, scaled

I then invert this image, so that the parts with no noise are solid white, and then areas with noise pull down slightly from the white:

film grain alpha channel

Finally, I pull another copy of the same source video, apply this computed image to it as an alpha channel and overlay it on black, so that the film grain dots, which are slightly less white, become slightly darker pixels.

final image

The effect is pretty subtle, hard to see in a still like that when it's not moving, but if you tune the noise way up, you can get frames like this:

final image, exaggerated

MarianD
  • 13,096
  • 12
  • 42
  • 54
Jonathan Gilbert
  • 3,526
  • 20
  • 28
5

The filters "geq=random(1)*255:128:128;aevalsrc=-2+random(0)" is for white noise

For "a gritty black and white 16mm film look", you want something like instead,

-vf hue=s=0,boxblur=lr=1.2,noise=c0s=7:allf=t

The format you specified is a filter, and all filters applied on an input should be specified in a single chain, so it should be,

-vf hue=s=0,boxblur=lr=1.2,noise=c0s=7:allf=t,format=yuv420p

See filter docs at https://ffmpeg.org/ffmpeg-filters.html for descriptions and list of parameters you can tweak.

Gyan
  • 85,394
  • 9
  • 169
  • 201