I'm trying to display zebras (colorize totally black pixels one color and totally white pixels a second color) on a selected frame from a video using ffmpeg. Using an rgb lookup table expression I'm getting a workable solution:


The problem with this expression is that it's testing each pixel for clipping by color, then modding only if that color component = 0 or 255. My video is largely black and white, but a slight pixel level color shift results in a two toned zebra pattern. You can see the two shades of blue in this sample zebra frame:

inconsistent zebras

If I could test for the maxval for clipping into 0 and minval for 255 I would think this problem would be solved, but this code below doesn't work:


The if/then code for filters is poorly documented on the ffmpeg wiki. Any LUT experts out there?

  • My guess is that minval and maxval are set for the given codec, not the minimum and maximum values of a given RGB triple. I would suggest using 'ge' (greater or equal) and 'le' (lesser or equal) instead of 'eq' (equal) and let the inequalities rule whether you deliver black or white (or interpret black or white to a given color). Commented Jun 14, 2018 at 14:21
  • maxval and minval describe the storage ranges for the pixel format and color space. i.e. 0-255 or 16-235/240 for RGB and YUV respectively.
    – Gyan
    Commented Jun 14, 2018 at 14:27
  • The lut filter only allows one-to-one mapping i.e. r expr can only reference input r values. But a pixel can have 0 component without being black i.e. (0,25,44). You need to use the geq filter, which allows cross-referencing. I'll provide a sample invocation if you wish.
    – Gyan
    Commented Jun 14, 2018 at 14:29
  • a sample geq would be great, thanks for the input.
    – user255406
    Commented Jun 14, 2018 at 14:40
  • FWIW, I think a simple solution to this problem is to just test and modify the blue for black clipping and test/change the red for white clipping: lutrgb=r='if(eq(val,0),0,val)':g='if(eq(val,0),0,val)':b='if(eq(val,0),254,val)',lutrgb=r='if(eq(val,255),254,val)':g='if(eq(val,255),0,val)':b='if(eq(val,255),0,val)' ...but no customizable zebra colors =(
    – user255406
    Commented Jun 14, 2018 at 15:04

1 Answer 1


Assuming your input is full range RGB when fed to the filter, then something like


Here the two colors are (C1R,C1G,C1B) and (C2R,C2G,C2B). with each component range being 0-255.

The 15 and 750 are the thresholds for selecting black and white pixels. The sum of the values of the three components for each pixel is compared to those values.

The st and ld functions are store and load functions with the first argument being the 'register no.'.

You can also try the colorkey filter.

  • strange, I'm getting a Missing ')' or too many args in 'st(1' error, even though I only see 2 arguments and each parenthesis is appropriately closed.
    – user255406
    Commented Jun 14, 2018 at 17:25
  • Color look up functions should be lower-case. Edited.
    – Gyan
    Commented Jun 14, 2018 at 17:46
  • Thanks, now getting a Either YCbCr or RGB but not both must be specified error.
    – user255406
    Commented Jun 14, 2018 at 22:16
  • Share your full command
    – Gyan
    Commented Jun 15, 2018 at 5:34
  • ffmpeg -i in.mp4 geq=r='if(between(st(1,r(X,Y)+g(X,Y)+b(X,Y)),10,755),p(X,Y),if(lt(ld(1),10),0,254))':g='if(between(st(1,r(X,Y)+g(X,Y)+b(X,Y)),10,755),p(X,Y),if(lt(ld(1),10),153,102))':b='if(between(st(1,r(X,Y)+g(X,Y)+b(X,Y)),10,755),p(X,Y),if(lt(ld(1),10),254,0))' out.jpg
    – user255406
    Commented Jun 16, 2018 at 1:15

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.