c++ffmpeginterpolationimage-resizingbicubic

Bicubic interpolation results different from FFMPEG


I just implemented bicubic interpolation for resizing images. I have a test image 6x6 pixels (grayscale), its columns are black and white (x3). I am comparing the results of my code with the results from the tool ffmpeg and they are not correct. I can not understand why, I think I may be calculating the neighbourhood of pixels wrong or maybe the distance of the resized pixel to the original ones. Can someone look into my code (I will simplify it for better reading) and tell me where the error is?

    // Iterate through each line
    for(int lin = 0; lin < dstHeight; lin++){
        // Original coordinates
        float linInOriginal = (lin - 0.5) / scaleHeightRatio;

        // Calculate original pixels coordinates to interpolate
        int linTopFurther = clamp(floor(linInOriginal) - 1, 0, srcHeight - 1);
        int linTop = clamp(floor(linInOriginal), 0, srcHeight - 1);
        int linBottom = clamp(ceil(linInOriginal), 0, srcHeight - 1);
        int linBottomFurther = clamp(ceil(linInOriginal) + 1, 0, srcHeight - 1);

        // Calculate distance to the top left pixel
        float linDist = linInOriginal - floor(linInOriginal);

        // Iterate through each column
        for(int col = 0; col < dstWidth; col++){
            // Original coordinates
            float colInOriginal = (col - 0.5) / scaleWidthRatio;

            // Calculate original pixels coordinates to interpolate
            int colLeftFurther = clamp(floor(colInOriginal) - 1, 0, srcWidth - 1);
            int colLeft = clamp(floor(colInOriginal), 0, srcWidth - 1);
            int colRight = clamp(ceil(colInOriginal), 0, srcWidth - 1);
            int colRightFurther = clamp(ceil(colInOriginal) + 1, 0, srcWidth - 1);

            // Calculate distance to the top left pixel
            float colDist = colInOriginal - floor(colInOriginal);

            // Gets the original pixels values
            // 1st row
            uint8_t p00 = srcSlice[0][linTopFurther * srcWidth + colLeftFurther];
            // ...

            // 2nd row
            uint8_t p01 = srcSlice[0][linTop * srcWidth + colLeftFurther];
            // ...

            // 3rd row
            // ...

            // 4th row
            // ...

            // Bilinear interpolation operation
            // Y
            float value = cubicInterpolate(
                cubicInterpolate(static_cast<float>(p00), static_cast<float>(p10), static_cast<float>(p20), static_cast<float>(p30), colDist),
                cubicInterpolate(static_cast<float>(p01), static_cast<float>(p11), static_cast<float>(p21), static_cast<float>(p31), colDist),
                cubicInterpolate(static_cast<float>(p02), static_cast<float>(p12), static_cast<float>(p22), static_cast<float>(p32), colDist),
                cubicInterpolate(static_cast<float>(p03), static_cast<float>(p13), static_cast<float>(p23), static_cast<float>(p33), colDist),
                linDist);

            dstSlice[0][lin * dstWidth + col] = double2uint8_t(clamp(value, 0.0f, 255.0f));
        }
    }

Solution

  • I was forgetting to set the values of the second degree variables of the interpolation matrix. They were set to 0, so the resulting interpolation would resemble the bilinear interpolation.