4

3次スプラインと一般化された補間式を使用して、バイキュービック画像補間を実装しようとしています。
周囲の 16 個のピクセルすべてを使用してこれを実装し、これらの 16 個の関数値から補間係数を計算することができました。しかし、内挿は定義上分離可能であるため、双三次内挿を実現するために 2 つの 1-D 内挿を使用するバージョンを実装しようとしました。一般化された補間式については、たとえばhttp://bigwww.epfl.ch/publications/thevenaz9901.pdfの第 3 章を使用し、1-D 補間については、この背後にあるアイデアを使用します: http://www.vision-systems.com /articles/print/volume-12/issue-10/departments/wilsons-websites/understanding-image-interpolation-techniques.html
私の係数は、 https://en.wikipedia.org/wiki/Spline_interpolation#Example のように 4x4 行列を逆にすることによって計算されます。

補間は 3 つの関数で構成されます。
補間関数自体 (ファイ):

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);//-(p_fValue * p_fValue * p_fValue) / 6.0f + p_fValue * p_fValue - 2.0f * p_fValue + 4.0f / 3.0f;
        }
        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;
        }
    }

4 つの関数値から 4 つの係数を計算する:

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);
    }

そして、より小さいソース画像からの画像全体の補間:

// p_siImage = original (smaller) image
    // p_iImageWidth, p_iImageHeight = Image size (pixel count) of the new image
    // Interpolation with standard formula u(x) = sum_{k to N} c_k * phi(x-k); For N function values
    public SampledImage InterpolateImage(SampledImage p_siImage, int p_iImageWidth, int p_iImageHeight)
    {
        // Create interpolated image
        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++)
            {
                // Calculate pixel position "in space"
                float[] pixelSpace = resultImage.GetPixelInSpace(iX, iY);

                // Calculate the index in smaller image, as real number (pixel index between pixels)
                float[] pixelRealIndex = p_siImage.GetPixelRealFromSpace(pixelSpace[0], pixelSpace[1]);

                // Calculate X values of surrounding pixels
                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;

                // Calculate Y value of nearest pixel
                int y = (int)Math.Floor(pixelRealIndex[1]);

                // Arrays for "function values" (= color values)
                float[] red = new float[4];
                float[] green = new float[4];
                float[] blue = new float[4];

                // Create polynom for each column
                for (int iiX = -1; iiX < 3; iiX++)
                {
                    // Get column X-index
                    int x = (int)Math.Floor(pixelRealIndex[0]) + iiX;

                    // Used Y-Indices for polynom
                    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;

                    // Get "function values" for each pixel in the column
                    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);

                    // Coefficients c_k
                    float redC_2, redC_1, redC1, redC2;
                    float greenC_2, greenC_1, greenC1, greenC2;
                    float blueC_2, blueC_1, blueC1, blueC2;

                    // Calculate the coefficients for the column polynom
                    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);

                    // Interpolate in each column at the same Y-Index as the actual interpolation position
                    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);
                }

                //Now: interpolate the actual value

                // Get "function values" for each pixel in the row
                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);

                // Coefficients c_k
                float redCX_2, redCX_1, redCX1, redCX2;
                float greenCX_2, greenCX_1, greenCX1, greenCX2;
                float blueCX_2, blueCX_1, blueCX1, blueCX2;

                // Calculate coefficients by using the interpolated values of each column
                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);

                // Interpolate finally for the actual value
                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);

                // Make sure to care for under/overshoots
                redResult = Math.Max(0, Math.Min(redResult, 255));
                greenResult = Math.Max(0, Math.Min(greenResult, 255));
                blueResult = Math.Max(0, Math.Min(blueResult, 255));

                // Set the pixel to the calculated value
                Color resultColor = Color.FromArgb((int)redResult, (int)greenResult, (int)blueResult);
                resultImage.SetValue(iX, iY, resultColor);
            }
        }

        return resultImage;
    }

このような画像 (15x15px) の場合: ソース画像 15x15px

結果は次のようになります (80x80px に拡大): バイキュービック スプラインで補間された個別の 80x80

対照的に、一度に 16 個の係数すべてを計算すると (80x80px)、結果は次のようになります。 バイキュービック スプライン補間完了

私の質問は次のとおりです。分離はどのように正しく行われますか? または、何かが完全に欠けていて、補間関数 (ファイ) のみを分離できますが、係数の計算は分離できませんか?

4

0 に答える 0