Log in

View Full Version : lossless resizing (upscale, downscale)


2Bdecided
2nd April 2010, 13:20
This sounds such an easy problem, so hopefully I'm just being daft, because I'm finding it almost impossible to solve.

Let's say I want to process something at double height (or double width, or double size!), and then return to the original height.

Obviously(!) if the process becomes a NOP, then I want the whole thing to become a NOP. Therefore double_height followed by halve_height must be a NOP.

It works fine with point resize...
pointresize(width(last),height(last)*2)

#processing goes here

pointresize(width(last),height(last)/2)
...but I don't want to upscale using point resize! I want the "new" pixels to be interpolated (e.g. bilinear, bicubic, etc upsize), not duplicated.

This turns out to be really tricky!

This works...
nnedi2(field=1,dh=true)

#processing goes here

pointresize(width(last),height(last)/2)
...but nnedi2 is overkill.

This doesn't work...
bilinearresize(width(last),height(last)*2)

#processing goes here

pointresize(width(last),height(last)/2)
...you get a sub-pixel shift, and interpolated (rather than original) pixels come out at the end.

Any ideas?

Cheers,
David.

Didée
2nd April 2010, 15:48
up2 = input.lanczosresize(width*2,width*2, 0.25,0.25) # for RGB: 0.25,-0.25 (RGB is stored upside down)
down = up2.pointresize(width,height)

There still is some minimal loss on chroma, due to the chroma subsampling. Check with "mt_makediff(input,down,U=3,V=3).ColorYUV(analyze=true)" - it shows some numbers for U/V, but there's really not much to make out with the naked eye.

Gavino
2nd April 2010, 16:15
...you get a sub-pixel shift, and interpolated (rather than original) pixels come out at the end.
That's because the resizers (except PointResize) preserve the image centre position. You need to compensate the sub-pixel shift by using an appropriate offset, as Didée has shown.
up2 = input.lanczosresize(width*2,width*2, 0.25,0.25) # for RGB: 0.25,-0.25 (RGB is stored upside down)
(Obviously, he meant the second width*2 to be height*2)
There still is some minimal loss on chroma, due to the chroma subsampling.
Yes, you need different offsets for the chroma.
For YUY2, add:
up2 = up2.MergeChroma(input.lanczosresize(width*2,height*2, 0.5,0.25))
For YV12:
up2 = up2.MergeChroma(input.lanczosresize(width*2,height*2, 0.5,0.5))

2Bdecided
2nd April 2010, 17:27
That's because the resizers (except PointResize) preserve the image centre position.So that's why (apart from pointresize) you end up with all interpolated data when doubling the size of one or both dimensions?

Makes sense.

But it makes what I'm trying to do quite painful!

Will all resizers work "losslessly" with the same offsets? Or does it depends on the resizer? i.e. was there a reason you used lanczosresize Didée?

Cheers,
David.

Gavino
2nd April 2010, 18:37
Will all resizers work "losslessly" with the same offsets? Or does it depends on the resizer?
It works (and with same offsets) for all resizers that act as pure interpolators (their kernels have zero crossings at integer distances from the center), for which source pixels are preserved where the resampling points coincide.

This means everything except BicubicResize with b/=0 and GaussResize with values of p less than about 80.