PDA

View Full Version : Image enlargement with eedi2


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

dbzgundam
11th June 2006, 00:20
"There is no function named 'min'".

foxyshadis
11th June 2006, 01:14
Oh, dependencies are important:
EEDI2
stickboy's MinMax
FFT3DFilter
Masktools2
LimitedSharpenFaster
WarpSharp
AddGrainC
and of course, Avisynth 2.56+

I think that's all.

Mug Funky
14th June 2006, 05:19
I think that's all.

what about eedi2? :)

danpos
14th June 2006, 05:28
what about eedi2? :)

I think that title's topic speaks for itself: "Image enlargement with eedi2" ;)

See ya,

Pookie
14th June 2006, 07:21
Thanks, Foxyshadis. Turned a postage stamp into a billboard ;) Seriously, many thanks - just what I was looking for.

foxyshadis
14th June 2006, 10:12
Right, forgot the main filter. ;p

I have many online postage stamps whose prints are out of sale for good now, so my only choice is turning to divining their contents out of mush. ;_;

I think the sharpening (and the "multi" mess) is the most hacktastic part, since my original intuition was wrong and I just kludged it into something workable while I had time. When I escape work next I'll see if I can coax something more useful out. Sadly fft3dfilter tends to crash when frame size gets too large, but I usually end up with out of memory messages by then anyway. ;)

I'd also like to average out the normal and turned version, the problem tritical brought up recently, but the variable subpixel shifts make it too painful. I'll just have to wait for the next version of eedi2.

Didée
13th July 2006, 00:29
I'd also like to average out the normal and turned version, the problem tritical brought up recently, but the variable subpixel shifts make it too painful.
If with "averaging out" you mean a simple merge by 50% (or a masked-merge, for that matter), then it's quite easy. Just follow every call of eedi2 with
mt_convolution("1","1 1 0",u=3,v=3) [soft shift by a half pixel], or
mt_convolution("1","-1 2 2 -1 0",u=3,v=3) [sharp shift by a half pixel]

That's what I usually do when I need to combine eedi2's output with something else, to keep everything aligned.

Oh, and could it be that EKG better should internally adapt the warping settings along with the "iter" value? Currently, when using more iterations, things easily turn into a warpy mess. ;)

foxyshadis
13th July 2006, 01:10
It originally did! And it turned into a big warpy mess, so I just dropped it. :p But I agree, while it's less oversharpened now the warping is actually worse now unless you split it into two separate calls, with wsharp=0 in the first. I updated because it was better, but didn't trumpet it largely for that reason, and that's what I intend to look at when I next have a chance to hack at it.

Thanks for the hint though - I was trying to find a way to calculate the shifts right at the end, and eventually gave up (I came up with the equation, but with all the mod-x resizing I wasn't sure it'd work). Much better to do it when the shift occurs! Duh. Since I do both directions at once I'll just use the shift kernel both ways.