Dealing with NaN pixels

Written by Michael Garrett on .

Here's an approach to dealing with the occasional NaN pixel that can for example crop up when outputting through the ScanlineRender node.

So what does it look like and why is it bad?  On the left, the pixel sampler has selected a single NaN pixel being output through the ScanlineRender node and you can see that the information being displayed for that pixel is "nan" in each channel.  And on the right is with VectorBlur added:

nan01nan01

As you can see any kind of filtering will turn up as nasty blocky artefacts (in this case blocky motion blur) and suddenly the effect of the NaN pixel is much more apparent.  Luckily Nuke has some handy expressions for dealing with them in an elegant way:

1
isnan(r)

The Expression node shown on the right is using the expression isnan(r), adjusted accordingly for rgba channels, to show us the NaN pixels:

nan03nan04

 

Now that the pixel is isolated, we need to do that in the context of the original image.  By using a conditional statement we can say that if there is a NaN pixel then make it black, otherwise keep the existing pixel value for that channel:

1
isnan(r)?0:r

The conditional takes the form of if?then:else so in this case we are saying "if the pixel is a NaN then make it black, or else just leave it at its current value".

nan05nan06

 

So now we get our image back and the problematic NaN is neutralised to a black value which is not going to blow out when filtered.  But ideally we want to procedurally clone the colour from an adjacent pixel.  We can use an expression to tell a pixel to grab its colour from a given xy coordinate offset like this:

1
r(x+1, y)

This will pull the pixel in the red channel from the current xy position but with an offset of +1 in x.  Once that's wrapped into our existing conditional it looks like this:

1
isnan(r)?r(x+1,y):r

nan07nan08

 

To abstract the expressions a bit for the sake of usability we can create x and y offset knobs (xo and yo) as shown in the image on the left and then modify the expressions to take advantage of that (as shown in the image on the right):

1
isnan(r)?r(x+xo,y+yo):r

nan09nan10

Comments   

 
0 # Doug Hogan 2010-07-25 11:15
This is a really great technique man, and a very straight forward solution to a pesky studio problem. Thanks for writing it up!
 
 
0 # Michael Garrett 2010-07-26 12:26
No worries!
 
 
0 # Doug Hogan 2010-08-02 09:47
Hey Michael,

I tried using your isNaN expression this weekend, but it didn't isolate the pixels that were nan. Instead it just gave me back a black image. My image wasn't clamped and the nan pixels were floating well above the 1 rgba pixel value, around 22 in fact. And of course I'm using Nuke 6.0v3. I've double checked my expressions and they're identical to the ones you used in your tutorial. So do you have any ideas? What am I missing here?

Thanks,
Doug
 
 
0 # Michael Garrett 2010-08-02 12:27
Hi Doug, did you try all lower case?, ie. isnan(r)
Also, if the pixels have a value of 22 then they aren't nan.
 
 
0 # Doug Hogan 2010-08-02 13:33
Yep, I tried all lower case. But still no dice. And as far at the +22 pixel value, you'll have to excuse my typo. I meant to write -22. Sorry for the confusion. Any ideas though? I'll keep playing around and see if I can get a similar result as in your tutorial.

