【问题标题】:Correctly executing bicubic resampling正确执行双三次重采样
【发布时间】:2015-11-12 09:15:21
【问题描述】:

我一直在试验 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;
}

这是 500 像素 x 667 像素的原始图片。

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

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

谁能给我一些建议来解决这个问题?

注意 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;
}

【问题讨论】:

  • 你能发布你的扩展方法 ToByte(this double...) 吗?
  • 这也是我的想法。顺便说一句,即使登录到我的 Dropbox 帐户,我也看不到您链接到的那张图片。
  • 可以分享一下原图吗?我已经尝试过您之前发布的那个,但结果不同。我猜 stackoverflow 对发布的图片进行了一些修改。

标签: c# algorithm image-processing


【解决方案1】:

您将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;

【讨论】:

  • 天啊!我不敢相信我错过了!
【解决方案2】:

好吧,我遇到了一件非常奇怪的事情,它可能是问题的根源,也可能不是问题的根源。 我已经开始尝试自己实现卷积矩阵并遇到奇怪的行为。我在一个 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();
            }
        }

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

另一方面,您的解决方案部分有效,是什么让我发现的事情变得无关紧要。我还有一个猜测:你系统的 DPI 是多少?

根据我的发现,这里有一些链接:

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

【讨论】:

  • 第一个链接是一个很好的资源。感谢您挖掘它。
猜你喜欢
  • 2014-02-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-11-10
  • 1970-01-01
相关资源
最近更新 更多