Author |
Topic: Image scaling (bilinear interpolation) (Read 1840 times) |
|
David Williams
Developer
member is offline

meh

Gender: 
Posts: 452
|
 |
Image scaling (bilinear interpolation)
« Thread started on: Dec 28th, 2012, 03:44am » |
|
Here's the BASIC prototype intended for translation into assembly language (non-MMX as usual; too beyond my abilities). I don't think I'm interested in discussions about the merits (or otherwise) of this interpolation method over any other, I'm just glad I've got it working!
If you've got the GFXLIB package installed then you can copy, paste and run this program.
I'm guessing that, despite a hell of a lot of CPU cycles per pixel drawn, the non-MMX assembler version should be fast enough for my purposes.
Code:
REM. Version 1.1
REM.
REM. Bilinear interpolating image scaler (rough BASIC prototype),
REM. to be translated to assembly language.
REM.
REM. Requires GFXLIB (version 2.03 or higher)
HIMEM = PAGE + 5*&100000
MODE 8 : OFF
INSTALL @lib$ + "GFXLIB2" : PROCInitGFXLIB
INSTALL @lib$ + "GFXLIB_modules\PlotScale" : PROCInitModule
img% = FNLoadImg( @lib$+"GFXLIB_media\gfxlib_480x128x8.BMP", 0)
w% = 480
h% = 128
w2 = 0.8 * w% : w2% = w2
h2 = 0.8 * h% : h2% = h2
img2% = FNmalloc( 4 * (w2%+1)*(h2%+1) )
xr% = ((w% - 1) / w2) * 65536
yr% = ((h% - 1) / h2) * 65536
B% = img2%
PRINT '" Scaling image, please wait..."
FOR I% = 0 TO h2%-1
Y% = (yr% * I%) AND &FFFF0000
dy% = ((yr%*I% - Y%) << 8) >> 16
dy_% = 255 - dy%
Y% = Y% >> 16
FOR J% = 0 TO w2%-1
X% = (xr% * J%) AND &FFFF0000
dx% = ((xr%*J% - X%) << 8) >> 16
X% = X% >> 16
dx_% = 255 - dx%
dxdy% = dx% * dy%
dxdy_% = dx% * dy_%
dx_dy% = dx_% * dy%
dx_dy_% = dx_% * dy_%
A% = img% + 4*(Y%*w% + X%)
a& = ?A%
b& = A%?4
c& = A%?(4*w%)
d& = A%?(4*w% + 4)
?B% = ( a&*dx_dy_% + b&*dxdy_% + c&*dx_dy% + d&*dxdy% ) >> 16
a& = ?(A% + 1)
b& = A%?5
c& = A%?(4*w% + 1)
d& = A%?(4*w% + 5)
B%?1 = ( a&*dx_dy_% + b&*dxdy_% + c&*dx_dy% + d&*dxdy% ) >> 16
a& = ?(A% + 2)
b& = A%?6
c& = A%?(4*w% + 2)
d& = A%?(4*w% + 6)
B%?2 = ( a&*dx_dy_% + b&*dxdy_% + c&*dx_dy% + d&*dxdy% ) >> 16
B% += 4
NEXT
NEXT
CLS
REM. Draw normal, non-scaled image
SYS GFXLIB_BPlot%, dispVars{}, img%, w%, h%, 0, 0
REM. Draw scaled image (nearest neighbour)
SYS GFXLIB_PlotScale%, dispVars{}, img%, w%, h%, w2%, h2%, 0, 170
REM. Draw scaled image (bilinear interpolation)
SYS GFXLIB_BPlot%, dispVars{}, img2%, w2%, h2%, 0, 310
PROCdisplay
PRINT '" Bottom image is normal size (not scaled)"
PRINT " Middle image is scaled using nearest-neighbour algorithm"
PRINT " Top image is scaled using bilinear interpolation"
If anyone spots any optimizations (non-BASIC specific) or possible improvements in precision without too much CPU overhead, then please do tell.
Regards,
David.
|
|
|
|
David Williams
Developer
member is offline

meh

Gender: 
Posts: 452
|
 |
Re: Image scaling (bilinear interpolation)
« Reply #1 on: Dec 28th, 2012, 08:33am » |
|
Okay, here's a demo of the extremely sub-optimal, non-SIMD, assembly language version of the above BASIC prototype:
http://www.bb4wgames.com/misc/scaling_bilinear.zip
Sorry, no source code yet. Too messy.
David.
|
|
Logged
|
|
|
|
admin
Administrator
member is offline


Posts: 1145
|
 |
