Force Script DAT to cook every frame. Or: how to skip frames

Hi - I’ve got a slow process running in a script DAT. If I leave it alone, it slows the network to about 15fps.

I don’t really need the results of that script at every single frame-- 15fps would probably be fine.

So I naively tried to have the script return early on every other frame. And that causes the script never to cook again. Is there a way to force it to cook every [other] frame? I looked at the docs for Cook, but it wasn’t clear exactly how I should be pulling from that DAT (or the CHOP I make from it) to make sure it keeps getting cooked.

This may be the wrong way to think about this. Is there another way you’d approach a script that doesn’t run fast enough but which is OK running more slowly, as long as everything else doesn’t run slowly?

Here’s the relevant script source:

def onCook(scriptOp):
    scriptOp.clear()

    # Set some text in the parent dat so we can see when we cook
    scriptOp.text = str(absTime.frame)

    # On even frames, I don't need to run anything, so just return
    if absTime.frame %2 == 0:
        return  # commenting out this line causes 

    # Now Do some heavy OpenCV processing here on odd frames,
    # then set values in scriptOp based on the results
    # <your code here... >
    pass

If the data it relies on is changing, and the results are being displayed or used, it will cook every frame

This includes having the node itself visible.
What is using your data?

You may consider using the Execute DAT, and using onFrameStart(frame) to control execution instead.

Cheers
Rob.

OK, that helps! I’m feeding a TOP to the script CHOP, and I wasn’t accessing any of the TOP’s data on every frame. So once a frame was skipped, the node didn’t cook again since it wasn’t accessing that data at all.

If I access the TOP a tiny bit each frame, it cooks every time, allowing me to skip the expensive processing when I need to.

One last question. I force the Script CHOP to cook every frame by accessing a single pixel in the TOP. Is this the cheapest way to use the TOP’s data to force the cook?

Thanks for clearing that up!

Relevant source:

# press 'Setup Parameters' in the OP to call this function to re-create the parameters.
def onSetupParameters(scriptOp):
	page = scriptOp.appendCustomPage('Custom')
	p = page.appendTOP('Top')
	return

def onCook(scriptOp):

	topRef = scriptOp.par.Top.eval()
	# The magic: make sure we use some data from the input TOP every frame;
	# Otherwise this node won't cook after the frame is skipped
	# TODO: is this the cheapest way to pull info from the TOP?
	sample = topRef.sample(x=0, y=0)

	if absTime.frame % 4 != 0:
		# print('%d: script returning early'%(absTime.frame))
		return
	else:
		# Expensive processing would go here here...
		scriptOp.clear()
		# print('%d: script setting "frame" channel to %d'%(absTime.frame, absTime.frame))
		frameChan = scriptOp.appendChan('frame')
		frameChan[0] = absTime.frame 

		pixelChan = scriptOp.appendChan('upperLeftGreen')
		pixelChan[0] = sample[1]
		
	return

You might consider using a TopToCHOP first. There’s an optimization approach here that provides the data a frame behind the current frame that’s rendering allowing the download from the GPU to speed up significantly. You can also use the TopToCHOP to isolate a specific pixel, row, or column of pixels. You might then use this to drive a CHOP execute. Depending on what you’re up to this might prove to be a little faster / more efficient than a pure Python approach.

Thanks, Matt. I’ve taken a look with the Probe panel to compare checking a sample from the TOP on every frame with dummy_sample = topRef.sample(0,0) vs using a TopTo Chop & then checking that value with dummy_sample = scriptOp.inputs[0]['r'] .

It is definitely faster in my script to access the TopTo Chop like you suggested, although that chop incurs its own expense; looks like it’s more or less a wash so far.

If I can avoid cooking the OpenCV script at all except when I want it to, that would be the biggest win; I’ll try that out tomorrow.

Cheers!

Using a Chop Exec DAT did the trick!

What I’ve ended up with is:
Top to CHOP, extracting a single pixel =>
Chop Exec when that pixel changes =>
every nth frame, run an expensive script.

That’s working significantly faster than my previous approaches. Thanks, y’all!

1 Like