Log in

View Full Version : Stack frames sequentially?


JELSTUDIO
2nd January 2018, 02:52
I have been searching around but couldn't find any info on this.

Is it possible to add each next frame, in a video, to the frames already processed?

In pseudo-code something like:

-read current frame
-blend current frame with image stored in buffer
-replace image in buffer with this new blend result
-repeat

Basically building a stack frame by frame using one of the blend modes.

Thanks
jacob.

StainlessS
2nd January 2018, 03:12
Not sure if this matches your description, but Clipblend:- https://forum.doom9.org/showthread.php?t=168048&highlight=ClipBlend

Can blend all frames with equal weight, producing single frame result.

Or to get a single frame average of all frames in a clip
# ------------------------
Avisource("D:\avs\test.avi")
ClipBlend()
return Trim(FrameCount-1,-1)
# ------------------------


EDIT: It will scan entire clip before returning single frame result, so it may take some time but is as fast as it can be
(as no frames displayed until final result).

JELSTUDIO
3rd January 2018, 01:46
@StainlessS Thank you :)

This will 'average' a group of frames.

Is there any way I can use other blend-modes with this? (Such as 'lighten', for example)

StainlessS
3rd January 2018, 08:26
Such as 'lighten

Sorry, dont have the faintest idea how to acheive it, nor what that does, perhaps scripts in the other threads linked could be terrorized into doing what you require.

JELSTUDIO
3rd January 2018, 09:41
Sorry, dont have the faintest idea how to acheive it, nor what that does, perhaps scripts in the other threads linked could be terrorized into doing what you require.

'Lighten' only updates a pixel if it is a brighter value than the current.

It's useful for creating FX-images such as star-trails or car-trails (Images of streets with long lights of the car's front and rear-lights)

An example: https://upload.wikimedia.org/wikipedia/commons/5/51/Car_light_trails_in_Montreal.jpg

I wanted to create some photos like that from video-sequences of fireworks :)

There is star-trail software that can already do it from image-files, but they get really heavy with more than a few hundred images (And the video I have has over 100,000 images)

Anyway, thanks again for your input.
Happy New Year! :)

StainlessS
3rd January 2018, 10:19
So basically its a channel Max function of all prev frames (incl current).
So, we are talking about every frame processed as you said (not from n frames previous to current) ?
What color space ? (I dont do Avs+).

And the video I have has over 100,000 images

So every pixel channel is max of all 100,000 prev same pixel channels and single frame result ?

I guess I could do that.
EDITED:

hanfrunz
3rd January 2018, 11:45
Have a look at the overlay() function. Maybe you could use a recursive user defined function to loop through all images, but i have no idea if 100k images are possible. Maybe you have to split them up in groups.
regards
hanfrunz

poisondeathray
3rd January 2018, 16:32
'Lighten' only updates a pixel if it is a brighter value than the current.

It's useful for creating FX-images such as star-trails or car-trails (Images of streets with long lights of the car's front and rear-lights)

An example: <snip>

I wanted to create some photos like that from video-sequences of fireworks :)

There is star-trail software that can already do it from image-files, but they get really heavy with more than a few hundred images (And the video I have has over 100,000 images)

Anyway, thanks again for your input.
Happy New Year! :)


In avisynth there is a way with scriptclip, but it will crash probably so many images (it did for me back then with large dimension image sets, lots of images and avisynth x86). I would definitely try it with avisynth+ x64

Here is an example of star trails, with link to source/script/output
https://forum.videohelp.com/threads/360085-Time-lapse-But-with-trailing-stars#post2279836

real.finder
3rd January 2018, 18:23
I think this did what OP ask

ScriptClip("""
o = last
c = current_frame
try{b=b} catch(error_msg) {b=last}
trim(1,0)
c == 0 ? o : last
c == 0 ? last : merge(last,b)
global b = last
last
""")

edit: this realy work now but slow
ScriptClip("""
o = last
c = current_frame
try{bb=isclip(b)} catch(error_msg) {bb=false}
bb ? last : trim(1,0)
c == 0 ? o : last
bb ? merge(last,b.trim(1,0)) : last
global b = last
last
""")

