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

<pre><code>geq=r='if(between(st(1,r(X,Y)+g(X,Y)+b(X,Y)),15,750),p(X,Y),if(lt(ld(1),15),C1R,C2R))':
    g='if(between(st(1,r(X,Y)+g(X,Y)+b(X,Y)),15,750),p(X,Y),if(lt(ld(1),15),C1G,C2G))':
    b='if(between(st(1,r(X,Y)+g(X,Y)+b(X,Y)),15,750),p(X,Y),if(lt(ld(1),15),C1B,C2B))'
</code></pre>

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

<p>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.</p>

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

<p>You can also try the <a href="https://ffmpeg.org/ffmpeg-filters.html#colorkey" rel="nofollow noreferrer">colorkey</a> filter.</p>

CC BY-SA 4.0
2018-06-14T17:25:28.420Z
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
CC BY-SA 4.0
2018-06-14T17:46:06.520Z
Color look up functions should be lower-case. Edited.
Gyan
CC BY-SA 4.0
2018-06-14T22:16:11.547Z
Thanks, now getting a `Either YCbCr or RGB but not both must be specified` error.
user255406
CC BY-SA 4.0
2018-06-15T05:34:31.033Z
Share your full command
Gyan
CC BY-SA 4.0
2018-06-16T01:15:55.083Z
`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
CC BY-SA 4.0
2018-06-16T05:05:20.847Z
Ah, my answer started with `Assuming your input is full range RGB when fed to the filter`. Seems it's not. So, add `format=rgb24` before geq. That error msg needs to change.
Gyan
CC BY-SA 4.0
2018-06-17T03:16:25.093Z
now back to error `Missing ')' or too many args in 'st(1'`
user255406
CC BY-SA 4.0
2018-06-17T05:28:03.103Z
For your shell, looks like you need to the escape the commas in the exprs. So e.g.  `st(1,r(X,Y)` --> `st(1\,r(X\,Y)`
Gyan
CC BY-SA 4.0
2018-06-18T02:34:37.453Z
Ah.  Got it, just added quotes  to the -vf command and it works now: `ffmpeg -i in.mp4 -vf "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
CC BY-SA 4.0
2018-06-14T16:58:46.343Z
2018-06-14T17:45:50.690Z
2018-06-14T17:45:50.690Z
Gyan
<p>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: </p>

<pre><code>lutrgb=r='if(eq(val,0),0,val)':g='if(eq(val,0),153,val)':b='if(eq(val,0),254,val)',lutrgb=r='if(eq(val,255),254,val)':g='if(eq(val,255),102,val)':b='if(eq(val,255),0,val)'
</code></pre>

<p>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:</p>

<p><a href="https://i.sstatic.net/tw8VC.jpg" rel="nofollow noreferrer"><img src="https://i.sstatic.net/tw8VC.jpg" alt="inconsistent zebras"></a></p>

<p>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:</p>

<pre><code>lutrgb=r='if(eq(maxval,0),0,val)':g='if(eq(maxval,0),153,val)':b='if(eq(maxval,0),254,val)',lutrgb=r='if(eq(minval,255),254,val)':g='if(eq(minval,255),102,val)':b='if(eq(minval,255),0,val)'
</code></pre>

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

CC BY-SA 4.0
2018-06-14T14:21:53.237Z
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).
Michael Tiemann
CC BY-SA 4.0
2018-06-14T14:27:09.313Z
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
CC BY-SA 4.0
2018-06-14T14:29:38.590Z
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
CC BY-SA 4.0
2018-06-14T14:40:44.870Z
a sample geq would be great, thanks for the input.
user255406
CC BY-SA 4.0
2018-06-14T15:04:24.750Z
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
CC BY-SA 4.0
2018-06-14T16:22:45.023Z
@Gyan I'd love to see a sample invocation using geq if you have time.
user255406
<ffmpeg><lut>
ffmpeg lutrgb to create zebras
user255406
# ffmpeg lutrgb to create zebras

(Asked by: username_0 on 2018-06-14)

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: 
lutrgb=r='if(eq(val,0),0,val)':g='if(eq(val,0),153,val)':b='if(eq(val,0),254,val)',lutrgb=r='if(eq(val,255),254,val)':g='if(eq(val,255),102,val)':b='if(eq(val,255),0,val)'

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:

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:
lutrgb=r='if(eq(maxval,0),0,val)':g='if(eq(maxval,0),153,val)':b='if(eq(maxval,0),254,val)',lutrgb=r='if(eq(minval,255),254,val)':g='if(eq(minval,255),102,val)':b='if(eq(minval,255),0,val)'

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

**Tags:** ffmpeg, lut

> **Comments:**
> **username_1**: 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).  
> **username_2**: 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.  
> **username_2**: 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.  
> **username_0**: a sample geq would be great, thanks for the input.  
> **username_0**: 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 =(  
> **username_0**: @username_2 I'd love to see a sample invocation using geq if you have time.  

---

## 1 answers

### Answer by username_2 on 2018-06-14. Score: 4 | Accepted answer

Assuming your input is full range RGB when fed to the filter, then something like
geq=r='if(between(st(1,r(X,Y)+g(X,Y)+b(X,Y)),15,750),p(X,Y),if(lt(ld(1),15),C1R,C2R))':
    g='if(between(st(1,r(X,Y)+g(X,Y)+b(X,Y)),15,750),p(X,Y),if(lt(ld(1),15),C1G,C2G))':
    b='if(between(st(1,r(X,Y)+g(X,Y)+b(X,Y)),15,750),p(X,Y),if(lt(ld(1),15),C1B,C2B))'

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.

> **Comments:**
> **username_0**: 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.  
> **username_2**: Color look up functions should be lower-case. Edited.  
> **username_0**: Thanks, now getting a `Either YCbCr or RGB but not both must be specified` error.  
> **username_2**: Share your full command  
> **username_0**: `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`  
> **username_2**: Ah, my answer started with `Assuming your input is full range RGB when fed to the filter`. Seems it's not. So, add `format=rgb24` before geq. That error msg needs to change.  
> **username_0**: now back to error `Missing ')' or too many args in 'st(1'`  
> **username_2**: For your shell, looks like you need to the escape the commas in the exprs. So e.g.  `st(1,r(X,Y)` --> `st(1\,r(X\,Y)`  
> **username_0**: Ah.  Got it, just added quotes  to the -vf command and it works now: `ffmpeg -i in.mp4 -vf "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`  

---

