C#中的双三次插值工作有点错误

时间:2019-02-02 14:50:52

标签: c# image interpolation bicubic

我有一个在互联网上找到的用于调整图像大小的代码,但是有一个问题。我的宽度和高度偏移量为-1px。

我尝试添加该偏移量,但是它只是擦除了第一行,然后删除了1 px。

    private byte[] BicubicResize(byte[] Data, int w, int h, double scalex, double scaley)
    {
        int w_new = (int)(w * scalex);
        int h_new = (int)(h * scaley);
        byte[] NewData = new byte[w_new * h_new * 4];
        byte c;
        int x, y;

        BicubicInterpolation interpolation = new BicubicInterpolation(scalex, scaley, w_new, h_new);
        for (c = 0; c < 4; c++)
            for (y = 0; y < h_new; y++)
                for (x = 0; x < w_new; x++)
                    NewData[((x + y * w_new) << 2) | c] = interpolation.Bicubic(x, y, c, Data);
        return NewData;
    }

    public class BicubicInterpolation
    {
        double dx;
        double dy;
        double p0;
        double p1;
        double p2;
        double p3;
        double[] Rdx;
        double[] Rdy;
        double s;
        byte k;
        byte i;
        long pix0;
        long pix1;
        double[][] p;
        double scalex;
        double scaley;
        int w;
        int h;

        public BicubicInterpolation(double scalex = 1, double scaley = 1, int w = 0, int h = 0)
        {
            p = new double[4][];
            p[0] = new double[4];
            p[1] = new double[4];
            p[2] = new double[4];
            p[3] = new double[4];
            pix0 = 0;
            pix1 = 0;

            this.scalex = scalex;
            this.scaley = scaley;
            this.w = w;
            this.h = h;
        }

        public byte Bicubic(int x, int y, byte c, byte[] Data)
        {
            pix0 = (long)(x / scalex) + (long)(y / scaley) * (long)(w / scalex);
            for (k = 0; k < 4; k++)
                for (i = 0; i < 4; i++)
                {
                    pix1 = ((long)(pix0 + i + k * (w / scalex)) << 2) | c;
                    if (pix1 < Data.LongLength) p[i][k] = Data[pix1];
                    else p[i][k] = 0;
                }

            //Temp = BicubicInterpolate(p, x - (long)(x / scalex), y - (long)(y / scaley));
            return (byte)Interpolation(x, y, p);
        }

        private double Interpolation(double tx, double ty, double[][] P)
        {
            dx = F(tx);
            dy = F(ty);

            Rdx = new double[4];

            Rdy = new double[4];
            for (int n = 0; n < 4; n++)
            {
                Rdx[n] = R(n - 1 - dx);
                Rdy[n] = R(n - 1 - dy);
            }

            s = 0;

            for (byte k = 0; k < 4; k++)
                for (byte i = 0; i < 4; i++)
                    s += P[k][i] * Rdx[i] * Rdy[k];
            return s;
        }

        private double F(double x) => x - Math.Floor(x);
        private double P(double x) => (x > 0.0) ? x : 0.0;
        private double R(double x)
        {
            //return (Math.Pow(P(x + 2), 3) - 4 * Math.Pow(P(x + 1), 3) +
            //    6 * Math.Pow(P(x), 3) - 4 * Math.Pow(P(x - 1), 3)) / 6;
            p0 = P(x + 2);
            p1 = P(x + 1);
            p2 = P(x    );
            p3 = P(x - 1);
            return ((p0 * p0 * p0) - 4 * (p1 * p1 * p1) + 6 * (p2 * p2 * p2) - 4 * (p3 * p3 * p3)) / 6.0;
        }
    }

我期望得到类似这样的东西:Original

但是我得到的图像在宽度和高度上有问题的偏移-1px:Problem output

1 个答案:

答案 0 :(得分:0)

我以其他方式找到了答案。和这种方式是更快。

    public byte[,,] BicubicResize(byte[,,] Data, int w, int h, int w_new, int h_new, double scalex, double scaley)
    {
        byte[,,] NewData = new byte[w_new, h_new, 4];
        byte c;
        int x, y;
        double j, l;
        byte k, i;

        double[][] p = new double[4][];
        p[0] = new double[4];
        p[1] = new double[4];
        p[2] = new double[4];
        p[3] = new double[4];
        double result = 0;

        for (c = 0; c < 4; c++)
            for (y = 0; y < h_new; y++)
                for (x = 0; x < w_new; x++)
                {
                    for (k = 0; k < 4; k++)
                        for (i = 0; i < 4; i++)
                        {
                            j = x / scalex + i - 1;
                            l = y / scaley + k - 1;
                            while (j <  0) j++;
                            while (l <  0) l++;
                            while (j >= w) j--;
                            while (l >= h) l--;
                            p[i][k] = Data[(int)j, (int)l, c];
                        }
                    result = BicubicInterpolate(p, x % scalex / scalex, y % scaley / scaley);
                    NewData[x, y, c] = CDTB(result);
                }

        return NewData;
    }

    private readonly double[] arr = new double[4];
    private double result = 0;

    private double BicubicInterpolate(double[][] p, double x, double y)
    {
        arr[0] = CubicInterpolate(p[0], y);
        arr[1] = CubicInterpolate(p[1], y);
        arr[2] = CubicInterpolate(p[2], y);
        arr[3] = CubicInterpolate(p[3], y);
        result = CubicInterpolate(arr, x);
        return result;
    }

    private double CubicInterpolate(double[] p, double x) => p[1] + 0.5 * x * (p[2] - p[0] + x *
        (2.0 * p[0] - 5.0 * p[1] + 4.0 * p[2] - p[3] + x * (3.0 * (p[1] - p[2]) + p[3] - p[0])));


    public static byte CDTB(double c)
    {
        if (c * 10 % 10 > 5) c = Math.Ceiling(c);
        else                 c = Math.Floor  (c);
             if (c > 0xFF) c = 0xFF;
        else if (c < 0x00) c = 0x00;
        return (byte)c;
    }