edit: Loop(2,0,0) instead of trim(1,0) will make it faster

edit: ok, here is the last code
ScriptClip("""
try{bb=isclip(b)} catch(error_msg) {bb=false}
bb ? merge(last,b.Loop(2,0,0)) : last
global b = last
last
""")

and ofc you can use another function instead of merge()

lansing
3rd January 2018, 18:33
You can probably do it in vapoursynth with a for loop, the only problem is that I couldn't find an overlay function, I recall someone ported it somewhere on the internet. And I don't know how to load the avisynth.dll itself into vs so I can use the avs overlay function.


clip = core.avisource.avisource(r"test.avi")
clip_range = range(len(clip))

for n in clip_range:
if n == 0:
accum_clip = clip[n]
elif n < len(clip_range):
accum_clip = overlay(accum_clip, clip[n])

accum_clip.set_output()

raffriff42
3rd January 2018, 19:42
This seems to work okay. Tested on 20000 frames. Takes time but not memory.

Requires AVS+, but can be adapted to classic AviSynth by wrapping the function in a GScript call.

When 'blendfactor' = 0, the output is a still frame - a blend of all frames; when 'blendfactor' = (say) 10*framerate, the output is a series of dissolves between still frames at ten-second intervals - each still being a blend of one ten-second segment. Makes a weird effect.
#######################################
### Blend a number of frames together, up to all frames in the clip
## Output framerate and duration are same as the source.
##
## @ blendfactor - blend 'n' frames; if 1, do nothing; if <=0, blend all frames
## @ blend_mode - see Overlay
## @ blend_opacity - see Overlay
##
## Examples
## | ## average of all frames
## | FrameBlendX(0, "blend", 0.5)
## | ## streak effect (with the right source)
## | FrameBlendX(0, "lighten", 1.0)
##
function FrameBlendX(clip C, int blendfactor, string blend_mode, float blend_opacity)
{
C
limit = (blendfactor<=0) ? 1 : Max(1, FrameCount/blendfactor)

while (FrameCount>limit) {
Overlay(SelectEven, SelectOdd, mode=blend_mode, opacity=blend_opacity)
}
ConvertFPS(C)
return (blendfactor==1) ? C : Last
}

real.finder
3rd January 2018, 21:43
this is edit to make it work with classic AviSynth without anything


#######################################
### Blend a number of frames together, up to all frames in the clip
## Output framerate and duration are same as the source.
##
## @ blendfactor - blend 'n' frames; if 1, do nothing; if <=0, blend all frames
## @ blend_mode - see Overlay
## @ blend_opacity - see Overlay
##
## Examples
## | ## average of all frames
## | SFrameBlendX(0, "blend", 0.5)
## | ## streak effect (with the right source)
## | SFrameBlendX(0, "lighten", 1.0)
##
function SFrameBlendX(clip C, int "blendfactor", string "blend_mode", float "blend_opacity")
{
global SFrameBlendX_blend_mode=blend_mode
global SFrameBlendX_blend_opacity=blend_opacity
C
limit = (blendfactor<=0) ? 1 : Max(1, FrameCount/blendfactor)
global SFrameBlendX_limit = limit
FrameCount!=1 ? ScriptClip("""
try{bb=isclip(b)} catch(error_msg) {bb=false}
b = bb ? SFrameBlendX_limit>1 ? Overlay(b.Loop(2,0,0),last,opacity=float(SFrameBlendX_limit)/FrameCount) : b.Loop(2,0,0) : nop()
b = bb ? b.Overlay(last, mode=SFrameBlendX_blend_mode, opacity=SFrameBlendX_blend_opacity) : last
return b
""") : last
}


SFrameBlendX(5,"lighten")

it's not 100% same, but did the job

real.finder
4th January 2018, 00:54
In avisynth there is a way with scriptclip, but it will crash probably so many images (it did for me back then with large dimension image sets, lots of images and avisynth x86). I would definitely try it with avisynth+ x64

Here is an example of star trails, with link to source/script/output
https://forum.videohelp.com/threads/360085-Time-lapse-But-with-trailing-stars#post2279836

yes it did crash

