Expressions/Scripts/Presets

Moderators: Disciple, zlovatt

 
nab
Topic Author
Posts: 203
Joined: Tue Nov 29, 2005 3:00 am
Location: Royan
Contact:

An expression for determining the size of a text layer

Sun Feb 10, 2008 10:31 pm

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):

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;
 
nab
Topic Author
Posts: 203
Joined: Tue Nov 29, 2005 3:00 am
Location: Royan
Contact:

Re: An expression for determining the size of a text layer

Mon Feb 11, 2008 7:27 pm

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:

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.
 
nab
Topic Author
Posts: 203
Joined: Tue Nov 29, 2005 3:00 am
Location: Royan
Contact:

Re: An expression for determining the size of a text layer

Tue Feb 12, 2008 10:04 pm

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.
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
 
User avatar
lloydalvarez
Enhancement master
Posts: 460
Joined: Thu Jun 17, 2004 9:27 am
Location: New York City, NY
Contact:

Re: An expression for determining the size of a text layer

Wed Feb 13, 2008 1:56 pm

Wow! Nicely done! This will defintiely come in handy. I just wrote a script the other day just to use sourceRectAtTime. Thanks!
 
Dan Ebberts
Posts: 318
Joined: Sat Jun 26, 2004 10:01 am
Location: Folsom, CA
Contact:

Re: An expression for determining the size of a text layer

Wed Feb 13, 2008 8:57 pm

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:

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
 
nab
Topic Author
Posts: 203
Joined: Tue Nov 29, 2005 3:00 am
Location: Royan
Contact:

Re: An expression for determining the size of a text layer

Thu Feb 14, 2008 8:33 pm

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 :)
 
nab
Topic Author
Posts: 203
Joined: Tue Nov 29, 2005 3:00 am
Location: Royan
Contact:

Re: An expression for determining the size of a text layer

Sat Feb 16, 2008 1:39 pm

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:
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:
 
deadrocker
Posts: 1
Joined: Mon Mar 14, 2016 9:24 pm

Re: An expression for determining the size of a text layer

Tue Mar 15, 2016 11:25 pm

Wow so this is what I was not knowing about all this time. “textW = rmax - lmin;" and "textH = dmax - umin;” these are the commands that I did not use until now. No wonder I used to get idiotic results all this time. Thanks a ton for helping out.

::::::::::::::::::::::::::::::
Redrocker
sell citi thank you points
::::::::::::::::::::::::::::::

Who is online

Users browsing this forum: No registered users and 3 guests