A couple functions I had to make out of need

This forum is for questions and discussion of all the aspects of handling your footage. If you have questions about capturing/ripping footage, AviSynth, or compression/encoding/converting, look here.

A couple functions I had to make out of need

Postby mirkosp » Sun Nov 06, 2011 3:52 am

Ok, here we go, and I hope they don't have any leftover mistakes. Otherwise, please correct what needs to be corrected.

First function is another function that works like applyrange. Now, I know that there have been made various in the past, including Phantasmagoriat's range, which I had been using up until now.
However, for 3 reasons I decided to write my own:
1) Phan's range does all the fade etc things, which are cool advanced features if you need them, but end up using some unnecessary memory if you don't.
2) The fixes it did for the checks weren't completely correct. It would change the values with all (which is the framecount) instead of all-1 (which is the actual number of the last frame in the stream, since the first frame is frame 0).
3) It didn't allow me to use -num_frames from trim if I wanted to, so it was completely impossible to actually filter only on frame 0 by just specifying startframe and endframe, which is how I can most easily automate it within YATTA's Custom Lists.
While if it was only for reason 2, I could have just easily updated his range function, reasons 1 and 3 lead me to the decision to just redo it from the beginning. The former, because I'd have to trash some neat things it does, which are helpful from time to time, but not in my daily usage. The latter, because I didn't have the effort to actually look into the code enough to see what I had to change without screwing up anything. :P
So well, here's the code:
Code: Select all
#strange v1.3 by mirkosp
#Yet Another function similar to ApplyRange in purpose that works somewhat differently.
#Start and end work exactly like first_frame and last_frame work with trim(), for the
#sake of consistency, which means that you can use end as if it was -num_frames too.
#ofps parameter tells whether to keep the original fps (true) or not (false).
#For reference: http://avisynth.org/mediawiki/Trim

function strange (clip c, int "start", int "end", clip "edit", bool "ofps") {
#This function only makes sense with filters that return clips to begin with, so no
#point in bothering with strings. It's both easier and better.
start = default(start,0)
end = default(end,0)
edit = default(edit,blankclip(c,length=c.framecount()))#everybody loves blankclip
amount = c.framecount()
ofps = default(ofps,false)

#Brainfarts check ahead.
start = (start < 0) ? 0 : start
end = (-end > amount-start) ? 0 : end
start = (start > amount) ? amount-1 : start
end = (end > amount-1) ? 0 : end

#Match framerate in case user's custom filtering would change it
c = !ofps ? c.assumefps(edit) : c
edit = ofps ? edit.assumefps(c) : edit

#I'm not a good programmer, so I'm not sure if this is slower than it could be.
(start == 0) ? ((end == 0) || (end == amount-1)) ? edit :\
    (end < 0) ? edit.trim(0,end)+c.trim(start-end,0) :\
   edit.trim(0,end)+c.trim(end+1,0) :\
(start == 1) ? ((end == 0) || (end == amount-1)) ? c.trim(0,-1)+edit.trim(start,0) :\
    (end < 0) ? c.trim(0,-1)+edit.trim(start,end)+c.trim(start-end,0) :\
   c.trim(0,-1)+edit.trim(start,end)+c.trim(end+1,0) :\
((end == 0) || (end == amount-1)) ? c.trim(0,start-1)+edit.trim(start,0) :\
    (end < 0) ? c.trim(0,start-1)+edit.trim(start,end)+c.trim(start-end,0) :\
   c.trim(0,start-1)+edit.trim(start,end)+c.trim(end+1,0)
}


As you can see, you can just use it as
Code: Select all
strange(0,-1,subtitle("Hi I'm a string on just the first frame"))
Which was something that couldn't be done with range, plus it doesn't require the triple quotes for strings, as opposed to corran's sceneadjust.
I tried to check all odd cases that could throw trim off, and they worked fine. I might have still missed something, so do report bugs. The code should be legible and easy enough to understand, too.
Still, while you can use it on single filters, and that's fine if you only have a few instances in the script, if you need to use, say, nnedi3 on a couple hundred frames in different points, instead of having 200 strange(x,y,nnedi3), I'd suggest you to save a clip at the beginning and then calling that, ie:
Code: Select all
nnedi3clip = nnedi3()
strange(x,y,nnedi3clip)

Since this will help memory usage.

With strange out of the way, the second filter I had the need to make is one that I called fadefix since I'm bad with names. I'm positive other people made something like this too, but this one I haven't been able to find anywhere, so it might be more useful than strange:

Code: Select all
#fadefix v1.1 by mirkosp
#changes a frame with an overlay of the prev and next frames
#weight of the next frame is controlled with fad
#omode is the mode parameter of overlay
function fadefix (clip c, float "fad", int "prev", int "next", string "omode") {
fad = default(fad, 0.5)
prev = default(prev, 1)
next = default(next, 1)
omode = default(omode, "Blend")
assert( (fad > 0.0 && fad < 1.0) , "fad must have a value between 0.0 and 1.0." )
fads = string(fad)
prevs = string(prev)
nexts = string(next)
c
scriptclip("""overlay(last.trim(current_frame-"""+prevs+""",current_frame-"""+prevs+"""),last.trim(current_frame+"""+nexts+""",current_frame+"""+nexts+"""),opacity = float("""+fads+"""),mode = """+CHR(34)+omode+CHR(34)+""" )""")
return last
}


The scriptclip is meant to be on a single line, if it splits, remember to have it pasted as a single line in the avsi. Also I apologize, this one doesn't have really have much checking, but I didn't feel like it was worth the effort.

Ok, now, let's explain why this filter has a reason to exist.

Since MPEG-2 is very bad with fades and high luma changes in general, it can often show fucked up frames during fades. While in some cases one could just keep a single field, in others, if one does not feel like photoshopping, nuke would be the only option. I made this filter because in certain occasions, without the need to do heavy photoshopping, one could restore such bad frames without nuking the frame, or at least not in their entirety, which ends up being a better solution for quality and detail.

This filter is meant to be used in tandem with an applyrange-like filter (such as strange). To further explain how it works, I'll show an example.
Let's say that your MPEG-2 animu has fade, and a frame during that fade looks really bad. Now, let's say that this frame is frame 9001, and that by overlaying frames 8999 and 9003 with the same weight, you could restore it. Here's how it would look in avisynth with the former filters:
Code: Select all
strange(9001,9001,fadefix(0.5,2,2))

