Log in

View Full Version : How to incorporate "Selectevery() into WriteFileIf


johnmeyer
4th July 2015, 19:24
I've been working on scene detection scripts, and learning a lot from what StainlessS has been posting in another thread:

DBSC v0.051, Scene Change Detection - Work-in-Progress (http://forum.doom9.org/showthread.php?t=171624)

I think I may have an approach which works better for my low framerate film transfers. My version melds motion estimation code taken from StainlessS' approach with my YDifference scene detection.

Everything looks like it is going to work great, but I have one problem: I can't figure out how to get SelectEvery() to work within the conditional WriteFileIf() structure.

You can see my code below. I have temporarily included code which uses ScriptClip to show metrics on screen. As you can see, the "interleave()" statement creates a clip at 3x the original frame rate. The YDifference statements work on this clip, but the only relevant results are those which compare the current frame with the two adjacent motion estimated frames. All metrics created when the motion estimated frames are the reference frame should be removed. Therefore, the final result needs to be decimated using Selectevery(3,1), thus keeping the middle frame in each group consisting of an original frame and its two motion-estimated neighbors.

My problem is that I can't figure out how to have the WriteIf structure only return metrics for the original frames, while throwing out the metrics when the motion-estimated frames are the reference frame.

Any hints or ideas would be appreciated.

[edit]See Gavino's post #3 below, and my #4 post after that. He correctly points out that the code below will not work as expected.[/end edit]

#Scene detection script
#John Meyer - July 4, 2015

Loadplugin("C:\Program Files\AviSynth 2.5\plugins\MVTools\mvtools2.dll")

filename = "e:\Scenes.txt"
global scenethreshold = 1.8
BLOCKSIZE = 16
thSCD1 = (BLOCKSIZE*BLOCKSIZE) * 64

setMTMode(5,6)
source = AVISource("e:\fs.avi").convertTOYV12().killaudio()
setMTMode(2)

prefiltered = source.blur(1.0)
superfilt = MSuper(prefiltered,pel=2)
super = MSuper(source, pel=2)

back_vec = MAnalyse(superfilt,isb=true, blksize=BLOCKSIZE,overlap=0,search=0)
forw_vec = MAnalyse(superfilt,isb=false, blksize=BLOCKSIZE,overlap=0,search=0)

backcmp = source.MCompensate(super, back_vec, thSCD1=thSCD1, thSCD2=255)
forwcmp = source.MCompensate(super, forw_vec, thSCD1=thSCD1, thSCD2=255)

global mcomp_clip = interleave(forwcmp, source, backcmp)

#Next three lines let you view the metrics in order to set scenethreshold.
script = """Subtitle("\nNext/Prev = " + String( YDifferenceFromPrevious(mcomp_clip) / \
YDifferenceToNext(mcomp_clip) ), lsp=0)"""
metrics = ScriptClip(mcomp_clip,script)
final = selectevery(metrics,3,1) #select middle of each group of 3 mcomp'd frames
return final

#----------------------------------------------------------
#Need help getting "selectevery()" logic into WriteFileIf
#WriteFileIf(source, filename, "(YDifferenceFromPrevious(mcomp_clip)/YDifferenceToNext(mcomp_clip)>scenethreshold) && \
# (YDifferenceFromPrevious(mcomp_clip)/YDifferenceToNext(mcomp_clip) > \
# YDifferenceFromPrevious(loop(mcomp_clip,2,0,0))/YDifferenceFromPrevious(loop(mcomp_clip,0,0))", \
# "current_frame", append = false)

johnmeyer
4th July 2015, 19:58
[edit]The script I posted here (in this #2 post in the thread) was totally wrong, and did not solve my problem. I've removed it an hour after posting.

Gavino
4th July 2015, 20:26
OK, the following script seems to work...
That doesn't seem right to me as you seem to be now ignoring the motion-compensated frames altogether (mcomp_clip is decimated before each use of it in your new script).

I'm not sure I fully understand what you're trying to do, but the answer might be (after returning to the previous code) to apply the WriteFileIf() to mcomp_clip (rather than source) and add an extra condition in the WriteFileIf:
&& current_frame%3 == 1
thus only selecting the middle frame of each group of three.

johnmeyer
4th July 2015, 21:03
That doesn't seem right to me as you seem to be now ignoring the motion-compensated frames altogether (mcomp_clip is decimated before each use of it in your new script).

I'm not sure I fully understand what you're trying to do, ...Apparently, neither do I.

I should have also removed some of the code from my initial post, because you are correct: while the ScriptClip code was correctly using the motion-compensated frames, the WriteFileIf code was not.

As to what I'm trying to do, the goal is to improve scene detection by using motion compensated frames. Yes, it is true that MVTools2, Depan, and RemoveDirt (and perhaps other motion estimation functions) do have a Scene Detect parameter that can be accessed by a script for the purpose of creating a scene detection frame number list, the method of detection is not revealed to the user, and the variables needed to "tune" the script for optimal detection with a specific type of source material are not displayed on the screen.

StainlessS addressed many of these issues in the thread I referenced, but I was still getting some undetected scenes.

So, the purpose of this code is to create motion compensated scene detection. I think I have it working now (see the code below) and it seems to work better than the five previous scripts I wrote (the first four below) or used (the last two written by StainlessS):

1. Simple YDifference scene detection.
2. SCSelect-based scene detection.
3. MVtools2 scene detection using its MSCDetection parameter
4. Depanestimate detection using its DepanScenes parameter
5. StainlessS' earlier scene detection using RT_Stats
6. StainlessS' most recent scene detection, linked to above

When I say "better" (not wanting to run afoul of forum standards), I merely mean that I am having better results with my 23.976 progressive film transfers from old amateur movie film.

[edit]BTW, I solved the "SelecteEvery() problem" that was the purpose of my post, by punting. For those not familiar with American football, this means I gave up, and did it another way, using the AND operator and requiring that only metrics resulting from MOD(3) frame numbers would be output.


#Scene detection script
#John Meyer - July 4, 2015

Loadplugin("C:\Program Files\AviSynth 2.5\plugins\MVTools\mvtools2.dll")

filename = "e:\Scenes.txt"
global scenethreshold = 2.2
BLOCKSIZE = 8
thSCD1 = (BLOCKSIZE*BLOCKSIZE) * 64

#setMTMode(5,6)
source = AVISource("e:\fs.avi").convertTOYV12().killaudio()
#setMTMode(2)

prefiltered = source.blur(1.0)
superfilt = MSuper(prefiltered,pel=2)
super = MSuper(source, pel=2)

back_vec = MAnalyse(superfilt,isb=true, blksize=BLOCKSIZE,overlap=2,search=0)
forw_vec = MAnalyse(superfilt,isb=false, blksize=BLOCKSIZE,overlap=2,search=0)

backcmp = source.MCompensate(super, back_vec, thSCD1=thSCD1, thSCD2=255)
forwcmp = source.MCompensate(super, forw_vec, thSCD1=thSCD1, thSCD2=255)

global mcomp_clip = interleave(forwcmp, source, backcmp)

#Next three lines let you view the metrics in order to set scenethreshold.
#script = """Subtitle("\nNext/Prev = " + String( YDifferenceFromPrevious(mcomp_clip) / \
# YDifferenceToNext(mcomp_clip) ), lsp=0)"""
#final = ScriptClip(mcomp_clip,script)
#return selectevery(final,3,1)

WriteFileIf(source, filename, "(YDifferenceFromPrevious(selectevery(mcomp_clip,3,1)) / \
YDifferenceToNext(selectevery(mcomp_clip,3,1))>scenethreshold) \
&& frac( (current_frame + 2) / 3 ) == 0", \
"current_frame", append = false)

johnmeyer
4th July 2015, 21:16
The only issue I'm having with this code is that I do get occasional "double-hits" where I'll get two scene detection frames output in the space of just a few frames. This happens because of the "flash frame" that amateur film cameras introduce at the beginning of each scene. These happen because it takes a few milliseconds for the movie camera's rotating shutter wheel to get up to speed, and this causes the first few frames to get overexposed. The initial flash is actually a good thing for scene detection because it makes the discontinuity even greater. However, the autoexposure in the film-to-video transfer system reacts to this flash, overshoots, and then quickly comes back to the correct exposure. This secondary exposure change can fool the scene detection logic into wrongly identifying a second scene.

StainlessS solved this for his scripts by introducing a minimum scene length: once a scene is detected, additional scene changes are not allowed for "n" more frames.

I think I need to add something like this to the script shown above.

StainlessS
4th July 2015, 21:48
The dMinLen arg was introduced with minimum allowed 2, so as to ensure is possible to have unique Start and End of Scene frames in a scene.
Suggest go with Gavino offering rather than "frac( (current_frame + 2) / 3 ) == 0" which does exactly the same and as frac accepts a Float
arg, is probably doing a little more than required (ie use "current_frame%3 == 1").
EDIT: Also, I think would be handled by the parser rather than a function call to Frac() [Assuming Frac implemented as a function call].

By the way, just like in my earlier attempts, my scripts are Single Flash Frame tolerant, cannot do that with only two comparisons.
[EDIT: Cannot do that with two Differences, not comparisons]
EDIT: Above in Blue is rubbish, Cannot do that with only two differences if detecting on End OF Scene [like I do].

I'll have a play with your nice shiny new script :)

EDIT: Are you sure that the frac thing works OK ?


gscript("""
for(i=0,10) {
z = frac( (i + 2) / 3 )
RT_DebugF("%s",String(z))
}

""")

return Messageclip("DONE")

Produces all zeros (integer divide always has zero fractional part)

00000001 0.00000000 [1012] RT_DebugF: 0.000000
00000002 0.00071292 [1012] RT_DebugF: 0.000000
00000003 0.00138945 [1012] RT_DebugF: 0.000000
00000004 0.00205696 [1012] RT_DebugF: 0.000000
00000005 0.00271827 [1012] RT_DebugF: 0.000000
00000006 0.00338511 [1012] RT_DebugF: 0.000000
00000007 0.00405113 [1012] RT_DebugF: 0.000000
00000008 0.00471714 [1012] RT_DebugF: 0.000000
00000009 0.00539611 [1012] RT_DebugF: 0.000000
00000010 0.00607305 [1012] RT_DebugF: 0.000000
00000011 0.00674435 [1012] RT_DebugF: 0.000000

Gavino
4th July 2015, 22:10
[edit]BTW, I solved the "SelecteEvery() problem" that was the purpose of my post, by punting. For those not familiar with American football, this means I gave up, and did it another way, using the AND operator and requiring that only metrics resulting from MOD(3) frame numbers would be output.
Yes, that's exactly what my suggestion does too (except using a different expression).



WriteFileIf(source, filename, "(YDifferenceFromPrevious(selectevery(mcomp_clip,3,1)) / \
YDifferenceToNext(selectevery(mcomp_clip,3,1))>scenethreshold) \
&& frac( (current_frame + 2) / 3 ) == 0", \
"current_frame", append = false)
I think this is wrong as it is using differences between original frames only, not using the mo-comp ones at all.
I think it should be:

WriteFileIf(mcomp_clip, filename, "(YDifferenceFromPrevious(mcomp_clip) / \
YDifferenceToNext(mcomp_clip)>scenethreshold) \
&& frac( (current_frame + 2) / 3.0 ) == 0", /* or current_frame%3 == 1 */ \
"current_frame/3", append = false)
(Note some other corrections included here)

EDIT: Indeed, since mcomp_clip is now the clip argument to WriteFileIf(), it becomes last inside the run-time script, so can be omitted (and probably no longer needs to be global).

WriteFileIf(mcomp_clip, filename, "(YDifferenceFromPrevious() / \
YDifferenceToNext()>scenethreshold) \
&& current_frame%3 == 1" \
"current_frame/3", append = false)

Gavino
4th July 2015, 22:20
Are you sure that the frac thing works OK ?
...
Produces all zeros

It needs to be
frac( (current_frame + 2) / 3.0 ) == 0
to get the division done as float.

johnmeyer
5th July 2015, 00:29
OK, when I get back to this tomorrow I need to create a test version of the script that outputs to my text file the YDifference metrics as well as the current frame number. Once I have that working, I need to see what changes I get when I force the float computation using Gavino's advice of adding the decimal point.

Thanks to both of you for your help.

What I don't understand is why the script, as written, was the first script to correctly identify every single scene in my 12-minute test clip. This is especially puzzling if the MOD 3 approach is so fundamentally flawed. If it produces nothing but zeros, it seems like the scene frame numbers would be completely wrong, but they weren't.

More to come (tomorrow).

StainlessS
5th July 2015, 00:49
You were getting divide by zero errors, first part fixed (not touched WriteIf part)


#Scene detection script
#John Meyer - July 4, 2015

#Loadplugin("C:\Program Files\AviSynth 2.5\plugins\MVTools\mvtools2.dll")

filename = "D:\Scenes.txt"
global scenethreshold = 2.2
BLOCKSIZE = 8
thSCD1 = (BLOCKSIZE*BLOCKSIZE) * 64

#setMTMode(5,6)
source = AVISource("D:\AVS\StarWars.avi").convertTOYV12().killaudio()
#source = AVISource("D:\AVS\FlashTest.avi").convertTOYV12().killaudio()
#setMTMode(2)

prefiltered = source.blur(1.0)
superfilt = MSuper(prefiltered,pel=2)
super = MSuper(source, pel=2)

back_vec = MAnalyse(superfilt,isb=true, blksize=BLOCKSIZE,overlap=2,search=0)
forw_vec = MAnalyse(superfilt,isb=false, blksize=BLOCKSIZE,overlap=2,search=0)

backcmp = source.MCompensate(super, back_vec, thSCD1=thSCD1, thSCD2=255)
forwcmp = source.MCompensate(super, forw_vec, thSCD1=thSCD1, thSCD2=255)

global mcomp_clip = interleave(forwcmp, source, backcmp)

#Next three lines let you view the metrics in order to set scenethreshold.
Script = """
i = current_frame
P = YDifferenceFromPrevious
N = YDifferenceToNext + 0.01
Rat = P / N
Subtitle(string(i)+" : "+String(i/3)+" ]\nP="+String(P) + " : N="+
\ String(N)+"\nPrev/Next = " + String(Rat)+" "+String(Rat>scenethreshold), lsp=0)
(Rat>scenethreshold) ? RT_WriteFile(FileName,"%d ] P=%7.3f : N=%7.3f : RAT=%7.3f",i/3,P,N,RAT,Append=True) : NOP
Return (Rat>scenethreshold) ? Subtitle("BANG !!!",size=Height/3,align=5) : Last
"""
RT_FileDelete(FileName)
final = mcomp_clip.ScriptClip(Script)
return selectevery(final,3,1)

#WriteFileIf(source, filename, "(YDifferenceFromPrevious(selectevery(mcomp_clip,3,1)) / \
# YDifferenceToNext(selectevery(mcomp_clip,3,1))>scenethreshold) \
# && current_frame % 3 == 1", \
# "current_frame", append = True)




Check it out on mainstream movie with fade in from black, machine gun, detections.

EDIT:
First 187 frames of Starwars had all black frames (all zero), dont think I played with levels before UTVideo-ing it.
Machine gun detections at about frame 724 (after 20 Century Fox Intro), about 5 black frame detections,
a little more intro and then some more near black frame detections.

EDIT: Changed
N2 = (N<0.000001) ? 0.000001 : N
to
N2 = (N<0.01) ? 0.01 : N
Not as many false detections, but still some. Suggest dMinim type detect to allow for sensitivity control.

EDIT: Digitally remastered version, of two version set (also original version).

EDIT: Ich bin ein dickhead, 0.0 YdifferenceFromPrev etc not averageLuma, I'm such a silly billy.

EDIT: Changed to

N = YDifferenceToNext + 0.01
Rat = P / N

Still 18 false detects before the scrolling storyline intro.

EDIT:
YDifferenceToNext(selectevery(mcomp_clip,3,1))
You are just differencing the original frames, not mocomp frames, and
frac( (current_frame + 2) / 3 ) == 0
is always true. Use Big G's original version, not the floating point frac thing (with 3.0), is inefficient.

StainlessS
5th July 2015, 17:46
Hi john, just got back from pub with your sample and will try with it soon.

Here mod of your method (using my version method).
I usually use two comparisons of 3 differences, you one comparison of 2 diffs, yours will probably pick up some that my method
rejects (but I use it to reject possible problems, both comparisons have to succeed, my way).
I guess you choose whatever poison tastes best to you.

Here a one comparison of two difference method that is more akin to what you are using.

Not tuned in any way to any clip.

#Scene detection script
#John Meyer - July 4, 2015

#Loadplugin("C:\Program Files\AviSynth 2.5\plugins\MVTools\mvtools2.dll")

filename = "D:\Scenes.txt"
dFact = 2.0
dMin = 2.0

BLOCKSIZE = 8
thSCD1 = (BLOCKSIZE*BLOCKSIZE) * 64

#setMTMode(5,6)
source = AVISource("D:\AVS\StarWars.avi").convertTOYV12().killaudio()
#source = AVISource("D:\AVS\FlashTest.avi").convertTOYV12().killaudio()
#setMTMode(2)

prefiltered = source.blur(1.0)
superfilt = MSuper(prefiltered,pel=2)
super = MSuper(source, pel=2)

back_vec = MAnalyse(superfilt,isb=true, blksize=BLOCKSIZE,overlap=2,search=0)
forw_vec = MAnalyse(superfilt,isb=false, blksize=BLOCKSIZE,overlap=2,search=0)

backcmp = source.MCompensate(super, back_vec, thSCD1=thSCD1, thSCD2=255)
forwcmp = source.MCompensate(super, forw_vec, thSCD1=thSCD1, thSCD2=255)

global mcomp_clip = interleave(forwcmp, source, backcmp)

#Next three lines let you view the metrics in order to set scenethreshold.
Script = """
i = current_frame
P = YDifferenceFromPrevious
N = YDifferenceToNext
Z = (N *dFact + dMin)
T = (P > Z)
Subtitle(string(i)+" : "+String(i/3)+" ]\n" +
\ String(dFact,"dFact=%.2f") + " " + String(dMin,"dMin=%.2f\n") +
\ String(P,"P=%.2f") + String(N," N=%.2f\n") +
\ String(Z,"Z (N * dFact + dMin) = %.2f\n") +
\ "(P > Z) = " + String(T), lsp=0)
(T) ? RT_WriteFile(FileName,"%d ] P=%7.3f : N=%7.3f : Z=%7.3f",i/3,P,N,Z,Append=True) : NOP
Return (T) ? Subtitle("BANG !!!",size=Height/3,align=5) : Last
"""
RT_FileDelete(FileName)
final = mcomp_clip.ScriptClip(Script)
return selectevery(final,3,1)

#WriteFileIf(source, filename, "(YDifferenceFromPrevious(selectevery(mcomp_clip,3,1)) / \
# YDifferenceToNext(selectevery(mcomp_clip,3,1))>scenethreshold) \
# && current_frame % 3 == 1", \
# "current_frame", append = True)

EDIT: Z = (N * dFact + dMin) # where N = Curr->Next
dMin is sole govenor of scene change detection when Curr->Next is zero, so it does need to be of reasonable value.
(Curr=SOS, ie Start Of Scene, 1st Frame of scene, Next = frame After that)

EDIT: I prefer a higher dMin and lower Dfact (eg maybe about dMin=6.0, dFact=1.3333)

johnmeyer
5th July 2015, 19:34
Very useful code. I'll use that later today when I try to wrap up this project. Once again, thanks!

johnmeyer
9th July 2015, 21:43
Got interrupted several times, but finally tried your code in post #11.

It worked perfectly on that 12 minute football clip I sent to you.

I'm now going to try it out on several other files. Also, I need to optimize it because it is less than real-time speed, and it gags if I try to use SetMTMode. Finally, I'm going to try your more correct MVTools2 motion estimation code in my own script and see if I can get results as good as yours.

As always, thank you.

johnmeyer
10th July 2015, 00:14
As noted above, I got great results with this script. However, I only got about 21 fps when it was running, which for 24p material is slightly slower than real time.

So, I put the same detection logic and same motion estimation back into the AVISynth conditional logic.

This resulted in both good news and bad news.

The good news -- actually the great news -- is that this script (see below) runs at 233 fps which is more than 10x faster than the other script, and almost 10x real time.

The bad news is that, even though the on-screen metrics appear to be identical, there were two places in the 12-minute test clip where the scene detection performed differently.

This makes no sense to me, but I have a sneaking hunch that I have either run afoul of the "global gods" that Gavino has been warning me about for years, or I have an issue with variable typing that is causing the two variables to be compared as integers rather than float. For instance, in one place where my version fails to find a scene (frame 13509 in the test clip), the two variables are (using StainlessS' names):

P = 6.49
Z = 5.68

So, what stupid thing did I do?

[edit] Oops. I am, in fact, and idiot. It's the selectevery within the WriteIf. Using that means I'm throwing away all the motion estimation frames.

Back to the drawing board ...

Here's my script:
#Scene detection script
#John Meyer - July 9, 2015
#Use Detection logic from StainlessS
#Might get different results with different block size and overlap.

Loadplugin("C:\Program Files\AviSynth 2.5\plugins\MVTools\mvtools2.dll")

filename = "e:\Scenes.txt"
global dFact = 2.0
global dMin = 2.0
BLOCKSIZE = 8
thSCD1 = (BLOCKSIZE*BLOCKSIZE) * 64

source = AVISource("e:\fs.avi").convertTOYV12().killaudio()

prefiltered = source.blur(1.0)
superfilt = MSuper(prefiltered,pel=2)
super = MSuper(source, pel=2)

back_vec = MAnalyse(superfilt,isb=true, blksize=BLOCKSIZE,overlap=2,search=0)
forw_vec = MAnalyse(superfilt,isb=false, blksize=BLOCKSIZE,overlap=2,search=0)

backcmp = source.MCompensate(super, back_vec, thSCD1=thSCD1, thSCD2=255)
forwcmp = source.MCompensate(super, forw_vec, thSCD1=thSCD1, thSCD2=255)

global mcomp_clip = interleave(forwcmp, source, backcmp)

#---------------------------------

#Next four lines let you view the metrics in order to set detection metrics.
#script = """Subtitle("\nScene = " + String( (dFact * YDifferenceToNext (mcomp_clip) + dMin ) ) + \
# "\nYDiffP = " + String( YDifferenceFromPrevious(mcomp_clip) ), lsp=0)"""
#final = ScriptClip(mcomp_clip,script)
#return selectevery(final,3,1)

#I'm pretty sure my problem is hidden somewhere in this one statement
WriteFileIf(source, filename, "(YDifferenceFromPrevious(selectevery(mcomp_clip,3,1)) > \
(dFact * YDifferenceToNext(selectevery(mcomp_clip,3,1)) + dMin ))", \
"current_frame", append = false)

StainlessS
10th July 2015, 00:35
Subtitle is probably what was killing you for speed on other script, was only intended for metrics really (could have modded to RT_Subtitle, much faster).

Try this (untested)


#Scene detection script
#John Meyer - July 9, 2015
#Use Detection logic from StainlessS
#Might get different results with different block size and overlap.

Loadplugin("C:\Program Files\AviSynth 2.5\plugins\MVTools\mvtools2.dll")

filename = "e:\Scenes.txt"
global dFact = 2.0
global dMin = 2.0
BLOCKSIZE = 8
thSCD1 = (BLOCKSIZE*BLOCKSIZE) * 64

source = AVISource("e:\fs.avi").convertTOYV12().killaudio()

prefiltered = source.blur(1.0)
superfilt = MSuper(prefiltered,pel=2)
super = MSuper(source, pel=2)

back_vec = MAnalyse(superfilt,isb=true, blksize=BLOCKSIZE,overlap=2,search=0)
forw_vec = MAnalyse(superfilt,isb=false, blksize=BLOCKSIZE,overlap=2,search=0)

backcmp = source.MCompensate(super, back_vec, thSCD1=thSCD1, thSCD2=255)
forwcmp = source.MCompensate(super, forw_vec, thSCD1=thSCD1, thSCD2=255)

global mcomp_clip = interleave(forwcmp, source, backcmp)

#---------------------------------

#Next four lines let you view the metrics in order to set detection metrics.
#script = """Subtitle("\nScene = " + String( (dFact * YDifferenceToNext (mcomp_clip) + dMin ) ) + \
# "\nYDiffP = " + String( YDifferenceFromPrevious(mcomp_clip) ), lsp=0)"""
#final = ScriptClip(mcomp_clip,script)
#return selectevery(final,3,1)

#I'm pretty sure my problem is hidden somewhere in this one statement
Script="""(current_frame %3 == 1) && (YDifferenceFromPrevious > (dFact * YDifferenceToNext + dMin ))"""
WriteFileIf(source, filename,script, "current_frame", append = false)


EDIT: Should Append=false be Append=True ? (I never use Writexxxx since RT write functions, to weird for me).

johnmeyer
10th July 2015, 00:43
Hmmm ...

I fixed the problem, but ended up with a script that runs a 1/3 the speed (80 fps, which is still an improvement over 23 fps). As you can see below (go to the WriteFileIf at the end of the script), I'm now using the clip that contains the forward and backward estimated frames, so it contains 3x the frames and therefore takes 3x as long to evaluate.

So here's the question: is there a way to force WriteFileIf to only evaluate every third frame? It is a glorious waste of time to evaluate the 2 out of 3 instances where the estimated frame is the central frame.

#Scene detection script
#John Meyer - July 9, 2015
#Use Detection logic from StainlessS
#Might get different results with different block size and overlap.

Loadplugin("C:\Program Files\AviSynth 2.5\plugins\MVTools\mvtools2.dll")

filename = "e:\Scenes.txt"
global dFact = 2.0
global dMin = 2.0
BLOCKSIZE = 8
thSCD1 = (BLOCKSIZE*BLOCKSIZE) * 64

source = AVISource("e:\fs.avi").convertTOYV12().killaudio()

prefiltered = source.blur(1.0)
superfilt = MSuper(prefiltered,pel=2)
super = MSuper(source, pel=2)

back_vec = MAnalyse(superfilt,isb=true, blksize=BLOCKSIZE,overlap=2,search=0)
forw_vec = MAnalyse(superfilt,isb=false, blksize=BLOCKSIZE,overlap=2,search=0)

backcmp = source.MCompensate(super, back_vec, thSCD1=thSCD1, thSCD2=255)
forwcmp = source.MCompensate(super, forw_vec, thSCD1=thSCD1, thSCD2=255)

global mcomp_clip = interleave(forwcmp, source, backcmp)

#---------------------------------

#Next four lines let you view the metrics in order to set detection metrics.
#script = """Subtitle("\nScene = " + String( (dFact * YDifferenceToNext (mcomp_clip) + dMin ) ) + \
# "\nYDiffP = " + String( YDifferenceFromPrevious(mcomp_clip) ), lsp=0)"""
#final = ScriptClip(mcomp_clip,script)
#return selectevery(final,3,1)

WriteFileIf(mcomp_clip, filename, "(YDifferenceFromPrevious(mcomp_clip) > \
(dFact * YDifferenceToNext(mcomp_clip) + dMin ))", \
"current_frame/3", append = false)

StainlessS
10th July 2015, 00:46
See previous post.

johnmeyer
10th July 2015, 00:50
StainlessS,

Thanks, I'll look at that. I just realized that the reason I am "only" getting 80 fps rather than 240 has nothing to do with how things are being evaluated, but because the correct version of the script is now using the estimated clips, and MVTools2 has to do its thing. I'll see if I can get it to work with SetMTMode. Your functions weren't too happy when I tried that.

Let me try your revised code in your previous post.

[edit]Just tried it, and it runs fast (240 fps), but only detects about 10% of the scenes (although it is the correct scenes). I'll keep working ...

johnmeyer
10th July 2015, 02:00
OK, your code didn't quite work (it only detected some of the scene changes), but your Modulo 3 = 1 code is what I needed. The following script seems to provide the same detection accuracy as your script in post #11, but at 220 fps. So, here's the code I'm going to be using for the rest of my tests.

Loadplugin("C:\Program Files\AviSynth 2.5\plugins\MVTools\mvtools2.dll")

filename = "e:\Scenes.txt"
global dFact = 2.0
global dMin = 2.0
BLOCKSIZE = 8
thSCD1 = (BLOCKSIZE*BLOCKSIZE) * 64

setMTMode(5,6)
source = AVISource("e:\fs.avi").convertTOYV12().killaudio()
setMTMode(2)

prefiltered = source.blur(1.0)
superfilt = MSuper(prefiltered,pel=2)
super = MSuper(source, pel=2)

back_vec = MAnalyse(superfilt,isb=true, blksize=BLOCKSIZE,overlap=2,search=0)
forw_vec = MAnalyse(superfilt,isb=false, blksize=BLOCKSIZE,overlap=2,search=0)

backcmp = source.MCompensate(super, back_vec, thSCD1=thSCD1, thSCD2=255)
forwcmp = source.MCompensate(super, forw_vec, thSCD1=thSCD1, thSCD2=255)

global mcomp_clip = interleave(forwcmp, source, backcmp)

#---------------------------------

#Next four lines let you view the metrics in order to set detection metrics.
#script = """Subtitle("\nScene = " + String( (dFact * YDifferenceToNext (mcomp_clip) + dMin ) ) + \
# "\nYDiffP = " + String( YDifferenceFromPrevious(mcomp_clip) ), lsp=0)"""
#final = ScriptClip(mcomp_clip,script)
#return selectevery(final,3,1)

#Evaluate every 3rd frame using Mod(3) = 1
#Comment out thse lines when viewing metrics with code above
WriteFileIf(mcomp_clip, filename, "(YDifferenceFromPrevious(mcomp_clip) > \
(dFact * YDifferenceToNext(mcomp_clip) + dMin )) && (current_frame %3 == 1)", \
"current_frame/3", append = false)

StainlessS
10th July 2015, 02:08
Should still have SelectEvery(3,1) at end, (I forgot in last post).

Dont use

WriteFileIf(mcomp_clip, filename, "(YDifferenceFromPrevious(mcomp_clip) > \
(dFact * YDifferenceToNext(mcomp_clip) + dMin ))", \
"current_frame/3", append = false)


use (inside Writefile,mcomp_clip is Last)

mcomp_clip.WriteFileIf(filename, "(YDifferenceFromPrevious > \
(dFact * YDifferenceToNext + dMin ))", \
"current_frame/3", append = false)
SelectEvery(3,1)



also maybe try

#Scene detection script
#John Meyer - July 4, 2015

#Loadplugin("C:\Program Files\AviSynth 2.5\plugins\MVTools\mvtools2.dll")

filename = "Scenes.txt"
dFact = 2.0
dMin = 2.0
BLOCKSIZE = 8
thSCD1 = (BLOCKSIZE*BLOCKSIZE) * 64

#setMTMode(5,6)
source = AVISource("1955 Lions @ 49ers 4th Quarter_07 (23.976).mp4.AVI").convertTOYV12().killaudio()
#setMTMode(2)

RT_FileDelete(FileName)

prefiltered = source.blur(1.0)
superfilt = MSuper(prefiltered,pel=2)
super = MSuper(source, pel=2,levels=1) # Added Levels=1

back_vec = MAnalyse(superfilt,isb=true, blksize=BLOCKSIZE,overlap=2,search=0)
forw_vec = MAnalyse(superfilt,isb=false, blksize=BLOCKSIZE,overlap=2,search=0)

backcmp = source.MCompensate(super, back_vec, thSCD1=thSCD1, thSCD2=255)
forwcmp = source.MCompensate(super, forw_vec, thSCD1=thSCD1, thSCD2=255)

interleave(forwcmp, source, backcmp)

#Next three lines let you view the metrics in order to set scenethreshold.
ScriptShow = """
i = current_frame
P = YDifferenceFromPrevious
N = YDifferenceToNext
Z = (N *dFact + dMin)
T = (P > Z)
RT_Subtitle(string(i)+" : "+String(i/3)+" ]\n" +
\ String(dFact,"dFact=%.2f") + " " + String(dMin,"dMin=%.2f\n") +
\ String(P,"P=%.2f") + String(N," N=%.2f\n") +
\ String(Z,"Z (N * dFact + dMin) = %.2f\n") +
\ "(P > Z) = " + String(T))
(T) ? RT_WriteFile(FileName,"%d ] P=%7.3f : N=%7.3f : Z=%7.3f",i/3,P,N,Z,Append=True) : NOP
# (T) ? Subtitle("BANG !!!",size=Height/3,align=5) : Last
return Last
"""
ScriptNoShow = """
i = current_frame
P = YDifferenceFromPrevious
N = YDifferenceToNext
Z = (N *dFact + dMin)
T = (P > Z)
(T) ? RT_WriteFile(FileName,"%d ] P=%7.3f : N=%7.3f : Z=%7.3f",i/3,P,N,Z,Append=True) : NOP
return Last
"""

SHOW=False

ScriptClip((SHOW) ? ScriptShow : ScriptNoShow)
return selectevery(3,1)


EDIT: I changed Filename to current dir.