Log in

View Full Version : SlitScan() - a slit-scan effect function


boffy
10th June 2009, 02:37
SlitScan(clip c, string "Direction", int "SlitSize", bool "Debug")

This filter delays each line of video by 1 frame more than the line above it(or the line below it, depending which direction you choose). This is equivalent to the analogue "slit-scan" video effect.

Parameters
Direction is the direction the delay increases in. "Down" means that the bottom line is behind the top by a number of frames equal to the height of the clip. "Up" means that the top line is behind the bottom by a number of frames equal to the height of the clip.
Default is "Down"

SlitSize is the height of each "slit" that the video is sliced into. Larger values make it run quicker(fewer slits), but looks worse. I recommend leaving at the default.
Default is 1.

Debug displays debugging information about the filter.
Default is false.



It's pretty slow - seconds per frame on my laptop while I have too many Firefox tabs open.

It basically crops the video into lots of SlitSize-high clips, pads their beginnings by different amounts, then stacks them back together

As such, it currently converts input video to RGB32, because it crops to odd heights. When AviSynth 2.6 is released, I'll update it to use YV24(4:4:4 full chroma YUV) where the source is YUV.

FIXED - Thanks, Gavino! I have a small bug in my Debug parameter: If you leave SlitSize at its default(1), it doesn't show in the debugging info. If you explicitly set it to 1, or any other value, it shows correctly. Does anyone know how I could fix this?

This is my first function, first thread and first post here on doom9, so be gentle! That said, be brutal if need be ;)

Oh, and hello!

EDIT: Is my post widening the page? How do I fix that?
2nd EDIT: Found a bug (fixed as of 0.5.1) in the output video, so have retroactively re-numbered the first version I posted here to 0.5


#SlitScan 0.5.1 for AviSynth
#© 2008-2009 Andrew McAllister

#CHANGELOG
#0.5.1
#Eliminated all but one global variable
#Removed redundant Eval()
#Fixed debug bug
#0.5.1
#First public version

#TODO
#Choose delay rather than simply being based on clip height
#Vertical slits as well as horizontal
#Convert back to input colourspace - is this wanted?

#KNOWN BUGS
#FIXED(Thanks, Gavino): In debug mode, SlitSize is not shown unless explicity declared
#FIXED: Repeated section at the end of video - was due to a missing minus in TempSlit=Crop(...)
#SlitSize >= 7 raise the following errors in AvsP 2.0.2(Latest version at time of writing)
#######################################
#~ Traceback (most recent call last):
#~ File "AvsP.pyo", line 5804, in OnMenuVideoRefresh
#~ File "AvsP.pyo", line 8925, in ShowVideoFrame
#~ File "AvsP.pyo", line 9474, in PaintAVIFrame
#~ File "pyavs.pyo", line 322, in DrawFrame
#~ File "pyavs.pyo", line 301, in _GetFrame
#~ File "avisynth.pyo", line 277, in GetFrame
#~ WindowsError: exception: access violation reading 0x059FE220
#######################################
#This may be a bug in this script or in AvsP 2.0.2 (Haven't tried other versions)
#Slit sizes >= 7 work fine in VirtualDub
#

#UNKNOWN BUGS
#20-legged spider

#THANKS to Gavino on http://forum.doom9.org

function MakeSlit(clip SlitThis, int LinesLeft, int SlitNo, string SlitList, string ScanDirection, int SlitSize)
{
#create a slit SlitSize pixels high, full image width
TempSlit = crop(SlitThis, 0, (height(SlitThis) - LinesLeft), -0, SlitSize)

#Time displacement, padding with Freezeframe, and assign to a variable named Slit#, starting at Slit0
PadLength=((height(SlitThis)-LinesLeft)+1)
#Crashes if this is not global - Why?
Eval("global Slit" + String(SlitNo) + " = FreezeFrame(TempSlit,0,FrameCount(TempSlit),0).Trim(0,PadLength) + TempSlit")

#Build SlitList string(who needs arrays?)
SlitList = SlitList + ", Slit" + String(SlitNo)

SlitNo = SlitNo + 1
LinesLeft = LinesLeft - SlitSize

#IF LinesLeft>0 MakeSlit() ELSE StackVertical
LinesLeft>0 ? MakeSlit(SlitThis,LinesLeft,SlitNo,SlitList,ScanDirection,SlitSize) : Eval("StackVertical(" + SlitList +")")
}
#Eval("Subtitle(" + SlitList +")")
#Stack(Slits, SlitNo, 1)

