View Full Version : Compare two frames
erratic
15th November 2003, 18:57
In another forum someone asked if it was possible to check for dropped frames in an MPEG-2 file captured with ATI MMC. When capturing directly to MPEG-2, ATI MMC automatically repeats the previous frame when a frame is dropped, so it should be possible to find dropped frames by scanning the MPEG-2 file and comparing each frame with the previous one. Even when the picture is static, two consecutive frames are never identical because of the noise, so if both frames are completely identical it would mean a frame was dropped.
I'm wondering if finding and counting dropped frames in an MPEG-2 file that way is possible with an Avisynth script.
sh0dan
15th November 2003, 19:45
Basic concept:
org = MPEG2SOURCE("file.d2v")
eq = Subtitle(org, "Same as previous:YES")
neq = Subtitle(org, "Same as previous:NO")
Conditionalfilter(org, eq, neq, "YDifferenceFromPrevious()", "<", "0.1")
(I cannot check syntax here - hopefully someone can help you a bit here).
erratic
15th November 2003, 20:17
It works. I'm impressed. But I'm an Avisynth newbie (I've only used it for noise reduction) so I'd like to know how I can count the dropped frames and then create a text file with the number of dropped frames and the actual frame numbers. A text file containing something like this:
Dropped frames: 3 (48,114,310)
mf
15th November 2003, 20:25
It might be possible if you can have a variable be incremented everytime the conditional hits true, and then check when the framenumber is the last (currentframenumber == framecount) and throw an error with the amount of duplicate frames found. But I am far too AVISynth noob to know if this is possible or not without extensively reading the manual and cursing at AVISynth for plaguing me with errors that I will have caused 90% of the time :D.
sh0dan
15th November 2003, 21:22
OK - I'm guessing here - I cannot test anything.
(If it works at all) - load it into vdub. Let it run from start to end, and you should have the total number of drops.
global dropped = 0
org = MPEG2SOURCE("file.d2v")
org_p = scriptclip(org, "Subtitle(" + chr(34) + " Total Dropped:" + chr(34) + " + string(dropped), align=2)")
eq = Subtitle(org_p, "Same as previous:YES").frameevaluate("dropped=dropped+1", after_frame=true)
neq = Subtitle(org_p, "Same as previous:NO")
Conditionalfilter(org, eq, neq, "YDifferenceFromPrevious()", "<", "0.1")
Edit: Fixed problem
erratic
15th November 2003, 21:50
Yes, it works, but I noticed now that it reports one dropped frame too many. If I open the script in VirtualDubMod it always counts 1 dropped frame as soon as I hit the right arrow, although there is no dropped frame at the beginning of the file. All the other dropped frames are counted correctly. (This also happened with the first draft of the script.) Very interesting so far though.
Edit: After adding "after_frame=true" it now counts the extra dropped frame when I hit the right arrow twice.
erratic
15th November 2003, 22:11
I removed "after_frame=true" again. With "after_frame=true" it augments the counter one frame late. For example. If frame #107 is an exact copy of frame #106 the counter is augmented when I reach frame #108. Without "after_frame=true" the counter is augmented when I reach frame #107.
sh0dan
15th November 2003, 23:12
oh right - frame 0 will always count as a duplicate, as it will compare itself to frame -1, which is then corrected to frame 0 (itself). Just initialize it to "-1", and you'll get the correct count.
The "after_frame" funtionality is to be expected, as it will make the framescript evaluate after the frame with the counter has been drawn.
I know other people here will be able to help you print out frame numbers to a file.
qwerpoi
16th November 2003, 11:26
For outputting frames to a textfile, try using chr.dll (http://www.avisynth.org/users/warpenterprises/) , use the Write() function (you'll need to download the chr_25_dll_20030920 version). The example given in the readme (slightly modified):
LoadPlugin("c:\myprojects\dvinfo\release\CHR.dll")
AviSource("c:\something.avi")
filename = "c:\myprojects\output.txt"
Write(filename, "current_frame")
If you open this script in virtual dub and scroll through frame by frame, this will write the current frame number into the specified text file as you go through the frames (I was pretty amazed when I saw this). I don't have much experience using it, but you can go through the readme which discusses using conditional arguments, it's pretty advanced stuff.
erratic
16th November 2003, 13:38
I have installed Chr.dll and I've been able to create a text file. The script now looks like this:
global dropped = -1
org = MPEG2SOURCE("file.d2v")
org_p = scriptclip(org, "Subtitle(" + chr(34) + " Total Dropped:" + chr(34) + " + string(dropped), align=2)")
eq = Subtitle(org_p, "Same as previous:YES").frameevaluate("dropped=dropped+1").Write("drops.txt",String("dropped+1")," : ","current_frame")
neq = Subtitle(org_p, "Same as previous:NO")
Conditionalfilter(org, eq, neq, "YDifferenceFromPrevious()", "<", "0.1")
I'm testing with a small MPEG-2 file with 4 dropped frames. It outputs this:
0 : -1
0 : 1
1 : 95
2 : 96
3 : 115
4 : 126
The first two lines have to be ignored, but after that the dropped frames are reported correctly.
Now it would be interesting to optimize this script for speed, but it's pretty good already.
erratic
16th November 2003, 13:54
What would be the fastest way to use YDifferenceFromPrevious()?
I mean something like this:
If YDifferenceFromPrevious()<0.1 then dropped=dropped+1
erratic
16th November 2003, 14:35
This is faster. No text on screen (Subtitle) but only in the text file. I've replaced Subtitle with ConvertToYV12 for speed reasons, because I assume ConvertToYV12 doesn't really do anything in this case. If there's a more elegant way of doing nothing let me know. The first two faulty lines in the text file are no longer written now.
global dropped = -1
org = MPEGSource("capture.mpg")
eq = ConvertToYV12(org).FrameEvaluate("dropped=dropped+1").Write("drops.txt","(dropped>-1)",String("dropped+1")," : ","current_frame",linecheck=true)
neq = ConvertToYV12(org)
Conditionalfilter(org, eq, neq, "YDifferenceFromPrevious()", "<", "0.1")
Output to text file:
1 : 95
2 : 96
3 : 115
4 : 126
And I repeat that nothing appears on screen, so it's meant to be run in the background. If you want the number of dropped frames in the picture it can easily be done but it slows the script down.
Edit: By the way, I'm using Nic's MPEGDecoder.dll YV12 Version for AviSynth 2.5 Only (Version 2.03 Alpha). Now I don't have to create a DVD2AVI project first.
mf
16th November 2003, 15:01
Nop() effectively does nothing at all. :D
erratic
16th November 2003, 15:09
Tried Nop() but I get an error message. I need a command that accepts a clip as input. eq=Nop(org) doesn't seem to be allowed.
Avisynth open failure:
Script error: Invalid arguments to function "Nop"
erratic
16th November 2003, 18:41
Hmmm, I just noticed something strange. I captured directly to MPEG-2 with ATI MMC and ran Internet Explorer. That caused 4 dropped frames according to ATI MMC. The Avisynth script only found one so I used VirtualDub to step through the MPEG-2 file frame by frame. The one dropped frame the script had found was indeed an exact copy of the previous one.
I had captured a channel with a stock ticker at the bottom of the screen. Obviously scrolling text looks interlaced. However, where some dropped frames had been reported there was still some motion (so no exact copy of the previous frame) but this time the frame looked non-interlaced. It was as if only one field had been dropped and the one captured field had been resized to full screen. I don't know if that is what the MPEG-2 encoder does or if it is even possible, but obviously the Avisynth script can't detect those frames as dropped.
One thing I know now: the ATI MMC MPEG-2 encoder doesn't always insert an exact copy of the previous frame when a frame is dropped. It sometimes does some other sh*t too.
mf
16th November 2003, 20:50
Try this instead then:
function NullFilter(clip c, string "args") {
args = Default(args, "")
return c
}
You can then use NullFilter() anywhere you like. That should work.
erratic
17th November 2003, 01:37
Yes, a Nullfilter function works.
Sulik
18th November 2003, 05:21
That's a normal behavior, and indicates that the encoder is actually pretty smart about repeating dropped fields. (Also, maybe MMC reported 4 dropped fields, and no 4 dropped frames).
If you're encoding interlaced, let's say you have frame 1 consisting of field A and field B, then frame 2 consisting of field C and field D. Assuming field C and D were dropped:
If the encoder repeats frame 1, you'll get AB.AB, which will produce shakyness (the scrolling text will move backwards for 1 field, since field A is supposed to be displayed before field B).
Instead, it looks like the encoder did the following:
AB.B'B.
where B' is probably B deinterlaced.
erratic
18th November 2003, 13:53
Well, it looked like this: let's use 3 consecutive interlaced frames (6 fields) as an example.
Correct sequence without drops: 1a - 1b - 2a - 2b - 3a - 3b
What I saw looked like this:
1a - 1b - 2a resized to full frame (2b missing) - 3a - 3b
So on a TV the sequence would be: 1a - 1b - 2a - 2a - 3a - 3b
When I wrote that de encoder does some ohter sh*t too I was being too derogatory, because I suppose the previous sequence will look better than: 1a - 1b - 1a (repeated) - 1b (repeated) - 3a - 3b
But there are also instances where the previous frame is simply repeated, interlaced as usual, and I also found a spot where an entire frame seemed missing (the previous frame wasn't repeated) causing a sequence like: 1a - 1b - 3a - 3b
Of course, in normal circumstances I wouldn't run other software while capturing, so my test was quick and dirty.
Anyway, the Avisynth script can only find repeated frames of course, not missing frames or resized fields.
mf
18th November 2003, 16:50
Originally posted by erratic
Anyway, the Avisynth script can only find repeated frames of course, not missing frames or resized fields.
missing fields: SeparateFields() and do the same thing as posted above
resized fields: BicubicResize(last.width, last.height/2) and do Compare() with SeparateFields()
:D
erratic
18th November 2003, 17:19
I tried SeparateFields() but the fields aren't aligned, they are bobbing. The script doesn't find anything. I also tried Bob() but no drops were found either.
Edit: To avoid confusion: I know that this bobbing (http://www.100fps.com/why_bobbing.htm) is normal.
Sulik
18th November 2003, 19:52
What version of MMC are you using ?
erratic
18th November 2003, 20:04
MMC 8.7.
mf
18th November 2003, 23:01
Originally posted by erratic
I tried SeparateFields() but the fields aren't aligned, they are bobbing. The script doesn't find anything. I also tried Bob() but no drops were found either.
Edit: To avoid confusion: I know that this bobbing (http://www.100fps.com/why_bobbing.htm) is normal.
Oh, one thing: 100fps is pretty evil. It has alot of errors which the author doesn't want to change. But that said, it's nice to get a general idea of interlacing.
vBulletin® v3.8.11, Copyright ©2000-2026, vBulletin Solutions Inc.