it start fast then slow then crash with access error

but this code don't crash!

ScriptClip("""
o = last
c = current_frame
try{bb=isclip(b)} catch(error_msg) {bb=false}
bb ? last : trim(1,0)
c == 0 ? o : last
bb ? merge(last,b.trim(1,0)) : last
global b = last
last
""")


but it's slow from begin and eat cpu

the other codes slowdown and make less use of cpu before crash, maybe use a lot of ram

anyway, why avs don't clean the unneeded old data in runtime to avoid that? is GRunT has some solution for this?

raffriff42
4th January 2018, 01:35
To run the script I posted in classic AviSynth:

1) Load GScript
http://avisynth.nl/index.php/GScript

2) Wrap "while" loop in GScript:function FrameBlendX(clip C, int blendfactor, string blend_mode, float blend_opacity)
{
C
limit = (blendfactor<=0) ? 1 : Max(1, FrameCount/blendfactor)
GScript("""
while (FrameCount>limit) {
Overlay(SelectEven, SelectOdd, mode=blend_mode, opacity=blend_opacity)
}
""")
ConvertFPS(C)
return (blendfactor==1) ? C : Last
}

real.finder
4th January 2018, 03:08
yes it work now in classic AviSynth

but it not same effect as the one I post

JELSTUDIO
4th January 2018, 04:07
To run the script I posted in classic AviSynth:

1) Load GScript
http://avisynth.nl/index.php/GScript

2) Wrap "while" loop in GScript:function FrameBlendX(clip C, int blendfactor, string blend_mode, float blend_opacity)
{
C
limit = (blendfactor<=0) ? 1 : Max(1, FrameCount/blendfactor)
GScript("""
while (FrameCount>limit) {
Overlay(SelectEven, SelectOdd, mode=blend_mode, opacity=blend_opacity)
}
""")
ConvertFPS(C)
return (blendfactor==1) ? C : Last
}


This works in classic avisynth :)

It creates a single blended image that lasts as long as the source-video (Takes a while to generate it, but not really very long considering the large amount of frames it has to go through)

Avisynth is amazing :)
Thank you very much everybody :)

I wonder why 'normal' video-editors don't have this (Frame accumulation/stacking) as a possible effect, but I haven't seen it in any.

Here's the final script I use (In case others want to try this)
#######################################
### Blend a number of frames together, up to all frames in the clip
## Output framerate and duration are same as the source.
##
## @ blendfactor - blend 'n' frames; if 1, do nothing; if <=0, blend all frames
## @ blend_mode - see Overlay
## @ blend_opacity - see Overlay
##
## Examples
## | ## average of all frames
## | FrameBlendX(0, "blend", 0.5)
## | ## streak effect (with the right source)
## | FrameBlendX(0, "lighten", 1.0)
##
function FrameBlendX(clip C, int blendfactor, string blend_mode, float blend_opacity)
{
C
limit = (blendfactor<=0) ? 1 : Max(1, FrameCount/blendfactor)
GScript("""
while (FrameCount>limit) {
Overlay(SelectEven, SelectOdd, mode=blend_mode, opacity=blend_opacity)
}
""")
ConvertFPS(C)
return (blendfactor==1) ? C : Last
}

DirectShowSource("F:\JEL's_OwnOriginalRecordings\Canon\Orig incoming\EOS 80D\20180101\EOS 80D_03571.MP4")

FrameBlendX(0, "lighten", 1.0)

JELSTUDIO
4th January 2018, 04:33
yes it work now in classic AviSynth

but it not same effect as the one I post

I tried yours as well and loved the ability to watch the effect accumulate over time. To actually see the final image being built, step by step, looks very cool :)

I could not get it to run for more than 200-300 images before it crashed though (In classic Avisynth)

I don't know enough about this to really make a qualified guess on why it crashes, but it seems like a memory-issue or perhaps just memory-mismanagement (Which I assume is related to how Avisynth handles memory when trying to buffer stuff)

Technically it shouldn't require much memory though, since you only really need to hold 2 images in memory at any time (The incoming new image, and the buffered one you add the new info to)