Re: Image scaling (bilinear interpolation)
« Reply #2 on: Dec 28th, 2012, 4:46pm » |
|
on Dec 28th, 2012, 03:44am, David Williams wrote:Here's the BASIC prototype |
|
As displayed on my PC, the bottom ('unscaled') image already has some obvious aliasing, particularly noticeable on the diagonal edges of the 'X' and the top of the 'G'. So it's not an ideal starting point!
It's also worth pointing out that whilst your 'original' image is perhaps typical of GFXLIB output it's a very poor test of the quality of a scaling algorithm - it still looks quite respectable with a nearest-neighbour method! A better choice would be either a circular or hyperbolic zone plate which will really separate the sheep from the goats:
Code: REM. Version 1.1
REM.
REM. Bilinear interpolating image scaler (rough BASIC prototype),
REM. to be translated to assembly language.
REM.
REM. Requires GFXLIB (version 2.03 or higher)
HIMEM = PAGE + 5*&100000
MODE 8 : OFF
INSTALL @lib$ + "GFXLIB2" : PROCInitGFXLIB
INSTALL @lib$ + "GFXLIB_modules\PlotScale" : PROCInitModule
img% = FNLoadImg( @lib$+"GFXLIB_media\gfxlib_480x128x8.BMP", 0)
FOR Y% = 0 TO 127
FOR X% = 0 TO 479
r2 = (Y%-64)^2 + (X%-240)^2
V% = &FF * (1 + SIN(r2/240)) / 2 + 0.5
img%!(Y%*&780+X%*4) = V% + (V% << 8) + (V% << 16)
NEXT
NEXT
w% = 480
h% = 128
w2 = 0.8 * w% : w2% = w2
h2 = 0.8 * h% : h2% = h2
img2% = FNmalloc( 4 * (w2%+1)*(h2%+1) )
xr% = ((w% - 1) / w2) * 65536
yr% = ((h% - 1) / h2) * 65536
B% = img2%
PRINT '" Scaling image, please wait..."
FOR I% = 0 TO h2%-1
Y% = (yr% * I%) AND &FFFF0000
dy% = ((yr%*I% - Y%) << 8) >> 16
dy_% = 255 - dy%
Y% = Y% >> 16
FOR J% = 0 TO w2%-1
X% = (xr% * J%) AND &FFFF0000
dx% = ((xr%*J% - X%) << 8) >> 16
X% = X% >> 16
dx_% = 255 - dx%
dxdy% = dx% * dy%
dxdy_% = dx% * dy_%
dx_dy% = dx_% * dy%
dx_dy_% = dx_% * dy_%
A% = img% + 4*(Y%*w% + X%)
a& = ?A%
b& = A%?4
c& = A%?(4*w%)
d& = A%?(4*w% + 4)
?B% = ( a&*dx_dy_% + b&*dxdy_% + c&*dx_dy% + d&*dxdy% ) >> 16
a& = ?(A% + 1)
b& = A%?5
c& = A%?(4*w% + 1)
d& = A%?(4*w% + 5)
B%?1 = ( a&*dx_dy_% + b&*dxdy_% + c&*dx_dy% + d&*dxdy% ) >> 16
a& = ?(A% + 2)
b& = A%?6
c& = A%?(4*w% + 2)
d& = A%?(4*w% + 6)
B%?2 = ( a&*dx_dy_% + b&*dxdy_% + c&*dx_dy% + d&*dxdy% ) >> 16
B% += 4
NEXT
NEXT
CLS
REM. Draw normal, non-scaled image
SYS GFXLIB_BPlot%, dispVars{}, img%, w%, h%, 0, 0
REM. Draw scaled image (nearest neighbour)
SYS GFXLIB_PlotScale%, dispVars{}, img%, w%, h%, w2%, h2%, 0, 170
REM. Draw scaled image (bilinear interpolation)
SYS GFXLIB_BPlot%, dispVars{}, img2%, w2%, h2%, 0, 310
PROCdisplay
PRINT '" Bottom image is normal size (not scaled)"
PRINT " Middle image is scaled using nearest-neighbour algorithm"
PRINT " Top image is scaled using bilinear interpolation" Richard.
|
|
Logged
|
|
|
|
admin
Administrator
member is offline


Posts: 1145
|
 |
Re: Image scaling (bilinear interpolation)
« Reply #3 on: Dec 28th, 2012, 5:16pm » |
|
on Dec 28th, 2012, 08:33am, David Williams wrote:Okay, here's a demo of the extremely sub-optimal, non-SIMD, assembly language version of the above BASIC prototype |
|
OK, so that's the 'promotional video' - designed to make it look as good as possible. Now I want to see the same thing with the zone-plate image used as the source! 
Richard.
|
|
Logged
|
|
|
|
David Williams
Developer
member is offline

meh

Gender: 
Posts: 452
|
 |
Re: Image scaling (bilinear interpolation)
« Reply #4 on: Dec 28th, 2012, 6:34pm » |
|
Here's a demo (with source) using Richard's rather striking "zone-plate image":
http://www.bb4wgames.com/temp/scalebilinear.zip
Press <space bar> to toggle between nearest-neighbour and bilinear-interpolated scaling.
Demo2 features a SFW female face image. Overall, bilinear looks a bit better (though struggles with her hair a bit).
Bear in mind GFXLIB_ScaleBilinear isn't intended for real-time scaling, at least not in its current, nasty form.
Nice article on MMX-powered bi. interp.: http://software.intel.com/sites/landingpage/legacy/mmx/MMX_App_Bilinear_Interpolation_RGB.pdf
Even I can understand much of it!
Regards,
Daiv.d
|
|
|
|
admin
Administrator
member is offline


Posts: 1145
|
 |
Re: Image scaling (bilinear interpolation)
« Reply #5 on: Dec 28th, 2012, 9:51pm » |
|
on Dec 28th, 2012, 6:34pm, David Williams wrote:Here's a demo.... Demo2 features a SFW female face image.... |
|
Thanks. I would imagine the quality jump from 'nearest neighbour' to 'bilinear' is more marked than any further improvement achievable by using a more complex algorithm.
Quote: Seems rather Dune-like. Do you wish always to be called Daiv.d from now on?
Richard.
|
|
Logged
|
|
|
|
David Williams
Developer
member is offline

meh

Gender: 
Posts: 452
|
 |
Re: Image scaling (bilinear interpolation)
« Reply #6 on: Dec 28th, 2012, 10:05pm » |
|
on Dec 28th, 2012, 9:51pm, Richard Russell wrote:Seems rather Dune-like. Do you wish always to be called Daiv.d from now on? |
|
It really was a typo.
David. --
|
|
Logged
|
|
|
|
|