An overview of the

In the bilinear interpolation algorithm, the newly created pixel value of the target image is calculated by weighted average of the values of four adjacent pixels in the 2*2 region near the source image. Bilinear interpolation algorithm magnifies the image with high quality and no discontinuous pixel values.

Mathematical principles

Image rotation with bilinear interpolation

Above there are four known pixels, respectively, P (j, k), P (j, k + 1), P (j + 1, k), P (j + 1, k + 1). Now I have a point D, and I draw a vertical line that intersects Q1 and Q2, and D distance Q1 is u, and Q1 distance P(j,k) is t. In addition, the four P points form a unit square, with sides of length 1.

The idea is to figure out Q1 and Q2, and then figure out D.

Based on the weight of the distance, we can get

Q1 = P (j, k) * (1 - t) + P (j, k + 1) * Q2 (t) = P (j + 1, k) * (1 - t) + P (j + 1, k + 1) * (t) * (1 - u) + D = Q1 Q2 * (u) / / on the Q1 and Q2 generation into the type D = P(j,k)*(1-t)*(1-u) + P(j,k+1)*(t)*(1-u) + P(j+1,k)*(1-t)*(u) + p(j+1,k+1)*(t)*(u)Copy the code

The key code

I implemented this algorithm in Objective-C on iOS. First we need to get the RGBA data of the image, also known as RAW data. This is a one-dimensional array, but in practice we need to traverse it in two dimensions.

  1. Get image RGBA data
UInt32* pixelData = (UInt32 *)calloc(width * height, sizeof(UInt32));
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef context = CGBitmapContextCreate(pixelData,
                                                width,
                                                height,
                                                bitsPerComponent,
                                                bytesPerRow,
                                                colorSpace,
                                                kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);

CGContextDrawImage(context, CGRectMake(0, 0, width, height), cgOriginalImage);
Copy the code
  1. Calculate the width and height scaling constants
float rowRatio = ((float)sourceHeight) / ((float)desHeight);
float colRatio = ((float)sourceWidth) / ((float)desWidth);
Copy the code
  1. According to the scaling constant, find the position of the source image where the current traversal position is located. This position has decimals. Here we directly take the integer part, j is in the row of the original image, K is in the column of the original image, and the decimal part is u and T, so we have four original pixel positions.
double srcRow = ((float)row) * rowRatio;
double j = floor(srcRow);
double u = srcRow - j;

double srcCol = ((float)col) * colRatio;
double k = floor(srcCol);
double t = srcCol - k;
Copy the code
  1. Loop through all pixels to calculate the final pixel value
static UInt32* scaleImageWithLinearInterpolation(UInt32* pixelData, int sourceWidth, int sourceHeight, int desWidth, int desHeight) {
    
    float rowRatio = ((float)sourceHeight) / ((float)desHeight);
    float colRatio = ((float)sourceWidth) / ((float)desWidth);
    UInt32* rgba = (UInt32 *)calloc(desWidth * desHeight, sizeof(UInt32));
    int offset=0;
    for(int row = 0; row < desHeight; row++) {
        double srcRow = ((float)row) * rowRatio;
        double j = floor(srcRow);
        double u = srcRow - j;
        
        for (int col = 0; col < desWidth; col++) {
            
            double srcCol = ((float)col) * colRatio; double k = floor(srcCol); double t = srcCol - k; Double coffiecent1 = (1.0-t) * (1.0-u); Double coffiecent2 = (1.0-t) * u; double coffiecent3 = t * u; Double coffiecent4 = (t) * (1.0-u); // Color value of four corners UInt32 inputColor00 = pixelData[(getClip((int)j,sourceHeight - 1 , 0) * sourceWidth + getClip((int)k, sourceWidth - 1, 0))];
            UInt32 inputColor10 = pixelData[(getClip((int)(j+1), sourceHeight - 1 , 0) * sourceWidth + getClip((int)k, sourceWidth - 1, 0))];
            UInt32 inputColor11 = pixelData[(getClip((int)(j+1), sourceHeight - 1 , 0) * sourceWidth + getClip((int)(k+1), sourceWidth - 1, 0))];
            UInt32 inputColor01 = pixelData[(getClip((int)j, sourceHeight - 1 , 0) * sourceWidth + getClip((int)(k+1), sourceWidth - 1, 0))]; // New transparency UInt32 newA = (UInt32)(CoffiecENT1 * A(inputColor00) + CoffiecENT2 * A(inputColor10) + CoffiecENT3 * A(inputColor11) + coffiecent4 * A(inputColor01) ); Double r00 = R(inputColor00) * (255.0 / A(inputColor00)); Double r10 = R(inputColor10) * (255.0 / A(inputColor10)); Double r11 = R(inputColor11) * (255.0 / A(inputColor11)); Double r01 = R(inputColor01) * (255.0 / A(inputColor01)); UInt32 newR = (UInt32)(( coffiecent1 * r00 + coffiecent2 * r10 + coffiecent3 * r11 + coffiecent4 * r01 ) * (newA / 255.0)); Double g00 = G(inputColor00) * (255.0 / A(inputColor00)); Double g10 = G(inputColor10) * (255.0 / A(inputColor10)); Double g11 = G(inputColor11) * (255.0 / A(inputColor11)); Double g01 = G(inputColor01) * (255.0 / A(inputColor01)); UInt32 newG = (UInt32)(( coffiecent1 * g00 + coffiecent2 * g10 + coffiecent3 * g11 + coffiecent4 * g01 ) * (newA / 255.0)); // Double b00 = B(inputColor00) * (255.0 / A(inputColor00)); Double b10 = B(inputColor10) * (255.0 / A(inputColor10)); Double b11 = B(inputColor11) * (255.0 / A(inputColor11)); Double b01 = B(inputColor01) * (255.0 / A(inputColor01)); UInt32 newB = (UInt32)(( coffiecent1 * b00 + coffiecent2 * b10 + coffiecent3 * b11 + coffiecent4 * b01 ) * (newA / 255.0)); rgba[offset] = RGBAMake(newR, newG, newB, newA); offset++; }}return rgba;
}
Copy the code

The final result

Source Code

GitHub

Refer to the link

  • Image Processing in iOS Part 1: Raw Bitmap Modification
  • UIImage resize with hard edges
  • Capturing Uncompressed Image Data
  • Image Processing in C
  • Converting RGB data into a bitmap in Objective-C++ Cocoa
  • Converting Array of Pixel Brightness Values Back into UIImage?
  • Common image resampling algorithms