正确执行双三次重采样

时间:2015-08-19 07:36:40

标签: c# algorithm image-processing

我一直在试验AForge框架中出现的图像双三次重采样算法,并想要在我的图像处理解决方案中引入类似的东西。查看原始算法here和插值内核here

不幸的是我撞墙了。它看起来像我在某种程度上错误地计算样本目的地位置,可能是由于算法是为Format24bppRgb图像而设计的,因为我正在使用Format32bppPArgb格式。

这是我的代码:

public Bitmap Resize(Bitmap source, int width, int height)
{
    int sourceWidth = source.Width;
    int sourceHeight = source.Height;

    Bitmap destination = new Bitmap(width, height, PixelFormat.Format32bppPArgb);
    destination.SetResolution(source.HorizontalResolution, source.VerticalResolution);

    using (FastBitmap sourceBitmap = new FastBitmap(source))
    {
        using (FastBitmap destinationBitmap = new FastBitmap(destination))
        {
            double heightFactor = sourceWidth / (double)width;
            double widthFactor = sourceHeight / (double)height;

            // Coordinates of source points
            double ox, oy, dx, dy, k1, k2;
            int ox1, oy1, ox2, oy2;

            // Width and height decreased by 1
            int maxHeight = height - 1;
            int maxWidth = width - 1;

            for (int y = 0; y < height; y++)
            {
                // Y coordinates
                oy = (y * widthFactor) - 0.5;

                oy1 = (int)oy;
                dy = oy - oy1;

                for (int x = 0; x < width; x++)
                {
                    // X coordinates
                    ox = (x * heightFactor) - 0.5f;
                    ox1 = (int)ox;
                    dx = ox - ox1;

                    // Destination color components
                    double r = 0;
                    double g = 0;
                    double b = 0;
                    double a = 0;

                    for (int n = -1; n < 3; n++)
                    {
                        // Get Y cooefficient
                        k1 = Interpolation.BiCubicKernel(dy - n);

                        oy2 = oy1 + n;
                        if (oy2 < 0)
                        {
                            oy2 = 0;
                        }

                        if (oy2 > maxHeight)
                        {
                            oy2 = maxHeight;
                        }

                        for (int m = -1; m < 3; m++)
                        {
                            // Get X cooefficient
                            k2 = k1 * Interpolation.BiCubicKernel(m - dx);

                            ox2 = ox1 + m;
                            if (ox2 < 0)
                            {
                                ox2 = 0;
                            }

                            if (ox2 > maxWidth)
                            {
                                ox2 = maxWidth;
                            }

                            Color color = sourceBitmap.GetPixel(ox2, oy2);

                            r += k2 * color.R;
                            g += k2 * color.G;
                            b += k2 * color.B;
                            a += k2 * color.A;
                        }
                    }

                    destinationBitmap.SetPixel(
                        x, 
                        y, 
                        Color.FromArgb(a.ToByte(), r.ToByte(), g.ToByte(), b.ToByte()));
                }
            }

        }
    }

    source.Dispose();
    return destination;
}

应该代表Wikipedia

上给定方程的内核
public static double BiCubicKernel(double x)
{
    if (x < 0)
    {
        x = -x;
    }

    double bicubicCoef = 0;

    if (x <= 1)
    {
        bicubicCoef = (1.5 * x - 2.5) * x * x + 1;
    }
    else if (x < 2)
    {
        bicubicCoef = ((-0.5 * x + 2.5) * x - 4) * x + 2;
    }

    return bicubicCoef;
}

这是500px x 667px的原始图像。

picture of a tree

图像大小调整为400px x 543px。

tree with bicubic algorithm applied

从视觉上看,图像过度缩小,然后在我们到达特定点时重复应用相同的像素。

有人可以给我一些指示来解决这个问题吗?

注意 FastBitmap是Bitmap的包装器,它使用LockBits来操作内存中的像素。它适用于everything else我将其应用于。

修改

根据请求,这里涉及ToByte

中涉及的方法
public static byte ToByte(this double value)
{
    return Convert.ToByte(ImageMaths.Clamp(value, 0, 255));
}

public static T Clamp<T>(T value, T min, T max) where T : IComparable<T>
{
    if (value.CompareTo(min) < 0)
    {
        return min;
    }

    if (value.CompareTo(max) > 0)
    {
        return max;
    }

    return value;
}

2 个答案:

答案 0 :(得分:2)

您将ox2oy2限制为目标图片尺寸,而不是源尺寸。

改变这个:

// Width and height decreased by 1
int maxHeight = height - 1;
int maxWidth = width - 1;

到此:

// Width and height decreased by 1
int maxHeight = sourceHeight - 1;
int maxWidth = sourceWidth - 1;

答案 1 :(得分:1)

好吧,我遇到了一个非常奇怪的事情,可能是或可能不是问题的原因。 我开始尝试自己实现卷积矩阵并遇到奇怪的行为。我正在测试4x4像素的小图像上的代码。代码如下:

 var source = Bitmap.FromFile(@"C:\Users\Public\Pictures\Sample Pictures\Безымянный.png");
 using (FastBitmap sourceBitmap = new FastBitmap(source))
        {
            for (int TY = 0; TY < 4; TY++)
            {
                for (int TX = 0; TX < 4; TX++)
                {
                    Color color = sourceBitmap.GetPixel(TX, TY);
                    Console.Write(color.B.ToString().PadLeft(5));
                }
                Console.WriteLine();
            }
        }

The output

虽然我只打印出蓝色通道值,但它仍然显然不正确。

另一方面,你的解决方案部分起作用,是什么使得我发现的东西无关紧要。我还有一个猜测:你的系统的DPI是什么?

从我发现的帮助中,这里有一些链接:

到目前为止,这是我的答案,但我会进一步尝试。