Trim layers based on Opacity - save a lot of render time

What type of scripts do you need?

Moderator: byronnash

kobyg
Posts: 128
Joined: December 7th, 2007, 10:11 am

Hi Guys,
I've noticed that After Effects does not optimize the rendering based on the opacity of the layers.
I mean that even if the layer's opacity is 0%, AE calculates it's pixels but then does not use it.
If you have a lot of layers (tens or hundredrs), but only a few of them with opacity greater than 0% at any given time, This would result in a VERY long render time. However, if you trim the inPoint and outPoint only to areas where the opacity is greater than 0%, the render time would shorten drastically !

I've encountered that problem while building animations for LARGE amount of pictures, that are controlled by expressions. The opacity is also controlled by an expression, and is 0% when the layer should not appear, but as I've said, that doesn't save any render time...

Does someone know a script that would do that opacity optimization: trim the inPoint & outPoint of each layer, based on the opacity values (regarding also opacity expressions) ?

Thanks,
Koby.
kobyg
Posts: 128
Joined: December 7th, 2007, 10:11 am

Hi!
Since I couldn't find such script anywhere, and no one here wrote me that he knows about such script, I decided to write it myself!
So here is it, for anyone who wants it.
It does exactly what I've written in the previous post (you can see it also at the beginning of script).

I've tested it on a photo album animation I made, that is done entirely by expressions which turn the pages of the album one by one (double sided pages album).
The animation had 80 pages, but because it is an album, most of the time only 2 pages (2 layers) are visible. I've added an opacity expression that would give 0% opacity to layers when they are not visible to the camera, and after using that script (which took about 25 seconds to run) it decreased the render time from 2:49 hours to 1:35 hours (which is almost half!)

Enjoy !
Koby.
Optimize Opacity.jsx.zip
(1.34 KiB) Downloaded 1195 times

Code: Select all

// Opacity Render Optimization
// Written by Koby Goldberg - AUG 2008
//
// What the script does:
//======================
// This script works on all AV layers in the current composition
// and trims the in-point and out-point of each layer such that,
// the in-point is the first frame in the timeline where the layer opacity is greater than 0.
// and the out-point is the last frame in the timeline where the layer opacity is greater than 0.
//
//
// The goal of the script:
//========================
// This script takes into count opacity expressions and keyframes, 
// and by eliminating the areas where the layer is fully transparent (opacity=0%)
// it actualy saves After Effects from unnecesarry rendering.
// (It might seem strange that AE doesn't do that automatically, but it doesn't. 
// Otherwise I wouldn't have written this script...
// AE calculates all layers, even if they are fully transparent)
//
//
// When to use this script:
//=========================
// The greatest advantage of this script is achieved when it is used on animations that are controlled by expressions,
// with LOTS of layers but only a few of them are visible in any given time.
// By adding an expression to the opacity, that would make certain that the opacity is 0% when the layer is out of frame, 
// (or from some other reason should not be visible) and is 100% when the layer should be seen, you will enable script to achieve his goal.
// (you could use keyframes too, instead of expressions, but I think that would be too much woek)
// The script will trim the layers based on the opacity values, and thus will save a lot of rendering time!
//
//

{
// create an undo group 
app.beginUndoGroup("Opacity Render Optimization");

var curItem = app.project.activeItem; 
var T = curItem.frameDuration;

// check if comp is selected 
if (curItem == null || !(curItem instanceof CompItem)){ 

// if no comp selected, display an alert
alert("Please establish a comp as the active item and run the script again"); 

} else { 

// otherwise, loop through each layer in the selected comp
for (var b = 1; b <= curItem.numLayers; ++b){

// define the layer in the loop we're currently looking at
var curLayer = curItem.layer(b);

// check if that layer is a footage layer
if (curLayer.matchName == "ADBE AV Layer"){

// serch for inPoint:
var start = 0;

for (var t=0; t<=curItem.duration; t+=T)
{
   if (curLayer.opacity.valueAtTime(t,false)>0)
   {
      start = t-T;
      break;
   }
}

// serch for outPoint:
var stop = 0;
for (var t=Math.floor(curItem.duration/T)*T; t>=0; t-=T)
{
   if (curLayer.opacity.valueAtTime(t,false)>0)
   {
      stop = t+2*T;
      break;
   }
}

curLayer.inPoint = start;
curLayer.outPoint = stop;

}

}

}

// close the undo group
app.endUndoGroup(); 
}
byronnash
Posts: 321
Joined: July 7th, 2004, 2:30 pm
Location: Charlotte, NC
Contact:

That looks pretty handy. I'll try it out on my next project. Thanks for sharing.
salvazalvi
Posts: 36
Joined: September 21st, 2004, 7:29 am

Hi,