But as said, my avisynth level of knowledge is limited to basic avs scripting of the built-in filters and a few addon filters :)

StainlessS
4th January 2018, 14:05
Just curious how this clip might look processed via RgbAmplifier:- https://forum.doom9.org/showthread.php?t=168293

With max radius [EDIT: Max radius is only +- 99 frames] and varying values for Multiplier.


RgbAmplifier (RGB24 and RGB32)
========================================
An Avisynth plugin to amplify color shifts

Given a clip, this filter examines every pixel of every frame and independently multiplies the difference of its specific
R, G and B values from the average R, G and B values of that same pixel location spanning a defined radius of adjacent frames.
If the new values are above or below the allowed RGB values, they are capped at those limits.
The revised RGB values replace the original values, and the amplified clip is returned.
If the Multiplier is set to a value of zero, the plugin acts as a temporal frame averager.

This plugin will:
Convert seemingly imperceptible color changes into obvious color changes
Highlight seemingly imperceptible movements of high contrast pixels
Sharpen the details of moving objects
Cause distortion or ghosting/transparency effect to moving objects (an unavoidable side effect)

This plugin requires RGB24 or RGB32 color space, and is intended for forensic analysis of videos created from fixed position cameras.
Usage on non-stationary cameras may produce unpredictable or content destructive results. The faster the object and/or the greater
the radius, the greater the risk of negatively impacting the visual accuracy of the moving object.

=====
Usage
RgbAmplifier(clip clip,int "Radius"=2,float "Multiplier"=2.0,int "Rmin"=0,int "GMin"=0,int "Bmin"=0, \
int "RMax"=255,int "GMax"=255,int "BMax"=255,bool "Precise"=false)


DavidHorman did a variation that will also support YUV, Amp:- http://horman.net/avisynth/

wonkey_monkey
4th January 2018, 15:03
If you're trying to average all frames, shouldn't it be moved to an RGB64 or float format to avoid accuracy loss?

Averaging 4096 numbers in pairs, as repeated use of overlay would, might result in being up to ±4 out on the result (and that's only on average, it could be higher).

raffriff42
5th January 2018, 01:25
davidhorman, you're right in theory. I haven't noticed a difference, but I wasn't looking too hard.
High bit depth would definitely be a good idea here.

One thing, there seems to be a bug in ChangeFPS (corrrection, ConvertFPS) at high bit depth.
The bug exists since AVS+ r2440 (at least) through r2580, x86 or x64, YUV or RGB.
The output has what looks like posterization or "stripes", but I think it's a possible frame seeking issue.

Here's my (quick & dirty) test codefunction _x(clip C, float x) {
return C.Subtitle("!", align=2, x=x, size=C.Height)
}
## make a ! mark scroll left to right over black
BlankClip(width=640, height=260, pixel_type="YV12", length=1024)
Animate(0, 1000, "_x",
\ -0.2*Width,
\ 1.2*Width
\ ).Trim(0, 1010)

ConvertBits(12) ## test high bit depth(s)
FrameBlendX(48, "blend", 0.5)

ConvertBits(8)
ConvertToYV12
TurnRight.Histogram.TurnLeft
return Last
8-bit output: showing tail end of crossfade between blended still frames
https://www.dropbox.com/s/mewolja8jt3qr8j/FrameBlendX-01%2048x%2C8bit%2CConvertFPS.png?raw=1

12-bit output: something is wrong
https://www.dropbox.com/s/kj845j2qticit01/FrameBlendX-02%2048x%2C12bit%2CConvertFPS.png?raw=1

Substitute ChangeFPS: output is good but crossfade effect is gone (crossfade only matters if blendfactor>1)
https://www.dropbox.com/s/884w8wci15r2xmz/FrameBlendX-03%2048x%2C12bit%2CChangeFPS.png?raw=1

StainlessS
5th January 2018, 02:04
Nice one Sherlock :)

EDIT: Maybe small prob

function _x(clip C, float x) {
return C.Subtitle("!", align=2, x=x, size=c.Height)
}


What is FrameBlendX ?

raffriff42
5th January 2018, 02:59
What is FrameBlendX ?
see post #11 (https://forum.doom9.org/showthread.php?p=1829133#post1829133)

