A couple functions I had to make out of need

This forum is for questions and discussion of all the aspects of handling and cleaning up your footage with Avisynth.
User avatar
mirkosp
The Absolute Mudman
Joined: Mon Apr 24, 2006 6:24 am
Status: (」・ワ・)」(⊃・ワ・)⊃
Location: Gallarate (VA), Italy
Contact:
Org Profile

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

Post by 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
The Absolute Mudman
Joined: Mon Apr 24, 2006 6:24 am
Status: (」・ワ・)」(⊃・ワ・)⊃
Location: Gallarate (VA), Italy
Contact:
Org Profile

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

Post by 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
The Absolute Mudman
Joined: Mon Apr 24, 2006 6:24 am
Status: (」・ワ・)」(⊃・ワ・)⊃
Location: Gallarate (VA), Italy
Contact:
Org Profile

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

Post by 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
The Absolute Mudman
Joined: Mon Apr 24, 2006 6:24 am
Status: (」・ワ・)」(⊃・ワ・)⊃
Location: Gallarate (VA), Italy
Contact:
Org Profile

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

Post by 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

Locked

Return to “AviSynth Help”