AE ENHANCERS

Expressions/Scripts/Presets
It is currently Thu Oct 02, 2014 1:22 am

All times are UTC - 8 hours [ DST ]




Post new topic Reply to topic  [ 7 posts ] 
Author Message
 Post subject: An expression for determining the size of a text layer
PostPosted: Sun Feb 10, 2008 10:31 pm 
Offline

Joined: Tue Nov 29, 2005 3:00 am
Posts: 201
Location: Paris
Here is a (slow) expression that determines the width and the height of a text layer using the new sampleImage(). It is assumed that the text is horizontal and entirely visible. The expression can probably be optimized (this is a job for you Dan ;) ), but here is the general idea:
    1. initialize the left, right, up and down variables of the bounding box we're looking for
    2. sample the pixels of the text layer
    3. if the current pixel has an alpha greater than 0, update the variables
    4. output width=right-left, height=down-up

This can be written like that (in another text layer):

Code:
L = thisComp.layer("MyTextLayer");
w = L.width; h = L.height;
lmin = w; rmax = 0;
umin = h; dmax = 0;

for (i = 0; i < h; i++)
{
    for (j = 0; j < w; j++)
    {
        p = L.sampleImage([j,i], [0.5,0.5]);
        if (p[3] > 0)
        {
            if (i < umin) umin = i;
            if (i > dmax) dmax = i;
            if (j < lmin) lmin = j;
            if (j > rmax) rmax = j;
        }
    }
}
textW = rmax - lmin;
textH = dmax - umin;

"Size of text:\r" + textW + "x" + textH;


Top
 Profile  
 
 Post subject: Re: An expression for determining the size of a text layer
PostPosted: Mon Feb 11, 2008 7:27 pm 
Offline

Joined: Tue Nov 29, 2005 3:00 am
Posts: 201
Location: Paris
Please guys don't let me alone in the playground :mrgreen:

Instead of sampling every pixels, we can directly sample rows or columns by changing the radius parameter in sampleImage(). The number of operations decreases from w*h to w+h which is a huge gain (considering that sampling a larger area doesn't increase that much the computation time).

So here is an optimized version of the previous expression:

Code:
function ExistPixelInCol(layer, colIndex)
{   
    return layer.sampleImage([colIndex,thisComp.height/2], [.5,thisComp.height/2])[3] > 0 ? true : false;
}

function ExistPixelInRow(layer, rowIndex)
{   
    return layer.sampleImage([thisComp.width/2,rowIndex], [thisComp.width/2,.5])[3] > 0 ? true : false;
}

L = thisComp.layer("MyTextLayer");
w = L.width; h = L.height;
lmin = L.width; rmax = 0;
umin = L.height; dmax = 0;

for (i = 0; i < h; i++)
{
    b = ExistPixelInRow(L,i);
    if (b && i < umin)
        umin = i;
    if (b && i > dmax)
        dmax = i;
}
for (j = 0; j < w; j++)
{
    b = ExistPixelInCol(L,j);
    if (b && j < lmin)
        lmin = j;
    if (b && j > rmax)
        rmax = j;
}
textW = rmax - lmin;
textH = dmax - umin;

"Size of text:\r" + textW + "x" + textH;


If you change the Vertical or Horizontal scale in the Character panel, you'll see the expression updates in 'real time', which was definitely not the case with the previous expression.


Top
 Profile  
 
 Post subject: Re: An expression for determining the size of a text layer
PostPosted: Tue Feb 12, 2008 10:04 pm 
Offline

Joined: Tue Nov 29, 2005 3:00 am
Posts: 201
Location: Paris
This one is more restrictive as it assumes the text is left aligned and doesn't handle 'fancy text' (experts will understand what the expression considers as fancy text), but it should be a little faster.
The idea is to start the search with a bounding box of zero area from the current position of the text layer. We then expand the box to the right/top until no more pixels are detected in the column/row.
Code:
function getTextPosition(layer)
{
    return layer.toWorld(layer.anchorPoint) - layer.anchorPoint;
}

function ExistPixelInCol(layer, colIndex)
{   
    return layer.sampleImage([colIndex,thisComp.height/2], [.5,thisComp.height/2])[3] > 0 ? true : false;
}

function ExistPixelInRow(layer, rowIndex)
{   
    return layer.sampleImage([thisComp.width/2,rowIndex], [thisComp.width/2,.5])[3] > 0 ? true : false;
}

L = thisComp.layer("MyText");
P = getTextPosition(L);
lmin = P[0]; rmax = lmin;
tmin = P[1]; bmax = tmin;

do { tmin--; } while (ExistPixelInRow(L,tmin));

for (j = P[0]+1; j < L.width; j++)
    if (ExistPixelInCol(L,j))
        rmax = j;

textW = rmax - lmin;
textH = bmax - tmin;

textW + "," + textH;

I don't claim these expressions are extremely useful but it's for sure an interesting training :D


Top
 Profile  
 
 Post subject: Re: An expression for determining the size of a text layer
PostPosted: Wed Feb 13, 2008 1:56 pm 
Offline
Enhancement master
User avatar

Joined: Thu Jun 17, 2004 9:27 am
Posts: 460
Location: New York City, NY
Wow! Nicely done! This will defintiely come in handy. I just wrote a script the other day just to use sourceRectAtTime. Thanks!

_________________
http://aescripts.com


Top
 Profile  
 
 Post subject: Re: An expression for determining the size of a text layer
PostPosted: Wed Feb 13, 2008 8:57 pm 
Offline

Joined: Sat Jun 26, 2004 10:01 am
Posts: 307
Location: Folsom, CA
Yeah, nice job. It works great for text and shape layers. For solids, you have to use the toWorld() transform to get it to work right. I tried to optimize the code a little, but it really isn't much better than what you have:

Code:
L = thisComp.layer("test text");;
top = bottom = left = right = 0;

gotOne = false;
for (i = 0; i < height; i++){
  if (L.sampleImage([width/2, i], [width/2, 0.5], true)[3] > 0){
    bottom = i;
    if (! gotOne) {
      top = i;
      gotOne = true;
    }
  }
}

gotOne = false;
for (i = 0; i < width; i++){
  if (L.sampleImage([i, height/2], [0.5, height/2], true)[3] > 0){
    right = i;
    if (! gotOne) {
      left = i;
      gotOne = true;
    }
  }
}

"UL = [" + left + "," + top + "]   LR = [" + right + "," + bottom + "]";


Dan

_________________
http://www.motionscript.com


Top
 Profile  
 
 Post subject: Re: An expression for determining the size of a text layer
PostPosted: Thu Feb 14, 2008 8:33 pm 
Offline

Joined: Tue Nov 29, 2005 3:00 am
Posts: 201
Location: Paris
Nice job too, Dan.
For the sake of writing the best code we can, we could add two variables for "width/2" and "height/2" so that the expression doesn't have to recalculate them at each iteration of the loops.
I had a new idea for this expression that I'd like to experiment soon...I don't expose it yet because I'm not sure I will succeed in writing it :)


Top
 Profile  
 
 Post subject: Re: An expression for determining the size of a text layer
PostPosted: Sat Feb 16, 2008 1:39 pm 
Offline

Joined: Tue Nov 29, 2005 3:00 am
Posts: 201
Location: Paris
Okay, I got my monster!

My starting point is the last expression posted by Dan. What is slow in this expression (and in previous expressions as well) is the fact that we sample every row and column even if this row or column is contained in a larger area that does not contain non-zero alpha pixels. In other words, say your text is written at the bottom of the comp, so that the upper half of the layer has no pixels. The expression will still sample each row of this area. This looks like too much work. So if we can determine that we won't find anything in that zone, there is no need to sample there.

This observation is the base idea of my new expression which is a sort of Divide and Conquer approach. We recursively split the layer into smaller parts and when an area is empty, we don't sample further in that area. The process is done vertically and horizontally. For instance, at the beginning, the expression determines whether there are some pixels in the upper part of the layer or not, if it finds some, then it analyzes the upper quarter, and so on until the area is around 1 pixel wide or empty.

I set up a little project to test these two techniques and the divide and conquer expression seems to outperforms the previous one in terms of computation time, while providing a bounding box of good quality. When I rendered the two comps in the project, the "new" comp was 375% faster than the "old" comp.

In both methods, I noticed that when the text is very small compared to the size of the comp, when we sample a row or a column that contains very few pixels, sampleImage() round the result to zero, so we may miss some pixel.

The expression isn't easily readable but if someone wants to improve it, here it is:
Code:
function getParams(area)
{
    var pointX = (area[0][0] + area[1][0]) / 2;
    var pointY = (area[0][1] + area[1][1]) / 2;       
    var radiusX = (area[1][0] - area[0][0]) / 2;
    var radiusY = (area[1][1] - area[0][1]) / 2;
    return [ [pointX,pointY], [radiusX,radiusY] ];   
}
function isAreaEmpty(area)
{
    var params = getParams(area);       
    if (params[1][0] < 0.5 || params[1][1] < 0.5)
        return true;
    return L.sampleImage(params[0], params[1], true)[3] > 0 ? false : true;
}
function sampleRow(area)
{
    var params = getParams(area);           
    if (L.sampleImage(params[0], params[1], true)[3] > 0)
    {
        if (params[0][1] < top)
            top = params[0][1];
        if (params[0][1] > bottom)
            bottom = params[0][1];
    }
}
function sampleCol(area)
{
    var params = getParams(area);           
    if (L.sampleImage(params[0], params[1], true)[3] > 0)
    {
        if (params[0][0] < left)
            left = params[0][0];
        if (params[0][0] > right)
            right = params[0][0];
    }
}
function sampleAreaHorizontally(area)
{   
    if (isAreaEmpty(area))
        return;

    if (area[1][1] - area[0][1] < 1.5)
        sampleRow(area);
       
    var UpperArea = [ area[0], [area[1][0],(area[0][1]+area[1][1])/2] ];
    var LowerArea = [ [area[0][0],(area[0][1]+area[1][1])/2], area[1] ];
    sampleAreaHorizontally(UpperArea);
    sampleAreaHorizontally(LowerArea);   
}
function sampleAreaVertically(area)
{   
    if (isAreaEmpty(area))
        return;

    if (area[1][0] - area[0][0] < 1.5)
        sampleCol(area);   
   
    var LeftArea  = [ area[0], [(area[0][0]+area[1][0])/2, area[1][1]] ];
    var RightArea = [ [(area[0][0]+area[1][0])/2, area[0][1]], area[1] ];
    sampleAreaVertically(LeftArea);
    sampleAreaVertically(RightArea);   

var L = thisComp.layer("Text");
var top = L.height, bottom = 0, left = L.width, right = 0;
var halfW = L.width / 2;
var halfH = L.height / 2;
var area = [[0,0], [L.width,L.height]];
sampleAreaHorizontally(area);
sampleAreaVertically(area);
"" + left + "," + top + "," + right + "," + bottom;


I know this looks like overkill but we are in the "Discuss the code" section :mrgreen:


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 7 posts ] 

All times are UTC - 8 hours [ DST ]


Who is online

Users browsing this forum: No registered users and 2 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group