Maybe small prob
That's what I get for "cleaning up" my code without testing it! Thx!

JELSTUDIO
5th January 2018, 04:33
I have uploaded a 60second clip in case somebody wants to do some testing themselves (I think you have to be Vimeo member to download the original video-file): https://vimeo.com/249754635

EDIT: added a resulting image from stacking the above video-clip using this software: http://www.markus-enzweiler.de/StarStaX/StarStaX.html

https://forum.doom9.org/attachment.php?attachmentid=16181&stc=1&d=1515124358

StainlessS
5th January 2018, 12:34
Filehost (eg MediaFire) would have been better idea, but thanx.

pinterf
5th January 2018, 16:20
davidhorman, you're right in theory. I haven't noticed a difference, but I wasn't looking too hard.
High bit depth would definitely be a good idea here.

One thing, there seems to be a bug in ChangeFPS at high bit depth.

Not ConvertFPS? That function was so well hidden that was never upgraded to high bit depth in Avisynth+

raffriff42
5th January 2018, 19:21
ConvertFPS, sorry. But yeah, I wouldn't have expected bit depth to make any difference to it.

JELSTUDIO
6th January 2018, 16:31
Filehost (eg MediaFire) would have been better idea, but thanx.

I uploaded it here since I don't have a mediafire account (This link is only valid for a month though) : https://ufile.io/fnry8

If anybody wants to put it on mediafire, feel free (I don't know if mediafire is permanent, but if people prefer it on there it's cool :) )

StainlessS
6th January 2018, 17:19
I don't know if mediafire is permanent

Yep, Permanent.
Free account available with (looking at my free account) 54GB of space. [191 MB used, so I gots me some space left :) ]
SendSpace (which some prefer), free account files are deleted 30 days after most recent download.

EDIT: For view single image direct within forum, PostImage account is free, does not expire, and no need for an account either [anonymous post].
(just post the "Hotlink for forums" link [there are many options] into a forum post.)
PostImage:- http://postimg.org

EDIT:
https://s20.postimg.org/otflyrvp9/hotlink_For_Forum.jpg (https://postimages.org/)

EDIT: Watch out, sometimes the 'HotLink for Forums' is the 2nd bottom option, sometimes the 3rd bottom option (no idea why).
EDIT: "no idea why", maybe additional option if image is eg .png rather than .jpg.

raffriff42
7th January 2018, 07:08
there seems to be a bug in ... ConvertFPS at high bit depth.
Here's a quick & dirty workaround: use Stack16 ConvertBits(16)
ConvertToStacked
ConvertFPS(C)
ConvertFromStacked
ConvertBits(C.BitsPerComponent)

pinterf
8th January 2018, 09:34
Here's a quick & dirty workaround: use Stack16 ConvertBits(16)
ConvertToStacked
ConvertFPS(C)
ConvertFromStacked
ConvertBits(C.BitsPerComponent)

Actually the code inside accumulates pixels on byte level so it won't be correct.
https://github.com/pinterf/AviSynthPlus/blob/MT/avs_core/filters/fps.cpp#L658
https://github.com/pinterf/AviSynthPlus/blob/MT/avs_core/filters/fps.cpp#L714

raffriff42
8th January 2018, 15:00
Right of course. Bottom eight bits will be garbage.

JELSTUDIO
9th January 2018, 11:57
Yep, Permanent.
Free account available with (looking at my free account) 54GB of space. [191 MB used, so I gots me some space left :) ]
SendSpace (which some prefer), free account files are deleted 30 days after most recent download.

EDIT: For view single image direct within forum, PostImage account is free, does not expire, and no need for an account either [anonymous post].
(just post the "Hotlink for forums" link [there are many options] into a forum post.)
PostImage:- http://postimg.org

EDIT:
https://s20.postimg.org/otflyrvp9/hotlink_For_Forum.jpg (https://postimages.org/)

EDIT: Watch out, sometimes the 'HotLink for Forums' is the 2nd bottom option, sometimes the 3rd bottom option (no idea why).
EDIT: "no idea why", maybe additional option if image is eg .png rather than .jpg.