Thanks,
Doug
 
 
0 # Michael Garrett 2010-08-02 13:41
Sounds like you need to clamp, since -22 is a valid float pixel value. How about you just do max(r,0)
 
 
0 # Oliver Hohn 2010-08-08 04:21
great article, looking forward to use it.
 
 
0 # Jared Glass 2010-12-06 08:33
I'm not too good with tcl but I get problems where there are a group of nan's. Could you write the tcl code that would basically loop, saying: if pixel is nan, pixel = temp, {while temp = nan} {temp = r(x+i,y) , i +=1} . Then set original nan pixel to temp once temp is a pixel that is not a nan
 
 
0 # Michael Garrett 2010-12-06 11:12
Do you mean "clusters" of nans larger than 1 pixel? I guess a simple way to deal with this is to use expressions to make them black ( isnan(r)?0:r ) then fill them in with the HealBrush gizmo or something. I don't think that the loop is necessary.
 
 
0 # Jared Glass 2010-12-07 01:02
Thanks, the thing is we get them in our motion vector passes in vast amounts and to heal 300 frames a shot... in stereo just takes too much time. This is why I ask about the loop.
 
 
0 # Michael Garrett 2010-12-09 23:12
Late reply...I suggest you kick your motion vector passes back to 3D! Is this mental ray with unpremultiplied alphas? That has created NaN pixels in the past, where there should be black. Solution was to render premulted.
 
 
0 # Jared Glass 2010-12-10 00:49
Ah thanks, but I just spoke to the lighting guys and they say they are rendering premultiplied. We're using xsi with the la_masion shader (coz mental ray's motion vector shader doesn't work with our scenes). Could the problem be simply that we're using exr's?
 
 
0 # Marijn Eken 2011-03-24 17:54
Thanks. This saved me today. I had a CG render and applied a CameraShake to it, which caused the black pixels to become huge. I didn't know how to get rid of it, so this saved the day.
 
 
0 # Michael Garrett 2011-03-25 17:35
Glad it was helpful to you!
 
 
0 # Adam Hazard 2012-08-08 16:07
sorry if this is old, I can get this to work in a normal expression, but for whatever reason not deepexpression. When i put this in the any of the rgb fields it says

"DeepExpression1: Nothing named 'isnan' in 'isnan(r)0:r

Is deepexpression different in that it doesn't understand this function? How can i get this to work when still dealing in deep?
 
 
0 # Michael Garrett 2012-08-08 16:29
Unfortunately DeepExpression is very limited right now, and I don't think it recognises "isnan". If you look at this thread, Denis Scolan talks about some methods to deal with NaN pixels when comping with deep images:

http://forums.thefoundry.co.uk/phpBB2/viewtopic.php?t=7057&sid=353b7e9a17d6ab4bd749955a56a89a28
 
 
0 # Adam Hazard 2012-08-08 19:33
ha, that is actually a topic/thread that I created. Anyways, I basically made a gizmo of this clone pixel offset that handles nans and infs, after the deeptoimage node. If I could use the deepexpression before a deepmerge, I could essentially just fill the nans and infs with black and then they would deepmerge perfectly with no artifacts, and no pixel clone tricks. but this pixel offset way works very well so far.

I tend to avoid using recolors and deep merges if i can, and just use image mattes created from deep renders to cut my beauty. But just trying to find solutions for the recolor deepmerge route for others that use them.

thanks, again
 
 
0 # Adam Hazard 2012-08-08 19:34
hmm, not sure why it has me listed as "a guest" in my first comment.
 
 
0 # Michael Garrett 2012-08-08 20:07
I know this isn't ideal but can you flatten the image, run the expressions, then DeepRecolor+Dee pMerge them back on? Or, are the NaN's appearing because of unpremult/premu lt issues in the DeepMerge? Can you prevent this by lifting the black point of the alpha slightly before going into the DeepMerge?
 
 
0 # Adam Hazard 2012-08-09 10:46
yeah it is happening because of unpremults/prem ult issues of recolor being used with deepmerge, I will try black point thing. Thanks.
 
 
0 # Alexey Kuchinski 2012-08-24 11:24
Thanks for tutorial, did use it and found that Expression Node does job but is very heavy to calculate, it also not to much flexible in which value to pick (i mean NAN, INF, or just too high values in luma/alpha)
I can recommend to use ColorLookup to isolate all bad pixels and create a matte + TransformMasked to offset , it is very flexible in how sharp you want cut, and it is very fast to calculate.
Off course for every case different solution, but this way worth to be considered too :-)
 
 
0 # Mike MacInnis 2014-01-21 18:43
This method worked beautifully for a Short Film I am working on. Modo seems to have some issues and develops NaN.
Thank you!
 
 
0 # Caro Lasil 2014-03-28 14:16
Thank you for this tutorial !
But, does anyone have a solution for pixels with a "3.2e-06" type of value ? Thanks !
 
 
0 # Daniel Hartlehnert 2014-06-15 16:03
"3.2e-06" is not an illegal value like NAN or INF. It's actually 0.00000032 so just very small. I suggest you simply clamp it?
 
 
0 # joie hey 2014-09-22 10:35
Hi there;
That method works great with pixels with RGB with NAN...
But what if I have pixels where the NAN value is only in one of the values randomly?.
I mean, one pixel with 0.9, 0.8, nan, and another pixel with nan, 0.5, 0.6...
 
 
0 # Frank Rueter 2014-09-22 22:00
the expressions are per channel so that case should be covered already
 
 
0 # Spencer Tweed 2018-01-05 22:07
This is awesome, just what I needed!