I'm trying your script but with a tweak : I don't want to trim the layers according to the layer's opacity but according to a percentage of a tint effect value. I've change two lines in the script from :

Code: Select all

if (curLayer.opacity.valueAtTime(t,false)>0)
to

Code: Select all

if (curLayer.effect("Tint")("Map White To").valueAtTime(t,false)>100)
but it returns me an error code : "null is not an object"

I don't see what I am doing wrong. Any help is welcome.

Thanks,

Salvador
Paul Tuersley
Posts: 704
Joined: June 5th, 2004, 7:59 am
Location: London, UK

As the script is looping through all the layers in a comp, perhaps you have layers that don't have a tint effect? That would cause the error.

Also, you should be aware that color properties such as "Map White To" are 4 dimensional arrays (RGBA) with values between 0-1, so you might need something like this instead, which just examines the first (red) value in the array:

Code: Select all

if (curLayer.effect("Tint")("Map White To").valueAtTime(t,false)[0] > 0);
afterguy
Posts: 4
Joined: October 12th, 2008, 9:15 am

I liked The script... Using it so much... thanx...
salvazalvi
Posts: 36
Joined: September 21st, 2004, 7:29 am

Thanks for the answers. I'll give it a try tomorrow. I guess this can't be a dynamic thing like an expression ?
kobyg
Posts: 128
Joined: December 7th, 2007, 10:11 am

I'm glad you like my script, and use it ! :)
I hope it saves you a lot of render time, like it saves me.

salvazalvi,
This can't be an expression, because expressions can't trim layers...
And even if you write an expression that makes the layer invisible (fully transparent) but not trimmed in the timeline, it will still be rendered in all frames where it is not trimmed.

Paul,
Is it possible to check in a script wheather an effect is applied to the layer (in this case: the TINT effect), and only then apply the desired command ?

Koby.
salvazalvi
Posts: 36
Joined: September 21st, 2004, 7:29 am

I'm using a Tint effect because I don't want the layers to become transparent as they go far from the camera, but to be affected by a "depth fog" simulated with the tint effect wich is linked to the background's color. I managed to use your script by adding the same expression I use for the tint to the opacity, run the script and then deactivate the opacity's expressions.

It would be nice to be able to specifie wich parameter we want to affect your script. Another problem I see is that if we don't have a regular camera movement, layers may have been trimmed by the script when I would prefer to see them splitted. This would require a two step process : split layers when 0 opacity is reached, then check if a layer has 0 opacity all along and delete this layer.

Thank you anyway, this will save me a lot of time as my comp will contain hundreds of layers in a 5k comp at 50i/s.

Salvador
Paul Tuersley
Posts: 704
Joined: June 5th, 2004, 7:59 am
Location: London, UK

kobyg wrote: Paul,
Is it possible to check in a script wheather an effect is applied to the layer (in this case: the TINT effect), and only then apply the desired command ?
You could do something like this:

Code: Select all

{	
	var activeItem = app.project.activeItem;
	
	for ( x = 1; x <= activeItem.numLayers; x++) {
		if (activeItem.layer(x).effect("Tint") != null) {
			alert("Layer: " + activeItem.layer(x).name + " has a Tint effect");
		}
	}
}
	
kobyg
Posts: 128
Joined: December 7th, 2007, 10:11 am

Thanks Paul !

Salvador,
First, if you change the line you've changed before:

Code: Select all

if (curLayer.opacity.valueAtTime(t,false)>0)
to these 2 lines:

Code: Select all

if (curLayer.effect("Tint") != null)
if (curLayer.effect("Tint")("Map White To").valueAtTime(t,false)[0]>0)
You will no longer get the error message.
Refer to what Paul wrote earlier about the RGBA color value you need to write at the second condition (I wrote [0] which is the RED channel, you can write anything you need).

Second, I didn't quit understand what you meant by "split layer".
I know that if the layer drops the opacity to zero, and then raises again, the script would not notice that (the way it is written now), and will only trim the begining and end areas where the opacity is zero. It will not trim the middle area, and AE will render that area unnecessarily... Is that what you meant ?
salvazalvi
Posts: 36
Joined: September 21st, 2004, 7:29 am

I know that if the layer drops the opacity to zero, and then raises again, the script would not notice that (the way it is written now), and will only trim the begining and end areas where the opacity is zero. It will not trim the middle area, and AE will render that area unnecessarily... Is that what you meant ?
Exactly. My camera goes backwards and then forward, so I have layers that disappear and reappear. Splitting a layer would do the trick (Edit>Split Layer) if it's then possible to delete a constant 0% opacity layer.
kobyg
Posts: 128
Joined: December 7th, 2007, 10:11 am

I'll try to do that when I return home in the evening.
kobyg
Posts: 128
Joined: December 7th, 2007, 10:11 am

