Archived

This topic is now archived and is closed to further replies.

samgzman

fast 2x image scaling algorithm

Recommended Posts

does anyone know of any super fast image scaling algorithm? I am simply trying to scale a set of grayscal data by a 2x. 32x32 -> 64x64. I dont really care if blurring occurs..in fact it would actually be beneficial in my case. Any help would be awsome.

Share this post


Link to post
Share on other sites
Blurring will slow things down. It''s usually a desired effect.

Since it''s greyscale, can we assume your data is 8 bits per pixel?

There is no really fast way that I know of to do this, aside from copying row by row, col by col.

That said, you can reduce the amount of overhead by doing something like this:

srcPtr = pointer to first Pixel In Image;
dstRowPtrA = pointer to first pixel in destination;
char color;
for ( y = 0; x < imageHeight; y++)
{
for ( x = 0; x < imageWidth; x++)
{
color = *srcPtr;
*dstRowPtrA = color;
*(dstRowPtrA + width of scaled image in bytes) = color;

dstRowPtrA ++;
srcPtr++;
}

// Skip a row //
dstRowPtrA += size of scaled image in bytes
}

Cheers,
Will

Share this post


Link to post
Share on other sites
One way would be to use your favourite graphics API(opengl or Directx). Create a texture of your object, and render a quad to a render texture of the desired target size. If you want blurring you can look at the many filtering options for textures that are available.

Share this post


Link to post
Share on other sites
Loop through each pixel in the source image and make 4 pixels at a time in the destination image. To add blurring, you can use simple bilinear filtering. Imagine you drag out the source image to its final size (x2) and there are holes around every pixel. Interpolate between the existing pixels to fill the holes and you got your blurred image.

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
the code u posted RPGeezus didnt make much sense to me in following it logically, but it did it anyhow and here is what i got

this is the original image at 32x32:
<img src="http://invis.free.anonymizer.com/http://zurph.tripod.com/good.jpg"> and then final using ur code at 64x64:
<img src="http://invis.free.anonymizer.com/http://zurph.tripod.com/bad.jpg">

My code as it is:
	
m_ucTest1 = new unsigned char[32*32];
m_ucTest2 = new unsigned char[64*64];

// m_ucTest1 is filled here

unsigned char* srcPtr = &m_ucTest1[0];
unsigned char* dstRowPtrA = &m_ucTest2[0];


unsigned char color;
for (int y = 0; y < 32; y++)
{
for (int x = 0; x < 32; x++)
{
color = *srcPtr;
*dstRowPtrA = color;
*(dstRowPtrA + 64) = color;
dstRowPtrA++;
srcPtr++;
}
dstRowPtrA += 64;
}



Share this post


Link to post
Share on other sites
You need to change this:

*dstRowPtrA = color;
*(dstRowPtrA + 64) = color;

to this:

int i;
for(i=0;i<64;i++)
*(dstRowPtrA+i)=color;

then in the y part of the loop you need to increase by screen width, not image width.

Share this post


Link to post
Share on other sites
here, use mine one - this code also draws the scaled image on a Win32 HWND/DC, via setpixel to check the result (setpixel is very slow!not realtime suitable! change this line, if needed)
it scales on both directions to arbitrary sizes, even overscaling is supported.


