foxyshadis
10th June 2006, 21:55
Some of you know I'm as interested in image quality as video quality, so I made a fun little function for turning EEDI2 into a max-quality resizer for 4x or more. ;) Hopefully when tritical releases his one-step variation it won't need a complete rewrite.
The idea is to put noise back into places noise was originally so it doesn't look too smooth, to crispen edges and keep them from being so fat when enlarged, and generally take any small pic online and enlarge them to printable quality. (For video temporal smoothing or soothing might be in order though.)
function EKGi(clip c, float "iter", float "bp", float "sharp", float "wsharp", int "itermax") {
iter=default(iter,1.0) # iterations of 2x resizes (.5 = vert-only resize)
bp=default(bp,1.0) # backprojection, or post-resize. .75 is a good value.
sharp=default(sharp,1.0) # post-sharpen
wsharp=default(wsharp,sharp) # post-sharpen
itermax=default(itermax,iter) # do not touch.
idiff=min((itermax-iter) * 2 + 1,3)
multi=2
c
eedi2.turnleft.eedi2.turnleft
bp == 1.0 ? last : spline36resize(m(8,width*bp),m(8,height*bp))
# work around awarpsharp bug, even though mode 2 is also buggy (fun!)
#cmode=(height % 16 == 0 && width % 16 == 0) ? 1 : 2
# stronger sharpening as the picture enlarges
sharp < 0.01 ? last \
: LimitedSharpenFaster(SMode=4,LMode=3,ss_x=1.0,ss_y=1.0,strength=int(15*idiff*sharp),soft=20,overshoot=int(2*sharp))
wsharp < 0.01 ? last \
: warpsharp(depth=int(100*wsharp),bump=int(100*wsharp))
done = iter <= 1.1
tn = int(itermax) % 2
turn = !done ? c : (tn == 0 ? last : turn180)
done ? turn : \
EKGi(iter=iter-1.0,bp=bp,sharp=sharp,itermax=itermax)
}
function m(int r, float x) {return(x<16?16:int(round(x/float(r))*r))}
function EKG(clip c, float "grain", float "gthresh", float "iter", float "bp", float "sharp", float "wsharp", int "multi", bool "turn") {
grain=default(grain,20.) # strength of grain, 5-20% are good values
gthresh=default(gthresh,1.0) # grain threshold, raise to expand, lower to constrain
iter=default(iter,1.0) # iterations of 2x resizes (.5 = vert-only resize)
bp=default(bp,1.0) # backprojection, or post-resize. .75 is a good value.
sharp=default(sharp,1.0) # post-sharpen
wsharp=default(wsharp,sharp) # post-sharpen
turned=default(turn,false) # turn image, sometimes improves eedi
c
# mask for grain
gmask = grain <= 0.01 ? last : fft3dfilter(sigma=gthresh*20,sigma2=gthresh*4,sigma3=0,sigma4=0,plane=4,bt=1,measure=false) \
.mt_lutxy(last,expr="x y - abs",chroma="process") \
.BilinearResize(m(4,width/4),m(4,height/4)).mt_lut(expr="x 2 ^ 16 +",chroma="process")
fft3dfilter(sigma=.5,sigma2=.5,sigma3=0,sigma4=0,plane=4,bt=1,measure=false)
turned ? turnleft : last
EKGi(iter=iter,bp=bp,sharp=sharp,wsharp=wsharp)
turned ? turnright : last
edgemask=mt_edge("sobel",thy1=0,thy2=255,thc1=0,thc2=255,chroma="process").mt_lut("x "+string(1.4*gthresh)+" ^",chroma="process").bilinearresize(m(4,width/4),m(4,height/4))
gmask=mt_average(gmask.bilinearresize(width,height),edgemask.bilinearresize(width,height),chroma="process")
grain <= 0.01 ? last : \
mt_merge(last,addgrainc(grain*5,grain*2,seed=12).addgrainc(grain*5,0,hcorr=.25,vcorr=.25,seed=12), \
gmask.bilinearresize(width,height),chroma="process").removegrain(mode=1)
LimitedSharpenFaster(SMode=4,LMode=3,strength=int(100*sharp),wide=true,special=true,soft=25)
warpsharp(depth=int(100*wsharp),bump=int(100*wsharp))
}
or hosted online (http://foxyshadis.slightlydark.com/random/ekg.avs).
Call like:
import("ekg.avs")
SeeSaw(ssx=1.25,SdampHi=15,sootheT=0,bias=75,Sstr=1.75) # use LSF if seesaw gives artifacts
EKG(grain=20,iter=2,bp=.75,sharp=0.9)
LimitedSharpenFaster(strength=50) # if it still isn't sharp enough, or you used sharp=0.0 for speed
Make sure the input is as sharp as you can make it, it's very important, and as free of dct artifacting as possible, but don't create artifacts. You might try this as a simpler alternative to IIP for translating SD into HD, although I'm sure it could still use some refinement and tweaking. (Also speed is pretty horrible, since I use it on single still images.) In particular it can still show supersharpening artifacts after several iterations.
A bp between .7 and .9 helps reduce squiggle artifacts by shrinking after enlarging. If used, it might skew the aspect ratio a bit (awarpsharp's fault), be sure to correct that with a final resize.
Oh, I use my addgrainc, but you can substitute addgrain, and set the masks to "ignore" and "copy first" respectively.
I leave the implementing of a smart general resize function as an exercise to the reader. ;) Comments welcome. ("too slow" is a given though.)
Sidenote: I need to find another warpsharp if awarpsharp's source isn't available, it's so buggy for non-mod-16.
Sidenote2: It turns out that when you spend all your free time on a project, everything starts looking like a testbed for it...
[Edit]
2006.6.10: minor rotation fix
2006.7.03: split and rewritten, better grain & sharpening
The idea is to put noise back into places noise was originally so it doesn't look too smooth, to crispen edges and keep them from being so fat when enlarged, and generally take any small pic online and enlarge them to printable quality. (For video temporal smoothing or soothing might be in order though.)
function EKGi(clip c, float "iter", float "bp", float "sharp", float "wsharp", int "itermax") {
iter=default(iter,1.0) # iterations of 2x resizes (.5 = vert-only resize)
bp=default(bp,1.0) # backprojection, or post-resize. .75 is a good value.
sharp=default(sharp,1.0) # post-sharpen
wsharp=default(wsharp,sharp) # post-sharpen
itermax=default(itermax,iter) # do not touch.
idiff=min((itermax-iter) * 2 + 1,3)
multi=2
c
eedi2.turnleft.eedi2.turnleft
bp == 1.0 ? last : spline36resize(m(8,width*bp),m(8,height*bp))
# work around awarpsharp bug, even though mode 2 is also buggy (fun!)
#cmode=(height % 16 == 0 && width % 16 == 0) ? 1 : 2
# stronger sharpening as the picture enlarges
sharp < 0.01 ? last \
: LimitedSharpenFaster(SMode=4,LMode=3,ss_x=1.0,ss_y=1.0,strength=int(15*idiff*sharp),soft=20,overshoot=int(2*sharp))
wsharp < 0.01 ? last \
: warpsharp(depth=int(100*wsharp),bump=int(100*wsharp))
done = iter <= 1.1
tn = int(itermax) % 2
turn = !done ? c : (tn == 0 ? last : turn180)
done ? turn : \
EKGi(iter=iter-1.0,bp=bp,sharp=sharp,itermax=itermax)
}
function m(int r, float x) {return(x<16?16:int(round(x/float(r))*r))}
function EKG(clip c, float "grain", float "gthresh", float "iter", float "bp", float "sharp", float "wsharp", int "multi", bool "turn") {
grain=default(grain,20.) # strength of grain, 5-20% are good values
gthresh=default(gthresh,1.0) # grain threshold, raise to expand, lower to constrain
iter=default(iter,1.0) # iterations of 2x resizes (.5 = vert-only resize)
bp=default(bp,1.0) # backprojection, or post-resize. .75 is a good value.
sharp=default(sharp,1.0) # post-sharpen
wsharp=default(wsharp,sharp) # post-sharpen
turned=default(turn,false) # turn image, sometimes improves eedi
c
# mask for grain
gmask = grain <= 0.01 ? last : fft3dfilter(sigma=gthresh*20,sigma2=gthresh*4,sigma3=0,sigma4=0,plane=4,bt=1,measure=false) \
.mt_lutxy(last,expr="x y - abs",chroma="process") \
.BilinearResize(m(4,width/4),m(4,height/4)).mt_lut(expr="x 2 ^ 16 +",chroma="process")
fft3dfilter(sigma=.5,sigma2=.5,sigma3=0,sigma4=0,plane=4,bt=1,measure=false)
turned ? turnleft : last
EKGi(iter=iter,bp=bp,sharp=sharp,wsharp=wsharp)
turned ? turnright : last
edgemask=mt_edge("sobel",thy1=0,thy2=255,thc1=0,thc2=255,chroma="process").mt_lut("x "+string(1.4*gthresh)+" ^",chroma="process").bilinearresize(m(4,width/4),m(4,height/4))
gmask=mt_average(gmask.bilinearresize(width,height),edgemask.bilinearresize(width,height),chroma="process")
grain <= 0.01 ? last : \
mt_merge(last,addgrainc(grain*5,grain*2,seed=12).addgrainc(grain*5,0,hcorr=.25,vcorr=.25,seed=12), \
gmask.bilinearresize(width,height),chroma="process").removegrain(mode=1)
LimitedSharpenFaster(SMode=4,LMode=3,strength=int(100*sharp),wide=true,special=true,soft=25)
warpsharp(depth=int(100*wsharp),bump=int(100*wsharp))
}
or hosted online (http://foxyshadis.slightlydark.com/random/ekg.avs).
Call like:
import("ekg.avs")
SeeSaw(ssx=1.25,SdampHi=15,sootheT=0,bias=75,Sstr=1.75) # use LSF if seesaw gives artifacts
EKG(grain=20,iter=2,bp=.75,sharp=0.9)
LimitedSharpenFaster(strength=50) # if it still isn't sharp enough, or you used sharp=0.0 for speed
Make sure the input is as sharp as you can make it, it's very important, and as free of dct artifacting as possible, but don't create artifacts. You might try this as a simpler alternative to IIP for translating SD into HD, although I'm sure it could still use some refinement and tweaking. (Also speed is pretty horrible, since I use it on single still images.) In particular it can still show supersharpening artifacts after several iterations.
A bp between .7 and .9 helps reduce squiggle artifacts by shrinking after enlarging. If used, it might skew the aspect ratio a bit (awarpsharp's fault), be sure to correct that with a final resize.
Oh, I use my addgrainc, but you can substitute addgrain, and set the masks to "ignore" and "copy first" respectively.
I leave the implementing of a smart general resize function as an exercise to the reader. ;) Comments welcome. ("too slow" is a given though.)
Sidenote: I need to find another warpsharp if awarpsharp's source isn't available, it's so buggy for non-mod-16.
Sidenote2: It turns out that when you spend all your free time on a project, everything starts looking like a testbed for it...
[Edit]
2006.6.10: minor rotation fix
2006.7.03: split and rewritten, better grain & sharpening