I've changed the script that it will split layers if the opacity drops to zero and then raises back again, thus saving AE from rendering in that area. If needed, the script will split the layer multiple times, and will isolate every area of non-zero opacity in a duplicated layer.

Salvador,
I think this is what you wanted.
In order to use with your TINT effect, you'll have to change the opacity condition with the TINT effect condition, the same as you've changed before, except that this time there are few more places where you need to change it.

Koby.
Optimize Opacity v2.jsx.zip
(1.49 KiB) Downloaded 1035 times

Code: Select all

// Opacity Render Optimization
// Written by Koby Goldberg - AUG 2008
//  Update - v.2 : NOV 2008
//
// What the script does:
//======================
// This script works on all AV layers in the current composition
// and trims the in-point and out-point of each layer such that,
// the in-point is the first frame in the timeline where the layer opacity is greater than 0.
// and the out-point is the last frame in the timeline where the layer opacity is greater than 0.
// if in the middle of that area the opacity would drop to zero, the script will split the layer to 2 layers
// and each copy will be treated as explained above.
//
//
// The goal of the script:
//========================
// This script takes into count opacity expressions and keyframes, 
// and by eliminating the areas where the layer is fully transparent (opacity=0%)
// it actualy saves After Effects from unnecesarry rendering.
// (It might seem strange that AE doesn't do that automatically, but it doesn't. 
// Otherwise I wouldn't have written this script...
// AE calculates all layers, even if they are fully transparent)
//
//
// When to use this script:
//=========================
// The greatest advantage of this script is achieved when it is used on animations that are controlled by expressions,
// with LOTS of layers but only a few of them are visible in any given time.
// By adding an expression to the opacity, that would make certain that the opacity is 0% when the layer is out of frame, 
// (or from some other reason should not be visible) and is 100% when the layer should be seen, you will enable script to achieve his goal.
// (you could use keyframes too, instead of expressions, but I think that would be too much woek)
// The script will trim the layers based on the opacity values, and thus will save a lot of rendering time!
//
//

{
// create an undo group 
app.beginUndoGroup("Opacity Render Optimization");

var curItem = app.project.activeItem; 
var T = curItem.frameDuration;

// check if comp is selected 
if (curItem == null || !(curItem instanceof CompItem)){ 

// if no comp selected, display an alert
alert("Please establish a comp as the active item and run the script again"); 

} else { 
// otherwise, loop through each layer in the selected comp
for (var b = 1; b <= curItem.numLayers; ++b){
	// define the layer in the loop we're currently looking at
	var curLayer = curItem.layer(b);
	// check if that layer is a footage layer
	if (curLayer.matchName == "ADBE AV Layer"){
		var state = 0;
		var outpoint = curLayer.outPoint;
		var show = curLayer.opacity.valueAtTime(t,false);
		for (var t=curLayer.inPoint; t<=curLayer.outPoint; t+=T){
			if (state==0 && curLayer.opacity.valueAtTime(t,false)>0)
				state = 1;
			if (state==1 && curLayer.opacity.valueAtTime(t,false)==0)
				state = 2;
			if (state==2 && curLayer.opacity.valueAtTime(t,false)>0){
				state = 1;
				curLayer.outPoint = t - T;
				curLayer.duplicate();
				curLayer.inPoint = t - T;
				curLayer.outPoint = outpoint;
				break;
			}
		}
	}
}
for (var b = 1; b <= curItem.numLayers; ++b){
// define the layer in the loop we're currently looking at
curLayer = curItem.layer(b);
// check if that layer is a footage layer
if (curLayer.matchName == "ADBE AV Layer"){

// serch for inPoint:
var start = curLayer.inPoint;

for (var t=curLayer.inPoint-T; t<=curLayer.outPoint; t+=T)
{
   if (curLayer.opacity.valueAtTime(t,false)>0)
	{
		start = t-2*T;
		break;
	}
}
// serch for outPoint:
var stop = 0;
for (var t=Math.floor(curLayer.outPoint/T)*T; t>=0; t-=T)
{
   if (curLayer.opacity.valueAtTime(t,false)>0)
   {
      stop = t+2*T;
      break;
   }
}
curLayer.inPoint = start;
curLayer.outPoint = stop;
}
}
}

// close the undo group
app.endUndoGroup(); 
}
Last edited by kobyg on November 14th, 2008, 8:47 am, edited 1 time in total.
salvazalvi
Posts: 36
Joined: September 21st, 2004, 7:29 am

Hi,

Thank you very much.
I get an error on line 55

Code: Select all

var show = curLayer.opacity.valueAtTime(t,false);
error message is : Unable to call "valueAtTime" because of parameter 1. Value is undefined.

Am I doing something wrong ?
Post Reply