View Full Version : Another frame duplication function
Corran
17th February 2008, 14:28
I posted about this at animemusicvdieos.org but the only person I really know that frequents that place from doom9 is Zarxrax... I'm not sure how useful it really is to amv editors, but perhaps someone here may find better a use for it.
This function acts much like Donald Graft's dup() in that it attempts to find frames that are exactly the same as the last frame that changed, a "new frame" so to speak, and then overwrite them with that "new frame".
In other words it works like this:
(Assume captial letters represent frames with actual changes)
AbcdeFGhiJkLmnop
This filter will produce:
AAAAAFGGGJJLLLLL
When the adjusted video is played back it should look exactly the same as the original. This works because most anime does not have new movement in every single frame.
What this ultimately means is that when the filtered footage is sent to the encoder, it is much easier to compress because a lot of the frames are mathematically identical. The noise that occurs between each frame is now limited to the frames that actually change.
Why not just use dup()? Well, firstly I wanted to give myself a challenge and only learned about his plugin until after I had started work. However, I found that his did not accurately identify very small changes in the video. If I adjusted the threshold to identify very slow pans, zooms, or other motion, it would start picking up on the noise in my source. (Honestly I thought this after only a minimal amount of testing, so there may be a better way to use it than I did. In other words, I'm not trying to say that the filter is no good or that it sucks... <3 DG >_>) Therefore my goal with this was to create a function that required little effort to configure assuming a relatively clean source, and the ability to detect small changes accurately.
So here are the details that probably matter to anyone reading this...
How can I use this practically?
The biggest benefit will probably be realized if you are the kind of person that uses avisynth to create lossless filtered AVI files and then use those, not the scripts themselves. The Lagarith codec has a nifty option called null frames.
[When null frames is enabled,] if the previous frame is mathematically identical to the current, the current frame is discarded and the decoder will simply use the previous frame again.
I took The Girl Who Leapt Through Time at 640x368, cleaned it up, and encoded it with lagarith twice using the null frames option. The first time was without dupped() and the second time was with.
The first encode was 10.9GB
The second one was 6.3GB. For a 98 minute lossless encode...
It may also prove useful for fansubbers that use softsubs I guess...
To use this function:
Right Click, Save as, and place in your avisynth plugins directory
dupped.avsi (http://www.randomdestination.com/members/corran/misc/dupped/dupped.avsi)
Currently there are only three arguments.
Thresh=20 will determine that a new frame exists when YMinMax(a,b) is >= 20. The value of YMinMax(a,b) can be found by setting stats=true. I've currently set the default for this parameter to 16. I've only used this on one clean source so it probably needs a little tweaking.
panthresh=1.65 is used in calculating the dynamic threshold of low motion scenes. The first two YMinMax values shown in the stats are averaged and then multiplied by panthresh. If the third YMinMax is equal to or greater than this calculation then the current frame is considered a new frame. The dynamic threshold is now displayed in the stats to the right of YMinMax(last,next).
stats=True will display the stats used to determine if there was a change in the frame.
Showme=True will show the video that the stats are created from.
I suggest using this function at the end of your script and only after you have cleaned up your source. Also, the video needs to be in a YUV colorspace. In otherwords, if get an error saying the "Image must be planar" then you need to put converttoyv12() right before the dupped() function.
Here is an example of such usage if needed.
AVISource("Videofile.avi")
converttoyv12()
dupped()
Also, don't use SetMTMode() if you are using a multi-threaded version of avisynth. (If you don't know if you are, then you aren't running it.) Instead, perform multi-threading on a per-filter basis using MT() like this:
AVISource("Videofile.avi")
MT("ttempsmooth()",4)
fft3dgpu()
dupped()
Also, it may be obvious... but make sure stats and showme are not enabled during your actual encodes otherwise the frames won't be identical where they should be. >_>
Here are some screenshots of the stats and showme parameters in use.
dupped(stats=true)
http://www.randomdestination.com/members/corran/misc/dupped/dupped_pan_with_debugging.jpg
dupped(stats=true,showme=true)
http://www.randomdestination.com/members/corran/misc/dupped/dupped_with_debugging.jpg
# Dupped() by Corran. Feel free to modify as you see fit.
# Attribution not required.
#
# This filter requires a YUV source. Use converttoyv12 if needed
# before calling dupped()
#
## Parameters
# thresh = Used to determine when to delare a frame as new
# Use stats=true to help determine the best value for your source
# Lower number = more sensitive. (Default=16)
#
# panthresh=1.7 This is used to determine when to consider a frame
# part of a low motion scene. (Default=1.65)
#
# stats = Enable/disable display of stats. (Default=false | Not shown)
# (Last = last frame, this = this frame, next = next frame)
#
# showme = Enable/disable display of video that most stats are
# derived from. (Default=false | Not shown)
#
## Notes
# Do not use with SetMTMode(). This function requires the frames to be
# accessed sequentially. Instead, use MT() to mulit-thread on a per-filter basis.
#
# Example:
# dupped(thresh=20,panthresh=1.7,stats=true,showme=true)
#Function to return the requested frame
function getFrame(clip c, int frameNum)
{
default(c, last)
finalframe = c.frameCount-1
Assert(frameNum >= 0, "GetFrame(): FrameNumber supplied is less than 0.")
Assert(frameNum < finalframe, "GetFrame(): FrameNumber supplied is larger than video's framecount.")
c = trim(frameNum,frameNum)
return c
}
function dupped(clip c, int "thresh", float "panThresh", bool "stats", bool "showme")
{
default(c, last)
global dupThresh = default(thresh, 16)
global panThresh = default(panThresh, 1.65)
global dupStats = default(stats, false)
showme = default(showme, false)
a = c
c = scriptclip(last,"""
c = last
finalframe = c.framecount-1
prev_frame = current_frame != 0 ? current_frame-1 : 0
next_frame = current_frame != finalframe ? current_frame+1 : finalframe
#Get Y related stats as needed
prevCurrentYMinMax = subtract(getframe(prev_frame),c).YPlaneMinMaxDifference
currentNextYMinMax = prevCurrentYMinMax < dupThresh ? subtract(c,getframe(next_frame)).YPlaneMinMaxDifference : 256
prevNextYMinMax = currentNextYMinMax < dupThresh ? subtract(getframe(prev_frame),getframe(next_frame)).YPlaneMinMaxDifference : 0
#calculate dynamic pan threshold. Set to 256 if looking for a pan is pointless
neighborAvgMinMax = (prevCurrentYMinMax + currentNextYMinMax) / 2
dynPanThresh = prevNextYMinMax != 0 ? Abs(neighborAvgMinMax * panthresh) : 256
#determine if the current frame is new
newframe = prevCurrentYMinMax >= dupThresh || prevNextYMinMax >= dynPanThresh ? true : false
lastNewFrame = newframe ? current_frame : lastNewFrame
c = getFrame(lastNewFrame)
c = dupStats ? c.subtitle("prevframe: "+string(prev_frame)+" currentframe: "+string(current_frame)+" nextframe: "+string(next_frame)) : c
c = dupStats ? c.subtitle("Last new frame: "+string(lastNewFrame)+" Newframe: "+string(newframe),y=15) : c
c = dupStats ? c.subtitle("prevCurrentYMinMax: "+string(prevCurrentYMinMax)+" (Threshold:"+string(dupThresh)+")",y=45) : c
c = dupStats && currentNextYMinMax != 256 ? c.subtitle("currentNextYMinMax: "+string(currentNextYMinMax),y=60) : c
c = dupStats && prevNextYMinMax >= dynPanThresh ? c.subtitle("prevNextYMinMax: "+string(prevNextYMinMax),y=90) : c
c = dupStats && prevNextYMinMax >= dynPanThresh ? c.subtitle("Pan Threshold: "+string(dynPanThresh)+" ("+string(neighborAvgMinMax)+" * "+string(panThresh)+")",y=105) : c
c = dupStats && prevNextYMinMax >= dynPanThresh ? c.subtitle("Slow pan detected",y=120) : c
return c
""")
c = showme ? stackvertical(c,subtract(a.duplicateframe(0),a)) : c
return c
}
stickboy
18th February 2008, 12:16
I didn't look at your script too carefully, but some comments:
Nested functions are misleading. AviSynth scoping is pretty basic, and all functions are in the global namespace.
Beware of using Trim when its arguments are variable. (http://forum.doom9.org/showthread.php?s=&threadid=58358)
Expressions like:
booleanExpression ? true : falseare unnecessarily complex. Just use booleanExpression by itself.
Corran
18th February 2008, 22:25
I didn't look at your script too carefully, but some comments:
Nested functions are misleading. AviSynth scoping is pretty basic, and all functions are in the global namespace.
Beware of using Trim when its arguments are variable. (http://forum.doom9.org/showthread.php?s=&threadid=58358)
Expressions like:
booleanExpression ? true : falseare unnecessarily complex. Just use booleanExpression by itself.
I am aware of the issues with trim (in fact it was you who last made me aware of it (http://forum.doom9.org/showthread.php?p=580528#post580528) a few years ago), but as far as I've noticed in testing, the videos have not been adversely affected by the way I've used it. As for the boolean, is it really not fast to do it this way than repeating the mathematical expression in multiple conditions? I did it mostly to cut down on the number of times calculations had to be repeated. Otherwise, the script would become much more complex to read and calcuations that I needed to reference multiple times would have to be computed several times before the script was fully completed.
Adub
18th February 2008, 22:48
Have you considered making it into a C plugin instead?
Kumo
19th February 2008, 00:14
i only get this message on the video preview:"i don't know what newframe means([scriptclip], line 16)"
is anything wrong in the script?
stickboy
19th February 2008, 07:59
but as far as I've noticed in testing, the videos have not been adversely affected by the way I've used it.Only because it happens that the videos you've tested it with haven't required replacing frames near the beginning and end, but can you guarantee that you'll never need to replace those?
Maybe you can make that guarantee. I haven't looked at your script carefully enough to tell. Personally I'd rather use my safer versions of Trim() and not have to bother thinking about that stuff.
But if you can make that guarantee, you should enforce it with Assert(). A hard failure at the beginning is much better than allowing people to waste hours encoding videos that they later realize don't contain the correct frames.
As for the boolean, is it really not fast to do it this way than repeating the mathematical expression in multiple conditions?
I did it mostly to cut down on the number of times calculations had to be repeated. Otherwise, the script would become much more complex to read and calcuations that I needed to reference multiple times would have to be computed several times before the script was fully completed.That's not what I'm talking about at all.
If you want to save a boolean expression into a variable, that's perfectly fine. The "? true : false" part is completely useless.
foo = booleanExpression
foo = booleanExpression ? true : false
foo = (booleanExpression ? true : false) ? true : false
foo = ((booleanExpression ? true : false) ? true : false) ? true : falseAre all equivalent.
Corran
19th February 2008, 10:45
Have you considered making it into a C plugin instead?
If I knew how I would have definitely gone that route... Unfortunately, my only real programming experience is with using php. I'm self-taught and still rely heavily on the php.net references so I don't think jumping straight into C plugin development would be very fruitful...
i only get this message on the video preview:"i don't know what newframe means([scriptclip], line 16)"
is anything wrong in the script?
Could you please post your script? It sounds like your script is not feeding the video to dupped sequentially. I've noticed this can happen if you use SetMTMode. If this is the case, please see the original post for details on this..
Maybe you can make that guarantee. I haven't looked at your script carefully enough to tell. Personally I'd rather use my safer versions of Trim() and not have to bother thinking about that stuff.
I just made a couple changes to my function that will prevent the replacement of the first and last frames. Previously, I had been doing a check for the first frame to make sure the newframe variable was set to something, but I hadn't actually told it to not replace the frame... ...in fact every frame was previously getting replaced whether it needed to or not which was a rather large waste of cpu time. -_-;; Needless to say, the function runs much faster now...
If you want to save a boolean expression into a variable, that's perfectly fine. The "? true : false" part is completely useless.
foo = booleanExpression
foo = booleanExpression ? true : false
foo = (booleanExpression ? true : false) ? true : false
foo = ((booleanExpression ? true : false) ? true : false) ? true : falseAre all equivalent.
Ah, I understand now... thanks for explaining. : )
Kumo
19th February 2008, 16:36
here's my scriptDGDecode_mpeg2source("D:\dario\VTS_01rc2.d2v",info=3)
ColorMatrix(hints=true)
source = last
backward_vec3 = source.MVAnalyse(isb = true, delta = 3, pel = 2, overlap=4, sharp=1, idx = 1)
backward_vec2 = source.MVAnalyse(isb = true, delta = 2, pel = 2, overlap=4, sharp=1, idx = 1)
backward_vec1 = source.MVAnalyse(isb = true, delta = 1, pel = 2, overlap=4, sharp=1, idx = 1)
forward_vec1 = source.MVAnalyse(isb = false, delta = 1, pel = 2, overlap=4, sharp=1, idx = 1)
forward_vec2 = source.MVAnalyse(isb = false, delta = 2, pel = 2, overlap=4, sharp=1, idx = 1)
forward_vec3 = source.MVAnalyse(isb = false, delta = 3, pel = 2, overlap=4, sharp=1, idx = 1)
source.MVDegrain3(backward_vec1,forward_vec1,backward_vec2,forward_vec2,backward_vec3,forward_vec3,thSAD=400,idx=1)
dfttest(sigma= 0.8)
crop(4,4,-4,-0)
LimitedSharpenFaster(ss_x=1.1, ss_y=1.1, smode=4, strength=200,undershoot= 10,dest_x=720,dest_y=480)
fastlinedarkenmod()
ColorYUV(levels="tv->pc")
there's a way to let dupped run on it?i'd like to test it with my encoding
Corran
20th February 2008, 10:59
Well... everything seems to work fine for me. Your script is extremely slow but I'm not getting any errors about newframe not being defined when I tag dupped() to the end of it.
At this point, I'd try moving out any plugins and avsi scripts you aren't using and see if there is something auto-loading that is causing your problem.
Caroliano
26th February 2008, 21:42
I get the following error:
I don't know what "newframe" means
([ScriptClip], line 16
Printed on top of the top frame. What is it? This script depends on any non-core avisynth filter?
And what are the main diferences between yours motion-detection algorithm and dup's one? It uses more than two frames to make decisions? I also don't see the small blocks seen in dup subdivision of the frame (I use blksize=8 for clean sources). If you don't subdivide like this, how you detect small moviments?
Corran
26th February 2008, 23:54
I get the following error:
I don't know what "newframe" means
([ScriptClip], line 16
Printed on top of the top frame. What is it? This script depends on any non-core avisynth filter?
All filters used are internal to avisynth. The only time I get that error is when the frames are pulled to the function out of sequence. This most often happens as a result of using SetMTMode() on TSP's multi-thread avisynth (http://avisynth.org/mediawiki/index.php?title=MT_support_page#Differences_between_MT.28.29_and_SetMTMode.28.29). I.e. One thread does the even frames while the second does the odd frames. If you don't have that in your script, it may be possible that there is an avsi file in your plugin directory that does. This isn't to say you can't run filters multi-threaded, you have to do it per filter.
AVISource("Videofile.avi")
MT("ttempsmooth()",4)
fft3dgpu()
dupped()
And what are the main diferences between yours motion-detection algorithm and dup's one? It uses more than two frames to make decisions?
Yes. If needed it will check more than two frames. Beyond that, I'm not sure what the differences are. I'm not aware of how dup works internally.
I also don't see the small blocks seen in dup subdivision of the frame (I use blksize=8 for clean sources). If you don't subdivide like this, how you detect small moviments?
First, I'm taking a subtract() of the current and previous frame. If the frames were mathematically identical, such a subtract would result in a pure gray frame with a luma of about 128. When a change occurs, the luma of this subtract will also. Some areas will darken while others will brighten. With YPlaneMinMaxDifference() I can calculate the difference between the lowest and highest values of the luma. If this value passes a certain threshold, the frame is considered new. If not, then similar checks will be performed on the current and next frame as well as the previous and next frame as needed. This use of subtract() essentially makes small changes stand out like night and day. Tiny lip flap on a tv in the background is enough to make a huge difference in the MinMax values of the subtract's luma, whereas mosquito noise is still too subtle to be a major influence.
Ranguvar
27th February 2008, 00:03
Hey,
Would you please consider adding a parameter, that, when enabled, would make the filter create images (PNG, BMP, GIF, JPG, etc.) of any frames that were classified as duplicates but were close to the threshold by a user-inputed amount, and the file they were classified as duplicates of, with filenames that were recognizable as duplicates of a certain other frame? And then possibly, another parameter that would load a *.txt and exclude any frames listed in there from being duplicates?
Big request, but if you get bored :D
Thanks very much! Gonna have some fun with this one!
neuron2
27th February 2008, 00:51
It's not a windowed algorithm so it will not be as discriminating to signal versus noise as is Dup().
neuron2
27th February 2008, 00:53
It's not a windowed algorithm so it will not be as discriminating to signal versus noise as is Dup(). That tends to defeat its purpose.
Caroliano
27th February 2008, 03:21
I removed everything from avisynth plugins folder to C:/ leaving there only the DirectShowSource.dll and the dupped.avsi. Then I used the folowing script:
directshowsource("testfile.wmv", fps=23.976, convertfps=true)
Dupped()
This still give the same error on top of the frame. I'm runing an MT enabled avisynth, but I'm not using those capabilities ATM. I tried also using an Avisource("test.avi") but no joy.
And yours change detection algorithm is realy clever. I was afraid of an frame-wide averaging because there was no subdivision in the picture. I think that dup makes an simple (or not so simple) averaging in each block of the picture, and if any of them is higher than the threshold it considers the frame diferent. At least, that is what I deducted from dup's show=true.
foxyshadis
27th February 2008, 08:32
You should be able to fix that by adding a global newframe = a in the function definition (but not the scriptclip) somewhere. Definitely don't use MT mode, conditionals either don't work at all with it or burn tons of extra cpu time.
Caroliano
27th February 2008, 15:31
Yeah, that fixed here. Thanks.
Now to feature requests:
- Add the option to show the "showme screen" in the right of the picture. Useful especialy for editing non-widescreen content in widescreen monitors, but also fits better in AvsP in most cases.
- Add the blend=true option from dup() for help in denoising.
- Your funcion seems to not process chroma. Adding it would make the detection more precise, isn't? I think it would be especialy useful for detecting moviment in CG. Toki wo Kakeru Shoujo don't have explicit CG, but many others animes do. Or it don't matter and only slows down?
I'm now comparing dup and dupped with the folowing method:
dup1 = last.Dup(threshold=0.85, blksize=8, copy=false, show=true) # visual analize mode
dup2 = last.dupped(thresh=28,panthresh=1.7,stats=true,showme=false)
merge(dup1,dup2)
It is a bit anoying that dup compares (this,next) and dupped (last, this), but it's manageable. I preffer the dup one, because I know when to look harder for diferences before-hand.
Latter I will do an test encode with lagarith. In moviments with lines, both are good enought, with your script reporting the highest diferences when in moviment. The problem is with slow clouds moviment or pans, zooms and fading...
Zarxrax
27th February 2008, 18:08
neuron2: regarding signal to noise, since anime these days is produced digitally, there doesn't seem to be much problem with noise in many sources. On a nice clean source, your dup filter failed pretty badly on simple things like slow pans, detecting many duplicates that were not really duplicates (I should note that this was in my usage, which may or may not have been performed correctly; I don't mean to say bad things about your filter). However, I'm sure your filter would perform much better on sources with more noise in them, as it seems that this was one of your primary goals in designing your filter.
However it seems that this function of Corran's, although simple in it's approach, may be more useful than dup in some situations. Perhaps though, you could add functionality like this into your dup filter, as an extra check to avoid marking frames incorrectly as duplicates?
Personally, I am very wary of using either of these filters on my encodes, as I don't particularly trust it to make the best decision when it comes to something as major as completely dropping frames from the source. But I think a combination of these 2 filters would provide a very good safeguard against false positives, and would be something that I feel I could rely on. What do you think?
Also, I had an idea of using a strong denoiser as a preprocessor for the duplicate detection. Couldn't something like this improve the accuracy of detecting duplicates?
Ranguvar
27th February 2008, 22:18
I'm tending to agree with Zarxrax here. My intended use for a function like this would be to increase compressibility with x264, so I would put it near the end of a script, certainly after denoising.
Perhaps this would help, too:
[All filtering, including normal denoising]
beforeSDenoise = last
foo = somedenoiserhere(parametersstrongerthanusualusage)
return beforeSDenoise.dupped(foo, ......)
After all, I think it'd be extremely rare the case when a denoiser actually removed enough small details to create erroneous duplicate frames when it is dupped().
thetoof
29th May 2008, 06:29
Is this thread dead? IMO a "motion-adaptative" dup function would be very nice... especially with a denoised dclip. A hard threshold like dup() has is simply destructive for low motion scenes, so is there any further development considered?
McCauley
9th February 2009, 18:11
Added to the wiki (http://avisynth.org/mediawiki/External_filters#Duplicate_Frame_Detectors).
Regards
McCauley
MadRat
9th February 2009, 21:07
Thanks McCauley
lansing
11th June 2009, 21:48
I wonder if a flicker detector could be added to this, since older anime still flicker quite a bit even though they were stabilized. It will surely help save A LOT of time of some hardcore anime fans that I know who spends hours and hours replacing duplicated frame manually.
Corran
13th June 2009, 04:42
I wonder if a flicker detector could be added to this, since older anime still flicker quite a bit even though they were stabilized. It will surely help save A LOT of time of some hardcore anime fans that I know who spends hours and hours replacing duplicated frame manually.
Is this thread dead? IMO a "motion-adaptative" dup function would be very nice... especially with a denoised dclip. A hard threshold like dup() has is simply destructive for low motion scenes, so is there any further development considered?
I apologize for not working on this in the year and a half since I posted it. I don't have any immediate plans to continue work on it in the future. AVISynth's scripting language seems rather restrictive for something like this and it might be better off as a plugin. Additionally, my goal in all of this was to test out an idea I had using the internal subtract filter to detect motion. To that end I accomplished my goal. If any one wants, they can take what I've posted and tweak/re-write/remove attribution/etc as desired. Consider it public domain if it hasn't been considered such already. If I need to specify this in the script itself, let me know how I should do so.
@Kumo, If you see this post, I think I finally figured out why you were having problems. (maybe) I was recently trying out AvsP with this script and no mater where I would seek, I would get the newframe error message. When I opened the script in an external player (i.e. virtualdub) the problem did not occur. Not sure how frame access differs from AvsP and other tools. Back when I posted this I did all of my testing in VirtualDubMod.
Edit: Posted cleaner version of function. Should hopefully be easier to understand if anyone wants to tackle the requests in this thread later down the road.
andy_blah
1st June 2010, 21:21
I don't know why but no matter on what kind of a clip I get this error at the very first duplicated frame:
I don't know what "last NewFrame" means
([Script Clip], line 19)
I've recently got this problem after I've reinstalled Windows and had to place everything back where it was (including Avisynth and it's plugins)
Is there any solution for that message to be removed? It isn't that visible but it's quite annoying if there are lots of duplicate frames at the beginning of the clip.
Thank you in advance~
EDIT: Sorry, forgot to post in the script file
LoadPlugin("D:\untitled folder\DGIndex 1-5-8\DGDecode.dll")
sectionAvid = MPEG2source("sectionA.d2v").Crop(6, 0, -8, 0).Lanczos4resize(640, 480).Levels(0, 1.3, 255, 0, 255).ChangeFPS(29.970).Trim(196,0)
sectionAimg = ImageSource("sectionA.png", fps = 29.970).ConvertToYV12().Trim(0, 196)
secAvid = sectionAimg + sectionAvid
secAaud = NicAC3Source("sectionA.ac3")
secA = AudioDub(secAvid, secAaud).Trim(15,0)
return secA
Gavino
2nd June 2010, 00:01
Sorry, forgot to post in the script file
I don't see any call to 'dupped' in that script.
andy_blah
2nd June 2010, 00:05
Mistake, forgot that I've removed the .Dupped() part from the code to be able to encode that avs script but the script is still the same.
Gavino
2nd June 2010, 00:12
But where is/was it called?
This is vital information to diagnose the problem!
andy_blah
2nd June 2010, 00:14
LoadPlugin("D:\untitled folder\DGIndex 1-5-8\DGDecode.dll")
sectionAvid = MPEG2source("sectionA.d2v").Crop(6, 0, -8, 0).Lanczos4resize(640, 480).Levels(0, 1.3, 255, 0, 255).Tweak(cont = 1, sat=1.25).ChangeFPS(29.970).Trim(196,0)
sectionAimg = ImageSource("sectionA.png", fps = 29.970).ConvertToYV12().Trim(0, 196)
secAvid = sectionAimg + sectionAvid
secAaud = NicAC3Source("sectionA.ac3")
secA = AudioDub(secAvid, secAaud).Trim(15,0).Dupped()
return secA
Gavino
2nd June 2010, 01:43
Looking at the code of the 'dupped' function, I can see that the "I don't know what 'lastNewFrame' means" error is due to a bug which will show up when the first two frames of the clip are the same (as in andy_blah's case where the clip starts with an image sequence).
The line
newframe = prevCurrentYMinMax >= dupThresh || prevNextYMinMax >= dynPanThresh ? true : false
should be
newframe = prevCurrentYMinMax >= dupThresh || prevNextYMinMax >= dynPanThresh || current_frame == 0
There is also a separate bug in the function getFrame which can cause wrong results when frameNum=0
c = trim(frameNum,frameNum)
should be
c = trim(frameNum, -1)
Obviously, the author did not fully apply stickboy's advice in post #2. :(
Edit:
@andy_blah
A workaround for your script would be to move the application of 'dupped' to apply only to the non-static part of your output, ie use
secAvid = sectionAimg + sectionAvid.dupped()
You lose nothing by doing this since applying dupped to a static clip leaves it unchanged anyway.
AlfOng
2nd June 2010, 18:06
This is my first post and I thought that I would give something back for all the help I've gleaned from the site.
I post this "bad frame" fix that I have been using.
It takes one good frame on each side of the bad frames range and then does a "tweening effect" and replaces the bad frame range.
It uses frame numbers starting from zero. So if the clip is trimmed beforehand then 'startf' would equal the trim start frame number.
It works extremely well for a handfull of frames, but not too well if there is an object with high motion between the 2 good frames.
(I find delta=1 gives a sharper picture)
Use and modify as you like. I hope it helps someone.
function FrameMorph2(clip c, int startf, int in, int "out", int "blksize")
{
Function fill_loop(string s, int stop, int count)
{
return(stop==0) ? s :
string("tofill.mflowinter(super,fill_vb,fill_vf,time="+string(100*(count-stop+1)/float(count+1)))+").selectevery(numfr,0
),"+fill_loop(s,stop-1,count)
}
out =default(out, 0)
blksize =default(blksize,16)
tofill =c.trim(in-startf,out-startf)
numfr =tofill.framecount
dx =c.trim(in-startf-1,-1)++c.trim(out-startf+1,-1)
super =dx.MSuper(pel=2)
fill_vf =MAnalyse(super,search=3,isb=false,delta=1)
fill_vb =MAnalyse(super,search=3,isb=true,delta=1)
filled =eval("interleave("+fill_loop("",numfr,numfr)+"tofill.selectevery(numfr,numfr)).assumefps(c.framerate())")
return c.trim(0,in-startf-1)++filled.trim(0,numfr-1)++c.trim(out-startf+1,0)
}
Edit: There are some obvious line split errors in the fill-loop function which should not be there! I don't seem to be able to edit this post to fix it?
Gavino
2nd June 2010, 19:49
There are some obvious line split errors in the fill-loop function which should not be there! I don't seem to be able to edit this post to fix it?
Put code between code and /code tags to avoid automatic line break. If you are logged in, you should get the option to edit any of your posts.
I'm having trouble getting my head around your fill_loop function. Can you explain what it does please?
Also what do 'startf', 'in' and 'out' mean from the user's point of view?
Some usage examples would be helpful.
Finally, shouldn't this really be a new thread? What is the connection with 'dupped'?
AlfOng
2nd June 2010, 21:31
Put code between code and /code tags to avoid automatic line break. If you are logged in, you should get the option to edit any of your posts.
I'm having trouble getting my head around your fill_loop function. Can you explain what it does please?
Also what do 'startf', 'in' and 'out' mean from the user's point of view?
Some usage examples would be helpful.
Finally, shouldn't this really be a new thread? What is the connection with 'dupped'?
I'm no expert on coding.. but let me try and explain.
The code is taken from ideas posted in the forum and modified to fit my specific needs. I started out looking for "bad frame fixes" for several home videos I want to try and repair.
If it is posted here in the wrong part of the forum, then sorry.
The function takes "in" and "out" frame numbers, ie in=first bad frame, out=last bad frame. (They can be the same). Frame numbers are based on untrimmed total video length.
(So for example if you trim a scene, then startf would be the scene 'in' frame number)
The fill_loop simply generates new frames to replace the old frames eg 110to113
eg
Trim out a scene frames 100 to 500
Bad frames are 110-113 inclusive, based on original untrimmed video.
(Note you can also use startf=0 and bad frame numbers relative to the trim point, it really depends on how you want to identify the bad frame numbers)
sourcevideo
....
startf=100
trim(startf,500)
FrameMorph2(startf, 110,113)
...
Hope this makes sense and helps to clarify.
Gavino
2nd June 2010, 22:15
Thanks for the clarification, I see now how these parameters are meant to be used. Clearly it is a necessary precondition that in>=startf+1 and out-startf<c.Framecount-1 (otherwise you have no good frames to use). Perhaps your function should check for this and report an error if it is not satisfied (using Assert()).
Unfortunately, there is at least one bug in this line:
return c.trim(0,in-startf-1)++filled.trim(0,numfr-1)++c.trim(out-startf+1,0)
The first trim will not work if in equals startf+1 - use Trim(0, -(in-startf)) instead.
The second will not work if numfr equals 1, ie in=out (unless filled has only a single frame in this case - I can't easily see if this is so).
Also, declaring one function (fill_loop) inside another is permitted by a quirk of the parser, but it's not recommended since all functions are global in Avisynth - it is not actually in a separate scope and can confuse the reader.
And I'm still trying to figure out how fill_loop works - any hints? :)
Gavino
3rd June 2010, 12:50
Right, I see how fill_loop works now, and 'filled' will have 1 frame when in=out, so that second Trim is OK. (The first is still wrong.)
However, why use repeated applications of MFlowInter when you could use a single call to MFlowFps?
filled = dx.MFlowFPS(super, fill_vb, fill_vf, num=(numfr+1)*c.FrameRateNumerator, den=c.FrameRateDenominator).AssumeFPS(c)
return c.trim(0,-(in-startf))++filled.trim(1,-numfr)++c.trim(out-startf+1,0)
This also makes the fill_loop function unnecessary.
Other observations:
- default for 'out' is 0 which is never a valid value - a better default would be the value of 'in'
- parameter 'blksize' is not used.
AlfOng
4th June 2010, 07:21
Thanks Gavino, your comments appreciated.
I need to have a look at this when I get a bit more time.
I'm learning all the time with how to do the programming.
MFlowInter is being used for the tweening effect, ie the variable "time" based on the number of bad frames.
I'll look into MFlowFPS again... I looked at it earlier but could not get it to do the job for some reason. (probably just my poor understanding!)
Maybe you can advise me on how to do something.?
The way I currently use the function is by repeated calls to it in ano scripts. This is far from ideal. A better solution would be to use a text file listing the bad frames, and then call the function one time only, using that list.
I'm having trouble understanding how to read in a text file and then use those pairs of numbers in the function.
Gavino
4th June 2010, 09:04
MFlowInter is being used for the tweening effect, ie the variable "time" based on the number of bad frames.
I'll look into MFlowFPS again... I looked at it earlier but could not get it to do the job for some reason.
With MFlowInter, you have to generate the tweening frames individually and put them together using your very complicated fill_loop function. MFlowFPS does exactly the same job in one call, as I have shown.
The way I currently use the function is by repeated calls to it in ano scripts. This is far from ideal. A better solution would be to use a text file listing the bad frames, and then call the function one time only, using that list.
I'm having trouble understanding how to read in a text file and then use those pairs of numbers in the function.
Avisynth does not provide a facility to read a text file (other than ConditionalReader, which doesn't help here). I don't see a problem with calling the function several times, but a way to do what you want would be to write a wrapper function that takes a number of pairs and calls the original function for each pair.
Great Dragon
16th August 2010, 16:42
I still have the "I don't know what 'lastNewFrame' means" error with trim() function. Even with latest changes from Gavino.
Great Dragon
17th August 2010, 21:47
Sorry, guess I've mislabeled an error.
Actually I have the "GetFrame(): FrameNumber supplied is larger than video's framecount." error on frame 1789 with this script:
s1=trim(0,1790).dupped(thresh=70,panthresh=0.90,stats=false,Showme=false)
s2=trim(1791,1937)
s3=trim(1938,0).dupped(thresh=70,panthresh=0.90,stats=false,Showme=false)
s1++s2++s3
I'm using dupped script with latest recommended changes from Gavino.
neuron2
17th August 2010, 22:14
vid=last
s1=vid.trim(0,1790).dupped(thresh=70,panthresh=0.90,stats=false,Showme=false)
s2=vid.trim(1791,1937)
s3=vid.trim(1938,0).dupped(thresh=70,panthresh=0.90,stats=false,Showme=false)
s1++s2++s3
Great Dragon
18th August 2010, 07:29
neuron2, It's no difference with exactly named agrument or without. I have only Avisource() filter before trim section so "last" is explicitly provided I think. Anyway nothing changed, an error is still present.
neuron2
18th August 2010, 14:08
Quite right, sorry for the noise.
vBulletin® v3.8.5, Copyright ©2000-2012, Jelsoft Enterprises Ltd.