View Full Version : ApparentFPS v1.09
StainlessS
2nd November 2014, 02:09
ApparentFPS v1.09.
Plugin, ApparentFPS dll's for Avisynth v2.58 and v2.6, and avs26+ x64.
EDIT: zip includes full VS2008 Project files for easy re-build.
NOW Requires VS 2008 CPP Runtimes.
ApparentFPS: by StainlessS.
Plugin dll's for Avisynth versions v2.58 and v2.6/+ (x86) and v2.6/+ x64.
Shows underlying framerate where a clip has had many duplicates inserted, easier than counting unique frames.
ApparentFPS(clip c,Float "DupeThresh"=0.5,Float "FrameRate"=clp.FrameRate,Int "Samples"=Int(Ceil(FrameRate)),
\ Float "ChromaWeight"=1.0/3.0,String "Prefix"="", Bool "Show"=True,Bool "Verbose"=True, Bool "Debug"=False,
\ Int "Mode"=0,
\ Int "Matrix"=(c.Width>1100||c.Height>600)?3:2, # 2=Pc.601 : 3=PC.709
\ Int "BlkW"=64, Int "BlkH"=BlkW, Int "oLapX"=BlkW/2, Int "oLapY"=BlkH/2
\)
DupeThresh, Float, Default 0.5 (Greater than 0.0), suggest 0.25 -> 1.0. FrameDifference below or equal to this is a dupe.
FrameRate, Float, Default clp.FrameRate. # FrameRate of clp. Probably of no great use, just use default.
Samples, Int, Default Int(Ceil(FrameRate)) # (1 or more). Min Frames to sample for underlying framerate detection. (>= FrameRate).
ChromaWeight, Float, Default 1.0/3.0, (0.0 -> 1.0). # Suggest 1.0/3.0 -> 1.0/2.0. (Use 0.0 for GreyScale YUV).
Prefix, String, Default "". # Prefix for returned Local vars, Default "" = None returned.
Show, Bool, Default True. # Show Info
Verbose, Bool, Default True. # Additional Info, Sequence as binary digits.
Debug, Bool, Default False. # Not of any great use.
### Added v1.05
Mode Int, default 0. (0 -> 1).
0) Standard RT_FrameDifference mode.
1) FrameMovement mode, difference is greatest difference for any BlkWxBlkH block.
Matrix, Default (c.Width>1100||c.Height>600)?3:2. 2)=PC.601 : 3=Pc.709. Used in Conversion of RGB to Luma-Y.
BlkW Int, Default 64. (8 < BlkW, Even Only). Block Width for Mode 1 (not used for Mode=0).
BlkH Int, Default BlkW. (8 < BlkH, Even Only). Block Height for Mode 1 (not used for Mode=0).
oLapX Int, Default BlkW/2. (0 <= oLapX). Horizontal block Overlap for Mode = 1.
oLapY Int, Default BlkH/2. (0 <= oLapY). Vertical block Overlap for Mode = 1.
###
Function samples over Samples frames, and shows instantaneous ApparentFPS and MaxApparentFPS, the real and perhaps most useful
result is MaxApparentFPS.
If Prefix = "" (Default) then does not return any info Local vars. When set to eg "P_" will set Local P_ApparentFPS
and P_MaxApparentFPS as Float.
Also returns Locals P_MinAboveDupeDif, P_MaxBelowDupeDif and P_CurrentDif which may assist in choosing DupeThresh, they are min and
max values so far, and difference between current and previous frame. P_MinAboveDupeDif is the difference to previous frame of the
frame which has lowest difference that what considered NOT a dupe (above DupeThresh), MaxBelowDupeDif max difference of frame that WAS
considered a dupe (below DupeThresh). Ideally in a clip containing duplicates, there would be a distinct gap between these two
above/below dup dif values with DupeThresh somewhere in between.
v1.07, Also sets P_Maxndl and P_Maxndl_Frm, Max Non dupe length (up to samples length) and frame number where first occurs.
v1.08, Also sets P_MinAboveDupeDif_Frm and P_MaxBelowDupeDif_Frm.
!!! NOTE !!!, It may be better to set DupeThresh a little too high rather than too low, (where some motion frames will be mistaken as
dupes), as it is likely that there will be at least one sequence in clip where there is sufficient motion to accurately set
MaxApparentFPS.
Can switch Off metrics (Show=False), and set eg Prefix = "P_" to return Locals P_ApparentFPS and P_MaxApparentFPS with rough
instantaneous estimate of current underlying framerate and maximum underlying framerate detected so far.
NOTE, ApparentFPS can range from 0.0 in static scene (can be measure of how 'active' a scene is).
'Verbose' (Default True) shows additional info, a string of 1's and 0's showing duplicate and unique frames. Also shown in info is
the Unique frame count for the current Sample spread (which will be reduced at either end of clip), current ApparentFPS is calculated as
(FrameRate * UniqueFrameCount / CurrentSampleSpread), so where UniqueFrameCount == CurrentSampleSpread, the result is FrameRate, However,
if CurrentSampleSpread is reduced due to being near clip ends, then current ApparentFPS will NOT be assigned to P_MaxApparentFPS even
where greater than current P_MaxApparentFPS.
The rest should be pretty straight forward.
v1.01, Extended maximum samples from 256 to 1024 (silently limited to 1024).
Maximum number of binary digit flags limited to 64 when Verbose=True.
Max number of Debug Hex Values limited to 8 (ie 8 * 32 bits = 256 bit flags).
v1.03, Added 'UniqMax' Verbose metrics indicator, maximum number of unique frames in Sample range, so far.
NOTE, a clip that has been edited after dupe insertion may seem to jump up in MaxApparentFPS at edits, due to dupes being cut out.
You can reduce this effect by setting eg Samples=Int(Ceil(FrameRate))*10, but it will not show any MaxApparentFPS until you have
scanned forward Samples/2 frames (Samples are centered around current frame). It will also take a little time to start up as it
has to sample a lot of frames at startup. NOTE, Samples is silently (no error) limited to 1024.
v1.05.
Added Some args, Matrix and Mode/blk args.
Mode=1, ie RT_FrameMovement mode, measures frame differences using a Block mode, chops the frame into BlkWxBlkH blocks, and difference
is the maximum difference for any one block. Blocks can also be OverLapping, by default block sizes / 2, so by default each frame
is multiply sampled 4 times (ie is slower than Mode=0, but can set oLapX and oLapY to 0, for no overlaps).
Mode=1, might be of use where there is very little movement in a sequence, but for most uses, probably best left at default Mode=0.
Also Note, in v1.05, ChromaWeight also alters differencing method used for RGB. Where ChromaWeight=0.0, will use LumaDifferencing
(via Matrix arg), and where ChromaWeight > 0.0, then will use (AveRedDif+AveGrnDif+AveBluDif)/3.0, for both Mode=0 and Mode=1.
v1.07.
Added Maxndl, Max Non dupe length and frame number where first occurs.
v1.08.
Added P_MinAboveDupeDif_Frm and P_MaxBelowDupeDif_Frm.
StainlessS
v1.05, Mode=1 (RT_FrameMovement Block mode) will likely require higher DupeThresh, maybe 1.0 or more (default 0.5).
See post #23 for equivalent script with ALMOST identical functionality.[docs purposes only, dont use it] http://forum.doom9.org/showthread.php?p=1698788#post1698788
https://s20.postimg.cc/3ym8sejjx/Apparentfps_zpsmvocbjvh.png (https://postimg.cc/image/ap2q1u6pl/)
Above image, 0's show dupes, 1's non dupes, middle 0 (a bit lighter) is current frame.
Zip available via Mediafire (below this post).
StainlessS
2nd November 2014, 02:12
EDIT: Below is early version in script, dont use it, use the plugin.
This was how v0.0 was implemented, left here in situ. See post #23 for current implementation in script.
S_ApparentFPS.avs
Function S_ApparentFPS(clip clp,Float "DupeThresh",Float "FrameRate",Int "HalfFrameRes",Float "ChromaWeight",
\ String "Prefix", Bool "Show", Bool "Debug") {
/*
S_ApparentFPS: by StainlessS. Requires RT_Stats by StainlessS, GScript & Grunt Plugins (c) Gavino.
Multi-Instance script version.
Shows underlying framerate where a clip has had many duplicates inserted, easier than counting unique frames.
S_ApparentFPS(clip clp,Float "DupeThresh"=0.75,Float "FrameRate"=clp.FrameRate,Int "HalfFrameRes"=ceil(FrameRate),
\ Float "ChromaWeight"=1.0/3.0,String "Prefix"="", Bool "Show"=True, Bool "Debug"=False)
DupeThresh, Float, Default 0.75 (Greater than 0.0), suggest 0.5 -> 1.0. FrameDifference below this is a dupe.
FrameRate, Float, Default clp.FrameRate. # FrameRate of clp. Probably of no great use, just use default.
HalfFrameRes, Int, Default Ceil(FrameRate) (1 or more). # Min Frames to sample for underlying framerate detection. Suggest FrameRate as min.
ChromaWeight, Float, Default 1.0/3.0, (0.0 -> 1.0). # YUV Only. Suggest 1.0/3.0 -> 1.0/2.0. (Use 0.0 for GreyScale YUV).
Prefix, String, Default "". # Prefix for returned Global vars, Default "" = None returned.
Show, Bool, Default True.
Debug, Bool, Default False.
Function samples over 2*HalfFrameRes frames, but every HalfFrameRes frames it remembers frame position and Dupe Count and when it
reaches 2*HalfFrameRes frames it swaps start position to use previous HalfFrameRes frames position and Dupe Count, this enables it to
sample 2*HalfFrameRes frames starting with HalfFrameRes frames worth of data (See the code if that is as clear as mud).
The function may tend to underestimate underlying framerate slightly where HalfFrameRes is at default of about FrameRate. If supplied
with HalfFrameRes less than FrameRate, will tend to over-estimate underlying framerate.
If Prefix = "" (Default) then does not return any info Global vars. When set to eg "S_" will set Global S_ApparentFPS
and S_MaxApparentFPS as Float. If Multi-Instance then must give unique prefix for each instance returned Globals.
Can switch Off metrics (Show=False), and set eg Prefix = "S_" to return Globals S_ApparentFPS and S_MaxApparentFPS with rough
instantaneous estimate of current underlying framerate and maximum underlying framerate detected so far (perhaps of use for something).
NOTE, ApparentFPS can range from 0.0 in static scene (can be measure of how 'active' a scene is).
The rest should be pretty straight forward.
*/
myName="S_ApparentFPS: "
Assert(RT_FunctionExist("GScriptClip"),myName+"Essential GRunT plugin installed, http://forum.doom9.org/showthread.php?t=139337")
Assert(RT_FunctionExist("GScript"),myName+"Essential GScript plugin installed, http://forum.doom9.org/showthread.php?t=147846")
DupeThresh=Default(DupeThresh,0.75) # (0.75) Suggest about 0.5 -> 1.0
Assert(DupeThresh>0.0,myName+"DupeThresh Must be greater than 0.0")
FR=Float(Default(FrameRate,clp.FrameRate))
Assert(FR>0.0,myName+"FrameRate Must be greater than 0.0")
HalfFrameRes=Default(HalfFrameRes,Ceil(FR)) # (Ceil(FrameRate)), suggest about int(1.0 * framerate)
Assert(HalfFrameRes>0,myName+"HalfFrameRes Must be greater than 0")
ChromaWeight=Float(Default(ChromaWeight,1.0/3.0)) # (1.0/3.0) YUV Only, suggest 1.0/3.0 -> 1.0/2.0
Assert(ChromaWeight>=0.0 && ChromaWeight<=1.0,myName+"ChromaWeight 0.0 -> 1.0")
Prefix=Default(Prefix,"") # ("") Prefix for returned Global vars ("" = None returned)
Show=Default(Show,True) # (True) Show on frame
Debug=Default(Debug,False) # (False) Extra debug info, not terribly useful
FuncS="""
Function s_ApparentFPS@@@(clip clp,DupeThresh,Float FR,Int HalfFrameRes,Float ChromaWeight,bool Show,bool Debug) {
clp
n = current_frame # Local var
if(Prev@@@ != n-1 || n==0) {
# Start/Reset timer
start_n = (n==0) ? n : n-1
Global StartFrame@@@ = start_n
Global StartFrame2@@@ = start_n
Global StartTime@@@ = start_n/FR
Global StartTime2@@@ = start_n/FR
Global DupeCnt@@@ = 0
Global DupeCnt2@@@ = 0
}
Diff = RT_FrameDifference(clp,clp,n2=n-1,ChromaWeight=ChromaWeight)
if(Diff < DupeThresh && n > 0) {
Global DupeCnt@@@ = DupeCnt@@@ + 1
Global DupeCnt2@@@ = DupeCnt2@@@ + 1
}
CurrTime = n / FR
FrameCnt = n-StartFrame@@@
Tog1 = (FrameCnt==HalfFrameRes)
Tog2 = (FrameCnt>=HalfFrameRes*2)
UniqueFrameCnt = FrameCnt - DupeCnt@@@
TimeExpired = Max(CurrTime-StartTime@@@,0.000001)
# Kick in only after HalfFrameRes frames done
ApparentFPS = (FrameCnt<HalfFrameRes) ? 0.0 : UniqueFrameCnt/TimeExpired
Global MaxApparentFPS@@@ = ApparentFPS>MaxApparentFPS@@@ ? ApparentFPS : MaxApparentFPS@@@
if(Show || Debug) {
if(!Debug) {
RT_Subtitle("%6.3f MaxFPS=%6.3f\nDif=%.3f",ApparentFPS,MaxApparentFPS@@@,Diff)
} Else {
RT_Subtitle("%d] AppFPS=%6.3f (Max=%6.3f)" +
\ "\nFPS=%6.3f Dif=%.3f" +
\ "\nStartFrm =%d StartT =%.3f\nStartFrm2=%d StartT2=%.3f" +
\ "\nFrameCnt =%d ExpiredT=%.3f\nDupeCnt =%2d DupeCnt2=%d\nUniqueFrameCnt=%d\nTog1=%.1s Tog2=%.1s",
\ n,ApparentFPS,MaxApparentFPS@@@,
\ FrameCnt/TimeExpired,Diff,
\ StartFrame@@@,StartTime@@@,StartFrame2@@@,StartTime2@@@,FrameCnt,TimeExpired,
\ DupeCnt@@@,DupeCnt2@@@,UniqueFrameCnt,Tog1,Tog2)
}
}
if(Tog2 || Tog1) {
if(Tog2) {
Global StartFrame@@@ = StartFrame2@@@
Global StartTime@@@ = StartTime2@@@
Global DupeCnt@@@ = DupeCnt2@@@
}
Global StartFrame2@@@ = n
Global StartTime2@@@ = CurrTime
Global DupeCnt2@@@ = 0
}
### Insert returned Global Vars Below if required
@@@@
###
Global Prev@@@ = n
Return Last
}
Global MaxApparentFPS@@@ = 0.0 Global Prev@@@ = -666 # Init
ARGS = "Dupethresh,FR,HalfFrameRes,ChromaWeight,Show,Debug"
clp.GScriptClip("s_ApparentFPS@@@(last, "+ARGS+")", local=true, args=ARGS)
"""
RT_IncrGlobal("AFPS_InstanceNumber")
# Are return Globals required
IG_S = (Prefix=="") ? "" : RT_String("Global %sApparentFPS=ApparentFPS Global %sMaxApparentFPS=MaxApparentFPS@@@",Prefix,Prefix)
Fnd_S = RT_String("@@@@\n@@@\n")
Rep_S = RT_String("%s\n_AFPS_%d\n",IG_S,AFPS_InstanceNumber)
InstS = RT_StrReplaceMulti(FuncS,Fnd_S,Rep_S)
# RT_WriteFile(RT_String("DEBUG_AFPS_%d.txt",AFPS_InstanceNumber),"%s",InstS) # Debug script listing
Return GScript(InstS)
}
S_ApparentFPS_Client.avs
Import("S_ApparentFPS.avs")
#Avisource("D:\AVS\Test2.avi")
Avisource("D:\SCDump\CAP1.avi")
#Avisource("WebCam.avi")
KillAudio
DUPTHRESH_S = 1.0 # Script version
DUPTHRESH_P = 1.0 # Plug version
A = s_ApparentFPS(Dupethresh=DUPTHRESH_S,Prefix="S_",Show=false)
B = ApparentFPS(Dupethresh=DUPTHRESH_P,Prefix="P_",Show=false)
RT_Graphlink(A,B) # Ensure both clips processed [about same as Echo() in v2.6Alpha5]
ScriptClip("""
RT_SubTitle("%d] %6.3f : Max=%6.3f (Script)",current_frame,S_ApparentFPS,S_MaxApparentFPS,align=0)
RT_SubTitle("%d] %6.3f : Max=%6.3f (Plug)" ,current_frame,P_ApparentFPS,P_MaxApparentFPS,align=1)
""",After_Frame=True)
Assumefps(250.0)
Produces pretty much identical results to plugin.
turbojet
2nd November 2014, 06:59
This seems interesting, I'll give it a try, it would be awesome if it performed ivtc. Is this a step towards auto ivtc? MadVR has had this for awhile now and it's pretty accurate.
StainlessS
2nd November 2014, 11:43
Is this a step towards auto ivtc?
Nope, purely to save counting unique frames for decimating of dupes via eg multidecimate (or MDec2, YV12/YUY2/RGB, YV24 etc).
The script version is exactly how it is implemented.
EDIT: Globals are returned for whatever use people can make of them (Locals for the plug version).
(I live in PAL land and have little need for anything NTSC related).
I guess with a bit of bobbing it might be used to detect if IVTC were required, I look forward to seeing what use you can make of it.
EDIT:
The script version is exactly how it is implemented.
GetFrame() from source, what an almost exact conversion to plug might look like.
PVideoFrame __stdcall ApparentFPS::GetFrame(int n, IScriptEnvironment* env) {
n = (n<0) ? 0 : (n>= num_frames) ? num_frames - 1 : n; // Range limit n
PVideoFrame dst = child->GetFrame(n, env);
if(Show||Debug) env->MakeWritable(&dst);
if(Prev_n != n-1 || n==0) {
// Start/Reset
const int start_n = (n == 0) ? 0 : n-1;
StartFrame2 = StartFrame = start_n;
StartTime2 = StartTime = start_n/FrameRate;
DupeCnt2 = DupeCnt = 0;
}
AVSValue aFpsArgs[5]={child,child,n,n-1,ChromaWeight};
double Diff = RT_FrameDifference_Lo(AVSValue(aFpsArgs,5),env);
if(Diff < DupeThresh && n > 0) {
++DupeCnt;
++DupeCnt2;
}
const double CurrTime = n / FrameRate;
const int FrameCnt = n-StartFrame;
const bool Tog1 = (FrameCnt==HalfFrameRes);
const bool Tog2 = (FrameCnt>=HalfFrameRes*2);
const int UniqueFrameCnt= FrameCnt - DupeCnt;
const double TimeExpired= max(CurrTime-StartTime,0.000001);
// Only kick in after HalfFrameRes frames done
const double ApparentFPS = (FrameCnt<HalfFrameRes) ? 0.0 : UniqueFrameCnt/TimeExpired;
MaxApparentFPS = (ApparentFPS>MaxApparentFPS) ? ApparentFPS : MaxApparentFPS;
if(Show || Debug) {
if(!Debug) {
DrawFStr(dst,0,0,"%d] %6.3f MaxFPS=%6.3f\nDif=%.3f",n,ApparentFPS,MaxApparentFPS,Diff);
} else {
DrawFStr(dst,0,0,
"%d] AppFPS=%6.3f (Max=%6.3f)"
"\nFPS=%6.3f Dif=%.3f"
"\nStartFrm =%d StartT =%.3f\nStartFrm2=%d StartT2=%.3f"
"\nFrameCnt =%d ExpiredT=%.3f\nDupeCnt =%2d DupeCnt2=%d\nUniqueFrmCnt=%d\nTog1=%s Tog2=%s",
n,ApparentFPS,MaxApparentFPS,
FrameCnt/TimeExpired,Diff,
StartFrame,StartTime,StartFrame2,StartTime2,FrameCnt,TimeExpired,
DupeCnt,DupeCnt2,UniqueFrameCnt,Tog1?"T":"F",Tog2?"T":"F");
}
}
if(Tog2||Tog1) {
// FrameCnt cannot be 0
if(Tog2) {
StartFrame = StartFrame2;
StartTime = StartTime2;
DupeCnt = DupeCnt2;
}
StartFrame2 = n;
StartTime2 = CurrTime;
DupeCnt2 = 0;
}
if(*Prefix != '\0') { // Most of this could have been prepped in the constructor :(
int pfixlen=strlen(Prefix);
if(pfixlen>128) pfixlen=128;
char bf[128+32];
memcpy(bf,Prefix,pfixlen);
char *d=bf+pfixlen;
const char *p,*afps="ApparentFPS";
for(p=afps;*d++=*p++;); // strcat variable name part
AVSValue var = GetVar(env,bf);
env->SetVar(var.Defined() ? bf : env->SaveString(bf),ApparentFPS);
d = bf+pfixlen;
const char *afpsmx="MaxApparentFPS";
for(p=afpsmx;*d++=*p++;); // strcat variable name part
var = GetVar(env,bf);
env->SetVar(var.Defined() ? bf : env->SaveString(bf),MaxApparentFPS);
}
Prev_n=n;
return dst;
}
StainlessS
2nd November 2014, 14:14
turboJet,
I only have a single (good quality) NTSC FILM sample (about 16 mins rescued from a now totally corrupt DVD),
tried this: (EDIT: With 'Honour Pulldown Flags' in DGIndex, ie Not 'Forced Film').
MPeg2Source("NTSC_Top_FORCEFILM-ReturnOfTheKing_NTSC_FORCEFILM.d2v")
Robocrop(WMod=4)
Bob
ApparentFPS(Dupethresh=2.5)
Assumefps(250.0)
Showed MaxApparentFPS as 23.976FPS
(after full 16min scan, initial 23.976FPS detected at frame 121 of 57668 bobbed frames, ie 2* HalfFrameRes + 1 frames, frame 0 does not count [EDIT: we count frame 0 as neither a dupe not unique]).
EDIT: Fairly high Dupethresh to see both bobbed frames belonging to single progressive frame as same, detects
bobbed output @ 59.94FPS as 23.976 MaxApparentFPS.
EDIT: Script version detects as 23.977FPS, probably due to lower precision Float, I'm guessin at this line:
CurrTime = n / FR
johnmeyer
2nd November 2014, 22:58
Nope, purely to save counting unique frames for decimating of dupes via eg multidecimate (or MDec2, YV12/YUY2/RGB, YV24 etc).I can think of several situations where this is going to be useful.
Now, if I can just remember that it exists the next time I need it ...
Thanks!!
turbojet
2nd November 2014, 22:59
I tried it on a few and it detected 23.976 at 24.0*, 25 at 26.* and 29.97 30.0 fps on limited testing. Which is accurate enough for scanning like autocrop/robocrop to determine what it should be ivtc'd to. It's not possible currently to get this info to pass on to call an ivtc filter, is it something that might be considered in the future?
StainlessS
2nd November 2014, 23:48
So far as I know, TFM uses 'd2v' file to auto fix IVTC on MPEG2. (think it also outputs some kind of logfile, dont know if any
decimation info in there). EDIT: d2v has line at end eg something like "100% FILM", can use RT_Stats to get that line of text.
EDIT: Is eg "FINISHED 100.00% FILM".
From RT_Stats doc:
RT_ReadTxtFromFile(String ,Int "Lines"=0,Int "Start"=0)
Non-clip function.
String Filename, Name of text file to load into a string.
Lines=0=unlimited. Set to number of leading lines in text file to load, eg 1 = load only the first line of text file.
The return string is n/l ie Chr(10) separated, and carriage returns are removed from the returned string.
If source file was missing newline on very last line, it will append a newline so that all lines are similarly formatted.
v1.03, Added Start arg default=0=very first line (relative 0). Would have been nice to have start and lines in reverse
order but implemented as above to not break scripts.
Throws an error if your requested Start is >= to the number of lines in the file, or zero len file.
To fetch the last line of a text file, use eg Start = RT_FileQueryLines(Filename) - 1 (Start is zero relative).
You could eg get the last line of a d2v file which might look like this:- "FINISHED 100.00% VIDEO"
You could write a 'PreScan' script to scan part of a file and return results, and do with that whatever you want.
I dont have that much/many samples, knowledge nor inclination to do so myself, GScript could come in handy here.
something like:
Function fn(clip c) {
c
GSCript("""
z = bob.ApparentFPS(Dupethresh=2.5,Prefix="P_",Show=False)
FC=z.FrameCount
cnt=min(FC,1000)
for(i=0,cnt-1) {
RT_YankChain(z,n=i) # force process frame i
}
return P_MaxApparentFPS
""")
}
completely untested (would probably need to be a bit more involved than above with some kind of decision made based
on both original framerate and apparent framerate).
EDIT: I used RoboCrop(), just to remove any borders that may 'water down' metrics and possibly necessitate adjustment of DupeThresh.
johnmeyer
3rd November 2014, 00:23
TDecimate doc is here:
TDecimate Documenation (http://avisynth.org.ru/docs/english/externalfilters/tivtc_tdecimate.htm)
Decimation is set using two parameters:
cycle
This is the group of frames, prior to decimation, which contain the duplicates. If you have a complicated decimation pattern, you need to specify a large enough "cycle" number so that you include all the pattern variations, but only until those patterns repeat. For instance, if the video has one repeat in the first group of five frames, but then has one repeat in the next four, and then this same thing happens with the next group of five, and the following group of four, you need to specify a "cycle" of nine frames. For me, this is always the most difficult parameter to determine.
cycleR
This is how many frames you want to decimate in every "cycle." In my example above, this would be 2.
So, the challenge would be to be able to determine what "cycle" to use. Once that is determined, I think it should be easy to compute CycleR, using this new function, and pass that to TDecimate.
turbojet
3rd November 2014, 00:55
d2v parameter only helps when pulldown is used, which is common in big studio dvd's but not all that common in tv, it's only for 29 -> 23 too.
On a 23.976 telecined source: The script's return value was not a video clip, (Is a float, 24.169378).
On a 25 fps telecined source that's time compressed: The script's return value was not a video clip, (Is a float, 33.300034).
The latter is concerning, this same file when scrubbing is around 26-28.
How to take this info and apply these rules to TDecimate(cycle=n)
if fps 22-24.5 n = 5
if fps 24.5-28 n = 6
else omit TDecimate
StainlessS
3rd November 2014, 01:10
As I said, I dont know that much about it.
The script's return value was not a video clip
Was not intended to be full script, just something to start from and play with. Could do some kind of testing inside of the for loop
to judge whether we hit maximum (eg ApparentFPS varied much below MaxApparentFPS but then hit exact MaxApparentFPS, AGAIN,
if so exit loop early, probably need a bit more thought).
EDIT:
It's 23.976 fps so that's correct, how can I write the fps into a text file from cli?
There are a couple of examples somewhere on-site using MediaInfo (CMD Line version) and RT_Call() or CallCmd() funcs.
Think I've gotta bow out soon, before I fall down, exhausted.
turbojet
3rd November 2014, 01:31
Ya I never expected it to output video and it doesn't matter if it does. I can send you some samples if you want to work with this more.
Those will all report 29.97 which is the source fps. I'm trying to get apparentFPS into a text file to determine what to put in TDecimate, if any. Or better yet calling the correct TDecimate in the script, which would make it an auto ivtc function. However with significant blending srestore usually performs better.
Sleep well, this can wait. It's something I've been waiting for years for and I was shocked when it was mostly realized in MadVR.
StainlessS
3rd November 2014, 02:01
I can send you some samples
Please no, I love not having to play with stuff like that :)
You can output whatever you wish using eg
RT_WriteFile(Filename,"Int=%d Float=%f String=%s NewLine=\nI'm On a New Line",42,42.0,"42")
You can also read in lines of a text file, or complete text file, or create list of files of eg "*.BMP|TIFF|JPG" etc using RT_Stats funcs.
(I think currently something like 150 functions)
You could also use a temp RT_Stats DBase file to return multiple info from a function, caller supplies DB name, function creates and fills DB
and returns a status code with existing DB. [caller deals with results and then RT_FileDelete(DB)].
Some stuff MediaInfo related:
RT_Call(String Cmd,bool "Hide"=false,bool "Debug"=false)
Run an external program using supplied cmd command line, hiding the console window if Hide==True.
Debug, If true, send some debug info to debugview including return code from called process.
DOS Filenames should be enclosed in quotes to avoid the system misinterpreting spaces in filenamess, see RT_QuoteStr().
Returns 0 if process successfully started, otherwise non zero (ie 1).
Example to write Aspect Ratio in form "1.333" to file D:\OUT\MEDINFO.TXT using command line version of MediaInfo.
RT_Call(RT_QuoteStr("D:\TEST\MediaInfo.Exe") + " --LogFile=" + RT_Quotestr("D:\OUT\MEDINFO.TXT") + \
" --Inform=Video;%DisplayAspectRatio% " + RT_Quotestr("D:\VID\1.mpg"))
demo: http://forum.doom9.org/showthread.php?p=1589747#post1589747
EDIT: And CallCmd() plug here: http://forum.doom9.org/showthread.php?t=166063&highlight=callcmd
StainlessS
3rd November 2014, 17:47
Turbo,
Here, output from the NTSC FILM VOB, via MediaInfo NTSC.VOB > NTSC.txt
General
Complete name : ntsc.vob
Format : MPEG-PS
File size : 320 MiB
Duration : 16mn 2s
Overall bit rate mode : Variable
Overall bit rate : 2 789 Kbps
Video
ID : 224 (0xE0)
Format : MPEG Video
Format version : Version 2
Format profile : Main@Main
Format settings, BVOP : Yes
Format settings, Matrix : Custom
Format settings, GOP : M=3, N=12
Duration : 16mn 2s
Bit rate mode : Variable
Bit rate : 2 286 Kbps
Maximum bit rate : 9 802 Kbps
Width : 720 pixels
Height : 480 pixels
Display aspect ratio : 16:9
Frame rate : 23.976 fps
Standard : NTSC
Color space : YUV
Chroma subsampling : 4:2:0
Bit depth : 8 bits
Scan type : Progressive
Scan order : 2:3 Pulldown
Compression mode : Lossy
Bits/(Pixel*Frame) : 0.276
Stream size : 262 MiB (82%)
Audio
ID : 189 (0xBD)-128 (0x80)
Format : AC-3
Format/Info : Audio Coding 3
Mode extension : CM (complete main)
Format settings, Endianness : Big
Muxing mode : DVD-Video
Duration : 16mn 2s
Bit rate mode : Constant
Bit rate : 448 Kbps
Channel(s) : 6 channels
Channel positions : Front: L C R, Side: L R, LFE
Sampling rate : 48.0 KHz
Bit depth : 16 bits
Compression mode : Lossy
Delay relative to video : -717ms
Stream size : 51.4 MiB (16%)
Text #1
ID : 189 (0xBD)-32 (0x20)
Format : RLE
Format/Info : Run-length encoding
Muxing mode : DVD-Video
Duration : 15mn 50s
Delay relative to video : 2s 269ms
Text #2
ID : 189 (0xBD)-33 (0x21)
Format : RLE
Format/Info : Run-length encoding
Muxing mode : DVD-Video
Duration : 15mn 50s
Delay relative to video : 2s 269ms
Text #3
ID : 189 (0xBD)-34 (0x22)
Format : RLE
Format/Info : Run-length encoding
Muxing mode : DVD-Video
Duration : 15mn 35s
Delay relative to video : 2s 302ms
Text #4
ID : 189 (0xBD)-35 (0x23)
Format : RLE
Format/Info : Run-length encoding
Muxing mode : DVD-Video
Duration : 15mn 35s
Delay relative to video : 2s 302ms
Text #5
ID : 189 (0xBD)-36 (0x24)
Format : RLE
Format/Info : Run-length encoding
Muxing mode : DVD-Video
Duration : 15mn 35s
Delay relative to video : 2s 302ms
Text #6
ID : 224 (0xE0)-CC3
Format : EIA-608
Muxing mode : DVD-Video
Muxing mode, more info : Muxed in Video #1
Bit rate mode : Constant
Stream size : 0.00 Byte (0%)
Menu
Do your weird samples give any extra useful info ?
(I think Mediainfo gives more detail if given an IFO rather than VOB, I dont have the IFO, DVD corrupt).
EDIT: Output from ffprobe
[mpeg2video @ 032b0760] Invalid frame dimensions 0x0.
Last message repeated 10 times
Input #0, mpeg, from 'ntsc.vob':
Duration: 00:16:02.68, start: 1758.473567, bitrate: 2788 kb/s
Stream #0:0[0x1e0]: Video: mpeg2video (Main), yuv420p(tv), 720x480 [SAR 32:7 DAR 16:9], max. 9801 kb/s, 29.83 fps, 59.94 tbr, 90k tbn, 59.94 tbc
Stream #0:1[0x80]: Audio: ac3, 48000 Hz, 5.1(side), fltp, 448 kb/s
Stream #0:2[0x1bf]: Data: dvd_nav_packet
Stream #0:3[0x20]: Subtitle: dvd_subtitle
Stream #0:4[0x21]: Subtitle: dvd_subtitle
Stream #0:5[0x22]: Subtitle: dvd_subtitle
Stream #0:6[0x23]: Subtitle: dvd_subtitle
Stream #0:7[0x24]: Subtitle: dvd_subtitle
with weird 29.83 fps
EDIT: VOB probably went through DVD shrink without compression but with some streams removed.
StainlessS
3rd November 2014, 20:25
Treat ApparentFPS() definition as currently transient, just had a daft idea and want to try it out.
May drop HalfFrameRes arg altogether.
Gonna try scan ahead(& behind) a number of frames to get better current estimate, maybe about 15 ahead,+ 15 behind + current.
StainlessS
3rd November 2014, 22:54
OK, got this, need some guinea pigs (or a few small hamsters will do I suppose).
Function Test(clip clp,Float "DupeThresh",Float "FrameRate",Float "ChromaWeight", String "Prefix", Bool "Show", Bool "Debug") {
myName="s_ApparentFPS: "
Assert(RT_FunctionExist("GScriptClip"),myName+"Essential GRunT plugin installed, http://forum.doom9.org/showthread.php?t=139337")
Assert(RT_FunctionExist("GScript"),myName+"Essential GScript plugin installed, http://forum.doom9.org/showthread.php?t=147846")
DupeThresh=Default(DupeThresh,0.5) # (1.0) Suggest 1.0 (clean) down to 0.5 (lossy compressed)
Assert(DupeThresh>0.0,myName+"DupeThresh Must be greater than 0.0")
FR=Float(Default(FrameRate,clp.FrameRate))
Assert(FR>0,myName+"FrameRate Must be greater than 0.0")
ChromaWeight=Float(Default(ChromaWeight,1.0/3.0)) # (1.0/3.0) YUV Only, suggest 1.0/3.0 -> 1.0/2.0
Assert(ChromaWeight>=0.0 && ChromaWeight<=1.0,myName+"ChromaWeight 0.0 -> 1.0")
Prefix=Default(Prefix,"")
Show=Default(Show,True)
Debug=Default(Debug,False)
FuncS="""
Function ApparentFPS@@@(clip clp,DupeThresh,Float FR,Float ChromaWeight,bool Show,bool Debug) {
clp
n = current_frame # Local var
LastFrame = FrameCount - 1
if(Prev@@@ != n-1 || n==0) {
# Start/Reset timer
Prior_n = n - 1 # As if at previous n position (ie pretend previous iteration)
BITS = 0
SFrm = max(Prior_n - 15,1) # Frame 0 has no prior frame to compare with
EFrm = min(Prior_n + 15,LastFrame)
for(i= SFrm,EFrm) {
Dif = RT_FrameDifference(clp,clp,n=i,n2=i-1,ChromaWeight=ChromaWeight)
if(Dif >= Dupethresh) {
BITS = RT_BitSet(BITS,15-(i-Prior_n))
}
}
Global BITS@@@ = BITS
}
BITS=BITS@@@ # Local var, from previous iteration
BITS=BitAnd(RT_BitLSL(BITS,1),$7FFFFFFE) # shift over and discard oldest bit (and clear bit zero new bit)
if(n + 15 <= LastFrame) {
Dif = RT_FrameDifference(clp,clp,n=n+15,n2=n+15-1,ChromaWeight=ChromaWeight)
if(Dif >= Dupethresh) {
BITS = BITS + 1 # Bit zero was clear
}
}
SFrm = max(n - 15,1)
EFrm = min(n + 15,LastFrame)
Samples = EFrm - SFrm + 1 # Valid samples
Unique = RT_BitSetCount(BITS)
ApparentFPS = Unique * FR / Samples
Global MaxApparentFPS@@@ = (Samples!=31) ? MaxApparentFPS@@@ : max(ApparentFPS,MaxApparentFPS@@@)
if(Debug || Show) {
Dif = (n==0) ? 0.0 : RT_FrameDifference(clp,clp,n2=n-1,ChromaWeight=ChromaWeight)
LS=RT_NumberString(RT_BitLSR(BITS,16),2,15)
MS=RT_BitTst(BITS,15) ? "1":"0"
RS=RT_NumberString(RT_BitAnd(BITS,$7FFF),2,15)
RT_Subtitle("AppFPS=%6.3f MaxFPS=%6.3f\nDif=%.3f (Set=%2d Samples=%2d)\n\aR%s\a-%s\aR%s\a-",
\ ApparentFPS,MaxApparentFPS@@@,Dif,Unique,Samples,LS,MS,RS)
}
### Insert returned Global Vars Below if required
@@@@
###
Global BITS@@@ = BITS
Global Prev@@@ = n
Return Last
}
Global BITS@@@=0 Global MaxApparentFPS@@@ = 0.0 Global Prev@@@ = -666
ARGS = "Dupethresh,FR,ChromaWeight,Show,Debug"
clp.GScriptClip("ApparentFPS@@@(last, "+ARGS+")", local=true, args=ARGS)
"""
RT_IncrGlobal("AFPS_InstanceNumber")
# Are return Globals required
IG_S = (Prefix=="") ? "" : RT_String("Global %sApparentFPS=ApparentFPS Global %sMaxApparentFPS=MaxApparentFPS@@@",Prefix,Prefix)
Fnd_S = RT_String("@@@@\n@@@\n")
Rep_S = RT_String("%s\n_AFPS_%d\n",IG_S,AFPS_InstanceNumber)
InstS = RT_StrReplaceMulti(FuncS,Fnd_S,Rep_S)
RT_WriteFile(RT_String("DEBUG_AFPS_%d.txt",AFPS_InstanceNumber),"%s",InstS)
Return GScript(InstS)
}
#Avisource("D:\AVS\Test2.avi")
#Avisource("D:\SCDump\CAP1.avi")
Avisource("WebCam.avi")
KillAudio
#StackHorizontal(Last,Last)
Test()
AssumeFPS(250.0)
Will be better instantaneous ApparentFPS but is taken from sample of 31 frames so will likely be out some at MaxApparentFPS.
turbojet
4th November 2014, 00:47
Mediainfo reads 29.97i or 59.94p on all tv I just checked, the rare one that uses pulldown may state 23.976p but I don't have one at the moment. I don't think it will be any help.
Is the latest function relevant to what I'm trying to do?
StainlessS
4th November 2014, 01:35
Mediainfo reads 29.97i or 59.94p on all tv I just checked, the rare one that uses pulldown may state 23.976p but I don't have one at the moment.
Thank you.
The one I posted said:
Frame rate : 23.976 fps
Scan type : Progressive
Scan order : 2:3 Pulldown
Perhaps old version.
Is the latest function relevant to what I'm trying to do?
Hope so. Is replacement for previous [just different name ie Test()].
BUT, currently trying to make it sample up to 64 frames, even numbers probably better for 30fps (eg 30 or 60) and 25 or 50 for PAL. If even then will sample 1 less after current frame than before current frame.
Do you see yourself more as a large guinea pig, or a small hamster ? (stay away from Peru either way).
turbojet
4th November 2014, 06:56
Tried test() on a 23.976 telecined source and it bounce around between 22 and 27 while scrubbing, max fps was 29. How can I get an average fps of the file after quick scanning?
StainlessS
4th November 2014, 10:55
OK, thanks.
Probably need to do more samples, only doing 31 samples with that try (current + 15 either side, [limited 32 bits in Int]).
Not suitable for use with the bob function thing, not enough samples (old version did 2*framerate samples (2*2*framerate for bob thing).
The hi-lited binary digit is the current frame, is it showing the sequence you expect ? (if not, adjust Dupethresh)
I'll try to get 64 sample script going today (forget bob thing for now), and if I get it working OK, will attempt plug at 128 sample max using 64 bit int's.
It's not intended for scrubbing around, continuous play only, and the only numeric of real value is the MaxApparentFPS.
OK, I give in, can you up a sample of about 30-60 secs compressed (Not HD), with varying motion would be ok, at least 2 seconds of continuous motion at some point.
EDIT: And can you say what sequence I should expect from the sample, ta.
turbojet
4th November 2014, 23:15
https://www.mediafire.com/folder/8unc1go673669/IVTC
There's a bunch of samples in there 1080i and 1 720p. I didn't resize them because in past experience encoding destroys the pattern.
StainlessS
4th November 2014, 23:49
Thanks, I'll snatch them tomorrow.
Currently got 128 sample going in script, still not quite right but might as well have a go at 256 sample in script, probably not much diff in speed.
EDIT: 256 sample script pretty much done, just tinkering.
StainlessS
5th November 2014, 11:20
Turbo, try this, quite similar to 1st version but perhaps better, args have changed a little, use Samples at about framerate rather than double framerate
as in old HalfFrameRes. Shall do plug a bit later today I think. Can use with the bob thing as previous.
EDIT: Below is early version in script, dont use it, use the plugin.
Function S_ApparentFPS(clip clp,Float "DupeThresh",Float "FrameRate",Int "Samples",Float "ChromaWeight", String "Prefix",
\ Bool "Show", Bool "Verbose", Bool "Debug") {
/*
S_ApparentFPS: by StainlessS. Requires RT_Stats by StainlessS, GScript & Grunt Plugins (c) Gavino.
Multi-Instance script version.
Shows underlying framerate where a clip has had many duplicates inserted, easier than counting unique frames.
S_ApparentFPS(clip clp,Float "DupeThresh"=0.5,Float "FrameRate"=clp.FrameRate,Int "Samples"=Round(FrameRate),
\ Float "ChromaWeight"=1.0/3.0,String "Prefix"="", Bool "Show"=True,Bool "Verbose"=True, Bool "Debug"=False)
DupeThresh, Float, Default 0.5 (Greater than 0.0), suggest 0.5 -> 1.0. FrameDifference below or equal to this is a dupe.
FrameRate, Float, Default clp.FrameRate. # FrameRate of clp. Probably of no great use, just use default.
Samples, Int, Default Int(Ceil(FrameRate)) (1 or more).# Min Frames to sample for underlying framerate detection.
# Suggest as FrameRate or next whole number higher where framectional FrameRate.
ChromaWeight, Float, Default 1.0/3.0, (0.0 -> 1.0). # YUV Only. Suggest 1.0/3.0 -> 1.0/2.0. (Use 0.0 for GreyScale YUV).
Prefix, String, Default "". # Prefix for returned Global vars, Default "" = None returned.
Show, Bool, Default True. # Show Info
Verbose, Bool, Default True. # Additional Info, Sequence as binary digits.
Debug, Bool, Default False. # Not of any great use.
Function samples over Samples frames, and shows instantaneous ApparentFPS and MaxApparentFPS, the real and perhaps most useful
result is MaxApparentFPS.
If Prefix = "" (Default) then does not return any info Global vars. When set to eg "S_" will set Global S_ApparentFPS
and S_MaxApparentFPS as Float. If Multi-Instance then must give unique prefix for each instance returned Globals.
Also returns Globals S_MinAboveDupeDif, S_MaxBelowDupeDif and S_CurrentDif which may assist in choosing DupeThresh, they are min and
max values so far, and difference between current and previous frame. S_MinAboveDupeDif is the difference to previous frame of the
frame which has lowest difference that what considered NOT a dupe (above DupeThresh), MaxBelowDupeDif max difference of frame that WAS
considered a dupe (below DupeThresh). Ideally in a clip containing duplicates, there would be a distinct gap between these two
above/below dup dif values with DupeThresh somewhere in between.
Can switch Off metrics (Show=False), and set eg Prefix = "S_" to return Globals S_ApparentFPS and S_MaxApparentFPS with rough
instantaneous estimate of current underlying framerate and maximum underlying framerate detected so far.
NOTE, ApparentFPS can range from 0.0 in static scene (can be measure of how 'active' a scene is).
'Verbose' (Default True) shows additional info, a string of 1's and 0's showing duplicate and unique frames. Also shown in info is
the Unique frame count for the current Sample spread (which will be reduced at either end of clip), current ApparentFPS is calculated as
(FrameRate * UniqueFrameCount / CurrentSampleSpread), so where UniqueFrameCount == CurrentSampleSpread, the result is FrameRate, However,
if CurrentSampleSpread is reduced due to being near clip ends, then current ApparentFPS will NOT be assigned to S_MaxApparentFPS even
where greater than current S_MaxApparentFPS.
The rest should be pretty straight forward.
*/
myName="S_ApparentFPS: "
Assert(RT_FunctionExist("GScriptClip"),myName+"Essential GRunT plugin installed, http://forum.doom9.org/showthread.php?t=139337")
Assert(RT_FunctionExist("GScript"),myName+"Essential GScript plugin installed, http://forum.doom9.org/showthread.php?t=147846")
DupeThresh=Default(DupeThresh,0.5) # (0.5) Suggest about 0.1 -> 1.0, source dependant.
FR=Float(Default(FrameRate,clp.FrameRate))
Assert(FR>0.0,myName+"FrameRate Must be greater than 0.0")
Samples=Default(Samples,Int(Ceil(FR))) # Int(Ceil(FrameRate)), suggest about Ceil(framerate)
Samples = min(256,Samples) # Silent limit
Assert(Samples>0,myName+"Samples Must be greater than 0")
ChromaWeight=Float(Default(ChromaWeight,1.0/3.0)) # (1.0/3.0) YUV Only, suggest 1.0/3.0 -> 1.0/2.0
Assert(ChromaWeight>=0.0 && ChromaWeight<=1.0,myName+"ChromaWeight 0.0 -> 1.0")
Prefix=Default(Prefix,"") # ("") Prefix for returned Global vars ("" = None returned)
Show=Default(Show,True) # (True) Show on frame
Verbose=Default(Verbose,True) # (True) Extra info
Debug=Default(Debug,False) # (False) Extra debug info, not terribly useful
FuncS="""
Function ApparentFPS@@@(clip clp,DupeThresh,Float FR,Int Samples,Float ChromaWeight,bool Show,Bool Verbose,bool Debug) {
clp
n = current_frame # Local var
LastFrame = FrameCount - 1
LftSpan = (Samples / 2)
RgtSpan = Samples-LftSpan-1
if(Prev@@@ != n-1 || n==0) {
# Start/Reset
BITS0=0 BITS1=0 BITS2=0 BITS3=0 BITS4=0 BITS5=0 BITS6=0 BITS7=0
Prior_n = n - 1 # As if at previous n position (ie pretend previous iteration)
SFrm = max(Prior_n - LftSpan,1) # Start Frame. Frame 0 has no earlier frame to compare with
EFrm = min(Prior_n + RgtSpan,LastFrame) # End Frame
for(i= SFrm,EFrm) {
Dif = RT_FrameDifference(clp,clp,n=i,n2=i-1,ChromaWeight=ChromaWeight)
if(Dif > Dupethresh) {
Ix = RT_BitXor(i - Prior_n + 128,$1F) # Index into BITS and bit, Flip bit order (lo 5 bits only)
if(Ix<128) {
if(Ix<64) {
if(Ix<32) { BITS0 = RT_BitSet(BITS0,Ix) } # NOTE, 2nd arg to RT_BitSet is % 32, lo 5 bits only
else { BITS1 = RT_BitSet(BITS1,Ix) }
} else {
if(Ix<96) { BITS2 = RT_BitSet(BITS2,Ix) }
else { BITS3 = RT_BitSet(BITS3,Ix) }
}
} else {
if(Ix<192) {
if(Ix<160) { BITS4 = RT_BitSet(BITS4,Ix) }
else { BITS5 = RT_BitSet(BITS5,Ix) }
} else {
if(Ix<224) { BITS6 = RT_BitSet(BITS6,Ix) }
else { BITS7 = RT_BitSet(BITS7,Ix) }
}
}
if(Dif<MinAboveDupeDif@@@){Global MinAboveDupeDif@@@=Dif}
} else if(Dif>MaxBelowDupeDif@@@){Global MaxBelowDupeDif@@@=Dif}
}
} Else { # Get Local vars, from previous iteration
BITS0 = BITS0@@@ BITS1 = BITS1@@@ BITS2 = BITS2@@@ BITS3 = BITS3@@@
BITS4 = BITS4@@@ BITS5 = BITS5@@@ BITS6 = BITS6@@@ BITS7 = BITS7@@@
}
SFrm = max(n - LftSpan,0) # BLANK bit @ start frame that is gonna shift OUT, NOTE Limit to 0, NOT 1
Ix = RT_BitXor(SFrm - n + 128,$1F) # Index into BITS and bit, Flip bit order (lo 5 bits only)
if(Ix<128) {
if(Ix<64) {
if(Ix<32) { BITS0 = RT_BitClr(BITS0,Ix) } # NOTE, 2nd arg to RT_BitSet is % 32, lo 5 bits only
else { BITS1 = RT_BitClr(BITS1,Ix) }
} else {
if(Ix<96) { BITS2 = RT_BitClr(BITS2,Ix) }
else { BITS3 = RT_BitClr(BITS3,Ix) }
}
} else {
if(Ix<192) {
if(Ix<160) { BITS4 = RT_BitClr(BITS4,Ix) }
else { BITS5 = RT_BitClr(BITS5,Ix) }
} else {
if(Ix<224) { BITS6 = RT_BitClr(BITS6,Ix) }
else { BITS7 = RT_BitClr(BITS7,Ix) }
}
}
# Shift em all left 1
BITS0 = RT_BitLSL(BITS0,1) BITS0=(RT_BitTst(BITS1,31)) ? BITS0+1 : BITS0
BITS1 = RT_BitLSL(BITS1,1) BITS1=(RT_BitTst(BITS2,31)) ? BITS1+1 : BITS1
BITS2 = RT_BitLSL(BITS2,1) BITS2=(RT_BitTst(BITS3,31)) ? BITS2+1 : BITS2
BITS3 = RT_BitLSL(BITS3,1) BITS3=(RT_BitTst(BITS4,31)) ? BITS3+1 : BITS3
BITS4 = RT_BitLSL(BITS4,1) BITS4=(RT_BitTst(BITS5,31)) ? BITS4+1 : BITS4
BITS5 = RT_BitLSL(BITS5,1) BITS5=(RT_BitTst(BITS6,31)) ? BITS5+1 : BITS5
BITS6 = RT_BitLSL(BITS6,1) BITS6=(RT_BitTst(BITS7,31)) ? BITS6+1 : BITS6
BITS7 = RT_BitLSL(BITS7,1)
EFrm = min(n + RgtSpan,LastFrame)
Dif = RT_FrameDifference(clp,clp,n=EFrm,n2=EFrm-1,ChromaWeight=ChromaWeight)
if(Dif > Dupethresh) {
Ix = RT_BitXor(EFrm - n + 128,$1F) # Index into BITS and bit, Flip bit order (lo 5 bits only)
if(Ix<128) {
if(Ix<64) {
if(Ix<32) { BITS0 = RT_BitSet(BITS0,Ix) } # NOTE, 2nd arg to RT_BitSet is % 32, lo 5 bits only
else { BITS1 = RT_BitSet(BITS1,Ix) }
} else {
if(Ix<96) { BITS2 = RT_BitSet(BITS2,Ix) }
else { BITS3 = RT_BitSet(BITS3,Ix) }
}
} else {
if(Ix<192) {
if(Ix<160) { BITS4 = RT_BitSet(BITS4,Ix) }
else { BITS5 = RT_BitSet(BITS5,Ix) }
} else {
if(Ix<224) { BITS6 = RT_BitSet(BITS6,Ix) }
else { BITS7 = RT_BitSet(BITS7,Ix) }
}
}
if(Dif<MinAboveDupeDif@@@){Global MinAboveDupeDif@@@=Dif}
} else if(Dif>MaxBelowDupeDif@@@){Global MaxBelowDupeDif@@@=Dif}
Valid = EFrm - SFrm + 1 # Valid samples
Unique = RT_BitSetCount(BITS0) + RT_BitSetCount(BITS1) + RT_BitSetCount(BITS2) + RT_BitSetCount(BITS3) +
\ RT_BitSetCount(BITS4) + RT_BitSetCount(BITS5) + RT_BitSetCount(BITS6) + RT_BitSetCount(BITS7)
ApparentFPS = Unique * FR / Valid
Global MaxApparentFPS@@@ = (Valid!=Samples) ? MaxApparentFPS@@@ : max(ApparentFPS,MaxApparentFPS@@@)
# Make available for Show OR return Global vars
Dif = (n==0) ? 0.0 : RT_FrameDifference(clp,clp,n2=n-1,ChromaWeight=ChromaWeight)
if(Show) {
if(Verbose) {
Tmp1 = min(LftSpan,32)
LS = (Tmp1>0) ? RT_NumberString(RT_BitAnd(BITS3,RT_BitLSR(-1,32-Tmp1)),2,Tmp1) : ""
MS = RT_BitTst(BITS4,31) ? "1":"0"
Tmp2 = min(RgtSpan,31)
RS = (Tmp2>0) ? RT_NumberString(RT_BitLSR(RT_BitAnd(BITS4,$7FFFFFFF),31-Tmp2),2,Tmp2) : ""
nl = (Strlen(LS)+Strlen(MS)+Strlen(RS) <= 32) ? "" : Chr(10)
RT_Subtitle("AppFPS=%6.3f MaxAppFPS=%6.3f\nDif=%6.3f DupeThresh=%6.3f\nMinAboveDupeDif=%6.3f\n" +
\ "MaxBelowDupeDif=%6.3f\nUniq=%-3d Samp=%d\n\aS%s\a-%s%s\aS%s\a-",
\ ApparentFPS,MaxApparentFPS@@@,Dif,DupeThresh,MinAboveDupeDif@@@,MaxBelowDupeDif@@@,Unique,Valid,LS,nl,MS,RS)
} else {
RT_Subtitle("AppFPS=%6.3f MaxAppFPS=%6.3f\nDif=%6.3f Uniq=%2d Samp=%2d",
\ ApparentFPS,MaxApparentFPS@@@,Dif,Unique,Valid)
}
if(Debug) {
RT_Subtitle("0) $%08X 4) $%08X\n1) $%08X 5) $%08X\n2) $%08X 6) $%08X\n3) $%08X 7) $%08X\n",
\ BITS0,BITS4,BITS1,BITS5,BITS2,BITS6,BITS3,BITS7,align=1)
}
}
### Insert returned Global Vars Below if required
@@@@@@
@@@@@
@@@@
###
Global BITS0@@@ = BITS0 Global BITS1@@@ = BITS1 Global BITS2@@@ = BITS2 Global BITS3@@@ = BITS3
Global BITS4@@@ = BITS4 Global BITS5@@@ = BITS5 Global BITS6@@@ = BITS6 Global BITS7@@@ = BITS7
Global Prev@@@ = n
Return Last
}
Global BITS0@@@=0 Global BITS1@@@=0 Global BITS2@@@=0 Global BITS3@@@=0
Global BITS4@@@=0 Global BITS5@@@=0 Global BITS6@@@=0 Global BITS7@@@=0
Global MaxApparentFPS@@@ = 0.0 Global MaxBelowDupeDif@@@ = 0.0 Global MinAboveDupeDif@@@ = 255.0 Global Prev@@@ = -666
ARGS = "Dupethresh,FR,Samples,ChromaWeight,Show,Verbose,Debug"
clp.GScriptClip("ApparentFPS@@@(last, "+ARGS+")", local=true, args=ARGS)
"""
RT_IncrGlobal("AFPS_InstanceNumber")
# Are return Globals required
IG_S = (Prefix=="") ? "" : RT_String("Global %sApparentFPS=ApparentFPS Global %sMaxApparentFPS=MaxApparentFPS@@@",Prefix,Prefix)
IG2_S= (Prefix=="") ? "" : RT_String("Global %sMaxBelowDupeDif=MaxBelowDupeDif@@@ Global %sMinAboveDupeDif=MinAboveDupeDif@@@",
\ Prefix,Prefix)
IG3_S= (Prefix=="") ? "" : RT_String("Global %sCurrentDif=Dif",Prefix)
Fnd_S = RT_String("@@@@@@\n@@@@@\n@@@@\n@@@\n")
Rep_S = RT_String("%s\n%s\n%s\n_AFPS_%d\n",IG_S,IG2_S,IG3_S,AFPS_InstanceNumber)
InstS = RT_StrReplaceMulti(FuncS,Fnd_S,Rep_S)
# RT_WriteFile(RT_String("DEBUG_AFPS_%d.txt",AFPS_InstanceNumber),"%s",InstS)
Return GScript(InstS)
}
client_1
import("S_ApparentFPS.avs")
Avisource("D:\TIMER\WebCam.avi")
KillAudio
DUPETHRESH=0.5
ApparentFPS(DUPETHRESH=DUPETHRESH)
AssumeFPS(250.0)
client_2
# S_ApparentFPS_Client.avs
import("S_ApparentFPS.avs")
Avisource("D:\TIMER\WebCam.avi")
KillAudio
DUPTHRESH = 0.5
A = s_ApparentFPS(Dupethresh=DUPTHRESH,Prefix="S_",Show=false)
B = ApparentFPS(Dupethresh=DUPTHRESH,Prefix="P_",Show=false)
RT_Graphlink(A,B) # Ensure both clips processed [about same as Echo() in v2.6Alpha5]
ScriptClip("""
RT_SubTitle("%d] %6.3f Max=%6.3f (Script)\nDif=%6.3f\nMinAboveDupeDif=%6.3f\nMaxBelowDupeDif=%6.3f",
\ current_frame,S_ApparentFPS,S_MaxApparentFPS,S_CurrentDif,S_MinAboveDupeDif,S_MaxBelowDupeDif,align=0)
RT_SubTitle("%d] %6.3f Max=%6.3f (Plug)\nDif=%6.3f\nMinAboveDupeDif=%6.3f\nMaxBelowDupeDif=%6.3f",
\ current_frame,P_ApparentFPS,P_MaxApparentFPS,P_CurrentDif,P_MinAboveDupeDif,P_MaxBelowDupeDif,align=1)
""",After_Frame=True)
Assumefps(250.0)
return last
UPDATED
turbojet
6th November 2014, 00:03
S_ApparentFPS(Dupethresh=0.75,Debug=true)
Isn't considering blended frames, if the source is 29.97 it shows 29.97 even if it's 23 or 25 telecined played back in vdub.
StainlessS
7th November 2014, 16:12
Plugin update v0.01, see 1st post (script in post #23 update too).
ApparentFPS: by StainlessS.
Plugin dll's for Avisynth versions v2.58 and v2.6.
Shows underlying framerate where a clip has had many duplicates inserted, easier than counting unique frames.
ApparentFPS(clip clp,Float "DupeThresh"=0.5,Float "FrameRate"=clp.FrameRate,Int "Samples"=Round(FrameRate),
\ Float "ChromaWeight"=1.0/3.0,String "Prefix"="", Bool "Show"=True,Bool "Verbose"=True, Bool "Debug"=False)
DupeThresh, Float, Default 0.5 (Greater than 0.0), suggest 0.5 -> 1.0. FrameDifference below or equal to this is a dupe.
FrameRate, Float, Default clp.FrameRate. # FrameRate of clp. Probably of no great use, just use default.
Samples, Int, Default Round(FrameRate) (1 or more).# Min Frames to sample for underlying framerate detection. Suggest as FrameRate.
ChromaWeight, Float, Default 1.0/3.0, (0.0 -> 1.0). # YUV Only. Suggest 1.0/3.0 -> 1.0/2.0. (Use 0.0 for GreyScale YUV).
Prefix, String, Default "". # Prefix for returned Local vars, Default "" = None returned.
Show, Bool, Default True. # Show Info
Verbose, Bool, Default True. # Additional Info, Sequence as binary digits.
Debug, Bool, Default False. # Not of any great use.
Function samples over Samples frames, and shows instantaneous ApparentFPS and MaxApparentFPS, the real and perhaps most useful
result is MaxApparentFPS.
If Prefix = "" (Default) then does not return any info Local vars. When set to eg "P_" will set Local P_ApparentFPS
and P_MaxApparentFPS as Float.
Also returns Locals P_MinAboveDupeDif, P_MaxBelowDupeDif and P_CurrentDif which may assist in choosing DupeThresh, they are min and
max values so far, and difference between current and previous frame. P_MinAboveDupeDif is the difference to previous frame of the
frame which has lowest difference that what considered NOT a dupe (above DupeThresh), MaxBelowDupeDif max difference of frame that WAS
considered a dupe (below DupeThresh). Ideally in a clip containing duplicates, there would be a distinct gap between these two
above/below dup dif values with DupeThresh somewhere in between.
Can switch Off metrics (Show=False), and set eg Prefix = "P_" to return Locals P_ApparentFPS and P_MaxApparentFPS with rough
instantaneous estimate of current underlying framerate and maximum underlying framerate detected so far.
NOTE, ApparentFPS can range from 0.0 in static scene (can be measure of how 'active' a scene is).
'Verbose' (Default True) shows additional info, a string of 1's and 0's showing duplicate and unique frames. Also shown in info is
the Unique frame count for the current Sample spread (which will be reduced at either end of clip), current ApparentFPS is calculated as
(FrameRate * UniqueFrameCount / CurrentSampleSpread), so where UniqueFrameCount == CurrentSampleSpread, the result is FrameRate, However,
if CurrentSampleSpread is reduced due to being near clip ends, then current ApparentFPS will NOT be assigned to P_MaxApparentFPS even
where greater than current P_MaxApparentFPS.
The rest should be pretty straight forward.
StainlessS
S_ApparentFPS(Dupethresh=0.75,Debug=true)
Isn't considering blended frames, if the source is 29.97 it shows 29.97 even if it's 23 or 25 telecined played back in vdub.
Of course it aint, all it does is show predicted (reduced) framerate by taking duplicates into account, whether frames
are blended or not, does not come into it. I think you misunderstand what this filter is intended to do.
turbojet
8th November 2014, 01:29
Oh okay the first plugin could detect the original framerate somewhat accurately and I thought that was the intention.
S_ApparentFPS isn't taking duplicates into account in my testing, it was sitting at input fps and the only time it lowered was on slow motion or still pictures where it went to 0.
StainlessS
9th November 2014, 05:03
Try adjusting Dupethresh, thats what its for.
StainlessS
29th March 2015, 20:38
ApparentFPS v1.0, new version see 1st post.
Recompile v2.6 dll with Avisynth version 6 header.
Sparktank
30th March 2015, 00:44
Zip available via Mediafire (below this post).
Not to be a prude, but MediaFire doesn't seem to have the DLL zip.
Sendspace has a DLL zip but is dated to be "20151029", which would place it far into the future from now.
Otherwise, :thanks: for the update.
I presently have no use for this but always find these projects fascinating and like to archive them in case I do ever need them.
StainlessS
30th March 2015, 00:55
Oops, must be way past my bed time. both sites updated. Thanks for taking the time :)
scharfis_brain
7th April 2015, 07:29
I tried setting it to 1000 Samples,
but unfortunately it maxes out at 256 samples.
I've got some 24 Hz content wrapped in 25p and I wanted to estimate whether it was 23.976 or 24.000 fps ;-)
StainlessS
7th April 2015, 19:11
I'll take a look, and see if I can bump it up to 1024 samples.
StainlessS
7th April 2015, 21:20
v1.01, new version, see 1st post.
v1.01, Extended maximum samples from 256 to 1024 (silently limited to 1024).
Maximum number of binary digit flags limited to 64 when Verbose=True.
Max number of Debug Hex Values limited to 8 (ie 8 * 32 bits = 256 bit flags).
@SB
Have increased to 1024 samples.
The real result is the MaxApparentFPS, the instantaneous ApparentFPS will bounce around
as there will be some frames that are static without being artificial dupes.
MaxApparentFPS will likely be set correct at first sequence of continuous motion for Samples frames, and
Samples probably does not need to be 1000, about FrameRate will probably be sufficient, but have increased anyway.
If you jump around on timeline, will re-read samples frames around new current frame (EDIT: MaxApparentFPS not reset).
EDIT:
24 Hz content wrapped in 25p and I wanted to estimate whether it was 23.976 or 24.000 fps
Re-reading, not sure that ApparentFPS could do exactly as you want, you would need large Samples and continuous
motion for duration of Samples, not sure it would work even then.
StainlessS
7th April 2015, 22:35
scharfis,
Tried this simulation and it nailed it dead to 23.976FPS.
Avisource("D:\avs\testing.avi")
AssumeFPS(24000,1001) # Simulate 23.976 from 25FPS
ChangeFPS(25.0) # changes the frame rate by deleting or duplicating frames.
ApparentFPS(Samples=1001,Dupethresh=0.001) # DupeThresh very low as ChangeFPS will use EXACT dupes
NOTE, Samples=1001.
EDIT: Keeping Samples=1001 and Changing to
AssumeFPS(24000,1000) # Simulate 24.0 from 25FPS
Shows MaxApparentFPS as 24.001, so you should be able to get a good answer if you can select a good DupeThresh.
scharfis_brain
7th April 2015, 22:54
thanks!
I'll give it a try.
Great Dragon
7th April 2015, 23:33
Hello,
can this function replace duplicates frames with master frames like dupped() or at least pass its calculation to other filter?
http://forum.doom9.org/showthread.php?t=134930
StainlessS
8th April 2015, 02:12
Shows underlying framerate where a clip has had many duplicates inserted, easier than counting unique frames.
Above is its only functionality I'm afraid, however it does expose both Instantaneous ApparentFPS and MaxApparentFPS
if either of those can be of use.
Some Time Later ...
I've had a play with the linked script, started with the last posted one, but that seems to have had additional problems
incorporated, so I tried with the initial post #1 script.
Here's what I got (I dont have any anime to play with), perhaps you would like to give it a try.
# Dupped() by Corran. Feel free to modify as you see fit.
# Attribution not required.
#
# This filter requires a YUV source. Use converttoyv12 if needed
# before calling dupped()
#
## Parameters
# thresh = Used to determine when to delare a frame as new
# Use stats=true to help determine the best value for your source
# Lower number = more sensitive. (Default=16)
#
# panthresh=1.65 This is used to determine when to consider a frame
# part of a low motion scene. (Default=1.65)
#
# stats = Enable/disable display of stats. (Default=false | Not shown)
# (Last = last frame, this = this frame, next = next frame)
#
# showme = Enable/disable display of video that most stats are
# derived from. (Default=false | Not shown)
#
## Notes
# Do not use with SetMTMode(). This function requires the frames to be
# accessed sequentially. Instead, use MT() to mulit-thread on a per-filter basis.
#
# Example:
# dupped(thresh=20,panthresh=1.65,stats=true,showme=true)
Function Dupped(clip c, int "thresh", float "panThresh", bool "stats", bool "showme") {
c
Global dupThresh = default(thresh, 16)
Global panThresh = Float(default(panThresh, 1.65))
Global dupStats = default(stats, false)
Showme = default(showme, false)
Global lastNewFrame=0
scriptclip("""
c = Last
n=current_frame
FC = FrameCount
prev_frame = (n > 0) ? n-1 : 0
next_frame = (n < FC-1) ? n+1 : FC-1
#Get Y related stats as needed
prevCurrentYMinMax = Subtract(Trim(prev_frame,-1),c).YPlaneMinMaxDifference
currentNextYMinMax = prevCurrentYMinMax < dupThresh ? Subtract(Trim(next_frame,-1),c).YPlaneMinMaxDifference : 256
prevNextYMinMax = currentNextYMinMax < dupThresh ? Subtract(Trim(prev_frame,-1),Trim(next_frame,-1)).YPlaneMinMaxDifference : 0
#calculate dynamic pan threshold. Set to 256 if looking for a pan is pointless
neighborAvgMinMax = (prevCurrentYMinMax + currentNextYMinMax) / 2.0
dynPanThresh = prevNextYMinMax != 0 ? Abs(neighborAvgMinMax * panthresh) : 256.0
#determine if the current frame is new
IsNewFrame = (prevCurrentYMinMax >= dupThresh || prevNextYMinMax >= dynPanThresh)
Global lastNewFrame = IsNewFrame ? n : lastNewFrame
c = Trim(lastNewFrame,-1)
c = dupStats ? c.subtitle("prevframe: "+string(prev_frame)+" currentframe: "+string(n)+" nextframe: "+string(next_frame)) : c
c = dupStats ? c.subtitle("Last new frame: "+string(lastNewFrame)+" IsNewframe: "+string(IsNewFrame),y=15) : c
c = dupStats ? c.subtitle("prevCurrentYMinMax: "+string(prevCurrentYMinMax)+" (Threshold:"+string(dupThresh)+")",y=45) : c
c = dupStats && currentNextYMinMax != 256 ? c.subtitle("currentNextYMinMax: "+string(currentNextYMinMax),y=60) : c
c = dupStats && prevNextYMinMax >= dynPanThresh ? c.subtitle("prevNextYMinMax: "+string(prevNextYMinMax),y=90) : c
c = dupStats && prevNextYMinMax >= dynPanThresh ? c.subtitle("Pan Threshold: "+string(dynPanThresh)+
\ " ("+string(neighborAvgMinMax)+" * "+string(panThresh)+")",y=105) : c
c = dupStats && prevNextYMinMax >= dynPanThresh ? c.subtitle("Slow pan detected",y=120) : c
return c
""")
Return showme ? stackvertical(Last,subtract(c.duplicateframe(0),c)) : Last
}
Avisource("D:\avs\testing.avi")
Last=FreezeFrame(10,19,9)
dupped(Stats=True,ShowMe=true)
EDIT: Link on Wiki is broken (dupped).
EDIT: Main problem in original script was missing 'Global' marked above in blue.
StainlessS
8th April 2015, 03:28
Dupped() Multi-instance scripts moved to here:- http://forum.doom9.org/showthread.php?p=1716968#post1716968
StainlessS
28th April 2015, 16:50
Have changed script @ post #23 to use Samples = Default(Samples,Int(ceil(FrameRate))) from Default(Samples,Round(FrameRate)).
Also wanted to add this text but post has now reached maximum D9 Usage forum limit of 16KB, so here it is (addition marked in Blue).
If Prefix = "" (Default) then does not return any info Global vars. When set to eg "S_" will set Global S_ApparentFPS
and S_MaxApparentFPS as Float. If Multi-Instance then must give unique prefix for each instance returned Globals.
Also returns Globals S_MinAboveDupeDif, S_MaxBelowDupeDif and S_CurrentDif which may assist in choosing DupeThresh, they are min and
max values so far, and difference between current and previous frame. S_MinAboveDupeDif is the difference to previous frame of the
frame which has lowest difference that what considered NOT a dupe (above DupeThresh), MaxBelowDupeDif max difference of frame that WAS
considered a dupe (below DupeThresh). Ideally in a clip containing duplicates, there would be a distinct gap between these two
above/below dup dif values with DupeThresh somewhere in between.
!!! NOTE !!!, It may be better to set DupeThresh a little too high rather than too low, (where some motion frames will be mistaken as
dupes), as it is likely that there will be at least one sequence in clip where there is sufficient motion to accurately set
MaxApparentFPS.
Will change Plugin to use similar change to Samples default in next update.
EDIT: Also NOTE, if a clip has been edited since dupes were inserted, then it may result in a higher MaxApparentFPS than it should.
ie, clip1 ends on non-dupe frames, clip2 starts on non-dupe frames with some dupes cut out, therefore there are more non-dupes
in a row when spliced together yielding a false MaxApparentFPS. If MaxApparentFPS seems to be steady at some value with reasonable
amount of motion in at least one sequence, and then it suddenly jumps up to a higher MaxApparentFPs after a scene change, then
it is probable that the first estimate was correct and that the later higher MaxApparentFPS is in error.
(Perhaps we need scene change detection).
StainlessS
17th December 2015, 15:14
ApparentFPS, new version v1.03, see 1st post.
v1.01, Extended maximum samples from 256 to 1024 (silently limited to 1024).
Maximum number of binary digit flags limited to 64 when Verbose=True.
Max number of Debug Hex Values limited to 8 (ie 8 * 32 bits = 256 bit flags).
v1.03, Added 'UniqMax' Verbose metrics indicator, maximum number of unique frames in Sample range, so far.
NOTE, a clip that has been edited after dupe insertion may seem to jump up in MaxApparentFPS at edits, due to dupes being cut out.
You can reduce this effect by setting eg Samples=Int(Ceil(FrameRate))*10, but it will not show any MaxApparentFPS until you have
scanned forward Samples/2 frames (Samples are centered around current frame). It will also take a little time to start up as it
has to sample a lot of frames at startup. NOTE, Samples is silently (no error) limited to 1024.
EDIT: NOTE, if starting with eg 30FPS source, and settings Samples to eg 300,
If Shows UniqMax as 240, then 24 out of 30 frames should be kept (remove 6 out of 30 frame cycle),
if shows eg 241, then there has been variation/scene cut, and Cycle/Remove count not so clear cut.
edison
18th September 2016, 05:41
Can it be used for gaming framerate analysis where maybe tear?
StainlessS
18th September 2016, 20:25
Can it be used for gaming framerate analysis where maybe tear?
Sorry, dont know what 'gaming framerate analysis' is,
nor why you would want to do that.
All this plug does is tell what the underlying framerate is when a number of dupes have been inserted, nothing more, nothing less, nothing special.
EDIT: Eg, in webcam footage, tells what the real framerate is, ie 12FPS played at 30FPS, due to extra frame duplicate insertions.
(result 12.0 FPS)
EDIT: All it does is save YOU from counting duplicates, does nothing else.
StainlessS
17th December 2016, 12:11
ApparentFPS() v1.05, new version see first post.
v1.05, - 17 Dec 2016. Added FrameMovement Mode 1.
changes
ApparentFPS(clip c,Float "DupeThresh"=0.5,Float "FrameRate"=clp.FrameRate,Int "Samples"=Int(Ceil(FrameRate)),
\ Float "ChromaWeight"=1.0/3.0,String "Prefix"="", Bool "Show"=True,Bool "Verbose"=True, Bool "Debug"=False,
\ Int "Mode"=0,
\ Int "Matrix"=(c.Width>1100||c.Height>600)?3:2, # 2=Pc.601 : 3=PC.709 [EDIT: Swapped over numbers]
\ Int "BlkW"=64, Int "BlkH"=BlkW, Int "oLapX"=BlkW/2, Int "oLapY"=BlkH/2
\)
### Added v1.5
Mode Int, default 0. (0 -> 1).
0) Standard RT_FrameDifference mode.
1) FrameMovement mode, difference is greatest difference for any BlkWxBlkH block.
Matrix, Default (c.Width>1100||c.Height>600)?3:2. 2)=PC.601 : 3=Pc.709. Used in Conversion of RGB to Luma-Y.
BlkW Int, Default 64. (8 < BlkW, Even Only). Block Width for Mode 1 (not used for Mode=0).
BlkH Int, Default BlkW. (8 < BlkH, Even Only). Block Height for Mode 1 (not used for Mode=0).
oLapX Int, Default BlkW/2. (0 <= oLapX). Horizontal block Overlap for Mode = 1.
oLapY Int, Default BlkH/2. (0 <= oLapY). Vertical block Overlap for Mode = 1.
###
v1.05.
Added Some args, Matrix and Mode/blk args.
Mode=1, ie RT_FrameMovement mode, measures frame differences using a Block mode, chops the frame into BlkWxBlkH blocks, and difference
is the maximum difference for any one block. Blocks can also be OverLapping, by default block sizes / 2, so by default each frame
is multiply sampled 4 times (ie is slower than Mode=0, but can set oLapX and oLapY to 0, for no overlaps).
Mode=1, might be of use where there is very little movement in a sequence, but for most uses, probably best left at default Mode=0.
Also Note, in v1.05, ChromaWeight also alters differencing method used for RGB. Where ChromaWeight=0.0, will use LumaDifferencing,
(via Matrix arg) and where ChromaWeight > 0.0, then will use (AveRedDif+AveGrnDif+AveBluDif)/3.0, for both Mode=0 and Mode=1.
EDIT: Matrix doc marked in RED above, fixed and zip updated (no downloads so far).
ChaosKing
17th December 2016, 13:10
I get the error msg: There no function named RT_FunctionExist
It's also not included in the zip file.
I have an older anime DVD which seems to have 12fps, but also some higher fps scenes (maybe 15?). With your script I could find that out, right?
Edit: ahh ok, I didn't realize that there is a seperate script version^^. It works now, thx.
StainlessS
17th December 2016, 13:14
Arh, that threw me for a second, Your talking about the script version, like many of my scripts, it requires RT_Stats.
as noted in the script ie
Function S_ApparentFPS(clip clp,Float "DupeThresh",Float "FrameRate",Int "Samples",Float "ChromaWeight", String "Prefix",
\ Bool "Show", Bool "Verbose", Bool "Debug") {
/*
S_ApparentFPS: by StainlessS. Requires RT_Stats by StainlessS, GScript & Grunt Plugins (c) Gavino.
Multi-Instance script version.
Shows underlying framerate where a clip has had many duplicates inserted, easier than counting unique frames.
EDIT: The plugin just shows the Highest true framerate in clip, and the instantaneous framerate (may show eg 0.0 where static scene).
EDIT: You would normally just do something like below (using plugin, not the script which just shows how the plugin works).
AviSource("...")
ApparentFPS(DupeThresh=0.5)
StainlessS
17th December 2016, 13:33
Produces frame output like so (and a few local variables are set if required)
https://s20.postimg.cc/3ym8sejjx/Apparentfps_zpsmvocbjvh.png (https://postimg.cc/image/ap2q1u6pl/)
The real results is MaxAppFPS ie highest frame rate encountered so far in the clip.
AppFPS, is the instantaneous framerate, based on non dupes in the current sample spread with current frame in center of
the sample spread.
The binary digits show dupes as 0's and non dupes as 1's, with the lighter 0 in the middle being current frame.
EDIT: Above, I used the Block Mode=1 and so needed a high DupeThresh of 2.0 (not usual to have more than Dupethresh=1.0 where Mode=0)
The input framerate of above source clip was 29.97FPS [ and we used a sample spread of 30 frames ie Int(ceil(c.FrameRate)) ].
The ONLY purpose of ApparentFPS, is to save you from counting dupes manually, no other intended purpose.
ChaosKing
17th December 2016, 14:47
It works good, but I guess with my source it's a bit hard to detect all blends. At least in the scenes that I want to count... and some times I can barely see them too.
Is there maybe a not too complicated way to remove all blends (basiclly restore the original framerate) and save it as a VFR mkv? Currently I'm using QTGMC + srestore which gives me the best result so far. With srestore(12fps) it is nearly perfect but only in the 12fps scenes. I just don't know how to deal with the non 12fps scenes :-/
EDIT:
this worked a bit better for me:
ApparentFPS(DupeThresh=36,Mode=1)
StainlessS
17th December 2016, 15:56
It works good
Well then its doing more than it was intended to do.
ApparentFPS(DupeThresh=36,Mode=1)
Wow, savage settings !
Is there maybe a not too complicated way to remove all blends
Dont know sorry, QTGMC and srestore would have been my only suggestions, good luck.
mattygti
23rd October 2017, 13:43
Is it possible to output the results to a text/log file?
StainlessS
23rd October 2017, 17:34
How bout this
# Requires ApparentFPS and RT_Stats
#ASYNTHER Simple AVISource reader
AVISource("D:\AppFpsTest.AVI").Trim(144,19112)
DTH = 0.5
SAMPLES = Int(Ceil(FrameRate))
CHROMAWEIGHT = 1.0/3.0 # Or 0.0 for Luma only
SHOW = True
PREFIX = "Z_"
LOG = "AppFPS.Log"
###########################
LOG=RT_GetFullPathName(LOG)
RT_FileDelete(LOG)
###########################
SCRIPT="""
RT_WriteFile(LOG,"%d] %f : Max=%f : CurrentDif=%f : MinAboveDupeDif=%f : MaxBelowDupeDif=%f" ,
\ current_frame,Z_ApparentFPS,Z_MaxApparentFPS,Z_CurrentDif,Z_MinAboveDupeDif,Z_MaxBelowDupeDif,Append=True)
Return Last
"""
###########################
ApparentFPS(DupeThresh=DTh,Samples=SAMPLES,ChromaWeight=CHROMAWEIGHT,Prefix=PREFIX,Show=SHOW)
ScriptClip(SCRIPT,After_Frame=True)
Outputs something like:- (MaxApparentFPS not set until about frameNo FrameRate/2, IP FrameRate 30FPS)
0] 14.000000 : Max=0.000000 : CurrentDif=0.000000 : MinAboveDupeDif=0.605529 : MaxBelowDupeDif=0.495085
1] 13.125000 : Max=0.000000 : CurrentDif=2.290437 : MinAboveDupeDif=0.605529 : MaxBelowDupeDif=0.495085
2] 12.352942 : Max=0.000000 : CurrentDif=0.670800 : MinAboveDupeDif=0.605529 : MaxBelowDupeDif=0.495085
3] 13.333333 : Max=0.000000 : CurrentDif=0.011866 : MinAboveDupeDif=0.605529 : MaxBelowDupeDif=0.495085
4] 12.631579 : Max=0.000000 : CurrentDif=0.476676 : MinAboveDupeDif=0.605529 : MaxBelowDupeDif=0.495085
EDIT: bit more
...
5] 13.500000 : Max=0.000000 : CurrentDif=0.495085 : MinAboveDupeDif=0.605529 : MaxBelowDupeDif=0.495085
6] 12.857142 : Max=0.000000 : CurrentDif=0.156635 : MinAboveDupeDif=0.605529 : MaxBelowDupeDif=0.495085
7] 12.272727 : Max=0.000000 : CurrentDif=0.128656 : MinAboveDupeDif=0.605529 : MaxBelowDupeDif=0.495085
8] 13.043478 : Max=0.000000 : CurrentDif=0.706485 : MinAboveDupeDif=0.605529 : MaxBelowDupeDif=0.495085
9] 13.750000 : Max=0.000000 : CurrentDif=0.093044 : MinAboveDupeDif=0.605529 : MaxBelowDupeDif=0.495085
10] 13.200000 : Max=0.000000 : CurrentDif=0.640016 : MinAboveDupeDif=0.605529 : MaxBelowDupeDif=0.495085
11] 12.692307 : Max=0.000000 : CurrentDif=0.718715 : MinAboveDupeDif=0.605529 : MaxBelowDupeDif=0.495085
12] 13.333333 : Max=0.000000 : CurrentDif=0.024530 : MinAboveDupeDif=0.605529 : MaxBelowDupeDif=0.495085
13] 12.857142 : Max=0.000000 : CurrentDif=0.713118 : MinAboveDupeDif=0.605529 : MaxBelowDupeDif=0.495085
14] 13.448276 : Max=0.000000 : CurrentDif=0.605529 : MinAboveDupeDif=0.605529 : MaxBelowDupeDif=0.495085
15] 14.000000 : Max=14.000000 : CurrentDif=0.078526 : MinAboveDupeDif=0.605529 : MaxBelowDupeDif=0.495085
16] 14.000000 : Max=14.000000 : CurrentDif=0.101572 : MinAboveDupeDif=0.605529 : MaxBelowDupeDif=0.495085
17] 14.000000 : Max=14.000000 : CurrentDif=0.634060 : MinAboveDupeDif=0.605529 : MaxBelowDupeDif=0.495085
18] 13.000000 : Max=14.000000 : CurrentDif=0.074820 : MinAboveDupeDif=0.605529 : MaxBelowDupeDif=0.495085
19] 13.000000 : Max=14.000000 : CurrentDif=0.761264 : MinAboveDupeDif=0.605529 : MaxBelowDupeDif=0.495085
20] 14.000000 : Max=14.000000 : CurrentDif=0.114962 : MinAboveDupeDif=0.535323 : MaxBelowDupeDif=0.495085
21] 14.000000 : Max=14.000000 : CurrentDif=0.104566 : MinAboveDupeDif=0.535323 : MaxBelowDupeDif=0.495085
22] 15.000000 : Max=15.000000 : CurrentDif=1.081219 : MinAboveDupeDif=0.535323 : MaxBelowDupeDif=0.495085
23] 16.000000 : Max=16.000000 : CurrentDif=0.798456 : MinAboveDupeDif=0.535323 : MaxBelowDupeDif=0.495085
24] 16.000000 : Max=16.000000 : CurrentDif=0.034820 : MinAboveDupeDif=0.535323 : MaxBelowDupeDif=0.495085
25] 16.000000 : Max=16.000000 : CurrentDif=0.163632 : MinAboveDupeDif=0.535323 : MaxBelowDupeDif=0.495085
26] 16.000000 : Max=16.000000 : CurrentDif=0.804559 : MinAboveDupeDif=0.535323 : MaxBelowDupeDif=0.495085
27] 15.000000 : Max=16.000000 : CurrentDif=0.012473 : MinAboveDupeDif=0.535323 : MaxBelowDupeDif=0.495085
28] 16.000000 : Max=16.000000 : CurrentDif=0.808427 : MinAboveDupeDif=0.535323 : MaxBelowDupeDif=0.495085
29] 15.000000 : Max=16.000000 : CurrentDif=0.933175 : MinAboveDupeDif=0.535323 : MaxBelowDupeDif=0.495085
30] 15.000000 : Max=16.000000 : CurrentDif=0.085960 : MinAboveDupeDif=0.509095 : MaxBelowDupeDif=0.495085
31] 15.000000 : Max=16.000000 : CurrentDif=0.986158 : MinAboveDupeDif=0.509095 : MaxBelowDupeDif=0.495085
32] 16.000000 : Max=16.000000 : CurrentDif=0.061994 : MinAboveDupeDif=0.509095 : MaxBelowDupeDif=0.495085
33] 16.000000 : Max=16.000000 : CurrentDif=0.143366 : MinAboveDupeDif=0.509095 : MaxBelowDupeDif=0.495085
34] 16.000000 : Max=16.000000 : CurrentDif=0.535323 : MinAboveDupeDif=0.509095 : MaxBelowDupeDif=0.495085
35] 16.000000 : Max=16.000000 : CurrentDif=0.340412 : MinAboveDupeDif=0.509095 : MaxBelowDupeDif=0.495085
vBulletin® v3.8.11, Copyright ©2000-2025, vBulletin Solutions Inc.