Now, fad, which is 0.5 here, is quite easy to understand. The higher it is, the higher the opacity of the "next" frame (the 9003 one here) is when overlayed on "prev". 0 means that only prev is shown, 1 shows only next, and then you have float inbetweens.
But as for the values of prev and next, it might not be entirely immediate. Since I can't come up with an explanation in words, I'll show it visually:
Code: Select all
     prev                next
8998 8999 9000 9001 9002 9003 9004
  3    2    1    0    1    2    3 

Hopefully this is clear. Now, only one parameter left, which is omode. It works the same way as overlay's mode parameter works, so I suggest you to check overlay's page on the avisynth's wiki if you don't know the modes available. This is hardly used in fades, however it does have a point with flashes and such.
An advanced use of fadefix combined with masks in lieu of just nuking a frame could have unexpectedly pleasant results without too much effort.
Using previous numbers, let's say that we have frame 9001, 8999 and 9003. As you can see, the object at the top rotates, so we can't just overlay, but aside from that, the image is static, so with a simple mask to nuke only where we can't overlay, we can do:
Code: Select all
strange(9001,9001,mt_merge(nlmeanscl(h=6,plane=4),fadefix(0.6,2,2,"lighten"),imagesource("maskdelframe9001.png").converttoyv12.mt_binarize.blurmod(3),true))

Which isn't too bad compared to what we had before, at least in motion.

Since they don't seem to be buggy enough to be troublesome while being not unuseful enough to not be worth sharing, I decided that I should post them here. Try and see if you can get a use for them or something. :dino:
Image
User avatar
mirkosp
MODkip
 
Joined: 24 Apr 2006
Location: Gallarate (VA), Italy
Status: (」・ワ・)」(⊃・ワ・)⊃

Re: A couple functions I had to make out of need

Postby Phantasmagoriat » Thu Nov 10, 2011 12:09 pm

Oh I see, you use replacement clips as your main modifier. That makes sense. Range() can do that too, but I wasn't aware it uses unnecessary memory with some of the extra features. So I might consider [st]range() if I'm encoding something really long. Otherwise, for encoding an AMV, I still like the extra features of Range() if memory usage isn't an issue. (Plus I wrote it so I'm really familiar with it :P).

I can't believe I missed one of those pesky special cases when error-checking! So, I might have to look into that bug when I have time, and maybe address some of the other limitation you pointed out. Although as you probably noticed, I threw in so many features that it might be hard to modify without breaking anything. So I may have to write it from scratch one day.

Anyways, thanks for sharing your alternative mirko :up:
Image
Org Profile | AMVGuide | Phan Picks! | THE424SHOW | YouTube | "Galactic Escape"

"Effort to Understand; Effort to be Understood; to See through Different Eyes."
User avatar
Phantasmagoriat
 
Joined: 06 Feb 2006
Status: ☁SteamPunked≈☂

Re: A couple functions I had to make out of need

Postby mirkosp » Thu Nov 10, 2011 8:08 pm

Yup, I was quite aware range can work with clips, in fact I had been using it like that. However, I'd have lossless crash even two or three times while encoding an episode, and I could make sure it was due to it because the same scripts with just range replaced with strange run smoothly from start to end.
So yes, the extra things range can do are indeed useful if it's what you want to do, just that they do make it all heavier when not needed.
Image
User avatar
mirkosp
MODkip
 
Joined: 24 Apr 2006
Location: Gallarate (VA), Italy
Status: (」・ワ・)」(⊃・ワ・)⊃

Re: A couple functions I had to make out of need

Postby mirkosp » Mon Nov 21, 2011 2:51 pm

Code: Select all
#strange v1.2 by mirkosp
#Yet Another function similar to ApplyRange in purpose that works somewhat differently.
#Start and end work exactly like first_frame and last_frame work with trim(), for the
#sake of consistency, which means that you can use end as if it was -num_frames too.
#For reference: http://avisynth.org/mediawiki/Trim

function strange (clip c, int "start", int "end", clip "edit") {
#This function only makes sense with filters that return clips to begin with, so no
#point in bothering with strings. It's both easier and better.
start = default(start,0)
end = default(end,0)
edit = default(edit,blankclip(c,length=c.framecount()))#everybody loves blankclip
amount = c.framecount()

#Brainfarts check ahead.
start = (start < 0) ? 0 : start
end = (-end > amount-start) ? 0 : end
start = (start > amount) ? amount-1 : start
end = (end > amount-1) ? 0 : end

#I'm not a good programmer, so I'm not sure if this is slower than it could be.
(start == 0) ? ((end == 0) || (end == amount-1)) ? edit :\
    (end < 0) ? edit.trim(0,end)+c.trim(start-end,0) :\
   edit.trim(0,end)+c.trim(end+1,0) :\
(start == 1) ? ((end == 0) || (end == amount-1)) ? c.trim(0,-1)+edit.trim(start,0) :\
    (end < 0) ? c.trim(0,-1)+edit.trim(start,end)+c.trim(start-end,0) :\
   c.trim(0,-1)+edit.trim(start,end)+c.trim(end+1,0) :\
((end == 0) || (end == amount-1)) ? c.trim(0,start-1)+edit.trim(start,0) :\
    (end < 0) ? c.trim(0,start-1)+edit.trim(start,end)+c.trim(start-end,0) :\
   c.trim(0,start-1)+edit.trim(start,end)+c.trim(end+1,0)
}


Further usage showed me that with the old version, when using start != 0 and negative end, the video would become 1 frame shorter because I accidentally left an extra +1 that wasn't meant to be there due to a couple wrong copy and pastes.
This new version fixes the issue.
Image
User avatar
mirkosp
MODkip
 
Joined: 24 Apr 2006
Location: Gallarate (VA), Italy
Status: (」・ワ・)」(⊃・ワ・)⊃

Re: A couple functions I had to make out of need

Postby mirkosp » Mon Jan 09, 2012 9:25 am

Code: Select all
#strange v1.3 by mirkosp
#Yet Another function similar to ApplyRange in purpose that works somewhat differently.
#Start and end work exactly like first_frame and last_frame work with trim(), for the
#sake of consistency, which means that you can use end as if it was -num_frames too.
#ofps parameter tells whether to keep the original fps (true) or not (false).
#For reference: http://avisynth.org/mediawiki/Trim

