Log in

View Full Version : avs script to detect and log scene changes


Hobojobo
13th September 2013, 13:39
I have got videos material in lossless AVI.

Is there a script/plugin to automatically detect and log scene changes?
I want to use them as chapter information for authoring video DVDs.

poisondeathray
13th September 2013, 15:39
I haven't tried this one, but I had it bookedmarked in case I needed something like this in the future
http://forum.doom9.org/showthread.php?p=1627449#post1627449

StainlessS
13th September 2013, 17:15
If you are feeling adventurous you might like to investigate DynaCrop.avs in RT_Stats Zip.
http://forum.doom9.org/showthread.php?t=165479

It does however require GScript and RoboCrop plugins too.
(RoboCrop used to crop off borders so as not to 'water down' metrics, also a good idea if using PoisonDeathray link script,
letterboxing will make it less likely that a scene chage is detected).

Will NOT Work with blended scene transitions (also likely with previous given link).

The funcs in question are:-

SC_ShowMetrics(), which shows metrics for selecting best settings (you can jump about, and scroll backwards).

SC_MakeTrimFile, which does not show metrics and creates a text file of scene frame start,end numbers, must scan start to end only.

There is also a SC_SceneSelect() func which is similar to SCSelect() but I think better, uses text file created with SC_MakeTrimFile.

Also, DynaCrop() for cropping changing borders, uses text file created with SC_MakeTrimFile.

EDIT: Signifcant updates on those in RT_Stats thread.

johnmeyer
13th September 2013, 22:39
I have several scene detection scripts. They all work to varying degrees, but none of them is perfect. They all can be fooled by something entering a scene close to the camera, by photo flashes, and by other similar circumstances which cause a large change from one frame to the next. Here are the three I use most often. Perhaps you can make some improvements and share them with everyone.

#Script to find scene changes
loadPlugin("c:\Program Files\AviSynth 2.5\plugins\depanestimate.dll")
loadPlugin("c:\Program Files\AviSynth 2.5\plugins\depan.dll")

filename = "e:\scenes.txt"

AVISource("e:\fs.avi").convertTOYV12(interlaced=true).killaudio()
separatefields().selecteven().convertTOYV12(interlaced=false)

#The "trust" parameter is what determines the scene change threshold.
#Lower number results in fewer scenes.
data=DepanScenes( DepanEstimate(trust=0.25) )

#Uncomment following line for troubleshooting. It puts the scene change threshold numbers on the screen.
#ScriptClip("Subtitle(String(AverageLuma(DepanScenes(data) ) ),align=5)")
#WriteFileIf(filename, "(AverageLuma(DepanScenes(data))>30)" , "current_frame", flush=false)

#ScriptClip("Subtitle(String(AverageLuma(data ) ),align=5)")
WriteFileIf(filename, "(AverageLuma(data)>30)" , "current_frame", flush=false)


#Script to find scene changes
SetMemoryMax(768)

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

filename = "e:\scenes.txt"
BlockChangeThresh = 1050 # Default: 350. Increase to reduce number of scenes detected; Determines whether a block has changed from prev frame.
Num_blocks_changed = 90 # Default: 90. Increase to reduce number of scenes detected; How many changed blocks (given threshhold above) must change to trigger scene change

source=AVISource("e:\fs.avi").killaudio().colorYUV(autogain=true) #add this to increase contrast
source_fields=source.separatefields().selecteven().convertTOYV12(interlaced=false)
source_super = source_fields.MSuper(pel=2, sharp=0)

#Use really large blocksize to speed processing. Accuracy isn't important for this function
backward_vec = MAnalyse(source_super,isb = true, delta = 1, blksize=16,search=0)

SceneChange = MSCDetection (source_fields, backward_vec,thSCD1=BlockChangeThresh,thSCD2=Num_blocks_changed)

#Uncomment following line for troubleshooting. It puts the scene change threshold numbers on the screen.
#ScriptClip(source_fields,"Subtitle(String(AverageLuma(SceneChange ) ),align=5)")

