Log in

View Full Version : Conveniently apply many splices between filtered and original with optional blending


fvisagie
4th December 2012, 16:32
Hi All,

This function simplifies the task of creating a long sequence of splices or blends between filtered and original clips by allowing the user to enter splice frame numbers and optional duration of blend (overlap) into a single text list. It's mainly intended to be used with prefiltered or prerendered clips, and/or when splice blending is needed to mask filtering transitions.

Thanks to Gavino for pointing me towards the fix.

Feel free to use it as you see fit. Any comments or suggestions welcome.

Edit: updated for version 1.1.0
Main change: <overlapn> can now optionally be specified as an absolute frame number - AvsP/mod users can now just hit <F11> at all transition boundaries :). Default behaviour treating <overlapn> as relative offset remains unchanged.


#-------------------------------------------
# SpliceFiltered 1.1.0
# Simplifies creating long sequence of splices or blends between filtered and original clips by allowing
# the user to enter splice frame numbers and optional amount of blend (overlap) into a single text list
#
# Useful when
#
# [...]ApplyRange[...]() is not convenient or suitable, or
# filtered clip was prerendered or prepared externally (filtering before splicing), or
# filtering must be blended in/out gradually to mask transitions
#
# SpliceFiltered(clip input, clip filtered, string splicelist, bool "absoverlap")
#
# <input> unfiltered input clip
# <filtered> filtered clip
# <splicelist> specifies where and how filtered version must be applied
# <absoverlap> specifies whether overlap value is relative offset or absolute frame number, default false
#
# <splicelist> is of the form [<splice1>[,<splice2>]] ... [,<splicen>]]] ... [,<splicer>]]]]
# Each <splicen> is of the form [<framen>[:[<overlapn>]]]
# Function output starts with clip <input>, from each <splicen><framen> output alternates to the other clip
# When <overlapn> > 0 : current output clip frames are progressively blended with frames from other clip
# When <absoverlap> = false: from <framen>, blending lasts for <overlapn> frames (i.e. frame <framen>+<overlapn> is unblended)
# When <absoverlap> = true : from <framen>, blending ends on frame <overlapn> (i.e. frame <overlapn>+1 is unblended)
# <splicen+1><framen+1> not advancing past overlap of <splicen> will generate an error
# Empty <framen> or <overlapn> is treated as 0
# When ':' is ommitted whole <splicen> is interpreted as <framen>
#
# Usage
#
# a = BlankClip(length=60, color=$000000).ShowFrameNumber(size=100)
# b = BlankClip(length=60, color=$FFFFFF).ShowFrameNumber(size=100)
# # Blend from frame 0, cut on next allowable frame, various cuts/blends, blend to penultimate frame, replace last frame
# # With default <absoverlap> = false syntax
# splicelist1 = "0:5, 5:10, 20:0, 30:, 40, 50:1 , 56:3 , 59:"
# # With <absoverlap> = true syntax
# splicelist2 = "0:4, 5:14, 20:0, 30:, 40, 50:50, 56:58, 59"
# StackHorizontal(SpliceFiltered(a, b, splicelist1).Subtitle(splicelist1), \
# SpliceFiltered(a, b, splicelist2, absoverlap=true).Subtitle(splicelist2))
#
# Requirements
#
# input and filtered clips must have same clip properties, although equal length is not enforced
# http://avisynth.org/stickboy/jdl-util.avsi
#
# Version history
#
# 1.1.0 Francois Visagie
# * <overlapn> can now optionally be specified as an absolute frame number -
# AvsP/mod users can now just hit <F11> at all transition boundaries :).
# Default behaviour treating <overlapn> as relative offset remains unchanged.
# * When passed an empty splicelist '""' function now returns <input>, as probably expected.
# Previously spliced to <filtered> on frame 0.
#
# 1.0.2 Francois Visagie
# corrected documentation and description
#
# 1.0.1 Francois Visagie
# corrected check for <frame> advancing at least to end of previous <frame>+<overlap>
# corrected <overlap> bounds calculation too strict by 1 frame
# improved description
#
# 1.0.0 Francois Visagie
#

function SpliceFiltered(clip input, clip filtered, string splicelist, bool "absoverlap") {
absoverlap = Default(absoverlap, false)
return((splicelist == "") ? input : SpliceFiltered_int(input, filtered, splicelist, 0, 0, absoverlap))
}

# Internal function - do not call this directly
# Keeps track of splice frame pointer so original splicelist can stay intact for error reporting purposes
# Keeps track of any overlap in progress from previous iteration for next splice bounds checking

function SpliceFiltered_int(clip input, clip filtered, string splicelist, int frames, int prvoverlap, bool absoverlap) {
enumpos = FindStr(splicelist, ",")
enumpos = (enumpos == 0) ? StrLen(splicelist)+1 : enumpos # May go beyond string length
splice = LeftStr(splicelist, enumpos-1)
separatpos = FindStr(splice, ":")
separatpos = (separatpos == 0) ? StrLen(splice)+1 : separatpos # May go beyond string length
frame = Int(Value(LeftStr(splice, separatpos-1)))
Assert(((frame>=0) && (frame>=frames+prvoverlap)), "SpliceFiltered: splice '"+splice+"' frame invalid")
overlap = Int(Value(MidStr(splice, separatpos+1)))
Assert((overlap>=0), "SpliceFiltered: splice '"+splice+"' overlap invalid")
overlap = ((absoverlap == true) && (overlap > 0)) ? overlap-frame+1: overlap
frame = frame-frames # Adjust past processed frames to unprocessed clip length
Assert(((frame<input.Framecount()) && (frame<filtered.Framecount())), "SpliceFiltered: splice '"+splice+"' frame out of bounds")
Assert((((frame+overlap)<=input.Framecount()) && ((frame+overlap)<=filtered.Framecount())), \
"SpliceFiltered: splice '"+splice+"' overlap out of bounds")
headseg = input.Trim2(0, end=frame+overlap)
tailseg = (enumpos>=StrLen(splicelist)+1) ? \
filtered.Trim2(frame) : \
SpliceFiltered_int(filtered.Trim2(frame), input.Trim2(frame), MidStr(splicelist, enumpos+1), frames+frame, overlap, absoverlap)
return((overlap==0) ? headseg+tailseg : headseg.Dissolve(tailseg, overlap))
}