function strange (clip c, int "start", int "end", clip "edit", bool "ofps") {
#This function only makes sense with filters that return clips to begin with, so no
#point in bothering with strings. It's both easier and better.
start = default(start,0)
end = default(end,0)
edit = default(edit,blankclip(c,length=c.framecount()))#everybody loves blankclip
amount = c.framecount()
ofps = default(ofps,false)

#Brainfarts check ahead.
start = (start < 0) ? 0 : start
end = (-end > amount-start) ? 0 : end
start = (start > amount) ? amount-1 : start
end = (end > amount-1) ? 0 : end

#Match framerate in case user's custom filtering would change it
c = !ofps ? c.assumefps(edit) : c
edit = ofps ? edit.assumefps(c) : edit

#I'm not a good programmer, so I'm not sure if this is slower than it could be.
(start == 0) ? ((end == 0) || (end == amount-1)) ? edit :\
    (end < 0) ? edit.trim(0,end)+c.trim(start-end,0) :\
   edit.trim(0,end)+c.trim(end+1,0) :\
(start == 1) ? ((end == 0) || (end == amount-1)) ? c.trim(0,-1)+edit.trim(start,0) :\
    (end < 0) ? c.trim(0,-1)+edit.trim(start,end)+c.trim(start-end,0) :\
   c.trim(0,-1)+edit.trim(start,end)+c.trim(end+1,0) :\
((end == 0) || (end == amount-1)) ? c.trim(0,start-1)+edit.trim(start,0) :\
    (end < 0) ? c.trim(0,start-1)+edit.trim(start,end)+c.trim(start-end,0) :\
   c.trim(0,start-1)+edit.trim(start,end)+c.trim(end+1,0)
}


Updated strange to v1.3. This introduces a new parameter, ofps, to tell whether to keep the original or the new framerate in case the user's filtering would alter it. By default, the new framerate is kept.
Fixes issues with framerate-altering scripts and filters, which used to break in previous versions.
Image
User avatar
mirkosp
MODkip
 
Joined: 24 Apr 2006
Location: Gallarate (VA), Italy
Status: (」・ワ・)」(⊃・ワ・)⊃

Re: A couple functions I had to make out of need

Postby mirkosp » Fri Feb 10, 2012 9:40 am

Here I am with a new script.
Code: Select all
#quadratura v1.0 by mirkosp
#Substitutes the outer clip's selected area with the same area in the inner clip.
#Make sure outer and inner clips are of the same resolution and colourspace.
#If sources are, for example, both YUV, but with different subsampling, the filter
#will still work with the avisynth's default behaviour to determine which subsampling will be used.
#x, y, w, and h work as with avisynth's crop to determine the area.
#In order to allow odd values to be used with YUV sources, I am using Overlay.
#However, since overlay would internally convert RGB sources to YUV,
#I am using it only if the sources are YUV to avoid colourspace conversions,
#which means that the opacity parameter is ignored with RGB sources.
#If you want to overlay them so badly, convert to YUV yourself before using this script.
#I don't want to be responsible for incorrect RGB->YUV conversions,
#even more so because they're lossy and should be avoided if not necessary.
#For fine tuning of the area, turn on show.

function quadratura (clip outer, clip inner, int "x", int "y", int "w", int "h", float "opacity", bool "show") {
x = default(x,0)
y = default(y,0)
w = default(w,outer.width)
h = default(h,outer.height)
opacity = default(opacity,1.0)
show = default(show,false)

#brainfarts check
x = (x < 0) ? 0 : x
x = (x > outer.width) ? outer.width : x
y = (y < 0) ? 0 : y
y = (y > outer.height) ? 0 : y
w = (w < -(outer.width)) ? (outer.width-x) : w
w = (w > (outer.width-x)) ? (outer.width-x) : w
h = (h < -(outer.height)) ? (outer.height-y) : h
h = (h > (outer.height-y)) ? (outer.height-y) : h
opacity = (opacity < 0) ? 0 : (opacity > 1) ? 1 : opacity
assert(outer.width == inner.width, "Outer and inner clips must have the same width.")
assert(outer.height == inner.height, "Outer and inner clips must have the same height.")
assert(outer.isrgb == inner.isrgb, "Outer and inner clips must have the same colourspace.")
assert((x > (w+outer.width)) ? (w <=0) : (x < (w + outer.width)), "Dude... wait, what? Check x and w.")
assert((y > (h+outer.height)) ? (h <=0) : (y < (h + outer.height)), "Dude... wait, what? Check y and h.")
assert((x <> 0) || (y <> 0) || (( w <> inner.width) && (w <> 0)) || ((h <> inner.height) && (h <> 0)), "Just keep inner, bro.")

#I don't want to handle all of the special cases, so I'll be doing this instead
co = outer.isrgb ? outer : blankclip(outer,outer.framecount,color=$000000,pixel_type="RGB32")
ci = inner.isrgb ? (show ? outer.invert() : inner) : blankclip(inner,inner.framecount,color=$FFFFFF,pixel_type="RGB32")
co = co.addborders(2,2,2,2)
ci = ci.addborders(2,2,2,2)
x = x+2
y = y+2
w = (w > 0) ? w : w-2
h = (h > 0) ? h : h-2
#now stuff happens, though maybe it could happen faster
cs = stackvertical(co.crop(0,0,0,y),stackhorizontal(co.crop(0,y,x,h),ci.crop(x,y,w,h),co.crop(((w > 0) ? x+w : (co.width)+w),y,0,h)),co.crop(0,((h > 0) ? y+h : (co.height)+h),0,0))
cs = cs.crop(2,2,-2,-2)
return outer.isrgb ? cs : overlay(outer,(show ? outer.invert : inner),opacity=(show ? 1.0 : opacity),mask=cs)
}

I made this one, once again, out of need. I often have to stack clips due to filters I only want to apply on a certain portion of the image, and while doing a binary mask is a more elegant solution (and a better one too when a high precision is needed), stacking clips is faster and sometimes works fine, so for those times when drawing a binary mask would take more time than the result is worth it, I made this function.
Since this script uses overlay for YUV, but otherwise works in RGB, you're not bound to a bunch of limits crop has with subsampled YUV, namely odd values and very low output resolutions.
Hope someone else will find it useful.
Image
User avatar
mirkosp
MODkip
 