The internet; there's only a million options :)

real.finder
7th February 2018, 00:09
I tried yours as well and loved the ability to watch the effect accumulate over time. To actually see the final image being built, step by step, looks very cool :)

I could not get it to run for more than 200-300 images before it crashed though (In classic Avisynth)

I don't know enough about this to really make a qualified guess on why it crashes, but it seems like a memory-issue or perhaps just memory-mismanagement (Which I assume is related to how Avisynth handles memory when trying to buffer stuff)

Technically it shouldn't require much memory though, since you only really need to hold 2 images in memory at any time (The incoming new image, and the buffered one you add the new info to)

But as said, my avisynth level of knowledge is limited to basic avs scripting of the built-in filters and a few addon filters :)

days ago StainlessS made a nice solution for both avs and avs+ https://forum.doom9.org/showthread.php?t=175212

JELSTUDIO
7th February 2018, 14:50
I will take a closer look. Thank you :)

raffriff42
9th February 2018, 14:54
High bit depth would definitely be a good idea here. One thing, there seems to be a bug in ConvertFPS at high bit depth.
The bug exists since AVS+ r2440 (at least) through r2580, x86 or x64, YUV or RGB.
The output has what looks like posterization...
I made a ConvertFPS workaround script, mostly as an exercise. It turned out to be quite a difficult exercise, because of my lack of math skills. It attempts to duplicate ConvertFPS functionality, with the added option of blending more frames. Sometimes when converting framerate, instead of blending the 2 nearest frames, blending 3 or even 4 looks smoother. Blending more than that, up to hundreds, is available as a (bad) effect. AVS+ only.
(note - it does not enforce a minimum target framerate of 2/3 source like ConvertFPS -- a real benefit)
(note - it can blend frames without changing framerate)##################################
### attempted ConvertFPS workalike with expanded blend options
##
## alternate signature - set framerate to match template clip 'T'
##
function InterpolateFPS(clip C, clip T, bool "smoother",
\ float "blendrange", bool "bidirectional")
{
R = C.InterpolateFPS(T.Framerate, smoother, blendrange, bidirectional)
return R.AssumeFPS(T)
}

##################################
### attempted ConvertFPS workalike with expanded blend options
##
## alternate signature - set framerate to numerator/denominator
##
function InterpolateFPS(clip C, int fpsnum, int fpsden, bool "smoother",
\ float "blendrange", bool "bidirectional")
{
R = C.InterpolateFPS(Float(fpsnum)/Float(fpsden), smoother, blendrange, bidirectional)
return R.AssumeFPS(fpsnum, fpsden)
}

