Log in

View Full Version : detecting black frames


peacemaker69
6th May 2012, 08:52
I found a script that detects black frames in a video file. I created a test video with black frames. I opened this script in VirtualDubMod, it loads the video but there is no report text file with detected black frames. I am a professional editor but a TOTAL Avisynth noob. There must be something I am doing wrong. Below is the script I am using.


#Specify the name and location of the output file
filename = "c:\output_blank_frames.txt"

#Specify the threshold that will be considered black (0 = pure black)
global blankthreshold=28

#DirectShowSource("black_frames_av2i.avi").killaudio()
AVISource("black_frames_test.avi").killaudio()

#Only look at half the fields (speeds up processing
i=separatefields.selectodd.ConvertToYV12()

#Write the frame number
WriteFileIf(last, filename, "(AverageLuma(i)<blankthreshold)", "current_frame+1", append = false)


thanks for help

librarian
6th May 2012, 10:39
#Specify the name and location of the output file
global filename = "c:\output_blank_frames.txt"

#Specify the threshold that will be considered black (0 = pure black)
global blankthreshold=28

#DirectShowSource("black_frames_av2i.avi").killaudio()
AVISource("black_frames_test.avi").killaudio()

#Only look at half the fields (speeds up processing
i=separatefields.selectodd.ConvertToYV12()

#Write the frame number
i.WriteFileIf( filename, "(AverageLuma(i)<blankthreshold)", "current_frame+1", append = false)

If you want to use filename inside WRITEFILEIF make it global, the output file is written only if the result of WRITEFILEIF is rendered, from the manual:
Note that since output is produced only for ''rendered'' frames, there will be no output at all if the result of the filter is not used in deriving the final result of the script.

peacemaker69
6th May 2012, 11:45
I added "global" before "filename" and added "i." before "writefileif" but I get a message:

Script error: Invalid arguments to function "WriteFileIf"

If I delete "i.", I don't get any message but there is no output text file too.

librarian
6th May 2012, 11:53
I added "global" before "filename" and added "i." before "writefileif" but I get a message:

Script error: Invalid arguments to function "WriteFileIf"

If I delete "i.", I don't get any message but there is no output text file too.

With my solution, last line has to be:
i.WriteFileIf( filename, "(AverageLuma(i)<blankthreshold)", "current_frame+1", append = false)
not:
i.WriteFileIf(last, filename, "(AverageLuma(i)<blankthreshold)", "current_frame+1", append = false)
just like I posted above.

peacemaker69
6th May 2012, 12:17
Now I got a message like this:

[!] Couldn't locate decompressor for format 'YV12' (unknown).
VirtualDub requires a Video for Windows (VFW) compatible codec to
decompress video. DirectShow codecs, such as those used by Windows Media
Player, are not suitable. Only 'Direct stream copy' is available for this
video.

I tried this with DV avi and uncompressed avi, same answer. Where to download best codecs for Avisynth?

librarian
6th May 2012, 12:26
According to forum rules never ask for the "best".
You already got a good answer:
ffmpegsource2

http://code.google.com/p/ffmpegsource/
BTW this is off topic here.

Gavino
6th May 2012, 13:10
If you want to use filename inside WRITEFILEIF make it global
filename does not need to be global.
Neither does blankthreshold.

johnmeyer
7th May 2012, 01:25
Here's a script I use all the time. I'm sure you'll need to modify it, but perhaps it will get you started.

You'll note that I wrote the script to only output the first frame that falls below a given luma threshold. The reason for this is that there are lots of situations where a video will go to black for many frames (like between commercials), and with my editing workflow I only need to know where the blank/black series of frames starts. The script below has a line that is commented out that will give you every frame that falls below the blank luma threshold.

#Script to find blank frames
#The script outputs the frame number of the first frame that falls below
#the threshold luma value.

#Specify the name and location of the output file
filename = "m:\output_blank_frames.txt"

#Specify the blank threshold. Set to zero for pure black.
global blankthreshold=49

#Specify the name and location of your video file
AVISource("E:\fs.avi").killaudio().ConvertToYV12

#Optional: For analog captures, get rid of the borders.
# Analog video can have border garbage that affects averageluma
Crop(16,16,-16,-16)

# Use the following to reduce the number of fields by 50% in order to speed processing
separatefields.selectodd

