PDA

View Full Version : assigning runtime function value into a variable


wuziq
13th March 2007, 01:16
Is it possible to assign the value of a runtime function, evaluated on one frame, into a variable?

The reason I ask is because I want my script to do a certain thing based on the AverageLuma of a single frame of another clip.. basically, I want to do something like this:

return ( AverageLuma(singleFrame) < 235 )
\ ? clip2
\ : clip3

Can it be done?

sh0dan
13th March 2007, 15:57
Use ConditionalFilter, it does exactly what you describe.

wuziq
13th March 2007, 23:30
Is there a way to do it without using ConditionalFilter? Here is my script:

src = "320x240.bmp"

circles = imagesource(src, end=0)

function moveRight(clip c)
{
lastframe = c.trim(framecount(c)-1,-1)
nextframe = lastframe.crop(0,0,-1,0).addborders(1,0,0,0,color=color_white)
rightColumn = lastframe.crop((c.width-1),0,0,0)
# width must be at least 16 to get proper reading of averageluma
rightColumn = stackhorizontal(rightColumn, rightColumn)
rightColumn = stackhorizontal(rightColumn, rightColumn)
rightColumn = stackhorizontal(rightColumn, rightColumn)
rightColumn = stackhorizontal(rightColumn, rightColumn)
rightColumn = rightColumn.converttoyv12()
newclip = conditionalfilter(rightColumn, (c ++ nextframe), c, "AverageLuma()", "equals", "235")
return ( framecount(newclip) == framecount(c) ) ? newclip : moveright(newclip)
}

moveRight(circles)

The last line of the function never works because conditionalfilter doesn't return the c that I passed into moveRight(). Instead, c will always be of the same length as (c ++ nextframe). So what I get is VirtualDub disappearing (crashing) every time I load the script, because the clip grows infinitely.

I don't know the ins and outs of conditionalfilter, so I'm probably doing something totally wrong, but I figured I should include my script to give a little bit of context. So.. is there a way to do it without using conditionalfilter? :p

Edit: To be clear, I want the end of the function to act like this:

function moveRight(clip c)
{
...
rightColumn = rightColumn.converttoyv12()
return (AverageLuma(rightColumn) < 235) ? c : (c+nextframe).moveRight()
}

stickboy
14th March 2007, 09:59
Without using ConditionalFilter (or one of its cousins like ScriptClip), no, you can't. Scripts are evaluated when they're loaded. You can't have a normal user-defined function that constructs different filter chains based on criteria not known at load-time.

foxyshadis
14th March 2007, 11:47
You just can't modify certain things in conditionalfilter, like dimensions, framerate, and framecount. All of the frame properties are determined at compile time and must be static. You'll have to work around that, like making a too-long clip and filling it as you go, but I'm fuzzy on the details without a morning cup of tea yet.

Frankly, this is probably one of those things that's easier to express in a plugin function than in script.

gzarkadas
14th March 2007, 16:43
Is there a way to do it without using ConditionalFilter? Here is my script:

To me it seems that you want to make a kind of an animation. There are better ways than the one you try; but first you should tell what exactly you want to achieve by using this function (assuming you have made it to work) to give us a clue :) .

wuziq
14th March 2007, 19:00
Scripts are evaluated when they're loaded. You can't have a normal user-defined function that constructs different filter chains based on criteria not known at load-time.

You just can't modify certain things in conditionalfilter, like dimensions, framerate, and framecount. All of the frame properties are determined at compile time and must be static.

Got it, thanks!

To me it seems that you want to make a kind of an animation. There are better ways than the one you try; but first you should tell what exactly you want to achieve by using this function (assuming you have made it to work) to give us a clue

Yes, I have an image that has a subject against whitespace. There is a lot of whitespace on the right side. I basically want to move the subject to the right, into the whitespace, one pixel per frame at a time, by cropping off the right-most column and adding it as the left-most column, over and over, until there are no more white columns on the right side (and the subject is now totally on the right side of the image, with the whitespace on the left).

I had the idea that AviSynth would check the right-most column to see if it's totally white, to determine whether to crop it off or not. That is where my script is failing. On the other hand, I could just manually count how many white columns there are, then do the crop-and-add for that many times, but that is less "portable"...

gzarkadas
15th March 2007, 01:13
...I basically want to move the subject to the right, into the whitespace, one pixel per frame at a time,...

It can be done but you will have to be carefull to avoid crashes.

Below is a sample code using ScriptClip and FrameEvaluate that works - for my test-case of course ;) - in yv12 (4 pixels shift / frame). With proper ConvertTo... calls maybe can handle RGB also.

Note that you may have to set a value lower than 235 if you want to reach your object exactly at the right border.


# load an image and return a 'frames' frames clip of it
Function ImgSource(string file, int frames) {
return ImageSource(file).Loop(frames).FreezeFrame(1, frames - 1, 0).Trim(0, frames - 1)
}

# scriptclip "kernel"; assumes c is a YV12 clip with all white background
Function CropWR(clip c, int cur_frame) {
ofs = 4 * (cur_frame + 1)
global prev_ofs = stop_scroll || ofs > c.Width ? prev_ofs : ofs
return c.Crop(0, 0, -prev_ofs, 0).AddBorders(prev_ofs, 0, 0, 0, color_white)
}

# here our image is 640 pixels wide so we need 640/4 = 160 frames
# since AverageLuma needs 16 pixels add a white border that will be cropped at the end

global circles = ImgSource("whiteobj.jpg", 160).ConvertToYV12().AddBorders(0, 0, 16, 0, color_white)

# vars to stop scrolling and keep image still at last valid pos
global stop_scroll = false
global prev_ofs = 0

global cc = ScriptClip(circles, "CropWR(circles, current_frame)")
FrameEvaluate(cc, "global stop_scroll = cc.Crop(cc.Width - 16, 0, 0, 0).AverageLuma() < 235 || stop_scroll ? true : false")
return last.Crop(0, 0, -16, 0)

Also, if you want to make a function of it keep in mind that functions using the conditional environment can be called only once inside the script, unless they use a somewhat complicated setup (see here (http://forum.doom9.org/showthread.php?p=753227#post753227) and also there (http://avslib.cvs.sourceforge.net/avslib/avslib/filters/animate.avsi?view=markup) for practical examples).

foxyshadis
15th March 2007, 07:30
It seems like an Animate solution, using a resizer's crop, would be a simpler solution, but since I haven't tried to figure out the exact details it might not be.

gzarkadas
15th March 2007, 14:55
I was thinking a similar thing initially :) , but if the object's x-position is not known beforehand then the job has to be done within the runtime environment.