Joined: 24 Apr 2006
Location: Gallarate (VA), Italy
Status: (」・ワ・)」(⊃・ワ・)⊃

Re: A couple functions I had to make out of need

Postby mirkosp » Sun Sep 23, 2012 3:41 pm

Bump. Hi people!

Code: Select all
#edgefix v1.1 by mirkosp
#A function that supposedly fixes the edges of the image in upscaled stuff.
#Ideally similar results can be achieved through inpainting, but as that process is quite slow and as we can just try to restore the ideal pixel
#value through some process reminiscent of delogoing of partially opaque logos, I made this function, which is rather fast for what it does, as I'm having it work on the edges only.
#Either way, here's the gist of what I'm doing and why.
#Sometimes, with upscaled sources, it happens that the pixels at the borders of the picture are slightly darker or brighter than what they should be.
#Since it might be better to not just crop these pixels away (particularly if one wants to employ an algorithm like debilinear), I made this.
#As I've mostly seen this happen with upscaled anime, and particularly with edges darker than they should be, there is a slight bias towards increasing the brightness of a pixel when in doubt.
#This is due to the fact that in areas with slant lines and such it would recognize the pixel as lighter than it should be, whereas it's still
#supposed to be doing a brightening. I haven't had the occasion to properly test the darkening effect of the filter on brighter edges as I don't have any handy, so feedback for those
#would be helpful.
#Either way, keep in mind that some sources might require different settings based on the scene. Mostly if a certain scene was zoomed and thus doesn't need fixing or if
#an edge is darker/brighter than the rest in a single scene for whatever reason.
#
#Usage:
#xedge, yedge, wedge, and hedge are the amount of pixels that need fixing on each side.
#thrdark is the threshold for how darker a pixel is than how it should be. If a pixel is detected as such, it will be brightened by a threshold amount proportional to how bright
#the pixel currently is to begin with.
#thrbright works in a similar fashion, except it darkens pixels that are recognized as brighter than they should be.
#radius is used to decide the area on which to calculate the median, which is used as reference on what the pixel's value is supposed to be more or less.
#matrix is, well, avisynth's matrix value. Pick your poison between "Rec601", "PC.601", "Rec709", and "PC.709".
#I was too lazy to implement checks, so here's a few tips.
#Negative thresholds will do the opposite of what they're supposed to do, so just set them to 0 if you don't need either. Actually, negative values could be helpful if you ONLY need to do
#either brightening or darkening and the automatic detection is failing (it is a bit rough in how it works, I didn't have better ideas).
#Radius should always be bigger than the biggest edge value. Perhaps an equal value could work anyway, but I'd advise against it.
#The way I'm doing the speedup could mean that the areas around the corners might not be perfectly filtered. I'm not sure. Worst case, run multiple instances of the filter based
#on your needs and put stuff together with quadratura on your own. That might do. IDK.
#
#Oh, right. Requires avisynth 2.6, masktools 2.0a48+, fillmargins, and quadratura.
#

function edgefix (clip c, int "xedge", int "yedge", int "wedge", int "hedge", int "thrdark", int "thrbright", int "radius", string "matrix"){
xedge = default(xedge,1)
yedge = default(yedge,1)
wedge = default(wedge,xedge)
hedge = default(hedge,yedge)
thrdark = default(thrdark,45)
thrbright = default(thrbright,thrdark)
radius = default(radius,max(xedge,yedge,wedge,hedge)+2)
matrix = defined(matrix) ? matrix : (c.width > 1024) || (c.height > 600) ? "Rec709" : "Rec601"
c
converttoy8(matrix=matrix)
e = stackvertical(stackhorizontal(crop(0,0,0,radius+1),crop(width-radius-1,0,0,0).turnleft()),stackhorizontal(crop(0,0,-width+radius+1,0).turnleft(),crop(0,height-radius-1,0,0)))
c
fillmargins(xedge,yedge,wedge,hedge)
converttoy8(matrix=matrix)
stackvertical(stackhorizontal(crop(0,0,0,radius+1),crop(width-radius-1,0,0,0).turnleft()),stackhorizontal(crop(0,0,-width+radius+1,0).turnleft(),crop(0,height-radius-1,0,0)))
d = mt_luts(last,last,"med",mt_circle(radius),"y",u=1,v=1)
mt_lutxy(e,d,"y "+string(thrdark)+" + x > y "+string(thrdark)+" < y x "+string(thrdark)+" x 255 / * + ? y 255 "+string(thrbright)+" - > y x "+string(thrbright)+" x 255 / * - ? ?",u=1,v=1)
#y + thrdark > x ? y < thrdark ? y : x + thrdark*(x/255) : y > 255 - thrbright ? y : x - (thrbright)*(x/255)
e = last
se = stackvertical(stackhorizontal(crop(0,0,1,1),crop(c.width-1,0,1,1)),stackhorizontal(crop(c.height,height-1,1,1),crop(width-1,height-1,1,1)))
d
sd = stackvertical(stackhorizontal(crop(0,0,1,1),crop(c.width-1,0,1,1)),stackhorizontal(crop(c.height,height-1,1,1),crop(width-1,height-1,1,1)))
se = mt_lutxy(se,sd,"y "+string(thrdark)+" + x > y "+string(thrdark)+" < y x "+string(thrdark)+" x 255 / * + ? y 255 "+string(thrbright)+" - > y x "+string(thrbright)+" x 255 / * - ? ?",u=1,v=1)
stackvertical(stackhorizontal(se.crop(0,0,1,1),e.crop(1,0,c.width-2,-e.height+1),se.crop(1,0,1,1),e.crop(c.width,0,0,-e.height+1)),e.crop(0,1,0,-1),stackhorizontal(e.crop(0,e.height-1,-c.width,0),se.crop(0,1,1,1),e.crop(c.height+1,e.height-1,c.width-2,0),se.crop(1,1,1,1)))
stackvertical(crop(0,0,c.width,height/2),stackhorizontal(crop(radius+1,height/2,-c.width-radius-1,0).turnright(),c.converttoy8(matrix=matrix).crop(radius+1,radius+1,-radius-1,-radius-1),crop(c.width+radius+1,0,-radius-1,-height/2).turnright()),crop(c.height,height/2,0,0))
converttoyv12(matrix=matrix)
quadratura(last,c,xedge,yedge,-wedge,-hedge)
mergechroma(c)
return last}