void ScaleRGBImageF( int posX, int posY, int sizeX, int sizeY, unsigned char *imagedata, HWND hWnd, int newSizeX, int newSizeY )
{
HDC tempDC = GetDC( hWnd );
long lBytePos = 0;
float xScaleFactor = 0.0f;
float yScaleFactor = 0.0f;
long sx = 0; // set counter x
long sy = 0; // set counter y
float curXPos = 0.0f;
float curYPos = 0.0f;
long srcBytePos = 0;
long dstBytePos = 0;

xScaleFactor = (float)sizeX / (float)newSizeX;
yScaleFactor = (float)sizeY / (float)newSizeY;

// pixel loop
for( sy=0; sy {
for( sx=0; sx {
srcBytePos = (((int)curYPos*sizeX)+(int)curXPos)*3;
dstBytePos = ((sy*newSizeX)+sx)*3;
SetPixel( tempDC, sx+posX, sy+posY, RGB( imagedata[srcBytePos], imagedata[srcBytePos+1], imagedata[srcBytePos+2]) );
curXPos += xScaleFactor;
}
curXPos = 0.0f;
curYPos += yScaleFactor;
}
}

int posX, int posY = position where to draw on screen, in pixels
int sizeX, int sizeY = original size, in pixels
unsigned char *imagedata = pointer to RGB imagedata
HWND hWnd = the Win32 HWND for drawing
int newSizeX, int newSizeY = bew scaled size, in pixels



DJSnow
---
this post is manually created and therefore legally valid without a signature

[edited by - DJSnow on January 6, 2004 9:19:37 PM]

Share this post


Link to post
Share on other sites
The best way I found was to create two arrays of scaled offsets, and translate them into the bitmap and onto the screen...


BYTE* pImage = LoadImage("image.bmp");
BYTE* pScreen = GetScreen();
int iXOffsets[64];
int iYOffsets[64];
int loop = 0;

// look-up tables
float fScaleX = 64 / 0.5f;
float fScaleY = 64 / 0.5f;
for (loop = 0; loop < 64; loop++)
{
iXOffsets[loop] = loop * fXScale; // optimize this
iYOffsets[loop] = loop * fYScale * 64; // optimize this
}

// use look-up tables to render image
for (int iy = 0, int iy2 = 0; iy < 64; iy++, iy2+=SCREEN_WIDTH)
{
for (int ix = 0; ix < 64; ix++)
{
// WARNING: the offsets may off the edge of the image =+GPF
pScreen[ix + iy2] = pImage[iXOffsets[ix] + iYOffsets[iy]];
}
}


.. something like that.

R





Share this post


Link to post
Share on other sites
Hmm, I''d just do something like

BYTE px1, px2, *src = img, *srcRowEnd, *srcColEnd = img + 32 * 32;
DWORD *dest = destImg, pxOut;

while(src < srcColEnd)
{
srcRowEnd = src + 32;
while(src < srcRowEnd)
{
px1 = *src++;
px2 = *src++;
pxOut = px1 | (px1 << 8) | (px2 << 16) | (px2 << 24);
dest[16] = pxOut; //next row (16*4 bytes)
*dest++ = pxOut; //current row, and increment
}
dest += 16;
}

That''s about as fast as I can get it. It goes along reading 2 pixels at a time, and then making a temp variable that has both of those pixels scaled to 2x, and then writes that temp to the current dest pos, and to one row down, to scale 2x in the Y direction too. Then of course you have to skip over that second line you were writing to before moving onto the next line, so you don''t write over it.
That will just make each pixel into 4 pixels of the same color though, no filtering, though that wouldn''t be too hard to add, just a little slower. You could use shifts to do your averaging though, since it''s to exactly 2x. So just take like px1, ((px1+px2)/2, px2 and ((px2+px3)/2 for the first 4 pixels of the first dest row, then do the next source row like that, which woudl be 2 rows down on the dest image, and then average between those 2 sets of pixels for the middle row, and repeat.

Share this post


Link to post
Share on other sites
quote:
Original post by Anonymous Poster
the code u posted RPGeezus didnt make much sense to me in following it logically, but it did it anyhow and here is what i got

My code as it is:
	
m_ucTest1 = new unsigned char[32*32];
m_ucTest2 = new unsigned char[64*64];

// m_ucTest1 is filled here

unsigned char* srcPtr = &m_ucTest1[0];
unsigned char* dstRowPtrA = &m_ucTest2[0];


unsigned char color;
for (int y = 0; y < 32; y++)
{
for (int x = 0; x < 32; x++)
{
color = *srcPtr;
*dstRowPtrA = color;
*(dstRowPtrA + 64) = color;
dstRowPtrA++;
srcPtr++;
}
dstRowPtrA += 64;
}






My bad. Sorry, I wrote this code off the top of my head. I was trying to give you an idea as to how it might be done, not code to actually do it.

In any case, I left out two lines: in the main loop, where we set the color in the destination, were setting x,y and x,y+1. We also need to set x+1,y and x+1,y+1. In doing this, you'll also need to increment dstRowPtrA not by one (++) but by two (+= 2). This 'should' fix your problem.


unsigned char color;
for (int y = 0; y < 32; y++)
{
for (int x = 0; x < 32; x++)
{
color = *srcPtr;
*dstRowPtrA = color;
*(dstRowPtrA + 64) = color;
*(dstRowPtrA +1) = color;
*(dstRowPtrA + 64 + 1) = color;
dstRowPtrA += 2;
srcPtr++;
}
dstRowPtrA += 64;
}




In doing this you might notice that we could actually replace the four *setPixel lines with TWO lines by chainging:

char *dstRowPtrA
to
short *dstRowPtrA

and

unsigned char color
to
short color

then

color = *srcData // Same as before
color += color << 8; // This effectivley duplicates the color byte (0x00aa will become 0xaaaa).

then revert the += 2 in dstRowPtrA back to ++ (because a short is two bytes).

Then, outside of the for ( x= loop, change the dstRowPtrA += 64 to dstRowPtrA += 32; This is because when you increment a pointer, it increments to the next record-- that is, if you had a long *ta, and said ta++, ta would actually increment four bytes (the length of a long).

This might speed up the code a bit.

Sorry for the snafu. I was tryping off the top of my head.

Will

[edited by - RPGeezus on January 8, 2004 12:10:54 PM]

Share this post


Link to post
Share on other sites