function SlitScan(clip c, string "Direction", int "SlitSize", bool "Debug")
{
SlitThis = ConvertToRGB32(c)
LinesLeft = height(SlitThis)
SlitNo = 0
SlitList = "Slit0"
ScanDirection = Default(Direction,"down")
SlitSize = Default(SlitSize,1)
Debug = Default(Debug,false)

#IF ScanDirection == "up" SlitThis=FlipVertical(SlitThis) ELSE SlitThis=SlitThis
SlitThis = (ScanDirection == "up") ? FlipVertical(SlitThis) : SlitThis
MakeSlit(SlitThis,LinesLeft,SlitNo,SlitList,ScanDirection,SlitSize)

Crop(0,1,-0,Height(c))

#IF ScanDirection == "up" FlipVertical() ELSE Last
ScanDirection == "up" ? FlipVertical() : Last

#IF Debug == "true" show debugging info ELSE Last
Debug == true ? Subtitle("Direction = " + String(ScanDirection) + ", " + "SlitSize = " + String(SlitSize), size=36) : Last

}

Gavino
10th June 2009, 10:57
I have a small bug in my Debug parameter: If you leave SlitSize at its default(1), it doesn't show in the debugging info. If you explicitly set it to 1, or any other value, it shows correctly. Does anyone know how I could fix this?
That's because you do not change the local SlitSize (which is null), only setting the global one. Do this:
SlitSize = Default(SlitSize,1)
global SlitSize = SlitSize
IMHO, it would be better to pass parameters to MakeSlit rather than use globals anyway.

Other observations:

TempSlit = crop(SlitThis, 0, (height(SlitThis) - LinesLeft), 0, SlitSize)
I think this will go wrong on the last slice if the clip height is not a multiple of SlitSize, as you'll include pixels beyond the bottom. (Maybe explains problems with SlitSize >= 7)?

Eval("""Subtitle("Direction = """ + String(ScanDirection) + ", " + "SlitSize = " + String(SlitSize) + """ ", size=36)""")
Eval is redundant here.

boffy
10th June 2009, 19:33
Thanks loads, Gavino. I thought there must be a better solution than all those globals, but it never occurred to me to that one function could pass values to the other.


TempSlit = crop(SlitThis, 0, (height(SlitThis) - LinesLeft), 0, SlitSize)
I think this will go wrong on the last slice if the clip height is not a multiple of SlitSize, as you'll include pixels beyond the bottom. (Maybe explains problems with SlitSize >= 7)?

In versions before I posted, the video was coming out a few pixels too tall, hence Crop(0,1,-0,Height(c)), which also removes a duplicated line at the top(when Direction=Down) or bottom(when Direction=Up) of the output video.

Gavino
10th June 2009, 20:20
#FIXED: Repeated section at the end of video - was due to a missing minus in TempSlit=Crop(...)
...
TempSlit = crop(SlitThis, 0, (height(SlitThis) - LinesLeft), -0, SlitSize)
Changing 0 to -0 does nothing, -0=0 !
The problem I identified earlier is that the last slice may need to be less than SlitSize, so replace SlitSize here by min(SlitSize, LinesLeft). That should fix the problems with SlitSize >=7.
Crop(0,1,-0,Height(c))
This also seems wrong, you are cropping one line beyond the bottom of the clip and it could crash. I suspect you had to put this in to compensate for a fault in the delay calculations.

I'm glad you eliminated the global variables, but IMHO the way you have done it is still rather messy and obscures the logic of what is really quite a simple algorithm. (I offer this as constructive criticism. :))
If you think about it the right way, the whole thing can be written more simply as:
function SlitScan(clip c, string "Direction", int "SlitSize", bool "Debug") {
Direction = Default(Direction, "down")
SlitSize = Default(SlitSize, 1)
Debug = Default(Debug,false)

c.ConvertToRGB32()
Direction == "up" ? FlipVertical().MakeSlit(SlitSize).FlipVertical() : MakeSlit(SlitSize)

Debug ? Subtitle("Direction = "+String(ScanDirection)+", SlitSize = "+String(SlitSize), size=36) : Last
}

function MakeSlit(clip c, int SlitSize) {
c.Height <= SlitSize ? c : \
StackVertical(c.Crop(0, 0, 0, SlitSize), \
c.Crop(0, SlitSize, 0, 0).Loop(SlitSize+1, 0, 0).MakeSlit(SlitSize))
# The Loop function is used to delay each slice in turn by (a further) SlitSize frames.
}