Seems like I have a bad habit of placing unnecessary WoTs in my functions. Oh well. Comes without saying that you're supposed to use it BEFORE you do any resizing. If there are some pure black pixels too, crop them away before using this filter (eventually add them back and fillmargins over them, if you prefer).
Btw, it internally converts to y8 to work around yv12 limits (and for very slight speedups too I guess), but input has to be yv12. Yes, it means I'm doing a colourspace conversion internally. I really had to. I'm letting the user specify the matrix if they want to, but otherwise, I'm doing a rough check which should be fine most of the time.

EDIT: Update to 1.1. Fixes a small issue with thrbright.
Image
User avatar
mirkosp
MODkip
 
Joined: 24 Apr 2006
Location: Gallarate (VA), Italy
Status: (」・ワ・)」(⊃・ワ・)⊃

Re: A couple functions I had to make out of need

Postby mirkosp » Fri Jan 18, 2013 10:03 am

Hello gentlemen.

Code: Select all
#refadelinear v1.1 by mirkosp
#re-make a linear fade of two static shots
#if you need to use it in a static scene with some small things in motion or
#which do not have to fade, simply save a clip, use refadelinear, and then
#add whatever it was back with quadratura or similar approach
#
#the first and last frames must have 100% opacity and the frames inbetween must be
#linearly fading.
#has to be applied after decimation. if you need to apply it before,
#then you'll have to decimate, apply this, and dup back.
#
#if it's a fade from/to black/white and the supposedly pure black/white frame is fucked
#up, use strange on a blankclip to fix the frame and refadelinear afterwards.
#likewise, if the clean frame is a bit after the end of the fade, try to freezeframe
#or whatever approach works so that the end frame is clean before using this filter
#
function refadelinear(clip c, int firstf, int lastf) {
c = blankclip(c,length=2)+c+blankclip(c,length=2)
firstf = firstf+2
lastf = lastf+2
c.trim(firstf,lastf)
scriptclip("""overlay(freezeframe(0,framecount-1,0),freezeframe(0,framecount-1,framecount-1),opacity=(((framecount-1-(framecount-1-current_frame))/float((framecount-1)))))""")
c.trim(1,firstf-1)+last+c.trim(lastf+1,c.framecount-2)
return trim(1,framecount-2) }


An "alternative" to fadefix. Sometimes, instead of a single frame during a fade, you have a long fade with many fucked up frames, courtesy of mpeg-2 interlacism at tv bitrates, but you're lucky enough that it's a static shot. If that is your case, this function will help even more than fadefix as it's quicker to use (works especially well for YATTA post decimate custom lists assuming the frame count isn't off by 1 frame, which sometimes happens, particularly when doing vfr; you can still work around those by applying it to the range of frames that corrisponds to the decimated ones you want, help yourself with the preview to know if the count is off).
Of course, fadefix is still relevant for the fades with limited animation going on (so common with anime).
Image
User avatar
mirkosp
MODkip
 
Joined: 24 Apr 2006
Location: Gallarate (VA), Italy
Status: (」・ワ・)」(⊃・ワ・)⊃

Re: A couple functions I had to make out of need

Postby mirkosp » Fri Feb 01, 2013 2:43 am

Updated refadelinear to v1.1, fixes the special case handling, which I managed to get wrong the first time around despite the correct general approach.
Image
User avatar
mirkosp
MODkip
 
Joined: 24 Apr 2006
Location: Gallarate (VA), Italy
Status: (」・ワ・)」(⊃・ワ・)⊃

Re: A couple functions I had to make out of need

Postby mirkosp » Sun Feb 10, 2013 8:50 am

Code: Select all
#edgefix v2.0 by mirkosp
#A function that supposedly fixes the edges of the image in upscaled stuff.
#Ideally similar results can be achieved through inpainting, but as that process is quite slow and as we can just try to restore the ideal pixel
#value through some process reminiscent of delogoing of partially opaque logos, I made this function, which is rather fast for what it does, as I'm having it work on the edges only.
#Either way, here's the gist of what I'm doing and why.
#Sometimes, with upscaled sources, it happens that the pixels at the borders of the picture are slightly darker or brighter than what they should be.
#Since it might be better to not just crop these pixels away (particularly if one wants to employ an algorithm like debilinear), I made this.
#Keep in mind that some sources might require different settings based on the scene. Mostly if a certain scene was zoomed and thus doesn't need fixing or if
#an edge is darker/brighter than the rest in a single scene for whatever reason.
#
#Usage:
#xedge, yedge, wedge, and hedge are the amount of pixels that need fixing on each side.
#thr lightens the pixel with positive values and darkens them with negative ones. major difference with the older implementation
#radius is used to decide the area on which to calculate the median, which is used as reference on what the pixel's value is supposed to be more or less.
#matrix is, well, avisynth's matrix value. Pick your poison between "Rec601", "PC.601", "Rec709", and "PC.709".
#Radius should always be bigger than the biggest edge value. Perhaps an equal value could work anyway, but I'd advise against it.
#The way I'm doing the speedup could mean that the areas around the corners might not be perfectly filtered. I'm not sure. Worst case, run multiple instances of the filter based
#on your needs and put stuff together with quadratura on your own. That might do. IDK.
#
#Oh, right. Requires avisynth 2.6, masktools 2.0a48+, fillmargins, and quadratura.
#