I did run into the funny situation where I had two nan pixels side by side, so the expression just copied the nan value over to the left and I still ended up with one nan pixel (down from two). To fix it I simply duplicated the node so it would run twice - FYI if anyone else runs into that...
 
 
0 # Ernest Dios 2018-05-24 17:22
thanks man, 8 years later still very useful info!
 
 
0 # Prabhamrit Singh 2019-03-21 10:35
How to fix values like
r=4.3e-06
g=7.5e-06
b=9.4e-06
 
 
0 # Anton Lackner 2022-06-24 14:34
4.3e-06 equals 0,00000043 so just grade with blackpoint by a very small amount

e-06 is the amount of zeros after the comma
 
 
0 # tlanguebien tlanguebien 2019-05-07 12:48
I noticed a lot of people need to fix some pixels for different reasons, so i'll take a couple of minutes to explain the tcl boolean expression :

according to the documentation (https://www.tcl.tk/man/tcl/TclCmd/expr.htm#M21)
x ? y : z basically means : if confition(x), then do (y), else do (z)

for instance :
isnan(r) ? (r(x+1,y) + r(x-1,y))/2 : r
means :

for each pixel of the picture :
isnan(r) -> checks if the pixel has has no valid value
(r(x+1,y) + r(x-1,y))/2 - > gives the average value of the pixel on the left and and the right of the current pixel
r -> the current red value of the pixel

in a nutshell :
if r is not a number, then get the average value of the pixels around, else keep the current value.

You can then replace the condition with anything you need, for instance :
r < 0.0001 ? (do something here) : r
a != 0 ? 1 : 0

the possibilities are endless :)
 
 
0 # Frank Rueter 2019-05-07 20:13
thanks for that. Just a minor clarification: This is not a tcl expression but a straight up Nuke expression.
 
 
0 # Mehul Rathod 2022-05-01 13:08
Hello sir thank you very much for this, i can't find user panel in my expression node.
 
 
0 # Mehul Rathod 2022-05-01 13:08
Hello sir thank you very much for this, i can't find user panel in my expression node.
 
 
+1 # Travis Button 2022-05-04 23:26
Quoting Mehul Rathod:
Hello sir thank you very much for this, i can't find user panel in my expression node.


First off, thanks for this. I first came across this thread years ago, used it, and then never actually saved the tool. Today I came back to it and did make this an accessible tool out of the box that wouldn't need to be made from scratch each time.

@Mehul - This is a custom tab you have to make by right clicking the tabs and using "Manage User Knobs...". This doesn't exist in a native expression node. For you, and anyone else having issues or looking to utilize this approach to NaN pixels, just download this .nk script and import into your script and you'll have a fully functional custom Expression node to accomplish this with the additional attributes to input the offset values outside of the expression. I hope this helps some of you.

https://drive.google.com/file/d/1aDBVGnZsqNiuwad6yLlaJQ-Dr4NFE2x6/view?usp=sharing
 

You have no rights to post comments

We have 2477 guests and 123 members online