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

Reply
 
Thread Tools Search this Thread Display Modes
Old 21st June 2010, 18:18   #1  |  Link
Boulder
Pig on the wing
 
Boulder's Avatar
 
Join Date: Mar 2002
Location: Hollola, Finland
Posts: 4,672
Removing film scratches

Is there any better method for removing scratches other than Descratch? The problem with it is that even after tweaking the settings, it often removes stuff that actually belongs to the details (such as a narrow tree trunk etc.)

I was figuring that if there was a way to detect scratches that exist only for one frame, it would be possible to remove them and keep as much details intact as possible. Is there a way to achieve this with Avisynth filtering?
__________________
And if the band you're in starts playing different tunes
I'll see you on the dark side of the Moon...
Boulder is offline   Reply With Quote
Old 21st June 2010, 18:38   #2  |  Link
Didée
Registered User
 
Join Date: Apr 2002
Location: Germany
Posts: 5,394
a) a true detail that has been successfully compensated
b) a true detail that MVTools failed to compensate
c) a scratch that MVTools by accident did successfully compensate
d) a scratch that wasn't compensated

There surely is a way to programatically tell all these cases apart from each other. Problem is that nobody knows that way.

Spectacular results can be achieved by first using a generic temporal median filter, then using your favorite image editing application, mouse, and clone tool on all source frames.
__________________
- We´re at the beginning of the end of mankind´s childhood -

My little flickr gallery. (Yes indeed, I do have hobbies other than digital video!)
Didée is offline   Reply With Quote
Old 21st June 2010, 18:51   #3  |  Link
Boulder
Pig on the wing
 
Boulder's Avatar
 
Join Date: Mar 2002
Location: Hollola, Finland
Posts: 4,672
Truly encouraging Too bad Descratch is spatial only, if it was temporal, I guess at least some improvement would be possible.
__________________
And if the band you're in starts playing different tunes
I'll see you on the dark side of the Moon...
Boulder is offline   Reply With Quote
Old 22nd June 2010, 03:30   #4  |  Link
Mug Funky
interlace this!
 
Mug Funky's Avatar
 
Join Date: Jun 2003
Location: i'm in ur transfers, addin noise
Posts: 4,555
are we talking persistent vertical scratch caused by something pointy touching the film as it moves, or are we talking general crap on the film?

long ago i wrote a script for the former situation, and it could be enhanced with the exinpaint plugin plus motion compensation.
__________________
sucking the life out of your videos since 2004
Mug Funky is offline   Reply With Quote
Old 22nd June 2010, 04:41   #5  |  Link
Boulder
Pig on the wing
 
Boulder's Avatar
 
Join Date: Mar 2002
Location: Hollola, Finland
Posts: 4,672
In some cases it looks like something is touching the film, but in most cases it's just a one frame long hair or scratch. I uploaded a small sample clip (encoded with ffdshow's HuffYUV), I think that part has the worst quality. Fortunately most of my material looks much better. The trees are a good point to see when the scratch removal goes over the edge.

http://www.mediafire.com/download.php?ijdzygjmzyt
__________________
And if the band you're in starts playing different tunes
I'll see you on the dark side of the Moon...
Boulder is offline   Reply With Quote
Old 22nd June 2010, 09:11   #6  |  Link
videoFred
Registered User
 
videoFred's Avatar
 
Join Date: Dec 2004
Location: Gent, Flanders, Belgium, Europe, Earth, Milky Way,Universe
Posts: 663
Is it possible to do a new transfer with a CLEANED film, Boulder?

A wetgate transfer can remove all (film layer) scrathes, too. Emulsion scrathes can not be removed unfortunately.

I also see some blurred frames on your example. It looks like a timing error while transfering. The film frame was not standing completely still while the camera was capturing it. Or the camera setup was not stable enough.

Anyhow, RemoveDirtMC() deals very well with all those artefacts. But you know that of cource. The 'moving' picture on the result is caused by the blurred frames on the original.

Fred.
__________________
About 8mm film:
http://www.super-8.be
Film Transfer Tutorial and example clips:
https://www.youtube.com/watch?v=W4QBsWXKuV8
More Example clips:
http://www.vimeo.com/user678523/videos/sort:newest