function edgefix2 (clip c, int "thr", int "xedge", int "yedge", int "wedge", int "hedge", int "radius", string "matrix"){
xedge = default(xedge,1)
yedge = default(yedge,1)
wedge = default(wedge,xedge)
hedge = default(hedge,yedge)
thr = default(thr,45)
radius = default(radius,max(xedge,yedge,wedge,hedge)+1)
matrix = defined(matrix) ? matrix : (c.width > 1024) || (c.height > 600) ? "Rec709" : "Rec601"
c
converttoy8(matrix=matrix)
e = stackvertical(stackhorizontal(crop(0,0,0,radius+1),crop(width-radius-1,0,0,0).turnleft()),stackhorizontal(crop(0,0,-width+radius+1,0).turnleft(),crop(0,height-radius-1,0,0)))
c
fillmargins(xedge,yedge,wedge,hedge)
converttoy8(matrix=matrix)
stackvertical(stackhorizontal(crop(0,0,0,radius+1),crop(width-radius-1,0,0,0).turnleft()),stackhorizontal(crop(0,0,-width+radius+1,0).turnleft(),crop(0,height-radius-1,0,0)))
d = mt_luts(last,last,"med",mt_circle(radius),"y",u=1,v=1)
mt_lutxy(e,d," "+string(thr)+" 0 > y "+string(thr)+" < y x "+string(thr)+" x 255 / * + ? y 255 "+string(thr)+" + > y x "+string(thr)+" x 255 / * + ? ?",u=1,v=1)
#thr > 0 ? y < thr ? y : x + thr*(x/255) : y > 255 + thr ? y : x + (thr)*((x)/255)
e = last
se = stackvertical(stackhorizontal(crop(0,0,1,1),crop(c.width-1,0,1,1)),stackhorizontal(crop(c.height,height-1,1,1),crop(width-1,height-1,1,1)))
d
sd = stackvertical(stackhorizontal(crop(0,0,1,1),crop(c.width-1,0,1,1)),stackhorizontal(crop(c.height,height-1,1,1),crop(width-1,height-1,1,1)))
se = mt_lutxy(se,sd," "+string(thr)+" 0 > y "+string(thr)+" < y x "+string(thr)+" x 255 / * + ? y 255 "+string(thr)+" + > y x "+string(thr)+" x 255 / * + ? ?",u=1,v=1)
stackvertical(stackhorizontal(se.crop(0,0,1,1),e.crop(1,0,c.width-2,-e.height+1),se.crop(1,0,1,1),e.crop(c.width,0,0,-e.height+1)),e.crop(0,1,0,-1),stackhorizontal(e.crop(0,e.height-1,-c.width,0),se.crop(0,1,1,1),e.crop(c.height+1,e.height-1,c.width-2,0),se.crop(1,1,1,1)))
stackvertical(crop(0,0,c.width,height/2),stackhorizontal(crop(radius+1,height/2,-c.width-radius-1,0).turnright(),c.converttoy8(matrix=matrix).crop(radius+1,radius+1,-radius-1,-radius-1),crop(c.width+radius+1,0,-radius-1,-height/2).turnright()),crop(c.height,height/2,0,0))
converttoyv12(matrix=matrix)
quadratura(last,c,xedge,yedge,-wedge,-hedge)
mergechroma(c)
return last}


Edgefix v2 (now is called as edgefix2() since it differs a bit), changes a bit from the first one. Now it uses a single thr value, and brightens with positive values, darkens with negative ones. Radius is smaller by default, too. The parameter order has also been switched up since most often than not one would want to just touch up the thr, rather than the edges (I found the 1 pixel an all sides to be the most common situation).
Should give some small speedups compared to the older version, though I can't stress enough how this would really benefit from a proper implementation through a .dll, if only somebody would take the time to give it a try. :book:
Image
User avatar
mirkosp
MODkip
 
Joined: 24 Apr 2006
Location: Gallarate (VA), Italy
Status: (」・ワ・)」(⊃・ワ・)⊃

Re: A couple functions I had to make out of need

Postby mirkosp » Tue Mar 12, 2013 7:30 am

Code: Select all
#edgefix v2.1 by mirkosp
#A function that supposedly fixes the edges of the image in upscaled stuff.
#Ideally similar results can be achieved through inpainting, but as that process is quite slow and as we can just try to restore the ideal pixel
#value through some process reminiscent of delogoing of partially opaque logos, I made this function, which is rather fast for what it does, as I'm having it work on the edges only.
#Either way, here's the gist of what I'm doing and why.
#Sometimes, with upscaled sources, it happens that the pixels at the borders of the picture are slightly darker or brighter than what they should be.
#Since it might be better to not just crop these pixels away (particularly if one wants to employ an algorithm like debilinear), I made this.
#Keep in mind that some sources might require different settings based on the scene. Mostly if a certain scene was zoomed and thus doesn't need fixing or if
#an edge is darker/brighter than the rest in a single scene for whatever reason.
#
#Usage:
#xedge, yedge, wedge, and hedge are the amount of pixels that need fixing on each side.
#thr lightens the pixel with positive values and darkens them with negative ones. major difference with the older implementation
#radius is used to decide the area on which to calculate the median, which is used as reference on what the pixel's value is supposed to be more or less.
#matrix is, well, avisynth's matrix value. Pick your poison between "Rec601", "PC.601", "Rec709", and "PC.709".
#Radius should always be bigger than the biggest edge value. Perhaps an equal value could work anyway, but I'd advise against it.
#The way I'm doing the speedup could mean that the areas around the corners might not be perfectly filtered. I'm not sure. Worst case, run multiple instances of the filter based
#on your needs and put stuff together with quadratura on your own. That might do. IDK.
#
#Oh, right. Requires avisynth 2.6, masktools 2.0a48+, fillmargins, and quadratura.
#

