Answers.Body: <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>
  
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2018-06-14T17:25:28.420Z
Answers.Text: 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.
Answers.UserDisplayName: user255406
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2018-06-14T17:46:06.520Z
Answers.Text: Color look up functions should be lower-case. Edited.
Answers.UserDisplayName: Gyan
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2018-06-14T22:16:11.547Z
Answers.Text: Thanks, now getting a `Either YCbCr or RGB but not both must be specified` error.
Answers.UserDisplayName: user255406
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2018-06-15T05:34:31.033Z
Answers.Text: Share your full command
Answers.UserDisplayName: Gyan
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2018-06-16T01:15:55.083Z
Answers.Text: `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`
Answers.UserDisplayName: user255406
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2018-06-16T05:05:20.847Z
Answers.Text: 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.
Answers.UserDisplayName: Gyan
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2018-06-17T03:16:25.093Z
Answers.Text: now back to error `Missing ')' or too many args in 'st(1'`
Answers.UserDisplayName: user255406
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2018-06-17T05:28:03.103Z
Answers.Text: 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)`
Answers.UserDisplayName: Gyan
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2018-06-18T02:34:37.453Z
Answers.Text: 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`
Answers.UserDisplayName: user255406
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2018-06-14T16:58:46.343Z
Answers.LastActivityDate: 2018-06-14T17:45:50.690Z
Answers.LastEditDate: 2018-06-14T17:45:50.690Z
Answers.UserDisplayName: Gyan
Body: <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>
  
Comments.ContentLicense: CC BY-SA 4.0
Comments.CreationDate: 2018-06-14T14:21:53.237Z
Comments.Text: 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).
Comments.UserDisplayName: Michael Tiemann
Comments.ContentLicense: CC BY-SA 4.0
Comments.CreationDate: 2018-06-14T14:27:09.313Z
Comments.Text: 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.
Comments.UserDisplayName: Gyan
Comments.ContentLicense: CC BY-SA 4.0
Comments.CreationDate: 2018-06-14T14:29:38.590Z
Comments.Text: 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.
Comments.UserDisplayName: Gyan
Comments.ContentLicense: CC BY-SA 4.0
Comments.CreationDate: 2018-06-14T14:40:44.870Z
Comments.Text: a sample geq would be great, thanks for the input.
Comments.UserDisplayName: user255406
Comments.ContentLicense: CC BY-SA 4.0
Comments.CreationDate: 2018-06-14T15:04:24.750Z
Comments.Text: 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 =(
Comments.UserDisplayName: user255406
Comments.ContentLicense: CC BY-SA 4.0
Comments.CreationDate: 2018-06-14T16:22:45.023Z
Comments.Text: @Gyan I'd love to see a sample invocation using geq if you have time.
Comments.UserDisplayName: user255406
Tags: <ffmpeg><lut>
Title: ffmpeg lutrgb to create zebras
UserDisplayName: user255406
ThreadText: # 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:
  
  [![inconsistent zebras](https://i.sstatic.net/tw8VC.jpg)](https://i.sstatic.net/tw8VC.jpg)
  
  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](https://ffmpeg.org/ffmpeg-filters.html#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`  
  
  ---
  
  