I am trying to implement interpolation of a bicubic image using cubic splines and a generalized formula for interpolation.
I was able to realize this using all 16 surrounding pixels and calculated the interpolation coefficients from these 16 function values. But since interpolation is separated by definition, I tried to implement a version that uses two one-dimensional interpolations to achieve bicubic interpolation. For a generalized interpolation formula, I use, for example, http://bigwww.epfl.ch/publications/thevenaz9901.pdf chapter 3 and for one-dimensional interpolation I use this idea: http://www.vision-systems.com/articles/print /volume-12/issue-10/departments/wilsons-websites/understanding-image-interpolation-techniques.html
My coefficients are calculated by inverting the 4x4 matrix, as in https://en.wikipedia.org/wiki/Spline_interpolation#Example
Interpolation consists of three functions.
The interpolation function itself (phi):
private float interpolate(float p_fValue)
{
p_fValue = Math.Abs(p_fValue);
if (p_fValue >= 1 && p_fValue < 2)
{
return 1.0f / 6.0f * (2.0f - p_fValue) * (2.0f - p_fValue) * (2.0f - p_fValue);
}
else if (p_fValue < 1)
{
return 2.0f / 3.0f - p_fValue * p_fValue + 0.5f * (p_fValue * p_fValue * p_fValue);
}
else
{
return 0.0f;
}
}
Calculation of 4 coefficients from 4 function values:
private void calculateCoefficients(float p_f1, float p_f2, float p_f3, float p_f4, out float p_c1, out float p_c2, out float p_c3, out float p_c4)
{
p_c1 = 6.0f / 209.0f * (56.0f * p_f1 - 15.0f * p_f2 + 4.0f * p_f3 - p_f4);
p_c2 = 6.0f / 209.0f * (-15.0f * p_f1 + 60.0f * p_f2 - 16.0f * p_f3 + 4.0f * p_f4);
p_c3 = 6.0f / 209.0f * (4.0f * p_f1 - 16.0f * p_f2 + 60.0f * p_f3 - 15.0f * p_f4);
p_c4 = 6.0f / 209.0f * (-p_f1 + 4.0f * p_f2 - 15.0f * p_f3 + 56.0f * p_f4);
}
And interpolating the whole image from the smaller source image:
public SampledImage InterpolateImage(SampledImage p_siImage, int p_iImageWidth, int p_iImageHeight)
{
SampledImage resultImage = new SampledImage((int)p_siImage.GetWidth(), (int)p_siImage.GetHeight(), p_iImageWidth, p_iImageHeight, p_siImage.GetPixelWidth(), p_siImage.GetPixelHeight());
for (int iX = 0; iX < p_iImageWidth; iX++)
{
for (int iY = 0; iY < p_iImageHeight; iY++)
{
float[] pixelSpace = resultImage.GetPixelInSpace(iX, iY);
float[] pixelRealIndex = p_siImage.GetPixelRealFromSpace(pixelSpace[0], pixelSpace[1]);
int x_2 = (int)Math.Floor(pixelRealIndex[0]) - 1;
int x_1 = (int)Math.Floor(pixelRealIndex[0]);
int x1 = (int)Math.Floor(pixelRealIndex[0]) + 1;
int x2 = (int)Math.Floor(pixelRealIndex[0]) + 2;
int y = (int)Math.Floor(pixelRealIndex[1]);
float[] red = new float[4];
float[] green = new float[4];
float[] blue = new float[4];
for (int iiX = -1; iiX < 3; iiX++)
{
int x = (int)Math.Floor(pixelRealIndex[0]) + iiX;
int y_2 = (int)Math.Floor(pixelRealIndex[1]) - 1;
int y_1 = (int)Math.Floor(pixelRealIndex[1]);
int y1 = (int)Math.Floor(pixelRealIndex[1]) + 1;
int y2 = (int)Math.Floor(pixelRealIndex[1]) + 2;
Color fxy_2 = p_siImage.GetValueMirrored(x, y_2);
Color fxy_1 = p_siImage.GetValueMirrored(x, y_1);
Color fxy1 = p_siImage.GetValueMirrored(x, y1);
Color fxy2 = p_siImage.GetValueMirrored(x, y2);
float redC_2, redC_1, redC1, redC2;
float greenC_2, greenC_1, greenC1, greenC2;
float blueC_2, blueC_1, blueC1, blueC2;
calculateCoefficients(fxy_2.R, fxy_1.R, fxy1.R, fxy2.R, out redC_2, out redC_1, out redC1, out redC2);
calculateCoefficients(fxy_2.G, fxy_1.G, fxy1.G, fxy2.G, out greenC_2, out greenC_1, out greenC1, out greenC2);
calculateCoefficients(fxy_2.B, fxy_1.B, fxy1.B, fxy2.B, out blueC_2, out blueC_1, out blueC1, out blueC2);
red[iiX+1] = redC_2 * interpolate(pixelRealIndex[1] - (float)y_2) + redC_1 * interpolate(pixelRealIndex[1] - (float)y_1) +
redC1 * interpolate(pixelRealIndex[1] - (float)y1) + redC2 * interpolate(pixelRealIndex[1] - (float)y2);
green[iiX+1] = greenC_2 * interpolate(pixelRealIndex[1] - (float)y_2) + greenC_1 * interpolate(pixelRealIndex[1] - (float)y_1) +
greenC1 * interpolate(pixelRealIndex[1] - (float)y1) + greenC2 * interpolate(pixelRealIndex[1] - (float)y2);
blue[iiX+1] = blueC_2 * interpolate(pixelRealIndex[1] - (float)y_2) + blueC_1 * interpolate(pixelRealIndex[1] - (float)y_1) +
blueC1 * interpolate(pixelRealIndex[1] - (float)y1) + blueC2 * interpolate(pixelRealIndex[1] - (float)y2);
}
Color fx_2y = p_siImage.GetValueMirrored(x_2, y);
Color fx_1y = p_siImage.GetValueMirrored(x_1, y);
Color fx1y = p_siImage.GetValueMirrored(x1, y);
Color fx2y = p_siImage.GetValueMirrored(x2, y);
float redCX_2, redCX_1, redCX1, redCX2;
float greenCX_2, greenCX_1, greenCX1, greenCX2;
float blueCX_2, blueCX_1, blueCX1, blueCX2;
calculateCoefficients(red[0], red[1], red[2], red[3], out redCX_2, out redCX_1, out redCX1, out redCX2);
calculateCoefficients(green[0], green[1], green[2], green[3], out greenCX_2, out greenCX_1, out greenCX1, out greenCX2);
calculateCoefficients(blue[0], blue[1], blue[2], blue[3], out blueCX_2, out blueCX_1, out blueCX1, out blueCX2);
float redResult = redCX_2 * interpolate(pixelRealIndex[0] - (float)x_2) + redCX_1 * interpolate(pixelRealIndex[0] - (float)x_1) +
redCX1 * interpolate(pixelRealIndex[0] - (float)x1) + redCX2 * interpolate(pixelRealIndex[0] - (float)x2);
float greenResult = greenCX_2 * interpolate(pixelRealIndex[0] - (float)x_2) + greenCX_1 * interpolate(pixelRealIndex[0] - (float)x_1) +
greenCX1 * interpolate(pixelRealIndex[0] - (float)x1) + greenCX2 * interpolate(pixelRealIndex[0] - (float)x2);
float blueResult = blueCX_2 * interpolate(pixelRealIndex[0] - (float)x_2) + blueCX_1 * interpolate(pixelRealIndex[0] - (float)x_1) +
blueCX1 * interpolate(pixelRealIndex[0] - (float)x1) + blueCX2 * interpolate(pixelRealIndex[0] - (float)x2);
redResult = Math.Max(0, Math.Min(redResult, 255));
greenResult = Math.Max(0, Math.Min(greenResult, 255));
blueResult = Math.Max(0, Math.Min(blueResult, 255));
Color resultColor = Color.FromArgb((int)redResult, (int)greenResult, (int)blueResult);
resultImage.SetValue(iX, iY, resultColor);
}
}
return resultImage;
}
For an image like this (15x15px):

The result is as follows (scaled to 80x80 pixels):

On the contrary, this is what the result looks like if I immediately calculated all 16 coefficients (80x80px):

My question is: How is the separation done correctly? Or am I missing something completely, and I can only separate the interpolation function (phi), but not calculate the coefficients?