View Full Version : Finding individual "bad" frames in video; save frame number; or repair
johnmeyer
13th December 2016, 04:51
[edit]Here is a link to a better version of the script, later in this thread: Better Version of Script (http://forum.doom9.org/showpost.php?p=1789584) [/end edit]
Long ago I created a script that I used to find individual blank frames. I needed this because my NLE (Vegas (http://www.videohelp.com/software/Vegas)) has a nasty habit of creating random blank frames during certain types of renders, and you don't know if you have the problem without a tool which can look at every single frame.
I then found I needed a script to find "flash" frames because I do movie film transfers, and because of the way home movie cameras work, the first frame of every single scene is horribly overexposed. These therefore need to be found and removed.
As time went on, I found all sorts of other situations where a video can contain a single frame which does not match either adjacent frame.
I recently had a request for this script, so I went back and cleaned it up a bit and added some comments. The main tool used for detection is the YDifference function built into AVISynth (http://www.videohelp.com/software/Avisynth). It compares each pixel in the current frame to each corresponding pixel in the adjacent frame. The metric "blows up" whenever there is a big difference in lots of the pixels, something that happens at scene changes, but also when there is a single-frame corruption. What's more, the metrics blow up looking both backwards and forwards when there is an individual bad frame, whereas they only blow up in one direction at a scene change. Therefore, this function will not do anything at scene changes (which is what you want).
Other types of detection could easily be substituted, using either the other stat functions built into AVISynth, or by using the myriad of compare functions built into StainlessS' excellent RT_Stats package.
I look forward to any ideas for improving this. I couldn't figure out how to do the conditional without declaring one variable as global (Gavino always dings me on this). Also, I wasn't able to get it to both automatically fix bad frames AND output the bad frame numbers to a text file. I could get it to do one or the other, but not both. For me, this isn't a big deal, but some people might want to be able to easily go to the fixed video and check each and every new frame to make sure it looks OK.
One other thing: I wrote this so it works on interlaced video. Since progressive is a special case of interlaced (with no temporal differences between fields), it will work just fine with progressive as well.
#Find And (Optionally) Fix Bad Frames
#John Meyer - December 12, 2016
#This script detects single bad frames. You have two options of what to do.
#You can configure the script to write, to a file, the frame numbers of all frames which are detected as "bad".
#As an alternative, you can configure it to automatically replace bad frames with a new
#frame that is interpolated from its neighbors. This replacement is often near-perfect (no guarantees, however...)
#This script will fail if the bad frame happens immediately before or after a scene change.
#This script will also fail to find a bad frame if there is more than one bad frame in a row.
#It works very well for finding both blank frames and also "flash" frames (like those caused
#by a photographer's flash). It will also find single frames which have lots of
#static or pixels. It can also find a frame with large x or y displacement from adjacent frames, like
#a film frame that wasn't properly registered in the film gate, or an analog
#video frame that lost vertical sync.
#When using VirtualDub, to create the text file containing the bad frame numbers, first uncomment that code block.
#Then, select "Run Video Analysis Pass" in the VirtualDub File menu.
#The script uses ratios of the metrics for the current frame to the same metrics
#on the two adjacent frames. Under normal circumstances, the metrics should be quite
#similar, and therefore the ratio should be very near to unity (i.e., 1.00).
#Run through the video with the "script" variable enabled, and look at the metrics
#in order to determine an optimum threshold value. A larger threshhold will
#catch fewer bad frames, and a lower threshold will eventually create false positives.
#The replacement code works well enough that if you end up replacing a few frames that are
#actually good, you probably won't notice it.
#You need to un-comment the WriteFileIf lines to actually write the frame numbers to a file.
#You need to un-comment the script lines to cause the metrics to appear on screen in order to
#determine the optimum badthreshold value (2 is a good starting point, however).
#You need to un-comment the ReplaceBadI lines to automatically replace bad frames with interpolated frames.
#I recommend only having one of these three code blocks enabled at any one time.
#-----------------------------
loadplugin("C:\Program Files\AviSynth 2.5\plugins\MVTools\mvtools2.dll")
global badthreshold = 2
showdot = false # set to true to add "***" to each replacment frame (for troubleshooting)
filename = "e:\Bad.txt"
source=AVISource("e:\fs.avi").convertTOYV12().killaudio()
#TEMPORARILY remove comments from the following block in order to show the metrics.
/*
script = """Subtitle("\nPrevious Ratio = " + String( YDifferenceFromPrevious(source) \
/ YDifferenceFromPrevious( selectevery(source, 1, -1) )) + \
"\nNext Ratio = " + String( YDifferenceToNext(source) / YDifferenceToNext(selectevery(source, 1, 1) )), lsp=0)"""
final=Scriptclip(source, script)
return final
*/
#Uncomment the code in the next two lines to create a file which contains the frame numbers of all bad frames
/*
WriteFileIf(source, filename, "YDifferenceFromPrevious(source) / YDifferenceFromPrevious( selectevery(source, 1, -1) ) > badthreshold && \
YDifferenceToNext(source) / YDifferenceToNext(selectevery(source, 1, 1) )>badthreshold", "current_frame", append = false)
*/
#Add comments to the two lines of code below to stop replacement of
#each bad frame with a one that is interpolated from adjacent frames.
#The "I" in the function name stands or Interlaced, because this will
#work with interlaced video (as well as progressive)
output=ReplaceBadI(source,showdot)
return output
#------------------------------
function ReplaceBadI (clip c, bool showdot)
{
even = c.SeparateFields().SelectEven()
super_even = showdot ? even.subtitle("***").MSuper(pel=2) : even.MSuper(pel=2)
vfe=manalyse(super_even,truemotion=true,isb=false,delta=2)
vbe=manalyse(super_even,truemotion=true,isb=true,delta=2)
filldrops_e = mflowinter(even,super_even,vbe,vfe,time=50)
odd = c.SeparateFields().SelectOdd()
super_odd = showdot ? odd.subtitle("***").MSuper(pel=2) : odd.MSuper(pel=2)
vfo=manalyse(super_odd,truemotion=true,isb=false,delta=2)
vbo=manalyse(super_odd,truemotion=true,isb=true,delta=2)
filldrops_o = mflowinter(odd,super_odd,vbo,vfo,time=50)
Interleave(filldrops_e,filldrops_o)
Replacement = Weave()
global original = c
fixed = ConditionalSelect(c, "\
Prev = YDifferenceFromPrevious(original)" + \
chr(13) + "
Prev1 = YDifferenceFromPrevious(SelectEvery(original,1,-1))" + \
chr(13) + "
Next = YDifferenceToNext(original)" + \
chr(13) + "
Next1 = YDifferenceToNext(SelectEvery(original,1,1))" + \
chr(13) + "
Prev/Prev1 < badthreshold && Next/Next1 < badthreshold ? 0 : 1", \
original, selectevery(Replacement,1,-1))
return fixed
}
Gavino
13th December 2016, 11:13
I couldn't figure out how to do the conditional without declaring one variable as global (Gavino always dings me on this).
The global 'badthreshold' isn't a problem, since it's a global constant.
However, the global variable 'original' inside the function is a potential source of errors.
In particular, if the function is called more than once in a script, all the instances of the function will use the same clip for 'original', even if they are called with different inputs.
It turns out that the variable 'original' is actually unnecessary.
Since 'c' is the source clip for the ConditionalSelect, it can be referenced as 'last' inside the run-time script.
And since 'last' is the implicit default in clip functions, it can just be left out.
fixed = ConditionalSelect(c, "
Prev = YDifferenceFromPrevious()
Prev1 = YDifferenceFromPrevious(SelectEvery(1,-1))
Next = YDifferenceToNext()
Next1 = YDifferenceToNext(SelectEvery(1,1))
Prev/Prev1 < badthreshold && Next/Next1 < badthreshold ? 0 : 1", \
c, selectevery(Replacement,1,-1))
Note that Avisynth directly supports newlines inside string literals, so I have also removed the string concatenation, backslashes and chr(13) from this code.
Also, I wasn't able to get it to both automatically fix bad frames AND output the bad frame numbers to a text file. I could get it to do one or the other, but not both.
To get both at the same time, it should work if you replace WriteFileIf(source, ...) by
source = WriteFileIf(source, ...)
The reason is that for WriteFile(If) to produce anything, it must be in the filter chain contributing to the final script output.
EDIT: Actually, for this part to work, the WriteFileIf() call must also be simplified along the same lines, removing 'source' from the run-time script (otherwise an endless recursion would occur at run-time - see here).
source = WriteFileIf(source, filename, "
YDifferenceFromPrevious() / YDifferenceFromPrevious( selectevery(1, -1) ) > badthreshold && \
YDifferenceToNext() / YDifferenceToNext(selectevery(1, 1) )>badthreshold", "current_frame", append = false)
johnmeyer
13th December 2016, 17:19
Many thanks for the advice. I've made the changes and everything works, including the ability to create both the text file and the video at the same time. I wish you had a cubicle down the hall from me. Two minutes with you twice a day would save me hours of work, and everything would work better.
I'll wait a few days to see if I get other advice and then post an updated script (it would be too confusing to modify the original in post #1).
StainlessS
13th December 2016, 20:06
How bout summick like this (fixed your divide by zero's).
#source=AVISource("F:\V\StarWars.avi").convertTOYV12().killaudio()
source=AVISource("e:\fs.avi").convertTOYV12().killaudio()
global badthreshold = 2
filename = "e:\Bad.txt"
METRICS = True # True, Show Metrics ONLY (overrides other selctions)
SHOWDOT = False # True, set to true to add "***" to each replacment frame (for troubleshooting)
FILEWRITE = False # True, create a file which contains the frame numbers of all bad frames
REPLACE = False # True, Replace each bad frame with a one that is interpolated from adjacent frames.
# The "I" in the function name stands or Interlaced, because this will work with interlaced video (as well as progressive)
######
script = """Subtitle("\nPrevious Ratio = " + String( YDifferenceFromPrevious(source) / Max(YDifferenceFromPrevious( selectevery(source, 1, -1)),0.00001) ) + \
"\nNext Ratio = " + String( YDifferenceToNext(source) / Max(YDifferenceToNext(selectevery(source, 1, 1)),0.00001)), lsp=0)"""
MetClip = Scriptclip(source, script)
Source2 = (FILEWRITE)
\ ? WriteFileIf(source, filename, "
\ YDifferenceFromPrevious() / Max(YDifferenceFromPrevious( selectevery(1, -1)),0.00001) > badthreshold &&
\ YDifferenceToNext() / Max(YDifferenceToNext(selectevery(1, 1)),0.00001)>badthreshold", "current_frame", append = false)
\ : Source
output = (METRICS) ? MetClip : (REPLACE) ? ReplaceBadI(source2,showdot) : Source2
return output
#------------------------------
Function ReplaceBadI (clip c, bool showdot) {
even = c.SeparateFields().SelectEven()
super_even = showdot ? even.subtitle("***").MSuper(pel=2) : even.MSuper(pel=2)
vfe=manalyse(super_even,truemotion=true,isb=false,delta=2)
vbe=manalyse(super_even,truemotion=true,isb=true,delta=2)
filldrops_e = mflowinter(even,super_even,vbe,vfe,time=50)
odd = c.SeparateFields().SelectOdd()
super_odd = showdot ? odd.subtitle("***").MSuper(pel=2) : odd.MSuper(pel=2)
vfo=manalyse(super_odd,truemotion=true,isb=false,delta=2)
vbo=manalyse(super_odd,truemotion=true,isb=true,delta=2)
filldrops_o = mflowinter(odd,super_odd,vbo,vfo,time=50)
Interleave(filldrops_e,filldrops_o)
Replacement = Weave()
fixed = ConditionalSelect(c, "
Prev = YDifferenceFromPrevious
Prev1 = Max(YDifferenceFromPrevious(SelectEvery(1,-1)),0.00001)
Next = YDifferenceToNext
Next1 = Max(YDifferenceToNext(SelectEvery(1,1)),0.00001)
Prev/Prev1 < badthreshold && Next/Next1 < badthreshold ? 0 : 1", \
c, selectevery(Replacement,1,-1))
return fixed
}
EDIT: Little testing, dont have test clip handy (should be OK I think).
EDIT: Removed Global Original assignment.
johnmeyer
13th December 2016, 20:21
StainlessS,
I never liked the word "robust" to describe the quality of a computer algorithm, but since that is the word everyone uses, what you have done is to make my script more robust. I need to remember to use the Max function, in future scripts, to make sure the denominator doesn't go to zero. I also much prefer having metrics that you can set at the beginning of the script rather than forcing the user to edit comment blocks.
I'll add all those things to the script.
Thanks!!!
johnmeyer
13th December 2016, 20:59
Thanks to Gavino and StainlessS!!
Here is the script, revised to include the suggestions in posts #2 and #4. I cleaned up the formatting to make it look prettier, and also added a variable for the file name.
You can now, thanks to these changes, control the operation of the script simply by setting the values in the variable listed at the beginning of the script.#Find And (Optionally) Fix Bad Frames
#John Meyer - December 13, 2016
#Rev. 2.0
#Thanks to Gavino and StainlessS for making the script more professional.
#This script detects single bad frames.
#You can configure the script to write, to a file, the frame numbers of all frames which are detected as "bad".
#You can also configure it to automatically replace each bad frame with a new
#frame interpolated from its neighbors.
#This script will fail if the bad frame happens immediately before or after a scene change.
#This script will also fail to find a bad frame if there is more than one bad frame in a row.
#It works very well for finding both blank frames and also "flash" frames (like those caused
#by a photographer's flash). It will also find single frames which have lots of
#static or pixels. It can also find a frame with large x or y displacement from adjacent frames, like
#a film frame that wasn't properly registered in the film gate, or an analog
#video frame that lost vertical sync.
#When using VirtualDub, to create the text file containing the bad frame numbers,
#select "Run Video Analysis Pass" in the VirtualDub File menu. If you are simultaneously
#creating a fixed video file, you don't need to do this because the file will
#be created simultaneously as the fixed video file is created.
#The script uses ratios of the metrics for the current frame to the same metrics
#on the two adjacent frames. Under normal circumstances, the metrics should be quite
#similar, and therefore the ratio should be very near to unity (i.e., 1.00).
#Run through the video with the "METRICS" variable set to "True" and look at the metrics
#in order to determine an optimum threshold value. A larger threshhold will
#catch fewer bad frames, and a lower threshold will eventually create false positives.
#The replacement code works well enough that if you end up replacing a few frames that are
#actually good, you probably won't notice it.
#-----------------------------
loadplugin("C:\Program Files\AviSynth 2.5\plugins\MVTools\mvtools2.dll")
#Control script operation by changing the following values :
#=====================================================================
VideoFile = "E:\fs.avi"
global badthreshold = 2 # Set METRICS=TRUE to determine best value
METRICS = FALSE # TRUE will show Metrics ONLY (i.e., TRUE overrides all other selctions)
SHOWDOT = FALSE # TRUE will add "***" to each replacment frame (for troubleshooting)
REPLACE = TRUE # TRUE will replace each bad frame with a one that is interpolated from adjacent frames
FILEWRITE = TRUE # TRUE will create a file which contains the frame numbers of all bad frames
filename = "E:\Bad.txt" # Set to name and location where you want the frame numbers stored
#=====================================================================
source = AVISource(VideoFile).convertTOYV12().killaudio()
script = """Subtitle("\nPrevious Ratio = " + String( YDifferenceFromPrevious(source) /
\ Max(YDifferenceFromPrevious( selectevery(source, 1, -1)),0.00001) ) +
\ "\nNext Ratio = " + String( YDifferenceToNext(source) /
\ Max(YDifferenceToNext(selectevery(source, 1, 1)),0.00001)), lsp=0)"""
MetClip = Scriptclip(source, script)
FileFixed = (FILEWRITE)
\ ? WriteFileIf(source, filename, "
\ YDifferenceFromPrevious() / Max(YDifferenceFromPrevious( selectevery(1, -1)),0.00001)
\ > badthreshold && YDifferenceToNext() / Max(YDifferenceToNext(selectevery(1, 1)),0.00001)
\ > badthreshold", "current_frame", append = false) : Source
output = (METRICS) ? MetClip : (REPLACE) ? ReplaceBadI(FileFixed,showdot) : FileFixed
return output
#------------------------------
function ReplaceBadI (clip c, bool SHOWDOT)
{
even = c.SeparateFields().SelectEven()
super_even = SHOWDOT ? even.subtitle("***").MSuper(pel=2) : even.MSuper(pel=2)
vfe = manalyse(super_even,truemotion=true,isb=false,delta=2)
vbe = manalyse(super_even,truemotion=true,isb=true,delta=2)
filldrops_e = mflowinter(even,super_even,vbe,vfe,time=50)
odd = c.SeparateFields().SelectOdd()
super_odd = SHOWDOT ? odd.subtitle("***").MSuper(pel=2) : odd.MSuper(pel=2)
vfo = manalyse(super_odd,truemotion=true,isb=false,delta=2)
vbo = manalyse(super_odd,truemotion=true,isb=true,delta=2)
filldrops_o = mflowinter(odd,super_odd,vbo,vfo,time=50)
Interleave(filldrops_e,filldrops_o)
Replacement = Weave()
fixed = ConditionalSelect(c, "
Prev = YDifferenceFromPrevious()
Prev1 = Max(YDifferenceFromPrevious(SelectEvery(1,-1)),0.00001)
Next = YDifferenceToNext()
Next1 = Max(YDifferenceToNext(SelectEvery(1,1)),0.00001)
Prev/Prev1 < badthreshold && Next/Next1 < badthreshold ? 0 : 1", \
c, selectevery(Replacement,1,-1))
return fixed
}
johnmeyer
14th December 2016, 02:47
Here's a link to a very short, small test video showing how the script automatically removes photographer flashes at a wedding:
Photo Flash Removal (https://www.mediafire.com/?nre0vr9nx7v3dc4)
There are two flashes, a fraction of a second apart, about midway through this five-second clip.
Here is one frame from that clip, with the left side showing the before, and the right side showing the video which results from the script's automatic replacement:
https://i.imgur.com/KJqDCoh.png
The image on the right is a completely synthesized frame.
The one thing I did find out -- and I completely expected this -- is that the script does not work for certain types of photo flashes. The problem is that modern strobes often fire multiple times, especially during their "pre-flash" routine. As I said in my notes in the script, the script is not designed to handle two bad frames in a row. However, when the bad frame is all by itself, the script does really good things.
johnmeyer
14th December 2016, 06:08
I do have a question about my MVTools2 code: did I use the correct Delta? I tried using Delta=1 (default), but that ended up using part of the bad frame I was trying to replace. OK, so I make the reference the frame after the bad frame, and from the perspective of that frame, have it look backwards two frames. So I think I need to use Delta=2 for isb=true (isb="is backwards"). However, should I be using Delta=1 for the forward vector? And, if I do that, do I use a time of 66 or 33?
The synthesized frames look great, and appear to be from the correct moment in time, but I'm wondering if I would get cleaner results if I did this differently.
This part of MVTools2 still befuddles me (not hard to do these days).
Thanks to anyone who can shed some light on this.
StainlessS
14th December 2016, 07:47
Delta=2, looks OK to me John. (comparing with what I've got here, Included in RT_Stats demos)
Function TweenFlashFields(clip c,float "FlashThresh",bool "Show") {
# Replace single isolated bad fields eg Black or White with a field tweened from those either side using MvTools, and RT_Stats.
# Will Likely fix a single Bright/Dark Flash field after scene change where will be replaced with a blend of before and after fields.
# This due to NoPan failure, ie fields before and after flash are likely to be more similar (even though dif scenes) than to flash field.
# There has been no attempt to fix this fortunate failure.
# FlashThresh Default 1.15 (white flash detect), is a threshold of AveLuma_Ratio, Flash_Field_AveLuma / Adjacent_Field_AveLuma.
# Values above 1.0 detect WHITE/Flash fields, Below 1.0 detect BLACK/Flash fields (suggest for Black eg 1.0/1.15 = 0.87)
# When FlashThresh above 1.0 (detecting WHITE flash) :
# If AveLuma_Ratio > FlashThresh, for both adjacent fields then is possible white flash
# When FlashThresh below 1.0 (detecting BLACK flash) :
# If AveLuma_Ratio < FlashThresh, for both adjacent fields then is possible black flash
# When FlashThresh < 0.0, will simultaneously fix both White and Black Flash frames, eg -1.15 will use equivalent to 1.15 for white
# and 1.0/1.15 for Black.
# 1.0 or -1.0 exactly, Throws an Error.
# Show: Puts indicator on fixed fields.
c
FlashThresh = Float(Default(FlashThresh,1.15)) # Default detects White flash
Show=Default(Show,False)
Assert(FlashThresh != 1.0 && FlashThresh != -1.0,"TweenFlashFrames: FlashThresh Cannot be 1.0 Nor -1.0")
(FlashThresh < 0.0 && FlashThresh > -1.0) ? 1.0 / FlashThresh : FlashThresh
CondS="""
ave_p = Max(RT_AverageLuma(delta=-1), 0.01) # Avoid Division By Zero
ave = RT_AverageLuma()
ave_n = Max(RT_AverageLuma(delta=1), 0.01)
rat_p = ave / ave_p # AveLuma_Ratio for Prev
rat_n = ave / ave_n # AveLuma_Ratio for Next
Flash =(FlashThresh<0.0)
\ ? ((rat_p > -FlashThresh && rat_n > -FlashThresh) || (rat_p < -1.0/FlashThresh && rat_n < -1.0/FlashThresh))
\ : (FlashThresh>1.0)
\ ? (rat_p > FlashThresh && rat_n > FlashThresh)
\ : (rat_p < FlashThresh && rat_n < FlashThresh)
dif_p=RT_YDifference(delta=-1) # Diff Prev <-> Curr
dif_n=RT_YDifference(delta=1) # Diff Curr <-> Next
dif_pn=RT_YDifference(current_frame-1,delta=2) # Diff Prev <-> Next (either side of current)
NoPan = (dif_pn < dif_p && dif_pn < dif_n) # (Prev<->Next < Prev<->Curr) AND (Prev<->Next < Curr<->Next)
clpn = (Flash && NoPan) ? 1 : 0
# RT_DebugF("%d ] EVEN Rat_p=%.2f Rat_n=%.2f Flash=%s Dif_pn=%.2f Dif_p=%.2f Dif_n=%.2f NoPan=%s Tween=%s",
# \ current_frame,rat_p,rat_n,(Flash)?"T":"F",Dif_pn,Dif_p,Dif_n,(NoPan)?"T":"F",(clpn==1)?"Y":"N")
clpn
"""
SepC=SeparateFields()
EvenC=SEPC.SelectEven()
PrevC=EvenC.DeleteFrame(FrameCount-1).DuplicateFrame(0) # Make clip where prev fields are aligned with curr fields (same length)
super = PrevC.MSuper()
backward_vectors = MAnalyse(super, isb = true,truemotion=true, delta=2)
forward_vectors = MAnalyse(super, isb = false,truemotion=true, delta=2)
TweenC = PrevC.MFlowInter(super, backward_vectors, forward_vectors, time=50, ml=70) # Tweened clip
TweenC = (show) ? TweenC.Subtitle("EVEN FIELD FIXED",size=24,text_color=$0000FF,align=5,y=EvenC.Height/2-24) : TweenC
CondSE=RT_StrReplace(CondS,"FlashThresh",String(FlashThresh)) # Import explicit FlashThresh into condition string
EvenFixedC=ConditionalSelect(EvenC,CondSE,EvenC,TweenC) # Fix bad EVEN fields
OddC=SepC.SelectOdd()
PrevC=OddC.DeleteFrame(FrameCount-1).DuplicateFrame(0)
super = PrevC.MSuper()
backward_vectors = MAnalyse(super, isb = true,truemotion=true, delta=2)
forward_vectors = MAnalyse(super, isb = false,truemotion=true, delta=2)
TweenC = PrevC.MFlowInter(super, backward_vectors, forward_vectors, time=50, ml=70) # Tweened clip
TweenC = (show) ? TweenC.Subtitle("ODD FIELD FIXED",size=24,text_color=$0000FF,align=5,y=OddC.Height/2) : TweenC
CondSO=RT_StrReplace(CondSE,"EVEN","ODD")
OddFixedC=ConditionalSelect(OddC,CondSO,OddC,TweenC) # Fix bad Odd fields
Interleave(EvenFixedC,OddFixedC)
Weave()
return Last
}
With Delta=1, would be partially using bad frames.
docs
Motion interpolation function. It is not the same (but similar) as MVInterpolate function of older MVTools version. It uses backward mvbw and forward mvfw motion vectors to create picture at some intermediate time moment between current and next (by delta) frame. It uses pixel-based (by MFlow method) motion compensation from both frames. Internal forward and backward occlusion masks (MMask kind = 2 method) and time weighted factors are used to produce the output image with minimal artefacts. True motion estimation is strongly recommended for this function.
time
Interpolation time position between frames, in percent. Default value of 50.0 is half-way.
To recreate bad frames by interpolation with MFlowInter:
AVISource("c:\test.avi") # or MPEG2Source, DirectShowSource, some previous filter, etc
super = MSuper()
backward_vectors = MAnalyse(super, isb = true, delta=2)
forward_vectors = MAnalyse(super, isb = false, delta=2)
inter = MFlowInter(super, backward_vectors, forward_vectors, time=50, ml=70)
# Assume bad frames are 50 and 60
trim(0,49) ++ inter.trim(49,-1) \
++ trim(51,59) ++ inter.trim(59,-1) ++ trim(61,0)
EDIT: Note Above, @ bad frame 50, replace with synth frame from 49 (I know, it's weird), I have used pre-shift-over
clip to create synth frames whereas you do shift after detection, ie
fixed = ConditionalSelect(c, "
Prev = YDifferenceFromPrevious()
Prev1 = Max(YDifferenceFromPrevious(SelectEvery(1,-1)),0.00001)
Next = YDifferenceToNext()
Next1 = Max(YDifferenceToNext(SelectEvery(1,1)),0.00001)
Prev/Prev1 < badthreshold && Next/Next1 < badthreshold ? 0 : 1", \
c, selectevery(Replacement,1,-1))
From a synth frame point of view, creating synth frame at frame n, uses two vectors n<--->(n+delta), so no matter what
size delta, creates synth frame at always frame n. Trouble is, we want to replace bad frame at synth frame n+1, so
have to shift over 1. this probably dont make any more sense than the docs :D
johnmeyer
14th December 2016, 17:59
I'm still trying to wrap my head around how delta is really used. I did some searching, and found this very useful post:
Generate broken frames from neighbour frames using MVFlow (http://forum.doom9.org/showthread.php?p=1379456#post1379456)
"pbristow" provides an explanation of how the motion vectors are created. He also provides some code that may permit fixing two bad frames in a row. If this works, it would be interesting (although I don't know if I am up for the challenge) to include some conditional logic which, when two bad frames in a row are detected, uses the alternative code.
For the moment, I'll be happy if I can just better understand how this works. It sure seems to me that setting the backward vector to one and the forward vector to two (or vice versa) should produce better results. However, having tried it, I know that it does not work at all, and that doesn't make any sense.
wonkey_monkey
14th December 2016, 18:13
It sure seems to me that setting the backward vector to one and the forward vector to two (or vice versa) should produce better results.
You want to calculate the vectors between the good frames - from frame 0 (good) to frame 3 (good) (delta=3) and also from frame 3 to frame 0 (also delta=3, isb=true).
So in the two "vector" clips, frame 0 will represent the vectors between frame 0 and -3 (in the backwards clip) and frame 0 and 3 in the forwards clip.
Frame 1 will represent the vectors between frame 1 (bad frame) and frame -2 in the backwards clip, and frame 1 and frame 4 in the forwards clip (but since frame 1 is bad, this info won't get used).
Does that help?
You're not calculating vectors between x-3 and x+3, but between x and x-3, and also between x and x+3.
johnmeyer
14th December 2016, 19:01
Yes, that helps. However, here is why I am puzzled. The following visually shows how motion vectors for bad frame B4 are calculated using the delta=2 setting that I used in my script:
___________________
| | |
v v v
G1 G2 G3 B4 G5 G6
As can be seen, the reference good frame is G3 and the bad frame that we want to replace is B4. ("G"=Good; "B"=Bad). In the MVTools2 sample code, on which I based my code, it uses delta=2. This skips the adjacent frame and instead uses frames that are two frames away in time. Thus, the ISB=true (IS Backward) vector is calculated from G5 back to G3 and the delta=2, ISB=false is calculated from G1 to G3. Then, using MFlowInter, these vectors are used to create a replacement for frame B4 which is 50% of the way between G3 and G5.
I think I understand this part.
The next diagram shows where I get confused.
______________
| | |
v v v
G1 G2 G3 B4 G5 G6
Once again, the reference frame is "G3". The delta=2, ISB=true (IS Backward) vector is calculated from G5 back to G3 but in this example, I use delta=1, for the ISB=false vector (calculated from G2 to G3). Then, using MFlowInter, these vectors are used to create a replacement for frame B4 which is 50% of the way between G3 and G5.
This seems like it would produce better results because G2 is closer in time to the reference frame G3 and therefore the vectors should be more reliable.
However, using the different delta=1 for the ISB=false vector and delta=2 for the ISB=true vector doesn't work. My guess is that, even thought the MVTools2 documentation is mute on the subject, the delta values for both the forward and backward vectors have to be the same. This is the only explanation for why it fails even though, in theory, better results should be obtained using the closest possible good frames.
So, I think what is going on is that the way the function works requires skipping a nearer, better neighbor frame in order to make the internal logic of MVTools2 work.
StainlessS
14th December 2016, 19:40
This is how I believe that it works, everything is from the point of view of the synth frame (because that is what you are doing).
50%
|
<--------->
| |
v v
G1 G2 G3 B4 G5 G6
n n+1 n+2
delta=2
Frame n synth frame created using bidirectional vectors between n and n+delta(2),
however, frame requiring fixing is n+1 (B4, 50% between n and n+2) and so requires shift over. Frames are synthesised
from point of view of the synth frames not the bad frame.
Synth frame to fix B4 @ 50% is generated still at frame n (reqires shift forward 1 frame replacing n+1)
33% 66%
| |
<--------------->
| |
v v
G1 G2 G3 B4 B5 G6 G7
n n+1 n+2 n+3
delta=3
Synth frame to fix B4 (@ 33% of the way between n and n+3) is generated still at frame n (reqires shift forward 1 frames replacing n+1)
Synth frame to fix B5 (@ 66% of the way between n and n+3) is generated still at frame n (reqires shift forward 2 frames replacing n+2)
The 'between' percentage does NOT need to be directionally worked out by the user, it is looked after by MVTools
and governed again by isb, percentage is relative the synth frame n->n+delta.
EDIT: Delta is always +ve, isb sets whether it is a forward or backward vector between n and n + delta.
(Worrying too much about what is in which particular vector clip, and at which frame number, will lead to madness, dont do it :D,
but for the already insane, frame n vectors will contain vectors for n<->n+delta depending upon isb direction)
EDIT: I think some of what was posted in the PBristow thread was less than accurate. (not blaming you PB :) )
EDIT: Oops, delta=3, we have a B5,G5,G6, should have been B5,G6,G7, but I hope you get the drift.
EDIT:
but for the already insane, frame n vectors will contain vectors for n<->n+delta depending upon isb direction)
EDIT: Actually, backwards vectors will be at frame n, and forwards vector at n+delta.
johnmeyer
14th December 2016, 20:56
StainlessS,
Thanks for that. It does help further understand the sometimes murky world of MVTools2. However, I'm still left wondering why I can't use different vectors for isb=true than the ones I use for isb=false in order to be able to use frames which are closer in time to the frame I want to replace. My conclusion at this point is that this cannot be done because MVTools2 is "hard-wired" to use vectors that are symmetrical, backwards and forwards, in time. I can live with that, and will at this point get on with other things, giving up trying to wring one last ounce of quality out of my script.
StainlessS
14th December 2016, 22:08
Can't say that I've tried that John, perhaps it would work, think the bi-di vectors used to compare results, and if too different then blur or whatever. Both forwords and backwards vectors would have to coincide to the same synthsized frame exactly otherwise blurring due to mismatch.
Don't see any reason why would not work, both delta and isb for both separately relevant.
Edit, although I can't think of a scenario where useful.
Mobile
Edit, above, total guesswork.
wonkey_monkey
15th December 2016, 00:29
I'm just guessing here, but when trying to certain things to generate frame x, mvtools will use the backwards and forwards vector clips. For example, if frame x is meant to be an interpolation between source frames y and z, it will need to look at the forward vectors which go from y to z, and at the backward vectors which go from z to y. If the vector clips were generated with different deltas, there'll be no such matching pair of forward and backward vectors.
StainlessS
15th December 2016, 00:40
John. Methinks is BAD idea to do frames produced by diff vectors. They
Are a check on each other, as did forward from n match with backwards from n+z, ie self checking, sort of and reason for it.
If you eg wanted to recreate eg 120fps from25, and 30, then fix and recreate 25 & 30 separately, and then do whatever you can with both results.
Edit, pub shuts in 20 mins, life's a bitch.
Gavino
15th December 2016, 00:41
For example, if frame x is meant to be an interpolation between source frames y and z, it will need to look at the forward vectors which go from y to z, and at the backward vectors which go from z to y.
That's exactly right, and that's what StainlessS's first diagram in post #13 shows (using vectors from G3 to G5 and from G5 to G3).
Shinkiro
5th February 2021, 14:48
Is it possible using this script to compare frames from different sources and insert a frame from source 1 into source 2 when a difference is detected? Or maybe there's some other way?
I have two sources, one of better quality than the other, but it is censored, the problem is to replace places with censored in the source file 1 with places without censorship from source file 2.
I do it manually now, but it would be great to automate the process.
StainlessS
5th February 2021, 16:40
Are they exact same size/cropping and framerate ? [spatial and temporal aligned]
If not then is far far more difficult problem. [where probably a lot less work to do it by hand, after matching spatial and temporal].
Shinkiro
5th February 2021, 16:54
Are they exact same size/cropping and framerate ?
Yes everything is the same
I'm now trying to set up this script, it seems like something turns out
C1=DGSource("VTS_01_1_cen.dgi").QTGMC(preset="Medium",Lossless=0,SourceMatch=0,MatchPreset="Medium",MatchPreset2="Medium",MatchEdi="NNEDI3",EdiMode="NNEDI3",Edithreads=8,FPSDivisor=2)\
C2=DGSource("VTS_03_1 unc.dgi").QTGMC(preset="Medium",Lossless=0,SourceMatch=0,MatchPreset="Medium",MatchPreset2="Medium",MatchEdi="NNEDI3",EdiMode="NNEDI3",Edithreads=8,FPSDivisor=2).Trim(553, 0)\
C3=ConditionalFilter(C1, C1, C2, "LumaDifference(C1,C2)+ChromaUDifference(C1,C2)+ChromaVDifference(C1,C2)", "lessthan", "6.1",show=true)
Edite: I came to the conclusion that choosing a value that will not give a false alarm will not work, it will be more reliable manually
[spatial and temporal aligned]
You can please a little more in detail, or kick in the direction where to read, but with googltranslate it is difficult to find the necessary information.
StainlessS
5th February 2021, 20:50
C1=AviSource("...")
C2=AviSource("...")
Subtract(C1,C2)
If near identical, ie spatially and temporally aligned, then above should ALL look near mid grey, except for censoring.
Yes everything is the same
Yeah, I've heard that one before. If not then count me out, I got enough headaches to deal with at present.
Assuming that they are the same,
then this is an attempt to do s required.
Tested only insofar as it don't crash,
EDIT: Below in BLUE, ones to tweak
# Req RT_Stats v2.0 Beta 13
C1=AviSource("D:\Parade.avi")
C2=C1.Sharpen(1) # Make it different [just test script dont crash]
BLKW = 64
BLKH = BLKW
OLAPX = BLKW / 2
OLAPY = BLKH / 2
CW = 0.20 # ChromaWeight = 20%, Luma = 100-20 = 80% (difference weightings) : CW = combined weighting for Y + V. 0.0 = Luma Only
FACT = 8.0 # If BlkMaxDif >= (FACT * BlkAveDif) then is different
MINDIF = FACT/2.0 # Max Block differece has to be at least this (FACT alone not enough for detect, eg 0.01 could be bigger than 8.0 * 0.0)
PREFIX = "DAT_" # Local variables name prefix set by RT_FrameMovement.
FINAL = False # Set True when happy.
SSS = """
# Difference of the Block comparison with greatest difference.
BlkMaxDif = RT_FrameMovement(C1,C2,ChromaWeight=CW,BlkW=BLKW,BlkH=BLKH,olapX=OLAPX,olapY=OLAPY,Prefix=PREFIX)
# Average block difference.
BlkAveDif = DAT_BlkAveDf # Set as Local variable named via Prefix + "BlkAveDf"
Ratf = BlkMaxDif / Max(BlkAveDif,0.001) # How much bigger is it : avoid div by zero
MinOK = BlkMaxDif >= MINDIF # And also has to be of reasonable difference (at least FACT * 0.5)
IsDif = MinOK && Ratf >= FACT
(IsDif) ? C2 : C1 # Choose which frame to use
if(!FINAL) {
RT_Subtitle("%d] %.1s MaxDf=%.3f : AveDf=%.3f\nMinOK=%.1s : RatF=%f",current_frame,IsDif,BlkMaxDif,BlkAveDif,MinOK,RatF)
}
Return Last
"""
DUMMY = C1
CC = DUMMY.ScriptClip(SSS)
TOP = Stackhorizontal(C1,C2)
BOT = Stackhorizontal(CC,C1.BlankClip)
STK = StackVertical(Top,Bot)
Return (FINAL) ? CC : STK
EDIT: Variable Names changed.
RT_FrameMovement(clip c,clip c2,int "n"=current_frame,int "n2"=current_frame,Float "ChromaWeight"=1.0/3.0,
int "x"=0,int "y"=0,int "w"=0,int "h"=0,int "x2"=x,int "y2"=y,
bool "AltScan"=false,Bool "ChromaI"=False,int "Matrix"=(c.Width>1100||c.Height>600||c2.Width>1100||c2.Height>600?3:2),
int "BlkW"=64,int "BlkH"=BlkW, Int "OLapX"=BlkW/2, Int "OLapY"=BlkH/2,Float "BlkTh"=1.0,String "Prefix"="")
Returns FLOAT value (0.0 -> 255.0) movement detection between clip c frame n area x,y,w,h, and clip c2 frame n2 area x2,y2,w,h.
The frames are sectioned into blocks of BlkW x BlkH with block overlaps of OLapX and OLapY, and all blocks are differenced, the result
is the greatest difference for any one block comparison.
Is more sensitive to localized movement. Best control of localized movement sensitivity is by changing blk size, lower is more sensitive.
If Altscan is true, then only every other horizontal scanline in BLKH will be scanned.
Args:-
c, c2, comparison clips, usually the same clip.
n, default current_frame, frame from clip c.
n2, default current_frame, frame from clip c2.
ChromaWeight, default 1.0/3.0. Range 0.0 -> 1.0.
Difference method control when comparing blocks.
Y8, returns same as RT_LumaDifference. ChromaWeight ignored.
YUV,
Weighting applied YUV chroma:- (1.0 - ChromaWeight) * Lumadif + ChromaWeight * ((Udif + Vdif)/2.0).
RGB,
If ChromaWeight > 0.0, then returns same as RT_RGBDifference() [average RGB pixel channel difference].
If ChromaWeight == 0.0, then returns same as RT_LumaDifference() using Matrix arg to convert RGB to YUV-Y Luma.
x,y,w,h, all default 0 (full frame c).
x2,y2. x2 defaults x, y2 defaults y, for clip c2.
Altscan, default false. If true then scan only every other scanline starting at clip c y coord, and clip c2 y2 coord.
ChromaI, default false. If YV12 and ChromaI, then do YV12 interlaced chroma scanning.
Ignored if not YV12. Both YV12 clips must be same, cannot difference one that is progressive and one that has interlaced chroma.
Matrix, default (c.Width>1100||c.Height>600||c2.Width>1100||c2.Height>600)?3:2
Conversion matrix for conversion of RGB to YUV-Y Luma. 0=REC601 : 1=REC709 : 2 = PC601 : 3 = PC709.
Default = (c.Width > 1100 OR c.Height>600 OR c2.Width > 1100 OR c2.Height>600) then 3(PC709) else 2(PC601). YUV not used
Matrix only Used if RGB and ChromaWeight == 0.0, where returns same as RT_LumaDifference().
BlkW, default 64, Block size. Minimum 8 (silently limited to frame dimensions), Must be EVEN.
BlkH, default BlkW, Block size. Minimum 8 (silently limited to frame dimensions), Must be EVEN.
OlapX, default BlkW/2, Horizontal block overlap. 0 -> BlkW/2
OlapY, default BlkH/2, Vertical block overlap. 0 -> BlkH/2
BlkTh, default 1.0. Governs extra information returned when Prefix != "", as local variables.
Prefix, default "" is do not set additional local variables.
If set eg "FM_", then sets local variables as below.
"FM_Xoff" = X Coord of block in Clip c that was greatest.
"FM_Yoff" = Y Coord of block in Clip c that was greatest.
"FM_PercAboveTh" = Percentage of Blocks that were above BlkTh.
"FM_BlkAveDf" = Average Block difference.
"FM_nAboveTh" = Number of Blocks that were above BlkTh.
"FM_TotalBlks" = Total Number of Blocks.
NOTE, 'x2' and 'y2' default to 'x' and 'y' respectively.
c and c2 need not be same dimensions but BEWARE, x2 and y2 default to x and y also w and h default to 0
which is converted to c.width-x and c.height-y, may be best to provide x2, y2, w and h where c2 not same dimensions as c.
EDIT: I've used inputs C1=Censored, C2 no censor.
EDIT: FACT might be a bit high, try between about 4.0 and 8.0
EDIT: Also, maybe BLKW should be at most half of censor block area.
johnmeyer
5th February 2021, 21:11
I could be wrong, but doesn't LumaDifference work on the overall luma of a frame? If so, a frame which has pure black on the left side and pure white on the right side would have a LumaDifference of zero when comparing with a mirror image of that frame (i.e., pure white on the left and pure black on the right).
As far as LumaDifference is concerned, the two frames are identical.
By contrast, the YDifference functions compute a sum of the absolute differences between each and every pixel in the two frames. As a result, the situation I just described would create a massively large (largest possible, actually) YDifference value.
As a result, YDifference functions are much better suited for determining how visually identical two frames are. Lumadifference would be much better suited for creating a deflicker function, as one example, where you are trying to determine whether the overall exposure of the frame has changed.
Please correct me if I am wrong. I'm am pretty certain about how the YDifference functions work, but am not 100% sure that I am correct about LumaDifference.
StainlessS
5th February 2021, 21:24
LumaDifference is AveragePerPixelDifference,
Frame 1, Black Left half, and white right half,
Frame 2, White left half, black right half,
Every corresponding pixel will have difference of 255, and LumaDifference result of 255.0, ie [w * h * 255.0] / [w * h]
LumaDifference is similar to YDifference, but with two clips, (which could be same clip),
YDifferenceFromPrevious and YDifferenceToNext just 1 clip, and compare with adjacent frame.
EDIT: You could use LumaDifference in exact same was at YDifferencetoNext by trimming
a single frame from the 2nd source clip, eg LumaDifference(Src,Src.trim(1,0)) == YDifferenceToNext(Src).
EDIT: The RT_FrameMovement() thing, can have 2 clips, 2 different frame numbers,
we use default same frame number, ie current_frame.
Using RT_LumaDifference, we could use same clip, and current_frame + 1 for 2nd clip and it would be as YDifferenceToNext,
if using current_frame - 1 for 2nd clip, then as YDifferenceFromPrevious.
EDIT: LumaDifference uses 2 clip args, and both using current_frame frame number.
RT_LumaDifference uses 2 clip args, and both by default using current_frame frame number, but they can both be supplied
to the function as arguments and can both be different.
EDIT:
Difference for LumaDifference is problematic, in that it could be lots and lots of small differences (which are just noise),
or frame mostly identical with a few big differences, (which is what is really required for detection).
RT_FrameMovement better for localized difference detection, ie big diff in small area.
Lumadifference would be much better suited for creating a deflicker function, as one example, where you are trying to determine whether the overall exposure of the frame has changed.
Nah, thats AverageLuma's job.
EDIT: I used RT_FrameMovement in the SecurityCam motion detect script somewhere else (I know you tried it).
Here:- https://forum.doom9.org/showthread.php?p=1775305#post1775305
And what it looked like [Yellow marker marks the block of greatest movement ie greatest block difference].
https://s20.postimg.cc/ws8glsgal/SCMD_zpsjj5cduzt.png (https://postimg.cc/image/hjij80mm1/)
https://s20.postimg.cc/5ix37af7h/SCMD2_zpsiuzq505m.png (https://postimg.cc/image/uc6n7xy7t/)
johnmeyer
5th February 2021, 22:59
StainlessS,
Thanks for the explanation. I stand corrected. Also thanks for reminding about the various compare functions in RT_Stats. They are so good and so universally useful that those functions should be built into AVISynth.
StainlessS
6th February 2021, 13:30
NOTE,
In given detect script, variables I've called Maxblkdf and Aveblkdf are shown in the above images as BlkMaxDif and BlkAveDif.
BlkMaxDif is for the yellow marked block.
For bottom image, BlkAveDif has a value of 1.621, whereas the BlkMaxDif for the yellow block is 24.617.
EDIT: Variable names changed in detect script to match those in above images, ie BlkAveDif and BlkMaxDif.
StainlessS
6th February 2021, 17:54
A censored clip generator for testing, requires a YV12 live video clip more than 2500 frames.
CreateCensored.avs : Save Result as "Censored.avi".
# CreateCensored.avs : https://forum.doom9.org/showthread.php?p=1935393#post1935393
FN = "D:\Parade.avi" # Some test clip
AviSource(FN) # Source Clip
ConvertToYV24
DISK = True # True = Disk/Ellipse, Else Rectangle
PIXELATE = true # True = PIXELATE, False=FastBlur # False requires FastBlur() plugin
PIXSZ = 8 # PIXELATE Size, (maybe 8 or 4)
# FastBlur args # Fastblur args if PIXELATE=False
FBlurRad = 5
FBlurIter = 3
# Mask args [size of concealed area]
MWidth = 128
MHeight = MWidth
MSOFT = True # Soft Edge mask
# OTHER STUFF
BLANKFG = false # Show ForeGround(blurred/concealed Area) in Blue [see path traveled better].
BLANKBG = False # Show BackGround in Pink
####### END CONFIG ########################
Assert(IsRGB || (Height==ExtractU.Height&&Width==ExtractU.Width),"YV444 or RGB ONLY")
W = Width
H = Height
BPC = BitsPerComponent
#
BClip = (PIXELATE) ? BilinearResize(W/PIXSZ,H/PIXSZ).PointResize(W,H) : Last.Fastblur(FBlurRad,iterations=FBlurIter) # Concealing clip
MSK = Last.BlankClip(pixel_type="Y8").ConvertBits(BPC) # Mask
MSK = (DISK) ? MSK.EllipMsk(MWidth,MHeight,Soft=MSOFT) : MSK.RectMsk(MWidth,MHeight,Soft=MSOFT)
#
Last = (BLANKBG) ? Last.BlankClip(Color=$C04060) : Last
BCLip= (BLANKFG) ? BCLip.BlankClip(Color=$0000FF) : BClip
#################################################################
# Demo coords, Start,End Frm, Start X,Y End X,Y
ConcealRange(Last,BClip,Msk, 0, 499, 0, 0, W, H)
ConcealRange(Last,BClip,Msk, 500, 999, W, H, W, 0)
ConcealRange(Last,BClip,Msk, 1000, 1499, W, 0, 0, H)
ConcealRange(Last,BClip,Msk, 1500, 1999, 0, H, 0, 0)
ConcealRange(Last,BClip,Msk, 2000, 2499, 0, 0, W/2,H/2)
#################################################################
Return ConvertToYV12
##########################################################################
##########################################################################
##########################################################################
Function EllipMsk(clip c,int W, Int H,Bool "Soft") {
# Req mt_tools_2, Returns frame WxH same FPS as c and without audio
Soft=Default(Soft,False)
# INFIX_H="(((x-.5)^2 +(y-.5)^2) < .25 ? 255 : 0"
# Elliptical Disk (Hard Edge) :: mode = "relative", Radius=0.5, Rad^2=0.25 :: SOFT_Inner rad:(Rad*0.9)^2~=0.2
# INFIX_S="((x-.5)^2+(y-.5)^2)<.2?255 : (((x-.5)^2+(y-.5)^2)<.25 ? ((.25-((x-.5)^2+(y-.5)^2))*5100):0)"
# Elliptical Disk (Soft Edge) :: (.25-0.2)*5100=255 : (.25-.25)*5100=0.0] :: mode="relative", 0.25=1.0 : 0.2~=0.9
rpn = (!Soft)
\ ? "x 0.5 - 2 ^ y 0.5 - 2 ^ + 0.25 < 255 0 ? scalef" [* RPN: EllipMsk [Hard Edge] *]
\ : "x 0.5 - 2 ^ y 0.5 - 2 ^ + 0.2 < 255 x 0.5 - 2 ^ y 0.5 - 2 ^ + 0.25 < 0.25 x 0.5 - 2 ^ y 0.5 - 2 ^ + - 5100 * 0 ? ? scalef" [* RPN: EllipMsk [Soft Edge] *]
c.Blankclip(width=W,height=H,Length=1).Killaudio
return Last.mt_lutspa(mode = "relative", expr = rpn, chroma = "-128" )
}
Function RectMsk(clip c,int W, Int H,Bool "Soft") {
# Req mt_tools_2, Returns frame WxH same FPS as c and without audio
Soft=Default(Soft,False)
# INFIX_H = "255"
# INFIX_S = "max(abs(x-.5),abs(y-.5)) < .45 ? 255 : ((.5 - max(abs(x-.5),abs(y-.5)) ) * 5100)"
rpn=(!Soft)
\ ? "255 scalef"
\ : "x 0.5 - abs y 0.5 - abs max 0.45 < 255 0.5 x 0.5 - abs y 0.5 - abs max - 5100 * ? scalef"
c.Blankclip(width=W,height=H,Length=1).Killaudio
return Last.mt_lutspa(mode = "relative", expr = rpn, chroma = "-128" )
}
Function Conceal(clip c,clip bc,clip msk, Float x, float y) {
mw = Msk.Width mh=Msk.Height
ow=c.width-2*mw oh=c.Height-2*mh
xc = Round(min(max(0.0,x),ow))
yc = Round(min(max(0.0,y),oh))
x = xc + mw/2
y = yc + mh/2
bc=bc.crop(x,y,mw,mh)
c.Overlay(bc,mask=msk,x=x,y=y)
}
Function ConcealRange(clip c,clip bc,clip msk,Int "S", Int "E",float "sx",Float "sy",Float "ex",float "ey" ) {
/*
Blur/Conceal clip c with blurred clip clip bc using Mask msk, frames S to E, sx,sy start coords, ex,ey end coords.
Start and End Args S & E, are similar but not exactly like trim.
ConcealRange(c,bc,msk, 0,0) # Entire clip
ConcealRange(c,bc,msk, 100,0) # Frame 100 to End of Clip
ConcealRange(c,bc,msk, 0,-1) # Frame 0 Only
ConcealRange(c,bc,msk, 1,1) # Frame 1 Only
ConcealRange(c,bc,msk, 1,-1) # Frame 1 Only
ConcealRange(c,bc,msk, 1) # Frame 1 Only [Not same as Trim()], E defaults to -1 ie single frame.
ConcealRange(c,bc,msk, 1,-3) # Frames 1 to 3 (ie 3 frames)
ConcealRange(c,bc,msk, 100,200) # 101 Frames, 100 to 200
ConcealRange(c,bc,msk, 100,-50) # Frames 100 to 149 ie 50 frames
X coords sx, and ex, allowed range 0 -> c.width.
Y coords sy, and ey, allowed range 0 -> c.height.
*/
FMX=c.FrameCount-1
S = Min(Max(Default(S,0),0),FMX) E = Default(E,-1)
sx=Default(sx,0.0) sy=Default(sy,0.0)
ex=Default(ex,0.0) ey=Default(ey,0.0)
E = (E==0) ? FMX : E
E = Min(((E < 0) ? S-E-1 : E),FMX) # S <= E <= FMX : E is +ve END Frame number (may be 0)
Empty = c.BlankClip(Length=0)
CS = (S==0) ? Empty : c.Trim(0,-S)
C2E = (E==0?FMX:E)
mw=Msk.Width mh=Msk.Height
cpad = c.Addborders(mw,mh,mw,mh)
bcpad = bc.Addborders(mw,mh,mw,mh)
CM = cpad.Animate(S,C2E,"Conceal", bcpad,Msk,sx,sy, bcpad,Msk, ex,ey)
CM = CM.Trim(S,C2E).crop(mw,mh,-mw,-mh)
CE = (E==FMX) ? Empty : c.Trim(E+1,0)
CS ++ CM ++ CE
}
Shinkiro.avs : A mod of earlier given script : Result is Censored Source clip replaced with Uncensored frame when censoring detected
EDIT: See post #35 for update.
# Req RT_Stats v2.0 Beta 13
FN1 = "D:\SHINKIRO\Censored.avi" # Artificial censored test clip, created by CreateCensored.avs
FN2 = "D:\Parade.avi" # Source for artificial censored clip
C1=AviSource(FN1)
C2=AviSource(FN2)
BLKW = 64
BLKH = BLKW
OLAPX = BLKW / 2
OLAPY = BLKH / 2
CW = 0.20 # ChromaWeight = 20%, Luma = 100-20 = 80% (difference weightings) : CW = combined weighting for Y + V. 0.0 = Luma Only
FACT = 8.0 # If BlkMaxDif >= (FACT * BlkAveDif) then is different
MINDIF = FACT/2.0 # Max Block differece has to be at least this (FACT alone not enough for detect, eg 0.01 could be bigger than 8.0 * 0.0)
PREFIX = "DAT_" # Local variables name prefix set by RT_FrameMovement.
SUBS = True
FINAL = False # Set True when happy.
################################
SUBS = (SUBS && !FINAL)
HitMsk = Hit_Marker(BlkW,BlkH,true)
HitMrk = HitMsk.BlankClip(Color=$FFFF44,pixel_type="YV12")
SSS = """
# Difference of the Block comparison with greatest difference.
BlkMaxDif = RT_FrameMovement(C1,C2,ChromaWeight=CW,BlkW=BLKW,BlkH=BLKH,olapX=OLAPX,olapY=OLAPY,Prefix=PREFIX)
# Average block difference.
BlkAveDif = DAT_BlkAveDf # Set as Local variable named via Prefix + "BlkAveDf"
Ratf = BlkMaxDif / Max(BlkAveDif,0.001) # How much bigger is it : avoid div by zero
MinOK = BlkMaxDif >= MINDIF # And also has to be of reasonable difference (at least FACT * 0.5)
IsDif = MinOK && Ratf >= FACT
(IsDif) ? C2 : C1 # Choose which frame to use
if(!FINAL) {
if(IsDif) { # Yellow Marker
XOff = DAT_Xoff
YOff = DAT_Yoff
Overlay(HitMrk,x=XOff,y=YOff,Mask=HitMsk,Mode="Blend")
}
RT_Subtitle("%d] %.1s MaxDf=%.3f : AveDf=%.3f\nMinOK=%.1s : RatF=%f",current_frame,IsDif,BlkMaxDif,BlkAveDif,MinOK,RatF)
}
Return Last
"""
DUMMY = C1
CC = DUMMY.ScriptClip(SSS)
CD = C1.Subtract(c2) # Diff
C1R = (SUBS) ? C1.TSub("Censored") : C1
C2R = (SUBS) ? C2.TSub("NonCensored") : C2
CCR = (SUBS) ? CC.TSub("Result") : CC
CDR = (SUBS) ? CD.TSub("Subtract") : CD
TOP = Stackhorizontal(C1R,C2R)
BOT = Stackhorizontal(CCR,CDR)
STK = StackVertical(Top,Bot)
Return (FINAL) ? CCR : STK
##########################################
Function Hit_Marker(int W, Int H, Bool "Hit", Int "BW", Int "BH",Int "PW",Int "PH",Bool "YV12",Bool "Mod2") {
/*
Req RT_Stats & mt_tools v2. http://forum.doom9.org/showthread.php?t=174527
Creates a marker mask WxH, for use with Overlay as Mask arg.
Intended for use via some kind of detector to show detection Hit, or Miss.
Returns Single frame clip with null audio, the marker top left hand side is aligned to 0,0.
Default return clip colorspace is Y8 for Avisynth v2.6 and above, or YV12 if defunct version.
Args:-
W,H, Size of marker mask (can be odd), return clip dimensions are rounded up to next multiple of 2 if YV12 return clip.
Bare minimum usable size is 4x4 with defaulted BW, BH, PW, PH.
HIT, Default False, returns marker as angled corners (Thickness set by BW and BH).
True, returns marker as angled corners + outer perimeter where perimeter thickness set by PW and PH.
BW, Default Max(W/8,1). Horizontal thickness of the marker corners.
BH, Default Max(H/8,1). Vertical thickness of the marker corners.
PW, Default Max(BW/8,1). Horizontal thickness of the vertical perimeter.
PH, Default Max(BH/8,1). Vertical thickness of the horizontal perimeter.
YV12, Default False if Avisynth version 2.6 or greater(Y8), else true(YV12). Selects return clip colorspace.
Mod2, Default True. If Y8 then round odd dimensions up modulo 2 (the mask is still WxH).
Example:-
Import("Hit_Marker.avs")
WW=128 HH=WW HIT=True
BlankClip
Yell=Last.BlankClip(Width=WW,Height=HH,Length=1,Color=$FFFF00)
Return OverLay(Yell,x=(Width-WW)/2,y=(Height-HH)/2,mask=Hit_Marker(WW,HH,HIT))
*/
Hit=Default(hit,False) BW=Max(Default(BW,W/8),1) BH=Max(Default(BH,H/8),1)
BW2=(W>4)?BW*2:BW BH2=(H>4)?BH*2:BH
PW=Max(Default(PW,BW/8),1) PH=Max(Default(PH,BH/8),1) is26 = VersionNumber>=2.6
YV12=(!is26) ? True : Default(YV12,False) Mod2=Default(Mod2,True) Mod = (YV12||Mod2) ? 2 : 1
CanvasW=(W+Mod-1)/Mod*Mod CanvasH=(H+Mod-1)/Mod*Mod
Rpn= RT_String("x %d < x %d >= | y %d < | y %d >= | x %d < x %d >= | & y %d < y %d >= | & ",BW,W-BW,BH,H-BH,BW2,W-BW2,BH2,H-BH2)
Rpn= (Hit) ? Rpn + RT_String("x %d < x %d >= | y %d < | y %d >= | | ",PW,W-PW,PH,H-PH) : Rpn
Rpn= Rpn + RT_String("x %d < y %d < & & 255 0 ?",W,H)
Blankclip(width=CanvasW,height=CanvasH,Length=1,pixel_type=YV12?"YV12":"Y8").Killaudio
return mt_lutspa(relative = false,yExpr=Rpn, chroma = "-128")
}
# Stack Overhead Subtitle Text, with optional FrameNumber shown.
Function TSub(clip c,string Tit,Bool "ShowFrameNo",Int "Col"){
c.BlankClip(height=20,Color=Default(Col,0))
(Default(ShowFrameNo,False))?ScriptClip("""Subtitle(String(current_frame,"%.f] """+Tit+""""))"""):Trim(0,-1).Subtitle(Tit)
Return StackVertical(c).AudioDubEx(c)
}
Metrics and marker shown on Result frame
https://i.postimg.cc/4dWwrWpH/Shinkiro-00.jpg (https://postimages.org/)
Shinkiro
8th February 2021, 14:35
I'm missing something.
http://i114.fastpic.ru/big/2021/0208/3b/ed4c2beeb4a4be89a0879dbf857cbe3b.png
StainlessS
8th February 2021, 16:11
Is that on the test script provided, or your own script on non artificial censoring ?,
What are args to Hit_Marker(),
Post your version of this part
BLKW = 64
BLKH = BLKW
OLAPX = BLKW / 2
OLAPY = BLKH / 2
CW = 0.20 # ChromaWeight = 20%, Luma = 100-20 = 80% (difference weightings) : CW = combined weighting for Y + V. 0.0 = Luma Only
FACT = 8.0 # If BlkMaxDif >= (FACT * BlkAveDif) then is different
MINDIF = FACT/2.0 # Max Block differece has to be at least this (FACT alone not enough for detect, eg 0.01 could be bigger than 8.0 * 0.0)
PREFIX = "DAT_" # Local variables name prefix set by RT_FrameMovement.
SUBS = True
FINAL = False # Set True when happy.
################################
SUBS = (SUBS && !FINAL)
HitMsk = Hit_Marker(BlkW,BlkH,true)
HitMrk = HitMsk.BlankClip(Color=$FFFF44,pixel_type="YV12")
Are you using Hit_Marker() from this thread, or some other ? [ It shows as Hit_Marker.avsi in error message ]
I dont see any cause for problem, but I'm knackered, got to bed about 06:30 and up again at 09:00, waiting
for delivery some time before 9:00PM tonight, amazon ... dont you just luv'em.
Shinkiro
8th February 2021, 18:43
Hit_Marker and TSub used from the current thread
Nothing other than FACT and source has been changed
RT_Stats_x64 v.2.00.Beta13
Avisynth 3.7.0 x64
C1=DGSource("S:\VTS_01_2_cen.dgi").TFM(mode=1,pp=5,MI=25,display=false, slow=2,cthresh=9,mthresh=8,chroma=false,ubsco=false,hint=true,opt=4,metric=0).\
.Spline64Resize(720, 480, src_left=0.0, src_top=0.0, src_width=-0.0, src_height=-1.0)
C2=DGSource("S:\VTS_01_2_unc.dgi").TFM(mode=1,pp=5,MI=25,display=false, slow=2,cthresh=9,mthresh=5,chroma=false,ubsco=false,hint=true,opt=4,metric=0).Trim(272, 0)\
.Spline64Resize(720, 480, src_left=0.0, src_top=1.0, src_width=-0.0, src_height=-0.0)
BLKW = 64
BLKH = BLKW
OLAPX = BLKW / 2
OLAPY = BLKH / 2
CW = 0.20 # ChromaWeight = 20%, Luma = 100-20 = 80% (difference weightings) : CW = combined weighting for Y + V. 0.0 = Luma Only
FACT = 5.8 # If BlkMaxDif >= (FACT * BlkAveDif) then is different
MINDIF = FACT/2.0 # Max Block differece has to be at least this (FACT alone not enough for detect, eg 0.01 could be bigger than 8.0 * 0.0)
PREFIX = "DAT_" # Local variables name prefix set by RT_FrameMovement.
SUBS = True
FINAL = False # Set True when happy.
################################
SUBS = (SUBS && !FINAL)
HitMsk = Hit_Marker(BLKW,BLKH,true)
HitMrk = HitMsk.BlankClip(Color=$FFFF44,pixel_type="YV12")
SSS = """
# Отличие сравнения блоков с наибольшей разницей.
BlkMaxDif = RT_FrameMovement(C1,C2,ChromaWeight=CW,BlkW=BLKW,BlkH=BLKH,olapX=OLAPX,olapY=OLAPY,Prefix=PREFIX)
# Средняя разница блоков.
BlkAveDif = DAT_BlkAveDf # Установить как локальную переменную с именем Prefix + "BlkAveDf"
Ratf = BlkMaxDif / Max(BlkAveDif,0.001) # Насколько он больше: избегайте деления на ноль
MinOK = BlkMaxDif >= MINDIF # И также должна быть разумная разница (как минимум ФАКТ * 0,5)
IsDif = MinOK && Ratf >= FACT
(IsDif) ? C2 : C1 # Выберите, какую рамку использовать
if(!FINAL) {
if(IsDif) { # Желтый маркер
XOff = DAT_Xoff
YOff = DAT_Yoff
Overlay(HitMrk,x=XOff,y=YOff,Mask=HitMsk,Mode="Blend")
}
RT_Subtitle("%d] %.1s MaxDf=%.3f : AveDf=%.3f\nMinOK=%.1s : RatF=%f",current_frame,IsDif,BlkMaxDif,BlkAveDif,MinOK,RatF)
}
Return Last
"""
DUMMY = C1
CC = DUMMY.ScriptClip(SSS)
CD = C1.Subtract(c2) # Diff
C1R = (SUBS) ? C1.TSub("Censored") : C1
C2R = (SUBS) ? C2.TSub("NonCensored") : C2
CCR = (SUBS) ? CC.TSub("Result") : CC
CDR = (SUBS) ? CD.TSub("Subtract") : CD
TOP = Stackhorizontal(C1R,C2R)
BOT = Stackhorizontal(CCR,CDR)
STK = StackVertical(Top,Bot)
Return (FINAL) ? CCR : STK
StainlessS
8th February 2021, 19:39
Still cant see anything wrong, its probably staring me in the face.
Can you please try with this part, and see what comes out of it in DebugView
Function Hit_Marker(int W, Int H, Bool "Hit", Int "BW", Int "BH",Int "PW",Int "PH",Bool "YV12",Bool "Mod2") {
/*
Req RT_Stats & mt_tools v2. http://forum.doom9.org/showthread.php?t=174527
Creates a marker mask WxH, for use with Overlay as Mask arg.
Intended for use via some kind of detector to show detection Hit, or Miss.
Returns Single frame clip with null audio, the marker top left hand side is aligned to 0,0.
Default return clip colorspace is Y8 for Avisynth v2.6 and above, or YV12 if defunct version.
Args:-
W,H, Size of marker mask (can be odd), return clip dimensions are rounded up to next multiple of 2 if YV12 return clip.
Bare minimum usable size is 4x4 with defaulted BW, BH, PW, PH.
HIT, Default False, returns marker as angled corners (Thickness set by BW and BH).
True, returns marker as angled corners + outer perimeter where perimeter thickness set by PW and PH.
BW, Default Max(W/8,1). Horizontal thickness of the marker corners.
BH, Default Max(H/8,1). Vertical thickness of the marker corners.
PW, Default Max(BW/8,1). Horizontal thickness of the vertical perimeter.
PH, Default Max(BH/8,1). Vertical thickness of the horizontal perimeter.
YV12, Default False if Avisynth version 2.6 or greater(Y8), else true(YV12). Selects return clip colorspace.
Mod2, Default True. If Y8 then round odd dimensions up modulo 2 (the mask is still WxH).
Example:-
Import("Hit_Marker.avs")
WW=128 HH=WW HIT=True
BlankClip
Yell=Last.BlankClip(Width=WW,Height=HH,Length=1,Color=$FFFF00)
Return OverLay(Yell,x=(Width-WW)/2,y=(Height-HH)/2,mask=Hit_Marker(WW,HH,HIT))
*/
Hit=Default(hit,False) BW=Max(Default(BW,W/8),1) BH=Max(Default(BH,H/8),1)
BW2=(W>4)?BW*2:BW BH2=(H>4)?BH*2:BH
PW=Max(Default(PW,BW/8),1) PH=Max(Default(PH,BH/8),1) is26 = VersionNumber>=2.6
YV12=(!is26) ? True : Default(YV12,False) Mod2=Default(Mod2,True) Mod = (YV12||Mod2) ? 2 : 1
CanvasW=(W+Mod-1)/Mod*Mod CanvasH=(H+Mod-1)/Mod*Mod
RT_DebugF("D1: W=%d H=%d Hit=%.1s BW=%d BH=%d PW=%d PH=%d YV12=%.1s Mod2=%.1s",W,H,Hit,BW,BH,PW,PH,YV12,Mod2)
RT_DebugF("Set RPN1")
Rpn= RT_String("x %d < x %d >= | y %d < | y %d >= | x %d < x %d >= | & y %d < y %d >= | & ",BW,W-BW,BH,H-BH,BW2,W-BW2,BH2,H-BH2)
RT_DebugF("Set RPN2")
Rpn= (Hit) ? Rpn + RT_String("x %d < x %d >= | y %d < | y %d >= | | ",PW,W-PW,PH,H-PH) : Rpn
RT_DebugF("Set RPN3")
Rpn= Rpn + RT_String("x %d < y %d < & & 255 0 ?",W,H)
RT_DebugF("DONE RPN")
Blankclip(width=CanvasW,height=CanvasH,Length=1,pixel_type=YV12?"YV12":"Y8").Killaudio
return mt_lutspa(relative = false,yExpr=Rpn, chroma = "-128")
}
# Stack Overhead Subtitle Text, with optional FrameNumber shown.
Function TSub(clip c,string Tit,Bool "ShowFrameNo",Int "Col"){
c.BlankClip(height=20,Color=Default(Col,0))
(Default(ShowFrameNo,False))?ScriptClip("""Subtitle(String(current_frame,"%.f] """+Tit+""""))"""):Trim(0,-1).Subtitle(Tit)
Return StackVertical(c).AudioDubEx(c)
}
Shinkiro
9th February 2021, 00:41
Run script in VDub
I don't know if I did it right, but this is what I got.
00000001 0.00000000 [10716] RemoveDirt 0.9.2
if I delete RemoveDirt, the error remains, and DebugView does not show anything Q_Q
StainlessS
9th February 2021, 12:15
Its not being loaded for some reason. [ignore the removedirt thing].
If you are using Avs x86 then need use VD2 x86, if x64 the vd2 x64.
(maybe you have the debug hit_marker in wrong plugins)
Suggest move hit_marker stuff into main script, and delete separate file until we find problem.
Shinkiro
9th February 2021, 13:21
I decided to delete everything from the plugins folder and after that I started to gradually return everything back. In this way, I was able to identify the culprit of all the problems.
I do not know why, but I am to blame SoftSharpen.avsi (http://sendfile.su/1594975)
StainlessS
9th February 2021, 14:48
No matter, we all make life difficult for ourselves at times.
Anyways, here update, made into function. [Requires Grunt]
# Req RT_Stats v2.0 Beta 13 And Grunt.
FN1 = "D:\SHINKIRO\Censored.avi" # Artificial censored test clip, created by CreateCensored.avs
FN2 = "D:\Parade.avi" # Source for artificial censored clip
C1=AviSource(FN1)
C2=AviSource(FN2)
BLKW = 64
BLKH = BLKW
OLAPX = BLKW / 2
OLAPY = BLKH / 2
dFact = 4.0 # If BlkMaxDif > (dFact * BlkAveDif + Mindif) then is different
MinDif = 0.5
CW = 0.20 # ChromaWeight = 20%, Luma = 100-20 = 80% (difference weightings) : CW = combined weighting for Y + V. 0.0 = Luma Only
SHOW = True
SUBS = True
FINAL = False # Set True when happy.
################################
SHOW = (SHOW && !FINAL)
SUBS = (SUBS && !FINAL)
CC = CensorSwap(c1,c2,dFact=dFact,MinDif=MinDif,blkW=BlkW,blkh=BlkH,olapx=OLapX,olapy=OLapY,cw=CW,show=Show)
CD = C1.Subtract(c2) # Diff
C1R = (SUBS) ? C1.TSub("Censored") : C1
C2R = (SUBS) ? C2.TSub("NonCensored") : C2
CCR = (SUBS) ? CC.TSub("Result") : CC
CDR = (SUBS) ? CD.TSub("Subtract") : CD
TOP = Stackhorizontal(C1R,C2R)
BOT = Stackhorizontal(CCR,CDR)
STK = StackVertical(Top,Bot)
Return (FINAL) ? CCR : STK
###
Function CensorSwap(clip c1,clip c2,Float "dFact",Float "MinDif",Int "BlkW",Int "BlkH",Int "OLapX",Int "OLapY",Float "CW",Bool "Show") {
# https://forum.doom9.org/showthread.php?p=1935658#post1935658
dFact = Default(dFact,4.0)
MinDif = Default(MinDif,0.5)
BlkW = Default(BlkW,64)
BlkH = Default(BlkH,BlkW)
OLapX = Default(OLapX,BlkW/2)
OLapY = Default(OLapY,BlkH/2)
CW = Default(CW,0.2)
Show = Default(Show,False)
HitMsk = Hit_Marker(BlkW,BlkH,true)
HitMrk = HitMsk.BlankClip(Color=$FFFF44,pixel_type="YV12")
MissMsk= Hit_Marker(BlkW,BlkH,false)
MissMrk= MissMsk.BlankClip(Color=$44FF44,pixel_type="YV12")
SSS = """
# Difference of the Block comparison with greatest difference.
BlkMaxDif = RT_FrameMovement(Last,c2,ChromaWeight=CW,BlkW=BLKW,BlkH=BLKH,olapX=OLAPX,olapY=OLAPY,Prefix="CensorSwap_")
# Average block difference.
BlkAveDif = CensorSwap_BlkAveDf # Set as Local variable named via Prefix + "BlkAveDf"
BlkTh = BlkAveDif*dFact+MINDIF
IsDif = BlkMaxDif > BlkTh
(IsDif) ? C2 : Last # Choose which frame to use
if(Show) {
XOff = CensorSwap_Xoff
YOff = CensorSwap_Yoff
if(IsDif) { Overlay(HitMrk, x=XOff,y=YOff,Mask= HitMsk,Mode="Blend") }
else if(BlkAveDif>0.0) { Overlay(MissMrk,x=XOff,y=YOff,Mask=MissMsk,Mode="Blend") }
RT_Subtitle(
\ "%d] \a%sMax>Th=%.1s\a-\n" +
\ "Ave =%7.3f\n" +
\ "Max =%7.3f @ %dx%d\n" +
\ "Th =%7.3f {Ave*%.3f+%.3f}\n",
\ current_frame,IsDif?"!":"-",IsDif,
\ BlkAveDif,BlkMaxDif,XOff,YOff,BlkTh,dFact,MinDif)
}
Return Last
"""
Return c1.GScriptClip(SSS,args="c2,dFact,MinDif,BlkW,BlkH,OLapX,OLapY,CW,Show,HitMsk,HitMrk,MissMsk,MissMrk",local=true)
}
##########################################
Function Hit_Marker(int W, Int H, Bool "Hit", Int "BW", Int "BH",Int "PW",Int "PH",Bool "YV12",Bool "Mod2") {
/*
Req RT_Stats & mt_tools v2. http://forum.doom9.org/showthread.php?t=174527
Creates a marker mask WxH, for use with Overlay as Mask arg.
Intended for use via some kind of detector to show detection Hit, or Miss.
Returns Single frame clip with null audio, the marker top left hand side is aligned to 0,0.
Default return clip colorspace is Y8 for Avisynth v2.6 and above, or YV12 if defunct version.
Args:-
W,H, Size of marker mask (can be odd), return clip dimensions are rounded up to next multiple of 2 if YV12 return clip.
Bare minimum usable size is 4x4 with defaulted BW, BH, PW, PH.
HIT, Default False, returns marker as angled corners (Thickness set by BW and BH).
True, returns marker as angled corners + outer perimeter where perimeter thickness set by PW and PH.
BW, Default Max(W/8,1). Horizontal thickness of the marker corners.
BH, Default Max(H/8,1). Vertical thickness of the marker corners.
PW, Default Max(BW/8,1). Horizontal thickness of the vertical perimeter.
PH, Default Max(BH/8,1). Vertical thickness of the horizontal perimeter.
YV12, Default False if Avisynth version 2.6 or greater(Y8), else true(YV12). Selects return clip colorspace.
Mod2, Default True. If Y8 then round odd dimensions up modulo 2 (the mask is still WxH).
Example:-
Import("Hit_Marker.avs")
WW=128 HH=WW HIT=True
BlankClip
Yell=Last.BlankClip(Width=WW,Height=HH,Length=1,Color=$FFFF00)
Return OverLay(Yell,x=(Width-WW)/2,y=(Height-HH)/2,mask=Hit_Marker(WW,HH,HIT))
*/
Hit=Default(hit,False) BW=Max(Default(BW,W/8),1) BH=Max(Default(BH,H/8),1)
BW2=(W>4)?BW*2:BW BH2=(H>4)?BH*2:BH
PW=Max(Default(PW,BW/8),1) PH=Max(Default(PH,BH/8),1) is26 = VersionNumber>=2.6
YV12=(!is26) ? True : Default(YV12,False) Mod2=Default(Mod2,True) Mod = (YV12||Mod2) ? 2 : 1
CanvasW=(W+Mod-1)/Mod*Mod CanvasH=(H+Mod-1)/Mod*Mod
Rpn= RT_String("x %d < x %d >= | y %d < | y %d >= | x %d < x %d >= | & y %d < y %d >= | & ",BW,W-BW,BH,H-BH,BW2,W-BW2,BH2,H-BH2)
Rpn= (Hit) ? Rpn + RT_String("x %d < x %d >= | y %d < | y %d >= | | ",PW,W-PW,PH,H-PH) : Rpn
Rpn= Rpn + RT_String("x %d < y %d < & & 255 0 ?",W,H)
Blankclip(width=CanvasW,height=CanvasH,Length=1,pixel_type=YV12?"YV12":"Y8").Killaudio
return mt_lutspa(relative = false,yExpr=Rpn, chroma = "-128")
}
# Stack Overhead Subtitle Text, with optional FrameNumber shown.
Function TSub(clip c,string Tit,Bool "ShowFrameNo",Int "Col"){
c.BlankClip(height=20,Color=Default(Col,0))
(Default(ShowFrameNo,False))?ScriptClip("""Subtitle(String(current_frame,"%.f] """+Tit+""""))"""):Trim(0,-1).Subtitle(Tit)
Return StackVertical(c).AudioDubEx(c)
}
https://i.postimg.cc/0zzXwbqs/Shinkiro-2-00.jpg (https://postimg.cc/0zzXwbqs)
EDIT: CreateCensored.avs to create artificial test censored clip in post #27.
Shinkiro
9th February 2021, 16:37
StainlessS
Cool, and thanks for the help.
vBulletin® v3.8.11, Copyright ©2000-2026, vBulletin Solutions Inc.