This version has precision and decent performance. Uses the first version of AvgAll2 for speed. Splits the clip into blocks of power-of-2 lengths (effectively treats the frame count as binary). Each block's average is calculated correctly with the simple AvgAll2 since powers of 2 blocks will always split into equal odd/even lists. Blocks are averaged together with weighted merges. Processes 100000 480p frames in around 30s. Needs GScript:
Code:
function AvgAll(clip c) {
GScript("""
frms = c.FrameCount()
pow2 = 1
totl = 0
while (frms > 0) {
if (frms % 2 > 0) {
blkAvg = c.Trim(0,-pow2).AvgAll2()
c = c.Trim(pow2,0)
if (totl == 0) {
avg = blkAvg
} else {
avg = Merge( avg, blkAvg, Float(pow2)/(totl+pow2) )
}
totl = totl + pow2
}
frms = frms / 2
pow2 = pow2 * 2
}
""")
return avg
}
function AvgAll2(clip c) { c.FrameCount() <= 1 ? c : AvgAll2( Merge(c.SelectEven(), c.SelectOdd()) ) }
Edit: The integer rounding errors from so many small merges creep in with longer clips though - there's a gradual color shift.
Edit2: So go 16-bit. Accurate, slower, 10000 frames in a minute. Needs DitherTools:
Code:
function AvgAll_16(clip c) {
GScript("""
c = c.Dither_convert_8_to_16() # To 16 bit
c = c.Dither_lut16("x 0.5 *", U=3,V=3) # down to 15-bit to get faster average below
frms = c.FrameCount()
pow2 = 1
totl = 0
while (frms > 0) {
if (frms % 2 > 0) {
blkAvg = c.Trim(0,-pow2).AvgAll2_16()
c = c.Trim(pow2,0)
if (totl == 0) {
avg = blkAvg
} else {
avg = Merge_16( avg, blkAvg, Float(pow2)/(totl+pow2) )
}
totl = totl + pow2
}
frms = frms / 2
pow2 = pow2 * 2
}
""")
return Dither_add16(avg,avg).DitherPost(mode=-1) # Back to 16 bit then to 8 again
}
# Same as AvgAll2 for 8-bit, but uses faster average (assumes 15-bit input values)
function AvgAll2_16(clip c) {
c.FrameCount() <= 1 ? c \
: AvgAll2_16( Dither_add16(c.SelectEven(),c.SelectOdd()).Dither_lut16("x 0.5 *", U=3,V=3) )
}
# Like Avisynth native Merge but for 16-bit clips
function Merge_16(clip a, clip b, float "weight") {
weight = default(weight, 0.5)
mul1 = String(1.0-weight)
a = a.Dither_lut16("x "+mul1+" *", U=3,V=3)
mul2 = String(weight)
b = b.Dither_lut16("x "+mul2+" *", U=3,V=3)
return Dither_add16(a, b)
}