function edgefix2 (clip c, int "thr", int "xedge", int "yedge", int "wedge", int "hedge", int "radius", string "matrix"){
xedge = default(xedge,1)
yedge = default(yedge,1)
wedge = default(wedge,xedge)
hedge = default(hedge,yedge)
thr = default(thr,45)
radius = default(radius,max(xedge,yedge,wedge,hedge)+1)
matrix = defined(matrix) ? matrix : (c.width > 1024) || (c.height > 600) ? "Rec709" : "Rec601"
c
converttoy8(matrix=matrix)
e = stackvertical(stackhorizontal(crop(0,0,0,radius+1),crop(width-radius-1,0,0,0).turnleft()),stackhorizontal(crop(0,0,-width+radius+1,0).turnleft(),crop(0,height-radius-1,0,0)))
c
fillmargins(xedge,yedge,wedge,hedge)
converttoy8(matrix=matrix)
stackvertical(stackhorizontal(crop(0,0,0,radius+1),crop(width-radius-1,0,0,0).turnleft()),stackhorizontal(crop(0,0,-width+radius+1,0).turnleft(),crop(0,height-radius-1,0,0)))
d = mt_luts(last,last,"med",mt_circle(radius),"y",u=1,v=1)
mt_lutxy(e,d," "+string(thr)+" 16 <= y "+string(thr)+" < y x "+string(thr)+" x 255 / * + ? y 235 "+string(thr)+" + >= y x "+string(thr)+" x 255 / * + ? ?",u=1,v=1)
#thr > 0 ? y < thr ? y : x + thr*(x/255) : y > 255 + thr ? y : x + (thr)*((x)/255)
e = last
se = stackvertical(stackhorizontal(crop(0,0,1,1),crop(c.width-1,0,1,1)),stackhorizontal(crop(c.height,height-1,1,1),crop(width-1,height-1,1,1)))
d
sd = stackvertical(stackhorizontal(crop(0,0,1,1),crop(c.width-1,0,1,1)),stackhorizontal(crop(c.height,height-1,1,1),crop(width-1,height-1,1,1)))
se = mt_lutxy(se,sd," "+string(thr)+" 16 <= y "+string(thr)+" < y x "+string(thr)+" x 255 / * + ? y 235 "+string(thr)+" + >= y x "+string(thr)+" x 255 / * + ? ?",u=1,v=1)
stackvertical(stackhorizontal(se.crop(0,0,1,1),e.crop(1,0,c.width-2,-e.height+1),se.crop(1,0,1,1),e.crop(c.width,0,0,-e.height+1)),e.crop(0,1,0,-1),stackhorizontal(e.crop(0,e.height-1,-c.width,0),se.crop(0,1,1,1),e.crop(c.height+1,e.height-1,c.width-2,0),se.crop(1,1,1,1)))
stackvertical(crop(0,0,c.width,height/2),stackhorizontal(crop(radius+1,height/2,-c.width-radius-1,0).turnright(),c.converttoy8(matrix=matrix).crop(radius+1,radius+1,-radius-1,-radius-1),crop(c.width+radius+1,0,-radius-1,-height/2).turnright()),crop(c.height,height/2,0,0))
converttoyv12(matrix=matrix)
quadratura(last,c,xedge,yedge,-wedge,-hedge)
mergechroma(c)
return last}


Edgefix 2.1 out. Now it only uses the fillmargins clip if the pixel value of the border is actually black or actually white. Dramatically improves output with dark and bright details.

Source:
Image

Code: Select all
edgefix2(10,3,0,3,0)
edgefix2(78,2,0,2,0)
edgefix2(78,1,0,1,0)
quadratura(c.edgefix2(78,2,0,2,0),last,0,1,0,-1)


v2.0
Image

v2.1
Image
Image
User avatar
mirkosp
MODkip
 
Joined: 24 Apr 2006
Location: Gallarate (VA), Italy
Status: (」・ワ・)」(⊃・ワ・)⊃

Re: A couple functions I had to make out of need

Postby mirkosp » Sun Apr 07, 2013 5:37 am

Code: Select all
#edgepp v1.0 by mirkosp
#Quadratura lazymod to postprocess edges after edgefix.
#Technically could be used in lieu of it too, but I don't think
#that would be a good idea. Depends on the case, perhaps.
#rad is the strength of the median blurring. Big values, big blur.
#x, y, w, and h work like in crop and quadratura.
#Colorspaces with subsampled chroma need appropriate mod
#values (for example, mod2 for YV12).
#Required filters:
#Fillmargins ( http://avisynth.org/warpenterprises/ )
#Masktools v2.0 (a48 or later, possibly) ( http://manao4.free.fr/ )
#TEdgeMask ( http://web.missouri.edu/~kes25c/ )

function edgepp (clip c, int "rad", int "x", int "y", int "w", int "h") {
rad = default(rad,1)
x = default(x,2)
y = default(y,2)
w = default(w,-2)
h = default(h,-2)

#I don't want to handle all of the special cases, so I'll be doing this instead
co = c.addborders(2,2,2,2).fillmargins(2,2,2,2)
#For what I'm doing, this is actually better since it results in better masks
x = x+2
y = y+2
w = (w > 0) ? w : w-2
h = (h > 0) ? h : h-2
#now stuff happens, though maybe it could happen faster
cs = stackvertical(mt_luts(co.crop(0,0,0,y),co.crop(0,0,0,y),"median",mt_square(rad,true),"y"),stackhorizontal(mt_luts(co.crop(0,y,x,h),co.crop(0,y,x,h),"median",mt_square(rad,true),"y"),co.crop(x,y,w,h),mt_luts(co.crop(((w > 0) ? x+w : (co.width)+w),y,0,h),co.crop(((w > 0) ? x+w : (co.width)+w),y,0,h),"median",mt_square(rad,true),"y")),mt_luts(co.crop(0,((h > 0) ? y+h : (co.height)+h),0,0),co.crop(0,((h > 0) ? y+h : (co.height)+h),0,0),"median",mt_square(rad,true),"y"))
mt_merge(cs,co,co.tedgemask().mt_inflate(),true,u=3,v=3)
return crop(2,2,-2,-2)
}

While edgefix still has a few issues I should really get around to fix, it's in a usable enough state generally speaking.
However, sometimes, the output is still not perfect or perhaps the edge issues aren't exactly consistent and so on.
For this kind of small refinements, I wrote this filter. It basically mods quadratura's approach of stacking but is much faster than using quadratura with the filter since it only works on those edges.
Additionally, since I have the extra pixels with fillmargins, this allows me to draw a better mask on those borders.
Either way, if your source doesn't need this, try to avoid it, since it's a bit of an ugly and bruteforce approach. However if there are some leftovers which you aren't able to quite nail as appropriate, this might very well help out.
Image
User avatar
mirkosp
MODkip
 
Joined: 24 Apr 2006
Location: Gallarate (VA), Italy
Status: (」・ワ・)」(⊃・ワ・)⊃

Re: A couple functions I had to make out of need

Postby mirkosp » Wed Apr 10, 2013 10:52 am