Last edited by videoFred; 22nd June 2010 at 09:16.
videoFred is offline   Reply With Quote
Old 22nd June 2010, 10:59   #7  |  Link
Boulder
Pig on the wing
 
Boulder's Avatar
 
Join Date: Mar 2002
Location: Hollola, Finland
Posts: 4,672
Unfortunately a new transfer is not an option in this case, but fortunately most material only has normal film grain The problem with RemoveDirt and its variations that it tends to remove much more than just small defects. Still, maybe I should try tweaking it to avoid those problems.

The original material was captured at 25 fps on a DV camera through the projector gate if I've understood correctly. I then used the following function and script to recover progressive frames at 18 fps. I don't know whether there is a better way to get to the original frames without a film scanner.

Code:
# RestoreSuper8Frames()   ( 9 Mar 2007 )
#
# This function is an eliminator of combed frames for transfer of movies to video.
#
# It chooses those video frames that were best exposed with single images of the movie
# and makes all other frames black.
#
# It needs an input video stream with at least 3 and no more than 4 fields per movie image.
# i.e. the video frame rate must be between 1.5 and 2 of the movie image rate.
#
# With the help of FieldDeinterlace(), the function compares the combing of the previous, current and
# next frame. A black frame is returned if the current frame is not the least combed of hw three.
#
# The function returns irregular sequences of single best-choice frames and one or multiple black frames.
# MultiDecimate() can turn these into alternating black/good/black/good sequences.
# SelectOdd() finally removes these single black frames.
#
# To supply RestoreSuper8Frames() with the neccessary frames, use DoubleWeave().
#
# Principle of operation:
#
# Movie:	|   A  |~|   B   |~|   C  |~|   D	( A...D are the movie images - Projector slow at B,
#                                                        ~ is shutter)
# source vid.:  |a* a*|b  b*|c* c*|d  d*|e* e |		(a..e are the video frames, * shows the uncombed)
#               |1  2 |1  2 |1  2 |1  2 |1  2 | 	(1 and 2 are the the fields of the frames)
#               |  a  |  b  |  c  |  d  |  e  |		(this shows the complete frames before DoubleWeave()
# DoubleWeave:  |aa|ab|bb|bc|cc|cd|dd|de|ee|
#               |12|21|12|21|12|21|12|21|12|
#               |a |a'|b |b'|c |c'|d |d'|e |		(the frames after DoubleWeave())
#                A  -  -  B  -  -  -  C  -              (A-C=good frames after the function, '-'= black frames)
#
# The exact image sequence of the movie will be reconstructed for real life scenes if the camera takes not less
# than three and not quite four fields per image.
# Long static scenes and very noisy fields may produce bad results.
#
## --------
# Save this page as RestoreSuper8Frames.avsi in the plugin directory and embed the function as follows:
#
#    pass=3
#    raw=AVISource("c:\my.avi")
#    AssumeBFF()                 #this depends on your camera
#    ConvertToYV12(interlaced=true)
#    AssumeFrameBased()
#    DoubleWeave()
#    AssumeFrameBased()
#    AssumeBFF()
#    (pass<2) ? RestoreSuper8Frames(last,show=false) : last
#    ConvertToYUY2()
#    mdpass= (pass<2) ? 1 : 2
#    MultiDecimate(pass=mdpass)
#    (pass<2) ? last : SelectOdd()
#    (pass<2) ? AssumeFPS(50) : AssumeFPS(18)   # 50 is valid for PAL, 18 for Super8
#
## --------
#
# How to use this function (with AviSynth 2.5)
#
#    1) Get neuron2's Decomb and MultiDecimate packages.
#    2) set 'pass' above to one.
#    3) As said in the MultiDecimate doc, view or preview the whole AVI from first to last frame.
#       A metrics file 'mfile.txt' will be written. Open it and verify that the first line starts with a zero.
#       If this fails: rename mfile.txt, preview the first some frames again and merge the two metrics files.
#       The generation of the metrics file will take some time. Full PAL resolution viewed with VirtualDub
#       on a 1.8GHZ Athlon 3000 achieves ~ 20fps.
#    4) With MultiDecimate.exe and its default settings, create the decimation file 'dfile.txt'
#    5) Now you are ready to set 'pass' above to 2 and watch your synchronized clip.
#
## --------