#This line writes frame numbers to file. These can be imported into Vegas or other editor
WriteFileIf(source_fields,filename, "(AverageLuma(SceneChange)>30)" , "current_frame+1", flush=false)


#This script detects scene changes

filename = "e:\JHM_Scenes.txt"
global blankthreshold=29
AVISource("e:\fs.avi").convertTOYV12().killaudio()

i=last
j=trim(i,1,0) #Previous frame

#Uncomment the next line to see metrics in order to set proper "blankthreshold" level
#ScriptClip(last,"Subtitle(String(YDifferenceToNext(j) ))" )

WriteFileIf(i, filename, "(YDifferenceToNext(j)>blankthreshold)&&YDifferenceToNext(i)<blankthreshold", "current_frame+2", append = false)

Hobojobo
13th September 2013, 22:46
@all
:thanks:

I am not into coding. I am happy when I get the scripts running.:cool:

StainlessS
14th September 2013, 02:37
@JohnMeyer, or anyone else,

The RT_stats scripts were an interesting excursion, but my higher math is, well quite frankly cr@p,
sadly my lower math seems to be equally cr@p. I did something a couple of decades ago that involved
differences of differences (dont have a clue what it was), but tried same. When almost same as current RT_stats script,
scene change (lots of twiddling since then, lots of nearly good ideas, but much the same) I came across a Didee
post that basically said that it was the only way to go (same method, difference of differences). I believe it's
called something like Finite Difference Calculus, not usually found in the regular Math text book, more
Numerical Analysis. Anyway, the scripts in RT_stats Dynacrop script show results of difference of differences
in an upside down pyramid (metrics [SC_ShowMetrics] , even the non used ones for research purposes),. If anyone with an interest
in that sort of, stuff, then do take over, I'm happy to code but am really quite stupid. It would at least enable you to
explore possibilities.

I shall try to find the Didee post (cos I know you dont take no notice of the likes of me :) )
EDIT: here I think, (think Didee made a mistake in that post [without reading it]but mostly correct)
simple thresholded difference

I think this was this post or near it (bit laden with beer at the moment, cant be bothered, the self is willing but the flesh is weak).
http://forum.doom9.org/showpost.php?p=1140274&postcount=14

johnmeyer
14th September 2013, 04:00
I did something a couple of decades ago that involved differences of differences (dont have a clue what it was) ...I don't either, but it sounds similar to a first derivative which is the most basic calculus operation.

In reading your post above, as well as the Didée post you linked to, one thing that can improve detection when using simple algorithms, like differences, is to use moving averages. The various metrics calculated by AVISynth can change a lot over the course of a clip, depending on the source material. What matters is not the value of the change from one frame to the next, but the value of that change compared to the changes that happen in the frames immediately adjacent. Therefore, in several scripts I've written, I keep a moving average (of whatever metric I'm using) from three or four adjacent frames, and then ratio this to the metric for the current frame. What you are looking for is a "blip" in the current frame that is above or below the average of the adjacent frames, rather than a specific value.

I didn't do this moving average in any of the three scripts I posted above, but the detection would probably be better if I did. I wrote these for my own purposes and was more interested in having the script run really fast than in getting absolutely perfect scene detection.

BTW, the free "Scenalyzer" (not to be confused with SCLive) has optical scene detection built in. Andi (the author) closed down his site, but I think you can still find that really old freeware program. However, it only works with SD material. The shareware program, "VideoReDo" has a commercial sensing algorithm that I think can be tuned so that it acts as a scene detector.

StainlessS
14th September 2013, 04:14
John, before I go to bed (no beer left), can I ask you to inspect the RT_stats scripts, 1st, 2nd,3rd, 4th, (maybe 5th)
derivative, available, dont no how to manipulate them further. Already works way better than eg SCSelect().

[EDIT: there is a 10 or so line script in RT_stats thread that emulates SCSelect operation exactly (to the best of my knowledge)].
EDIT: NOT RT_Stats thread, actually RoboCrop thread, here:-
http://forum.doom9.org/showthread.php?p=1633738#post1633738