# Test code

a = BlankClip(length=60, color=$000000).ShowFrameNumber(size=100)
b = BlankClip(length=60, color=$FFFFFF).ShowFrameNumber(size=100)
# Blend from frame 0, cut on next allowable frame, various cuts/blends, blend to penultimate frame, replace last frame
# With default <absoverlap> = false syntax
splicelist1 = "0:5, 5:10, 20:0, 30:, 40, 50:1 , 56:3 , 59:"
# With <absoverlap> = true syntax
splicelist2 = "0:4, 5:14, 20:0, 30:, 40, 50:50, 56:58, 59"
StackHorizontal(SpliceFiltered(a, b, splicelist1).Subtitle(splicelist1), \
SpliceFiltered(a, b, splicelist2, absoverlap=true).Subtitle(splicelist2))


Kind regards,
Francois

Gavino
4th December 2012, 18:00
The parameters in the recursive step seem wrong. I think
SpliceFiltered_int(filtered.Trim2(frame), input.Trim2(frame), MidStr(splicelist, enumpos+1), frames+frame+overlap)
should be
SpliceFiltered_int(filtered.Trim2(frame+overlap), input.Trim2(frame+overlap), MidStr(splicelist, enumpos+1), frames+frame+overlap)

fvisagie
4th December 2012, 19:49
should be
SpliceFiltered_int(filtered.Trim2(frame+overlap), input.Trim2(frame+overlap), MidStr(splicelist, enumpos+1), frames+frame+overlap)

Note quite the answer, but very close! This was the problem:

SpliceFiltered_int(filtered.Trim2(frame), input.Trim2(frame), MidStr(splicelist, enumpos+1), frames+frame+overlap)


I.e. the correct parameters are

SpliceFiltered_int(filtered.Trim2(frame), input.Trim2(frame), MidStr(splicelist, enumpos+1), frames+frame)


:thanks: for pointing me in the right direction.

fvisagie
4th December 2012, 19:59
PS. This is why it's so easy to get tripped up by Dissolve()

frame-count ... sum of all clips minus the overlap


To make matters slightly worse, this information is fairly obscure in the sense that it isn't included on the Dissolve() documentation page, albeit linked to from there.

Gavino
5th December 2012, 12:46
Not quite the answer, but very close!
Yes, I could see that there was an inconsistency between the trim numbers and the 'frame' parameter, but it was actually the latter that was wrong rather than the former.


# Test code
a = BlankClip(length=240, color=$000000).ScriptClip("Subtitle(String(current_frame), size=100)")
b = BlankClip(length=240, color=$FFFFFF).ScriptClip("Subtitle(String(current_frame), size=100, align=1)")
Note that you can use ShowFrameNumber (http://avisynth.org/mediawiki/ShowFrameNumber)() instead of ScriptClip("Subtitle(String(current_frame))" (though the effect is essentially the same - it's just easier to write).
I find it particularly useful for testing these sort of functions, to check the right frames are being used at each point (as you do here).

fvisagie
5th December 2012, 14:06
@Gavino,

The joke's on me, of course the truth was even more complicated still. ;)

With the current implementation, the check for

<frame> not advancing at least to end of previous <frame>+<overlap> will generate an error

no longer works correctly with blending.

This is caused by the fact that the <frames> value is used for two purposes: calculating segment boundaries, as well as tracking frames processed/affected. With Dissolve(), the tail segment boundary needs to be before the dissolved part, but <frames> should (but does not) step over that. Adding <overlap> to <frames> before the next iteration would correctly account for frames processed/affected, but would then incorrectly cause truncation by <overlap> on the next iteration, as happened with my first attempt at this script.

Three options seemed available at this point:

change handling of <overlap> for it to be completed by <frame> (i.e. blending BEFORE the splice) - no overlap in progress to report to the next iteration, or
split the two uses of <frames> into two parameters by adding another called <prvoverlap> (previous overlap in progress), or
leave things as they are, which allows for never-completed (overlapping) overlaps but also for hard cuts during blending.

To me the first option - altough technically viable - appears counter-intuitive from the user's workflow perspective. The middle option is more in keeping with the purpose originally intended for the script, which is why I opted for it. First post updated accordingly. Note the ShowFrameNumber()s ;).

Many thanks again.

fvisagie
8th December 2012, 21:58
First post updated for 1.1.0:


# 1.1.0 Francois Visagie
# * <overlapn> can now optionally be specified as an absolute frame number -
# AvsP/mod users can now just hit <F11> at all transition boundaries :).
# Default behaviour treating <overlapn> as relative offset remains unchanged.
# * When passed an empty splicelist '""' function now returns <input>, as probably expected.
# Previously spliced to <filtered> on frame 0.