Welcome to Doom9's Forum, THE in-place to be for everyone interested in DVD conversion.

Before you start posting please read the forum rules. By posting to this forum you agree to abide by the rules.

 

Go Back   Doom9's Forum > Capturing and Editing Video > Avisynth Usage
Register FAQ Calendar Today's Posts Search

 
 
Thread Tools Search this Thread Display Modes
Prev Previous Post   Next Post Next
Old 8th March 2012, 12:38   #1  |  Link
sven_x
Registered User
 
Join Date: Oct 2011
Location: Germany
Posts: 39
MorphDups - replace duplicate frames by interpolations

USB video grabbers automatically replace disturbed frames (with bad sync) by identical copies of the last intact frame. As a result parts of the grabbed video are filled with one ore more duplicate frames.
Especially grabbings from VHS suffer from this problem.
A grabbing from a bad VHS copy that was recorded on a cheap recorder without a sync track had up to 1500 duplicate frames per hour.

..abc...........opq..stuvwxyz......... Original frames of the movie (good frames)
..a-c...........o-q..s------z......... VHS with drop outs (tape wrinkel)
..aac...........ooq..sssssssz......... Output of USB Video Grabber (duplicates instead of original frames)

Figg 1: Duplicate frames replace corrupted frames

The following script searches for duplicate frames. From the result it generates a second script, that is using Mud Funky's Morph function to replace duplicate frames by interpolated frames. The result is very smooth and looks fine.

Version 0.7a of the script.
PHP Code:
# http://forum.doom9.org/showthread.php?p=1563921 
# Morphdups.avs by Sven_x 2012 version 0.7a 
# search duplicate frames in a clip and generate a function "morphmydups.avs"  
# that interpolates between the border frames 

# Guide: 
# Run a first pass with this script. It searches for duplicate frames. 
# From that it generates a script with a function "morphmydups.avs"  
# that you can use to replace automaticly the duplicate frames by interpolations 

# To do: 
# perhaps YDifferenceFromPrevious(N)  can be replaced by YDifferenceToNext(N-1)  

# Changelog 

# YDifferenceFromPrevious() can ONLY be used within conditional functions, as it changes on every frame 
# Also remember that conditional variables must be assigned bottom up. 
# http://avisynth.org.ru/docs/english/corefilters/conditionalfilter.htm 