Take a peek. Peace Bro. (rained for about 8 hours here now, water water everywhere, and not a drop to drink :( )

EDIT: Also, might be wrong about SC_SCSelect needing pre written txt file, think it might work on its own free-style,
not sure off-hand (but using same args as established by the show metrics function).

EDIT: Only 4th I think, 5 initial tween frame differences. All diffs shown in metrics, down to 4th.

johnmeyer
14th September 2013, 16:37
I looked at the SC_SceneSelect function in the DynaCrop.AVS file, but was not able to quite figure out what is going on inside the Motion.ScriptClip call. I've written quite a few scripts that use conditionals, but it is hard -- even with my own scripts -- to go back months later and remember exactly how it worked. I do see the various differences, like:

AB=SCM_B-SCM_A

It looks like you are then using a combination of several comparisons (T1, T2, and T3) to determine if a scene has changed, but one again I get lost because it looks like the same code is first executed inside the Eval and then again outside the Eval.

So, I'm afraid that I am not quite good enough to keep up with you on this one and therefore cannot provide any useful comments. Sorry about that.

P.S. If you think you have rain, tune in CNN International and look at the 14 inches of rain that has fallen in Boulder, CO in the past two days.

StainlessS
15th September 2013, 04:56
14 inches, well not quite that bad here I think.

Roughly, when scanning forward (and when not starting new or jumping about), moves all 5 differences from previous iteration frames over 1,
eg old current becomes new previous, old next becomes new current etc, and then samples difference between current + 2 and current + 3,
this only does 1 difference at each frame and re-uses previous metrics, making it considerably faster than reading 5 differences at every frame.
If new start at frame 0, or jumping about, or scanning backwards, it reads all 5 differences as if it were at the previous frame (the detector always
detects scene changes where current frame at Scene End frame, and passes that info as Global SCM_SC to the next scriptclip iteratation where it
becomes psc [previous frame was scene change]). The Eval is only called when the current scriptclip frame is NOT the one after the previous
iteration (ie new start or jumped about or going backwards). Global SCM_C is difference between current and next, SCM_B and SCM_A are the two before
it and, SCM_D and SCM_E are following differences.
T1 T2 & T3 are conditions that determine whether scene change or not and status of each (T or F) flagged in metrics so you can see which conditions
failed.

These are the difference variables and position

999 ] Trim=3 {T:F:F} L=0.0 : R=0.0 # status line, L and R give hints on choosing arg dfact
SCM_A SCM_B SCM_C SCM_D SCM_E # differences (Globals)
AB BC CD DE # Diff of diff
ABC BCD CDE # etc
ABCD BCDE # Only shown in SC_ShowMetrics
ABCDE # Only shown in SC_ShowMetrics


The conditions where: minim is minim arg, df is the dfact arg.

T1 = minim<SCM_C # Not static scene ?
T2 = ABC>0.0 && BCD<0.0 && CDE>0.0 # Scene Change sign OK ?
T3 =(df*SCM_B<SCM_C && df*SCM_D<SCM_C) # sufficiently bigger than either side ?



# Three conditions must be satisfied for an End Of Scene to be detected (and therefore the following Start Of Scene).
# 1) The center value shown in Row 2 Curr->Next should be at least Minim.
# 2) The 4th row holds the Scene Change Sign, both outer values should be +ve and the middle one -ve.
# 3) The Row 2 Curr->Next (center) value must be greater than DFact * value Left of it, and also greater than DFact * value on right of it.