##################################
### attempted ConvertFPS workalike with expanded blend options
##
## @ C - source; bit depth >= 10 suggested
## @ fps - target FrameRate
## @ smoother - if true, the default 'blendrange' is a little larger; default false
## (with 'smoother'=false, the result looks like ConvertFPS)
## . . . . . . . . . . . . . . . . . . . .
## @ blendrange - range of frames to be blended in each direction;
## it is possible to specify a huge range; performance will be terrible.
## Note, all frames may not be visible; frames farther from
## the current frame are given less weight (aka strength aka opacity).
## Default is "auto" - a value determined by (new fps) / (old fps).
## If 'blendrange' is set, it overrides 'smoother'.
## @ bidirectional - if true (default), blend both earlier and later frames;
## if false, only blend earlier frames, for a bad motion trails effect.
##
## @ version 0.1.6 2018-02-09
##
## TODO: sometimes a little brighter or darker than the source (fixed?)
## TODO: needs thorough stress testing
## TODO: maybe constrain max diff-per-pixel (like TemporalSoften, LimitedSharpen, SeeSaw)
##
function InterpolateFPS(clip C, float fps, bool "smoother",
\ float "blendrange", bool "bidirectional")
{
C
KillAudio

## default 'blendrange' - interpolation of empirically determined values:
rnge = Default(smoother, false)
\ ? Spline(fps/C.FrameRate,
\ 0.1, 1.0,
\ 0.2, 2.0,
\ 0.5, 1.5,
\ 0.7, 1.8,
\ 1.0, 3.0,
\ 2.0, 5.0,
\ 10.0, 20.0,
\ false)
\ : Spline(fps/C.FrameRate,
\ 0.1, 0.2,
\ 0.2, 0.5,
\ 0.5, 1.0,
\ 0.7, 1.0,
\ 1.0, 1.2,
\ 2.0, 3.0,
\ 10.0, 15.0,
\ false)

rnge = Default(blendrange, Min(Max(0.1, rnge), 10.0))
bidi = Default(bidirectional, true)

global gifps_C = Last
global gifps_rng = rnge
global gifps_bidi = bidi

## "destination" clip, will become the output (no audio until later)
BlankClip(Last, audio_rate=0, fps=fps, length=Round(C.FrameCount*fps/C.FrameRate))

#################################
ScriptClip(Last, """

## c... = clip
## n... = frame number
## r... = frame rate
## t... = time

csrc = gifps_C ## source clip
cdst = Last ## dest clip: start w/ BlankClip
ndst = current_frame
rdst = cdst.FrameRate ## output framerate
rsrc = csrc.FrameRate ## source ' '
trng = (gifps_rng / rdst) ## interp range

## get time @ current destination frame and ± range extremes
t0 = Float(ndst) / rdst
tfrst = t0 - trng
tlast = t0 + trng

## get source frame numbers @ current time and ± range extremes
ns0 = Round(t0 * rsrc)
nsfrst = Round(tfrst * rsrc)
nsfrst = Min(Max(0, nsfrst), csrc.FrameCount-1)
nslast = Round(tlast * rsrc)
nslast = (gifps_bidi)
\ ? Min(Max(0, nslast), csrc.FrameCount-1)
\ : Min(Max(0, ns0), csrc.FrameCount-1)

#############################
## loop 1: get sum of all frame weights
tot_wgt = 0.0
subtot = 1.0
for (n=nsfrst, nslast) {

## time corresponding to source frame 'n'
tsrc = Float(n) / rsrc

## assign a weight based on distance from current frame
wgt = Max(0.0, 1.0 - (Abs(t0 - tsrc) / trng))

## adjust weight for position in blend loop
## (earlier frames get more weight)
subtot = subtot + wgt
wgt = wgt / subtot

## accumulate sum of weights
tot_wgt = tot_wgt + wgt
}

#############################
## loop 2: merge the frames
Last = cdst
cprev = Trim(0, ndst-1) ## previous frames
cpost = Trim(ndst+1, 0) ## subsequent frames
subtot = 1.0
for (n=nsfrst, nslast) {

## time corresponding to source frame 'n'
tsrc = Float(n) / rsrc

## re-calc weights (same as above)
wgt = Max(0.0, 1.0 - (Abs(t0 - tsrc) / trng))

subtot = subtot + wgt
wgt = wgt / subtot

## adjust weight for aprox. equal brightness to input
## (HACK) (seems to reduce added flicker)
wgt = 3.0 * wgt / tot_wgt
wgt = Min(Max(0.0, wgt), 1.0)

## add this frame to the output, using weight value
csrc1 = csrc.Trim(n, length=1)
cdst1 = Trim(ndst, length=1)
(wgt<0.0005) ? Last
\ : cprev + cdst1.Merge(csrc1, wgt) + cpost

## (you can try something like this instead, but it's not amazing)
#\ : cprev + cdst1.Overlay(csrc1, mode="softight", opacity=wgt) + cpost
}
return Last
""")
(C.HasAudio) ? AudioDub(C) : Last
return Last
}


A better 'blendrange' calculation is needed. (EDIT fixed with Spline (http://avisynth.nl/index.php/Internal_functions#Spline) function)

pinterf
9th February 2018, 17:08
(note - it does not enforce a minimum target framerate of 2/3 source like ConvertFPS -- a real benefit)

Do you happen to know the reason of the original behaviour?

raffriff42
9th February 2018, 18:52
pinterf, I think it's because ConvertFPS blends a maximum of 2 frames.
(EDIT I think that's what it does. The wiki (http://avisynth.nl/index.php/FPS#ConvertFPS) says, "This is to prevent frame skipping.")