function RestoreSuper8Frames(clip "clip",bool "show")
{
	show= default(show,false)
	global ip=0
	global ic=0
	global in=0
	dt=8
	global blank=BlankClip(clip,length=clip.FrameCount(),color=0)
	global cf=clip.GreyScale().Blur(1.58,0)
	global cp=cf.DuplicateFrame(0).FieldDeinterlace(threshold=255,dthreshold=dt,map=true).Blur(1.58).Levels(140,1,255,0,255,coring=false)
	global cc=cf.FieldDeinterlace(threshold=255,dthreshold=dt,map=true).Blur(1.58).Levels(140,1,255,0,255,coring=false)
	global cn=cf.DeleteFrame(0).DuplicateFrame(clip.Framecount()-1).FieldDeinterlace(threshold=255,dthreshold=dt,map=true).Blur(1.58).Levels(140,1,255,0,255,coring=false)
	f1= clip.ScriptClip("clip=(ip<=ic)?blank:last"+chr(13)+"return((in<ic)?blank:clip)")
	f0= f1.FrameEvaluate("ip=AverageLuma(cp)"+chr(13)+"ic=AverageLuma(cc)"+chr(13)+"in=AverageLuma(cn)")
	return((show) ? Overlay(f0,clip,opacity=0.5).Overlay(BilinearResize(cc,clip.Width(),clip.Height()),opacity=0.5,mode="add") : f0)
__________________
And if the band you're in starts playing different tunes
I'll see you on the dark side of the Moon...
Boulder is offline   Reply With Quote
Old 22nd June 2010, 11:08   #8  |  Link
videoFred
Registered User
 
videoFred's Avatar
 
Join Date: Dec 2004
Location: Gent, Flanders, Belgium, Europe, Earth, Milky Way,Universe
Posts: 663
Quote:
Originally Posted by Boulder View Post
I don't know whether there is a better way to get to the original frames without a film scanner.
Moviestuffs Workprinter can do this. The software Cinecap grabs individual progressive frames from the incoming DV stream. Then it saves those frames to a 'growing' AVI file.

A machine vision camera with trigger does the very same: it grabs a frame every time the trigger gets a pulse. The machine vision software then saves the frames as individual images or again in a 'growing' AVI file.

PS: nice function to recover progressive frames from an interlaced source, Boulder!
People are often asking me this.

Fred.
__________________
About 8mm film:
http://www.super-8.be
Film Transfer Tutorial and example clips:
https://www.youtube.com/watch?v=W4QBsWXKuV8
More Example clips:
http://www.vimeo.com/user678523/videos/sort:newest
videoFred is offline   Reply With Quote
Old 22nd June 2010, 12:28   #9  |  Link
Gavino
Avisynth language lover
 
Join Date: Dec 2007
Location: Spain
Posts: 3,385
Quote:
Originally Posted by Boulder View Post
Code:
function RestoreSuper8Frames(clip "clip",bool "show")
Neat idea.
The code shows how messy this sort of thing can be with the standard ScriptClip, etc.
Nowadays it could be written much more simply, using GRunT:
Code:
function RestoreSuper8Frames(clip clip,bool "show")
{
	show= default(show,false)
	blank=BlankClip(clip)
	cf=clip.GreyScale().Blur(1.58,0)
	cc=cf.FieldDeinterlace(threshold=255,dthreshold=dt,map=true).Blur(1.58).Levels(140,1,255,0,255,coring=false)
	f0 = clip.ScriptClip("
	  ip=AverageLuma(cc,-1)  ic=AverageLuma(cc)  in=AverageLuma(cc, 1)
	  return (ip<=ic || in<ic)? blank : last
	", args="cc, blank")
	return((show) ? Overlay(f0,clip,opacity=0.5).Overlay(BilinearResize(cc,clip.Width(),clip.Height()),opacity=0.5,mode="add") : f0)
}
BTW The code you posted is missing the final '}' from the function.
Gavino is offline   Reply With Quote
Old 22nd June 2010, 12:53   #10  |  Link
Boulder
Pig on the wing
 
Boulder's Avatar
 
Join Date: Mar 2002
Location: Hollola, Finland
Posts: 4,672
It's not my idea at all.. I don't know if it's been in some thread in the forum, but I found it in the Avisynth Wiki after some googling. Finding it was pure luck IMO
__________________
And if the band you're in starts playing different tunes
I'll see you on the dark side of the Moon...
Boulder is offline   Reply With Quote
Old 22nd June 2010, 17:02   #11  |  Link
um3k
Registered User
 
Join Date: May 2007
Posts: 220
I'm using the same script with quite good results from a 60i HDV source. However, the multipass thing (for MultiDecimate) is kind of cumbersome when large amounts of footage need processing. If only there was a better way*!

*That didn't cost any additional money!
um3k is offline   Reply With Quote
Old 1st July 2010, 02:42   #12  |  Link
um3k
Registered User
 
Join Date: May 2007
Posts: 220
!

After a bit of thought(?), I came up with a clever(?) little trick that looks like this:

Code:
SetMemoryMax(192)
foot = MPEG2Source("L:\8mmTransfer\1973\SamiWashingtonWinthropBlank73-2010_06_24-10_43_14.m2t.d2v", cpu=0).DoubleWeave()
filt = RestoreSuper8Frames(foot)
save = foot.ImageWriter("L:\8mmTransfer\1973\SWWB\SamiWashingtonWinthropBlank73")
ConditionalFilter(filt, foot, save, "AverageLuma()", "equals", "16")
What it does is analyse the output of RestoreSuper8Frames, and writes the frames that are not black to disc. Voilà, original film frames in one pass. Now to figure out where to go from there.

EDIT: I replaced ImageWriter with TWriteAVI, which allows for compression, as well as a directly usable AVI file. Not entirely sure I'm going in the right direction with this, though. Even with compression, the files are quite large. Still working on it...

REEDIT: Awesome! I think I've got a workable system. Here is the full Avisynth Batch Scripter template I'm using:

Code:
SetMemoryMax(256)
foot = MPEG2Source("%CLIP%.%EXT%", cpu=0).ThreadRequest()
flat = foot.Trim(0, 99).MergeFrames()
dw = foot.DoubleWeave()
rest = RestoreSuper8Frames(dw)
proc = foot.FixChromaEcho().mt_lutxy(flat.Loop(FrameCount(foot)), "x 16 - 255 16 - / 1 1.0 / ^ 255 0 - * 0 + y 16 - 255 16 - / 1 1.0 / ^ 255 0 - * 0 + / 256 * 16 +").DoubleWeave().Crop(0, 0, -0, -0).LanczosResize(1440, 1080).FlipHorizontal().ThreadRequest()
blnk = BlankClip(proc)
save = TWriteAVI(proc, "%CLIP%.avi", overwrite=true, idxname="%CLIP%.idx", fourcc="lags")
ConditionalFilter(rest, blnk, save, "AverageLuma()", "equals", "16")
#~ return proc

function FixChromaEcho (clip c)
{
    c = c.SeparateFields()
    chrstk = StackHorizontal(c.UToY(), c.VToY())
    mathed = mt_lutxy(chrstk, chrstk.DuplicateFrame(0).DuplicateFrame(0), "x y - 0.26 * x 128 - + 128 +")
    merg = MergeChroma(c, YToUV(mathed.Crop(0, 0, mathed.width/2, 0), mathed.Crop(mathed.width/2, 0, 0, 0))).Weave()
    return merg
}
After generating the scripts, I open them in AvsP and change the crop and resize values to fit the source. I'd like to eliminate or simplify that step, as well, but I'm not quite sure how to go about doing so. Then, I do a batch analysis pass in VirtualDub, and I end up with 480p AVIs ready for color correction.

If you're wondering, the additional processing I do includes removing a chroma ghost effect endemic to my camera (Canon HV20), and reducing vignetting (and sometimes dust) from the uneven backlight (via flat frame division, a technique borrowed from astronomical imaging).

By the way, I'm using Gavino's simplified version of the RestoreSuper8Frames script, which seems to run a fair bit faster than the original (though it could be the placebo effect, I haven't tested it properly).

Last edited by um3k; 1st July 2010 at 17:36.
um3k is offline   Reply With Quote
Reply

Thread Tools Search this Thread
Search this Thread:

Advanced Search
Display Modes

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 22:38.


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