From SC_ShowMetrics func (did not bother duplicating for SC_SceneSelect.

# Row 2 shows difference metrics between frames, the center value shows metric between Curr->Next frame, value left of center
# shows metric between Prev->Curr frame, right of center shows Next->Next+1 etc.
# Row 3, shows differences between the differences of Row 2. Row 4 shows differences between differences in Row 3, etc.
# Three conditions must be satified for an End Of Scene to be detected (and therefore the following Start Of Scene).
# 1) The center value shown in Row 2 Curr->Next should be at least Minim.
# 2) The 4th row holds the Scene Change Sign, both outer values should be +ve and the middle one -ve.
# 3) The Row 2 Curr->Next (center) value must be greater than DFact * value Left of it, and also greater than DFact * value on right of it.
# The True/False flags in Row 1 show result of above conditions.
# Also shown on Row 1, "L=" shows the Highest Value of DFact that could pass conditions for the value left of centre in Row 2, 0.0 means
# no setting of DFact could pass conditions, 999999.9 means any DFact setting above 1.0 would pass conditions. "R=" shows same thing
# for the Row 2 value right of Curr->Next.


The SC_SceneSelect() in full if anyone is interested:

Function SC_SceneSelect(clip dclip,clip start,clip end,clip motion,
\ float "dfact",float "minim",
\ Float "Bias",Float "Gain",float "Cont",Float "RPow",Float "SPow",Float "SPMid",bool "Pord",bool "Show") {
# Selects frames depending upon scene change status. Similar to SCSelect()
# Dclip, detection clip. Start, Frames after scene change. End, Frames before scene change. Motion, not scene change.
# Start, End and Motion must be same size and colorspace, Dclip can be other.
myName="SC_SceneSelect: "
df=Float(Default(dfact,4.0)) #
minim=Float(Default(minim,4.0)) # Scene change Diff has to be at least this, avoids eg scene change in near static scene.
Bias=Float(Default(Bias,0.0)) # Arg to RT_LumaSceneChange(), -511.0 -> 511.0, Default 0.0
Gain=Float(Default(Gain,1.0)) # Arg to RT_LumaSceneChange(), -8.0 -> 8.0, Default 1.0
Cont=Float(Default(Cont,1.0)) # Arg to RT_LumaSceneChange(), -8.0 -> 8.0, Default 1.0
RPow=Float(Default(RPow,1.0)) # Arg to RT_LumaSceneChange(), 0.1 -> 4.0, Default 1.0
SPow=Float(Default(SPow,1.0)) # Arg to RT_LumaSceneChange(), 0.1 -> 4.0, Default 1.0
SPMid=Float(Default(SPMid,0.5)) # Arg to RT_LumaSceneChange(), 0.01 -> 0.99, Default 0.5
Pord=Default(Pord,False) # Arg to RT_LumaSceneChange(), Default False
Show=Default(Show,False) # True = Show a little extra Info (Default False)
Assert(df>1.0,myName+"dfact MUST be greater than 1.0")
Assert(minim>0.0,myName+"minim MUST be greater than 0.0")
dclip=dclip.RoboCrop(WMod=4,HMod=4) # Dont allow common border to 'water down' metrics
Global SCM_B=0.0 Global SCM_C=0.0 Global SCM_D=0.0 Global SCM_E=0.0
Global SCM_SC=False Global TrimN=0 Global SCM_Prev=-2 # Init vars, SCM_Prev=-2 forces initalize
Start =(Show)? Start.SubTitle("START OF SCENE",align=1,size=30) : Start
End =(Show)? End.SubTitle("END OF SCENE",align=3,size=30) : End
Motion=(Show)? Motion.SubTitle("MOTION",align=5,size=30) : Motion
# Make format string only once, not at every frame
Fmt=(Show)? "%d ] Trim=%d {\a%c%.1s\a-:\a%c%.1s\a-:\a%c%.1s\a-} L=%.2f : R=%.2f\n" +
\ "%6.2f \a%c%6.2f %6.2f %6.2f\a- %6.2f\n %6.2f %6.2f %6.2f %6.2f\n \a%c%6.2f %6.2f %6.2f\a-\n" +
\ "DFact=%6.2f : Minim=%6.2f\nBias =%6.2f : Gain =%6.2f : Cont =%6.2f\nRPow =%6.2f : SPow =%6.2f : SPMid=%6.2f : Pord=%.1s":""
Motion.ScriptClip("""
NotNext = (current_frame!=SCM_Prev+1)
# If jumped about, get values for Previous frame, else keep same.
# Below Eval avoids extra calcs when scanning forward but is slower going backwards.
psc=(NotNext) ? Eval("
Global SCM_A=RT_LumaSceneChange(dclip,dclip,n=current_frame-3,n2=current_frame-2,bias=BIAS,gain=GAIN,cont=Cont,
\ rpow=RPOW,spow=SPow,Spmid=SPMid,pord=Pord)
Global SCM_B=RT_LumaSceneChange(dclip,dclip,n=current_frame-2,n2=current_frame-1,bias=BIAS,gain=GAIN,cont=Cont,
\ rpow=RPOW,spow=SPow,Spmid=SPMid,pord=Pord)
Global SCM_C=RT_LumaSceneChange(dclip,dclip,n=current_frame-1,n2=current_frame ,bias=BIAS,gain=GAIN,cont=Cont,
\ rpow=RPOW,spow=SPow,Spmid=SPMid,pord=Pord)
Global SCM_D=RT_LumaSceneChange(dclip,dclip,n=current_frame ,n2=current_frame+1,bias=BIAS,gain=GAIN,cont=Cont,
\ rpow=RPOW,spow=SPow,Spmid=SPMid,pord=Pord)
Global SCM_E=RT_LumaSceneChange(dclip,dclip,n=current_frame+1,n2=current_frame+2,bias=BIAS,gain=GAIN,cont=Cont,
\ rpow=RPOW,spow=SPow,Spmid=SPMid,pord=Pord)
AB=SCM_B-SCM_A BC=SCM_C-SCM_B CD=SCM_D-SCM_C DE=SCM_E-SCM_D # Diff of Diff
ABC=BC-AB BCD=CD-BC CDE=DE-CD # Diff Of Diff Of Diff
#
# BELOW MUST BE SAME AS MAIN CODE.
# Big enough compared to movement immediately either side of scene change
T1 = minim<SCM_C # Not static scene ?
T2 = ABC>0.0 && BCD<0.0 && CDE>0.0 # Scene Change sign OK ?
T3 =(df*SCM_B<SCM_C && df*SCM_D<SCM_C) # sufficiently bigger than either side ?
# ABOVE MUST BE SAME AS MAIN CODE.
#
return (T1 && T2 && T3)
") : SCM_SC
Global SCM_A=SCM_B Global SCM_B=SCM_C Global SCM_C=SCM_D Global SCM_D=SCM_E # Pass the parcel
Global SCM_E=RT_LumaSceneChange(dclip,dclip,n=current_frame+2,n2=current_frame+3,bias=BIAS,gain=GAIN,cont=Cont,
\ rpow=RPOW,spow=SPow,Spmid=SPMid,pord=Pord)
AB=SCM_B-SCM_A BC=SCM_C-SCM_B CD=SCM_D-SCM_C DE=SCM_E-SCM_D # Diff of Diff
ABC=BC-AB BCD=CD-BC CDE=DE-CD # Diff Of Diff Of Diff

# Big enough compared to movement immediately either side of scene change
T1 = minim<SCM_C # Not static scene ?
T2 = ABC>0.0 && BCD<0.0 && CDE>0.0 # Scene Change sign OK ?
T3 =(df*SCM_B<SCM_C && df*SCM_D<SCM_C) # sufficiently bigger than either side ?

psc=(psc||current_frame==0)
Global SCM_SC=((T1 && T2 && T3) || current_frame==FrameCount()-1) # Last frame in scene ?
Last = (psc&&!SCM_SC)?start:(SCM_SC&&!psc)?end:Last
CC=(T1&&T2&&T3)?33:45 # Hilite or Default color ?
LFACT=(SCM_C<=SCM_B || !T1 || !T2)?0.0:(SCM_B<0.000001)?999999.9:(SCM_C-0.000001)/SCM_B
RFACT=(SCM_C<=SCM_D || !T1 || !T2)?0.0:(SCM_D<0.000001)?999999.9:(SCM_C-0.000001)/SCM_D
(Show) ? RT_Subtitle(Fmt,current_frame,TrimN,CC,T1,CC,T2,CC,T3,LFACT,RFACT,SCM_A,CC,SCM_B,SCM_C,SCM_D,SCM_E,AB,BC,CD,DE,CC,ABC,BCD,CDE,
\ df,minim,bias,gain,cont,rpow,spow,spmid,pord) : NOP
Global TrimN = (SCM_SC) ? TrimN+1 : TrimN # Incr for next time
Global SCM_Prev=current_frame
Return Last
""",args="start,end,dclip,myName,Show,df,minim,Bias,Gain,Cont,RPow,SPow,SPMid,Pord,Fmt") # Needs Grunt for args
return Last
}


I guess it might not be too easy to follow, you have to put yourself (in your mind) in the position of the next iteration(near end of script), or previous one
(near early scriptclip).

EDIT: RT_lumaScenechange is identical to LumaDifference with linear defaulted args.

EDIT: The script flow works just fine as it is, the only assistance really required is in the three T conditions.
I did try variations of about 5 additional conditions, some of which worked great for some changes but detrimental to others
and so fell back to the obvious ones only. Might be best to view SC_ShowMetrics as you can see all of the pyramid
of differences.

StainlessS
16th September 2013, 11:44
Here is an updated version of SCSelect_Like() script linked in a previous post #8.
Uses RT_Stats, Grunt, and SCSelect from RemoveDirt for calling snippit.

SCSelect_Like() Planar, YUY2, RGB32/24


Avisource("D:\avs\test.avi")

# RemoveDirt's SCSelect(clip input, clip scene_begin, clip scene_end, clip global_motion, float dfactor, bool debug, bool planar)

Function SCSelect_Like(clip dclip,clip Start,clip End,clip Motion, float "dfactor",bool "debug") {
# Start, End, Motion MUST all be same, dclip can be other colorspace/size (unlike SCSelect).
dfactor=Float(Default(dfactor,4.0))
debug=Default(debug,false)
Global SCM_A=0.0 Global SCM_B=0.0 Global SCM_SC=0 Global SCM_Prev=-1
Motion.ScriptClip("""
NotNext = (current_frame!=SCM_Prev+1)
Global SCM_A=(NotNext)? RT_LumaDifference(dclip,dclip,n=current_frame-1,n2=current_frame) : SCM_B
Global SCM_B= RT_LumaDifference(dclip,dclip,n=current_frame,n2=current_frame+1)
# 0 = Start of scene, 1 = End of scene, 2 = Global motion
Global SCM_SC=(current_frame==FrameCount-1)?1:(SCM_A>dfactor*SCM_B || current_frame==0)?0:(SCM_B>dfactor*SCM_A)?1:2
(SCM_SC==0) ? Start : (SCM_SC==1) ? End : Last # Choose Start, End or Motion(ie Last)
(debug)?RT_Subtitle("%d ] %6.2f %6.2f SC=%d",current_frame,SCM_A,SCM_B,SCM_SC):NOP
Global SCM_Prev=current_frame
Return Last
""",args="dfactor,Start,End,Dclip,debug") # Needs Grunt for args
return Last
}

Start=Subtitle("START OF SCENE",align=3,size=30)
End=Subtitle("END OF SCENE", align=1,size=30)
Motion=Subtitle("GLOBAL MOTION",align=5,size=30)
L=SCSelect_Like(Last,Start,End,Motion,debug=true)
R=SCSelect(Last,Start,End,Motion)
StackHorizontal(L,R)


EDIT: I'm not condoning the usage of this script, it just emulates what the popular SCSelect does, it aint at all very good.

johnmeyer
16th September 2013, 16:36
Thanks for the in-depth explanation. That helps. I can now follow what you are doing, more or less. I can see how this approach helps emphasize discontinuities (i.e., scene changes) in the video material. However, if you are looking for ideas on how to further improve the quality of scene detection, I think that time would be better spent looking at alternatives to RT_LumaDifference which, in the end, is the heart of your approach. I am not in any way saying bad things about this function but am instead pointing out that scene changes can be detected by means other than brightness or color changes. In my work I explored the scene change thresholds provided by MVTools2 and Depan, as I posted above (in addition to a luma difference function which I used in the third of my three functions). For some material, these motion estimation functions provide more accurate results than luma differences, although they are harder to tune.

There are quite a few other AVISynth functions that also have scene detection logic built in, and perhaps some of them could provide better input to your "first derivative" function.

StainlessS
16th September 2013, 17:40
Thanks for your input John. I have intent at some time to incorporate UV stats into RT_, would also like to take
all three channels into account (as in RGB color CUBE Sqrt(Rd^2+Gd^2+Bd^2)) rather than just adding separate
channel differences together. I shall at some point look at MVTools/Depan, although I think they just throw a
'wobbler' if there is a little too much motion, and declare a scene change. One of the T conditions I tried was
to prevent scene change when diff FrameBeforeCurrent->Current OR Next->FrameAfterNext was near
to diff Current->Next, worked well in some cases but not always.
The SCSelect/SCSelect_Like funcs in previous post are not at all good, terrible with flash photography
and mismatched start and end scenes, 2 frame differences just dont work proper.
Dont know if worst one ever, but X-Men II is a nightmare for scene detection. Think SC_SceneSelect
falsely id's about 3 scene changes in first two minutes (should be none) whereas I'm guessing SCSelect/SCSelect_Like
would call wolf several dozen times.

johnmeyer
16th September 2013, 18:10
SCSelect/SCSelect_Like funcs in previous post are not at all good, terrible with flash photographyOn this particular subject, I have written several very simple scripts to detect and remove "bad frames." These can result not only from photo flashes, but also from dropouts, blank frames (a byproduct of a bug in Vegas that sometimes produces a single, all-black frame), and other similar anomalies. What they all have in common is that the bad frame is different from both adjacent frames. This is different than a scene transition where the current frame is different only from the next frame. Therefore, when actually trying to detect scene changes, you would want to not flag any of these cases as a scene change. Thus, in some "ultimate" version of this script, you'd want to have some exception logic to suppress the scene detection logic if the current frame differs from both of its neighbors.

StainlessS
16th September 2013, 19:24
Is already immune to flash photography, the 'sign' T2 condition (EDIT: also) detects that.
There is also a script to replace (interpolate) isolated bad frames between lumamin and lumamax, in RT_Stats zip,
also one for interlaced replace of bad fields (together with test clip generators).

johnmeyer
16th September 2013, 19:30
As usual, you are way ahead of me ...

raffriff42
17th September 2013, 13:58
Peanut gallery here :) Wouldn't detecting audio silence (http://forum.doom9.org/showthread.php?p=1619363#post1619363) be a good additional test which you might add to your total "scene change likelihood" score? If silence occurs with black video for more than 15(?) frames, you have a surefire scene change.

