PDA

View Full Version : AdaptiveResize: Idea for an edge aware resizer


bcn_246
30th November 2010, 16:21
First post here in years. Based largely on HybridResize. Uses NNEDI3 for image edges and Lanczos(3) for the rest of the image.


### AdaptiveResize v0.1 (alpha)
### ---------------------------
### by bcn_246
###
### Resizes image edges using NNEDI3 and the rest using Lanczos(3)
###
### This is the first release of this filter. If it is of any use I will
### contuine to work with it.
###
### Usage:
### "AdaptiveResize" followed by (output width,output height). i.e. "AdaptiveResize(1280,720)"
###
### Requires:
### * NNEDI3 v0.9.2.0
### * MaskTools v1.5.1.0
### * AviSynth v2.57
###
### Thanks to mf and LaTo_INV, the following is largely based on code by them.
###

function AdaptiveResize(clip input, int outwidth, int outheight)
{
inwidth = input.width
inheight = input.height

factx = float(outwidth)/float(inwidth)
facty = float(outheight)/float(inheight)
calcrf = (factx > facty) ? factx : facty

resizemask = input.edgemask(0, type="sobel").inflate.levels(0, 1.0, 35, 0, 255)
\. lanczosresize(outwidth, outheight).greyscale.converttorgb32

resizemaskinverse = resizemask.levels(0, 1.0, 255, 255, 0)

nnedi3 = input.nnedi3_rpow2(rfactor=ceil(calcrf/2.0)*2, cshift="spline64resize",
\ fwidth=outwidth, fheight=outheight)
lanczos = input.lanczosresize(outwidth, outheight)

nnedi3rgb = nnedi3.converttorgb32
lanczosrgb = lanczos.converttorgb32

maskednnedi3 = mask(nnedi3rgb, false ? resizemaskinverse : resizemask)
edgednnedi3 = layer(lanczosrgb, maskednnedi3, "add", 255)

resizedyv12 = nnedi3.mergeluma(edgednnedi3.converttoyv12)

return resizedyv12
}

Original Filename: AdaptiveResize_v0.1_alpha.avsi

Can think of loads of other options/tweaks for this. It it proves to be of much use I will keep working with it.

Pretty slow, largely due to NNEDI3... tested only on x86, will have a look at x64.

- Ben

Dark Shikari
30th November 2010, 21:11
NNEDI already uses NNEDI for image edges (and other hard areas), and bicubic for the rest of the image.

dansrfe
30th November 2010, 23:18
NNEDI already uses NNEDI for image edges (and other hard areas), and bicubic for the rest of the image.

lol, yea that's true

Didée
30th November 2010, 23:21
Without judging whether or not this is useful ...

What I see is

a) usage of outdated (and slow) MaskTools v1 instead of MaskTools v2, and

b) the original convoluted / suboptimal /unnecessarily complicated masking-style of mf


Same script with optimized workflow via MaskTools v2: no levels, no back-and-forth from YUV to RGB and back, ...
function AdaptiveResize_optimized(clip input, int outwidth, int outheight)
{
inwidth = input.width
inheight = input.height

factx = float(outwidth)/float(inwidth)
facty = float(outheight)/float(inheight)
calcrf = (factx > facty) ? factx : facty

resizemask = input.mt_edge("sobel",0,255,0,255,U=1,V=1).mt_inflate().mt_lut("x 255 35 / *")
\ .lanczosresize(outwidth, outheight)

nnedithree = input.nnedi3_rpow2(rfactor=ceil(calcrf/2.0)*2, cshift="spline64resize",
\ fwidth=outwidth, fheight=outheight)
lanczos = input.lanczosresize(outwidth, outheight)

mt_merge(lanczos,nnedithree,resizemask,U=4,V=4) # UV=4 since the original script also used NNEDI's chroma everywhere
return( last )
}
Speed improvement overall: 8% faster - not much, since most time goes into NNEDI3.

Looking only at the script framework, completely knocking-out NNEDI3, the speed is more than doubled: before 24.1 fps, after 53.2 fps. Framework's speed improved by factor 2.2.