i=last
j=trim(i,1,0) #Previous frame [edit] Actually, it is the NEXT frame

# Temporarily un-comment next line to display the average luma value on screen to help determine threshold value
#ScriptClip(i,"Subtitle(String(AverageLuma(i) ))" )

#This line below will output EVERY frame that is below threshold, which results in LOTS of frames
#Normally you don't do this, but it is included for those who want this capability.
#WriteFileIf(last, filename, "(AverageLuma(i)<blankthreshold)", "current_frame+1", append = false)

#This is the line that actually writes the frame number of the FIRST frame that falls below the threshold
WriteFileIf(last, filename, "(AverageLuma(i)<blankthreshold)&&AverageLuma(j)>blankthreshold", "current_frame+1", append = false)

#The line below finds the LAST blank frame.
#WriteFileIf(last, filename, "(AverageLuma(i)>blankthreshold)&&AverageLuma(j)<blankthreshold", "current_frame+1", append = false)

Groucho2004
7th May 2012, 01:48
Try this: Run your script from the first post (which works perfectly fine and creates the file with the frames numbers) through AVSMeter (http://forum.doom9.org/showthread.php?t=162155).

Mug Funky
8th May 2012, 03:43
as an alternative, you might also find that using silence detection on the audio gets you something usable in less time.

both would be nice of course...

i have an EDL generating script that finds hard cuts if you need that. very good for splitting up a tape capture into shots that can be graded in an app that supports CMX format EDLs.

peacemaker69
8th May 2012, 10:04
I am a pro editor but a total avisynth noob, although I think avisynth is a great tool that could help me in my everydays work, that's why I am not giving up!

When I try to work with a DV AVI I get the message:

Avisynth open failure:
Avisource: couldn't locate a decompressor for fourdcc dvsd

when I try uncompressed avi I get:

Couldn't locate decompressor for format YV12 (uknown).
VirtualDub requires a Video for Windows (VFW) compatible codec to decompress video. DirectShow codecs, such as those used by Windows Media Player, are not suitable. Only 'Direct stream copy' is available for this video.

Groucho2004
8th May 2012, 10:20
Did you try it with FFMpegsource as suggested to you (twice)?

StainlessS
8th May 2012, 11:19
It is perhaps likely that you will in future encounter further problems without a YV12 decoder, see here if you do:
http://forum.doom9.org/showthread.php?t=56972&highlight=helix+yv12

peacemaker69
8th May 2012, 12:41
Groucho2004 yes I should have done it but.. to load an avi, I use avisource command. How to use ffmpeg in avisynth script to load a video?

Groucho2004
8th May 2012, 13:16
Download the latest release and use it like this:
LoadPlugin("ffms2.dll")
FFVideoSource("vid.avi")

The DLL also comes with documentation.

lintran
23rd February 2013, 16:03
........................

Chikuzen
23rd February 2013, 18:11
I used peacemaker69 script and it work well though AVSMeter. So anyone can give me avs script that can detect all frames that have silence audio?
Thanks.


#detect_silence.avs
LoadPlugin("ffms2.dll")
LoadPlugin("MinMaxAudio.dll")

src = "file path to the source"
aud = FFAudioSource(src)
vid = BlankClip(FFVideoSource(src), width=32, height=32)
AudioDub(vid, aud)

#assume max is lesser than -100db as silence frame
WriteFileIf("silence.log", "AudioMax(0) < -100", "current_frame")


> avs2pipemod -benchmark detect_silence.avs

you will get the list of all silence frames.

dwilbank
12th March 2013, 03:31
Love the "find silence" script, but now how could I make it write out just the first and/or last frames where there is silence?

following johnmayer's example...

I would make i the last frame and j the previous frame,

Then create another && condition and put the "i" and "j" into AudioMax?

Somehow I fear that wouldn't work, would it?

What is the "0" in AudioMax(0) ?

Thanks

StainlessS
12th March 2013, 05:10
What is the "0" in AudioMax(0) ?

Generally the best place to find such info is in the documentation:


Conditional Audio Filters - MinMaxAudio v0.2

Usage:

Computes the root mean square, maximal or minimal value over all samples in all channels,
or just over all samples in channel, and outputs the value (in decibels) as a float.
It's a conditional audio filter, so the computation is done framewise. The audio is converted
to float internally.


Syntax:

AudioMax(channel) # maximal volume in channel (framewise)
AudioMin(channel) # minimal volume in channel (framewise)
AudioRMS(channel) # root mean square volume in channel (framewise)
# channel = 0 (default) means that the max/min/rms is taken over all samples in all channels (framewise)

Examples:

v = BlankClip(pixel_type="YV12")
a = Tone(type="Triangle", frequency=2, level=0.4)
w = AudioDub(v,a) #.Histogram(mode="audiolevels")
#w = ScriptClip(w, "Subtitle(String(AudioMax(1)))")
w = ScriptClip(w, "Subtitle(String(AudioRMS(0)))")
#w = ScriptClip(w, "Subtitle(String(AudioMin(1)))")
w = w #.ConvertToRGB32.AudioGraph(20)
return w

Note that the values are given in decibels (see also histogram docs for an explanation).

License:

Licensed under GPL by Wilbert Dijkhof

dwilbank
12th March 2013, 06:17
Thx

But the real question remains.
I see no way to make audiomax() pay attention to specific "i" and "j" frames like johnmayer did with averageluma()

StainlessS
12th March 2013, 12:03
i and j are clips not frames. (EDIT: The frame being worked on is current_frame)
It should work similarly to the previous script, you are just swapping apples for oranges.

Both AverageLuma() and AudioMax() are just functions returning a float used in the conditional part of WriteFileIf().


EDIT: Instead of this:


#This is the line that actually writes the frame number of the FIRST frame that falls below the threshold
WriteFileIf(last, filename, "(AverageLuma(i)<blankthreshold)&&AverageLuma(j)>blankthreshold", "current_frame+1", append = false)


Something like:


#This is the line that actually writes the frame number of the FIRST frame that falls below the threshold
WriteFileIf(last, filename, "(AudioMax(i,0) < -100) && AudioMax(j,0) > -100", "current_frame+1", append = false)

Gavino
12th March 2013, 12:20
However, I think johnmeyer's example is incorrect.
j=trim(i,1,0) #Previous frame
will make j correspond to the next frame, not the previous one. I believe it should be:
j = SelectEvery(i,-1)

StainlessS
12th March 2013, 12:29
Have to admit to a little head scratching when I saw that and the current_frame+1 bit.
(guess I just thought 'must be right' and moved on).
Would I be right in thinking that the output should then be on current_frame rather than current_frame+1. ?

EDIT: on looking again, looks to me like the JM example outputs the frame number of the 1st frame after black frames, perhaps just labeled wrongly.

EDIT: Below looks correct to me (not checked)

j = SelectEvery(i,-1) # Previous frame
#This is the line that actually writes the frame number of the FIRST frame that falls below the threshold
WriteFileIf(last, filename, "(AverageLuma(i)<blankthreshold)&&AverageLuma(j)>blankthreshold", "current_frame", append = false)


and so the audio thing,


j = SelectEvery(i,-1) # Previous frame
#This is the line that actually writes the frame number of the FIRST frame that falls below the threshold
WriteFileIf(last, filename, "(AudioMax(i,0) < -100) && AudioMax(j,0) > -100", "current_frame", append = false)


ie if current frame (i) < -100 && previous frame(j) > -100, then output current_frame, the silent one.

Gavino
12th March 2013, 12:36
Have to admit to a little head scratching when I saw that and the current_frame+1 bit.
(guess I just thought 'must be right' and moved on).
Would I be right in thinking that the output should then be on current_frame rather than current_frame+1. ?
Yes, you're right.
(I was assuming he used current_frame+1 to get frames numbered from 1 instead of 0, but it may just be a mistake.)

johnmeyer
12th March 2013, 18:04
Have to admit to a little head scratching when I saw that and the current_frame+1 bit.
(guess I just thought 'must be right' and moved on).Like many of you, I have hundreds of these scripts and script fragments. I use this one all the time, and the comments I made were for my own benefit. I usually work on the script until it gets the job done for whatever is at hand, and then I move on. The comments are there to remind me what I was doing. In general, they are correct, but they reflect the workflow I created. In this case, the frame number file was imported back into Vegas where the first frame is frame number 1 and not frame number 0. That will explain why some of the numbers may be off by one.

As for the trim function, my comment was an error, and I modified my post to add to that comment to explain the error.