Also, you might start with a high scene change threshold, then ramp it down as the running time approaches your desired chapter running time, then reset it to a high value again for the next chapter break search...

StainlessS
17th September 2013, 18:22
Good additional test Raffriff42, particularly for true scene change rather than change of camera angle.

A very difficult frame change condition to cope with is demo'ed in X-Men II where blue skinned naked lady (metamorph)
is at a computer terminal and a dialog box appears on computer screen, changing from black to white
and vice versa. The dilog box is only a small fraction of the frame but the sudden localized (EDIT: massive) change produces
high metrics even though rest of frame is static. Perhaps some kind of average of block sized metrics would work well
to cut down on false +ve's. (I guess motion guided solutions would naturally cope better with this situation rather
than luma metric).

EDIT: Just had an idea. The function RT_YInRange() returns number of luma pixels within a range, returns 0.0 -> 1.0,
where 1.0 is equivalent to 100% of pixels within that range. Perhaps a similar type function returning amount
of pixels differences where 1.0 would be equivalent to 100% frame area has pixel differences within the given range.
Could use to establish 'goodness' of metrics, if only small area changes significantly, then metrics are BAD, ie no scene change.

EDIT: RaffRiff42, did actually try audio for suggested use, could not ID a scene change at all, although I did
try on (again) Xmen-II, constant music no discernable difference across scene (real scene changes) changes.
Home shot scenes may fair better than movies with constant music (which I dont really even notice myself).