bcn_246
1st December 2010, 01:23
Thanks for the feedback, and for working with the script Didée. The one I posted was basically just HybirdResize with NNEDI3 instead of Lanczos and Lanczos instead of Billinear. I am well aware it is nothing all that original. Just (as many scripts are) an alias for a chain of filters.

While I have been using AviSynth for some time I am pretty new to writing anything other than a chain of standard filters for some video. While it is really just me playing around I would rather make something that was of some use. Maybe adding some options/other functions to choose speed/quality of interpolator... GPU for the Lanczos... masking threshold... post-sharpening... AntiAliasing the edges prior to interpolation etc...

What I am wondering is if such a filter would be of much, if any, use.

Ben

bcn_246
1st December 2010, 04:12
Tried your updated version Didee... found that when using anything other than YV12 as an input MaskTools throws up an error.

While converting back and forth from YV12 -> RGB32 (as in the first script) isn't necessary I found that ConvertToYV12 was still required before the 3 processes (resizemask, nnedithree and lanczos).

### AdaptiveResize v0.2 (beta)
### --------------------------
### by bcn_246
###
### Resizes image edges using NNEDI3 and the rest using Lanczos(3)
###
### Usage:
### "AdaptiveResize" followed by (output width,output height).
### i.e. "AdaptiveResize(1280,720)"
###
### Requires:
### * NNEDI3
### * MaskTools v2
### * AviSynth
###
### Thanks to mf, LaTo_INV and Didée.
###
function AdaptiveResize(clip input, int outwidth, int outheight)
{
inwidth = input.width
inheight = input.height

factx = float(outwidth)/float(inwidth)
facty = float(outheight)/float(inheight)
calcrf = (factx > facty) ? factx : facty

resizemask = input.converttoyv12.mt_edge("sobel",0,255,0,255,U=1,V=1).mt_inflate().mt_lut("x 255 35 / *")
\ .lanczosresize(outwidth, outheight)

nnedithree = input.converttoyv12.nnedi3_rpow2(rfactor=ceil(calcrf/2.0)*2, cshift="spline64resize"
\ ,fwidth=outwidth, fheight=outheight)

lanczos = input.converttoyv12.lanczosresize(outwidth, outheight)

mt_merge(lanczos,nnedithree,resizemask,u=4,v=4)
return( last )
}


- Ben

Gavino
1st December 2010, 09:19
While converting back and forth from YV12 -> RGB32 (as in the first script) isn't necessary I found that ConvertToYV12 was still required before the 3 processes (resizemask, nnedithree and lanczos).
Only if you want the function to accept non-YV12 input.
But since the output is always YV12, you might as well restrict the input to YV12 as well (use Assert(IsYV12(input)).

Otherwise you should convert the output back to the input format - a function that changes the colorspace behind the user's back is a bad idea.

Didée
1st December 2010, 10:33
Support for YUY2 is not difficult. Could be done with Convert*InterleavedToPlanar/*PlanarToInterleaved ... or like this:

inwidth = input.width
inheight = input.height

factx = float(outwidth)/float(inwidth)
facty = float(outheight)/float(inheight)
calcrf = (factx > facty) ? factx : facty

work = isYUY2(input) ? input.ConvertToYV12() : input

resizemask = work.mt_edge("sobel",0,255,0,255,U=1,V=1).mt_inflate()
\ .mt_lut("x 255 35 / *")
\ .lanczosresize(outwidth, outheight)

nnedithree = input.nnedi3_rpow2(rfactor=ceil(calcrf/2.0)*2, cshift="spline64resize"
\ ,fwidth=outwidth, fheight=outheight)

lanczos = work.lanczosresize(outwidth, outheight)

isYUY2(input) ? mt_merge(lanczos,nnedithree.ConvertToYV12(),resizemask,u=1,v=1).ConvertToYUY2().MergeChroma(nnedithree)
\ : mt_merge(lanczos,nnedithree,resizemask,u=4,v=4)
return( last )
(It's rather easy here, since the original script didn't use edge-masking for chroma.) ;)

RGB in ---> not supported, add some Assert() if you like
YUY2 in --> YUY2 out
YV12 in --> YV12 out