Code: Select all
#edgefix v2.21 by mirkosp
#A function that supposedly fixes the edges of the image in upscaled stuff.
#Ideally similar results can be achieved through inpainting, but as that process is quite slow and as we can just try to restore the ideal pixel
#value through some process reminiscent of delogoing of partially opaque logos, I made this function, which is rather fast for what it does, as I'm having it work on the edges only.
#Either way, here's the gist of what I'm doing and why.
#Sometimes, with upscaled sources, it happens that the pixels at the borders of the picture are slightly darker or brighter than what they should be.
#Since it might be better to not just crop these pixels away (particularly if one wants to employ an algorithm like debilinear), I made this.
#Keep in mind that some sources might require different settings based on the scene. Mostly if a certain scene was zoomed and thus doesn't need fixing or if
#an edge is darker/brighter than the rest in a single scene for whatever reason.
#
#Usage:
#xedge, yedge, wedge, and hedge are the amount of pixels that need fixing on each side.
#thr lightens the pixel with positive values and darkens them with negative ones. major difference with the older implementation
#radius is used to decide the area on which to calculate the median, which is used as reference on what the pixel's value is supposed to be more or less.
#matrix is, well, avisynth's matrix value. Pick your poison between "Rec601", "PC.601", "Rec709", and "PC.709".
#Radius should always be bigger than the biggest edge value. Perhaps an equal value could work anyway, but I'd advise against it.
#The way I'm doing the speedup could mean that the areas around the corners might not be perfectly filtered. I'm not sure. Worst case, run multiple instances of the filter based
#on your needs and put stuff together with quadratura on your own. That might do. IDK.
#
#Oh, right. Requires avisynth 2.6, masktools 2.0a48+, fillmargins, and quadratura.
#

function edgefix2 (clip c, int "thr", int "xedge", int "yedge", int "wedge", int "hedge", int "radius", string "matrix"){
xedge = default(xedge,1)
yedge = default(yedge,1)
wedge = default(wedge,xedge)
hedge = default(hedge,yedge)
thr = default(thr,45)
radius = default(radius,max(xedge,yedge,wedge,hedge)+1)
matrix = defined(matrix) ? matrix : (c.width > 1024) || (c.height > 600) ? "Rec709" : "Rec601"
c
converttoy8(matrix=matrix)
e = stackvertical(stackhorizontal(crop(0,0,0,radius+1),crop(width-radius-1,0,0,0).turnleft()),stackhorizontal(crop(0,0,-width+radius+1,0).turnleft(),crop(0,height-radius-1,0,0)))
c
fillmargins(xedge,yedge,wedge,hedge)
converttoy8(matrix=matrix)
stackvertical(stackhorizontal(crop(0,0,0,radius+1),crop(width-radius-1,0,0,0).turnleft()),stackhorizontal(crop(0,0,-width+radius+1,0).turnleft(),crop(0,height-radius-1,0,0)))
d = mt_luts(last,last,"med",mt_circle(radius),"y",u=1,v=1)
mt_lutxy(e,d,string(thr)+" 0 > x 16 <= y x "+string(thr)+" x 255 / * + ? x 235 >= y x "+string(thr)+" x 255 / * + ? ?",u=1,v=1)
#thr > 0 ? x <= 16 ? y : x + thr*(x/255) : x >= 235 ? y : x + (thr)*((x)/255)
e = last
se = stackvertical(stackhorizontal(crop(0,0,1,1),crop(c.width-1,0,1,1)),stackhorizontal(crop(c.height,height-1,1,1),crop(width-1,height-1,1,1)))
d
sd = stackvertical(stackhorizontal(crop(0,0,1,1),crop(c.width-1,0,1,1)),stackhorizontal(crop(c.height,height-1,1,1),crop(width-1,height-1,1,1)))
se = mt_lutxy(se,sd,string(thr)+" 0 > x 16 <= y x "+string(thr)+" x 255 / * + ? x 235 >= y x "+string(thr)+" x 255 / * + ? ?",u=1,v=1)
stackvertical(stackhorizontal(se.crop(0,0,1,1),e.crop(1,0,c.width-2,-e.height+1),se.crop(1,0,1,1),e.crop(c.width,0,0,-e.height+1)),e.crop(0,1,0,-1),stackhorizontal(e.crop(0,e.height-1,-c.width,0),se.crop(0,1,1,1),e.crop(c.height+1,e.height-1,c.width-2,0),se.crop(1,1,1,1)))
stackvertical(crop(0,0,c.width,height/2),stackhorizontal(crop(radius+1,height/2,-c.width-radius-1,0).turnright(),c.converttoy8(matrix=matrix).crop(radius+1,radius+1,-radius-1,-radius-1),crop(c.width+radius+1,0,-radius-1,-height/2).turnright()),crop(c.height,height/2,0,0))
converttoyv12(matrix=matrix)
quadratura(last,c,xedge,yedge,-wedge,-hedge)
mergechroma(c)
return last}


Finally fixed the couple bugs in the lut. I think.
Also thanks to nodame.
Image
User avatar
mirkosp
MODkip
 
Joined: 24 Apr 2006
Location: Gallarate (VA), Italy
Status: (」・ワ・)」(⊃・ワ・)⊃

Re: A couple functions I had to make out of need

Postby mirkosp » Mon May 27, 2013 2:50 pm

Code: Select all
#restack16 v1.0 by mirkosp
#Basically takes LSmashSource.dll high bitdepth input
#and turns it into stacked 16bit for dithertools
#bitdepth is the source bitdepth, default for the common 10bit
#le is little endian; default is true 'cause that's what
#LSmashSource seems to spit out, but one never knows
#
#Thanks to Firesledge for telling me how to do the bitdepth conversion
#
function restack16(clip c, int "bitdepth", bool "le") {
bitdepth = default(bitdepth,10)
assert(bitdepth >= 8 && bitdepth <= 14,"Nope.")
le = default(le,true)
c
turnleft()
separatefields()
le ? stackhorizontal(selectodd(),selecteven()) : stackhorizontal(selecteven(),selectodd())
assumeframebased()
turnright()
return dither_lut16 ("x "+string(Pow(2,(16-bitdepth)))+" *", u=3, v=3)
}


With a LSmashWorks version that now supports high bitdepth and 4:2:2 and 4:4:4 video subsampling, and considering how much of a speed boost I've enjoyed compared to ffms2, I'd like to transition to it.
It also turns out that LWLibavVideoSource gives out an actual 10bit video when called in avisynth. This means that reading it as 8bit looks funny.
Fortunately enough, it's quite possible to bring this to a much more usable stacked 16bit format (which also means we get to keep the extra bits, instead of getting a dithered 8bit input!), so with a little help from firesledge a wrote a simple function that does just that.
Obviously requires dithertools in order to work.
Image
User avatar
mirkosp
MODkip
 
Joined: 24 Apr 2006
Location: Gallarate (VA), Italy
Status: (」・ワ・)」(⊃・ワ・)⊃


Return to Footage Help

Who is online

Users browsing this forum: No registered users and 1 guest

cron