0

I found an image resampling algorithm that produces an extremely nice image when down-sizing. It works on a 3-channel image that's padded to 4 bytes per pixel. What do i change to make this work for a image that's not padded to 4 bytes per channel? Like a jpeg image. The destination image is 4-byte padded. Also, what name would you give this algorithm?

Originally found here: http://www.geisswerks.com/ryan/FAQS/resize.html

Code simplified:

void Resize_HQ(unsigned char* src, int w1, int h1, unsigned char* dest, int w2, int h2)
{
    // arbitrary resize.
    unsigned int *dsrc  = (unsigned int *)src;
    unsigned int *ddest = (unsigned int *)dest;

    bool bUpsampleX = (w1 < w2);
    bool bUpsampleY = (h1 < h2);

    // If too many input pixels map to one output pixel, our 32-bit accumulation values
    // could overflow - so, if we have huge mappings like that, cut down the weights:
    //    256 max color value
    //   *256 weight_x
    //   *256 weight_y
    //   *256 (16*16) maximum # of input pixels (x,y) - unless we cut the weights down...
    int weight_shift = 0;
    float source_texels_per_out_pixel = (   (w1/(float)w2 + 1) 
    * (h1/(float)h2 + 1)
    );
    float weight_per_pixel = source_texels_per_out_pixel * 256 * 256; //weight_x * weight_y
    float accum_per_pixel = weight_per_pixel*256; //color value is 0-255
    float weight_div = accum_per_pixel / 4294967000.0f;
    if (weight_div > 1)
        weight_shift = (int)ceilf( logf((float)weight_div)/logf(2.0f) );
    weight_shift = min(15, weight_shift);  // this could go to 15 and still be ok.

    float fh = 256*h1/(float)h2;
    float fw = 256*w1/(float)w2;

    // FOR EVERY OUTPUT PIXEL
    for (int y2=0; y2<h2; y2++)
    {   
        // find the y-range of input pixels that will contribute:
        int y1a = (int)((y2  )*fh); 
        int y1b = (int)((y2+1)*fh); 
        if (bUpsampleY) // map to same pixel -> we want to interpolate between two pixels!
            y1b = y1a + 256;
        y1b = min(y1b, 256*h1 - 1);
        int y1c = y1a >> 8;
        int y1d = y1b >> 8;

        for (int x2=0; x2<w2; x2++)
        {
            // find the x-range of input pixels that will contribute:
            int x1a = (int)((x2  )*fw); 
            int x1b = (int)((x2+1)*fw); 
            if (bUpsampleX) // map to same pixel -> we want to interpolate between two pixels!
                x1b = x1a + 256;
            x1b = min(x1b, 256*w1 - 1);
            int x1c = x1a >> 8;
            int x1d = x1b >> 8;

            // ADD UP ALL INPUT PIXELS CONTRIBUTING TO THIS OUTPUT PIXEL:
            unsigned int r=0, g=0, b=0, a=0;
            for (int y=y1c; y<=y1d; y++)
            {
                unsigned int weight_y = 256;
                if (y1c != y1d) 
                {
                    if (y==y1c)
                        weight_y = 256 - (y1a & 0xFF);
                    else if (y==y1d)
                        weight_y = (y1b & 0xFF);
                }

                unsigned int *dsrc2 = &dsrc[y*w1 + x1c];
                for (int x=x1c; x<=x1d; x++)
                {
                    unsigned int weight_x = 256;
                    if (x1c != x1d) 
                    {
                        if (x==x1c)
                            weight_x = 256 - (x1a & 0xFF);
                        else if (x==x1d)
                            weight_x = (x1b & 0xFF);
                    }

                    unsigned int c = *dsrc2++;//dsrc[y*w1 + x];
                    unsigned int r_src = (c    ) & 0xFF;
                    unsigned int g_src = (c>> 8) & 0xFF;
                    unsigned int b_src = (c>>16) & 0xFF;
                    unsigned int w = (weight_x * weight_y) >> weight_shift;
                    r += r_src * w;
                    g += g_src * w;
                    b += b_src * w;
                    a += w;
                }
            }

            // write results
            unsigned int c = ((r/a)) | ((g/a)<<8) | ((b/a)<<16);
            *ddest++ = c;//ddest[y2*w2 + x2] = c;
        }
    }
}

I tried changing:

unsigned int *dsrc2 = &dsrc[y*w1 + x1c];

To:

unsigned char *dsrc2 = (unsigned char *) &dsrc[y*w1 + x1c];

And:

unsigned int c = *dsrc2++;

To:

unsigned int c = *(unsigned int *)dsrc2;
dsrc2 += 3;

Still didn't work... :|

b.sullender
  • 163
  • 1
  • 16

1 Answers1

1

The original code uses 3 bytes for color, and one byte for alpha or padding, for 4 bytes per pixel. It reads 32-bit integers, and masks the colors out. You don't have that 4th byte, so you need to change things to read from unsigned chars.

Change dsrc and ddest to be an unsigned char *. The declaration for dsrc2 then becomes

unsigned char *dsrc2 = &dsrc[(y*w1 + x1c) * 3];

Throw out the unsigned int c (both places). Replace the r_src etc with

                unsigned int r_src = *dsrc2++;
                unsigned int g_src = *dsrc2++;
                unsigned int b_src = *dsrc2++;

Writing the results would also have to be a byte at a time

        *ddest++ = unsigned char(r / a);
        *ddest++ = unsigned char(g / a);
        *ddest++ = unsigned char(b / a);
1201ProgramAlarm
  • 32,384
  • 7
  • 42
  • 56
  • I think you made a typo, dsrc2 should be declared as unsigned char * also. After doing that your formula works. Thanks! – b.sullender Jan 15 '18 at 00:35