BBC BASIC for Windows
Programming >> Assembly Language Programming >> Image scaling (bilinear interpolation)
http://bb4w.conforums.com/index.cgi?board=assembler&action=display&num=1356669891

Image scaling (bilinear interpolation)
Post by David Williams 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.

Re: Image scaling (bilinear interpolation)
Post by David Williams 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.

Re: Image scaling (bilinear interpolation)
Post by admin 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.
Re: Image scaling (bilinear interpolation)
Post by admin 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! wink

Richard.

Re: Image scaling (bilinear interpolation)
Post by David Williams 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
Re: Image scaling (bilinear interpolation)
Post by admin 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:
Daiv.d

Seems rather Dune-like. Do you wish always to be called Daiv.d from now on?

Richard.

Re: Image scaling (bilinear interpolation)
Post by David Williams 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.
--