# source  and parameters .................................................................................. 
global source1=AVISource("J:\test\test79400.avi"
#DirectshowSource("raw.avi",30,true,false,true) 
#global source1=SegmentedAVISource("segmented.avi") 

last=source1 
ConvertToYV12
()  #  YV12 needed for MVTools and for CCE Basic 

reduce=true #true = crop output to 16x16 px to speed up encoding (150%) if you run the script by encoding 

# variables  ................................................................................. 
dup_thresh=0.0001    #threshold, below threshold is considered as duplicate frame 
sc_change_thresh=5.1   #a difference above this threshold is considered as scene change (excluded from list) 

gap_pos=0               #Gap start 
gap_l=0                   #Gap length 
pregap=0                 #Gap YDifference before gap 
postgap=0               #Gap YDifference after gap 
gap_end=false         # Bolean, triggers writing of gap properties into dup.txt file 
gapmarker=""         #Visual Gap length marker 

#Test function: display parameter results in clip ................................................................................. 

#ScriptClip("""Subtitle("frame_number: "+String(current_frame)+", YdiffP: "+String(ydfp)+", YdiffN: "+String(ydfn)) """\ 
#               +"""Subtitle(x=8, Y=20,text_color=$FF4422,\ 
#           "pos: "+String(gap_pos)+", gap_l: "+String(gap_l)+", pregap: "+String(pregap)\ 
#         +", postgap: "+String(postgap)+", gapmarker: "+LeftStr("===============",gap_l) ) """) 

#Search gaps with duplicate frames and write dups_report.txt + txt-files 
#................................................................................ 
fg1() #find begin of gap 
fg2() #find middle of gap 
fg3() #find end of gap 

colon=" "   #define colon 


WriteFileStart("morphmydups.avs"""" "# Function that replaces dups by interpolated streams of frames" +chr(13) """, \ 
+""" "# generated by MorphDups.avs v0.7, see http://forum.doom9.org/showpost.php?p=1561928&postcount=52" +chr(13)+chr(13)""", \ 
+""" "# needs:" +chr(13)""", \ 
+""" "# - Mud Funky's Morph function, see http://forum.doom9.org/showpost.php?p=1499620&postcount=2" +chr(13)""", \ 
+""" "# - MvTools2" +chr(13)+chr(13) """,\ 
+""" "# Example: Morph (19,22) replaces frame 20 and 21 by an interpolation between 19 and 22" +chr(13)+chr(13) """,\ 
+""" "# sc_change_thresh=" ""","sc_change_thresh",""" "   # morph statements are disabled when YDifferenceToNext is greater" +chr(13)+chr(13) """,\ 
+""" "function morphmydups (clip c) {" +chr(13) """,\
+
""" "   c     #load clip"  """) 

WriteFileIf("morphmydups.avs""gap_end==true && postgap<sc_change_thresh"""" "   morph("  """,  "gap_pos-1"""" "," """ ,"gap_pos+gap_l" ,\ 
+
""" ")           # YDiffNext=" ""","postgap" ,""" ", length = "  """ , "gap_l", """LeftStr("===========",gap_l)""") 

# when postgap > sc_change_thresh morph statements are disabled by a leading "'" 
WriteFileIf("morphmydups.avs""gap_end==true && postgap>=sc_change_thresh"""" " # morph("  """,  "gap_pos-1", """ "," """ ,"gap_pos+gap_l" ,\ 
+""" ")           # YDiffNext=" ""","postgap" ,""" ", length = "  """ , "gap_l", """LeftStr("===========",gap_l)""") 

WriteFileEnd ("morphmydups.avs"""" chr(13) +"# End of function" +chr(10)""" ) 


# functions to locate gap .............................................................. 
# only one boolean expression per FrameEvaluate 
# begin of gap: gap_pos=current_frame, gap_l=1 
# YDifferenceFromPrevious() > dup_thresh && YDifferenceToNext() < dup_thresh ?  
function fg1(clip c) { 
c=FrameEvaluate(c,"gap_pos= ydfp > dup_thresh && ydfn < dup_thresh ? current_frame+1 : gap_pos" 
c=FrameEvaluate(c,"gap_l= ydfp > dup_thresh && ydfn < dup_thresh ? 0 : gap_l" 
c=FrameEvaluate(c,"pregap= ydfp > dup_thresh && ydfn < dup_thresh ? ydfp : pregap" 
return(
c


#middle gap: gap_l=gap_l+1 
#YDifferenceFromPrevious() < dup_thresh && YDifferenceToNext() < dup_thresh ?  
function fg2(clip c) { 
c=FrameEvaluate(c,"gap_l= ydfp < dup_thresh && ydfn < dup_thresh ? gap_l+1 : gap_l" 
return(
c


#end gap: gap_l=gap_l+1 , gap_end=true, postgap=yDifferenceToNext, call writefileif 
#YDifferenceFromPrevious() < dup_thresh && YDifferenceToNext() > dup_thresh ?  
#store ydiffp and ydiffn in variables 
function fg3(clip c) { 
c=FrameEvaluate(c,"gap_end= ydfp < dup_thresh && ydfn > dup_thresh && current_frame > 0 ? true : false" 
c=FrameEvaluate(c,"gap_l= ydfp < dup_thresh && ydfn > dup_thresh ? gap_l+1 : gap_l" 
c=FrameEvaluate(c,"ydfp=YDifferenceFromPrevious(source1)"
                            +
"ydfn=YDifferenceToNext(source1)" 
                            +
"postgap= ydfp < dup_thresh && ydfn > dup_thresh ? ydfn : postgap" 
return(
c
}  

#Cropping to increase encoding speed ...................................................................... 
#cropping increases speed up to 400% for first pass if you run the script by encoding 
reduce last.crop(001616) : last 
The generated output of the script, named morphmydups.avs, looks so:
Code:
function morphmydups (clip c) {
   c
 # morph(56,67)           # YDiffNext=10.803629, length = 10==========
   morph(103,105)           # YDiffNext=4.838520, length = 1=
   morph(106,108)           # YDiffNext=4.491889, length = 1=
   morph(109,111)           # YDiffNext=4.452096, length = 1=

} # End of function
Please note, that this script provides you with the possibility of interactive correction. You get a list that you can edit. Before rendering the whole video, you can open the script in AvsP and test only that gaps that look critical. Either because they are marked as very long gaps (========) , or because YDifference across the gap is to high. In the latter case the script automaticly disables those morph statements that have a YDifference above a limit. You have the opportunity to delete the "#'" characters, thus re-enabling interpolation across that gap.

And this is how a calling script may look:
PHP Code:
#0. Imports, Loadplugin ..................................................................................
import ("J:\plugins\Morph1b.avs"# overlays info text on interpolated frames
#import ("J:\plugins\Morph.avs") 
loadplugin ("J:\plugins\mvtools2.dll")
import ("morphmydups.avs")

stack=true #true= debug view: small version of source and interpolated are stacked 

#1. Open file ..................................................................................
AVIFileSource("J:\test\test79400.avi")
source=last

#morph function ..................................................................................
morphmydups(source)

#Debug view  ..................................................................................
stack stackvertical(lastsource.subtitle("Input (unprocessed)") ).BilinearResize(width(source)/2height(source))  : last 
Changelog:
Version 0.7a implemented correction from Gavino, please see posting below

German explanation/deutsche Erklärung hier.

Last edited by sven_x; 9th March 2012 at 11:26.
sven_x is offline   Reply With Quote
 


Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT +1. The time now is 11:00.


Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2024, vBulletin Solutions Inc.