Floyd - Steinberg 抖动实现会产生奇怪的工件

问题描述 投票:0回答:1

我已经尝试在 C# 中创建自己的 Floyd-Steinberg 抖动实现有一段时间了,但它似乎无法正常工作。以下是它对 Windows XP 背景执行的操作的示例

原图:

我的输出:

所需输出:

正如你所看到的,有这些奇怪的波浪图案。它与我输入的大多数图像执行非常相似的操作,当我在调色板中添加更多颜色时,它的行为也类似。我完全不知道为什么会发生这种情况,特别是因为我之前在 Java 中做过这件事,而且效果很好。这是代码:

public static void ditheredImage(Bitmap source)
{
    double ri = 7 / 16d;
    double rd = 1 / 16d;
    double d = 5 / 16d;
    double ld = 3 / 16d;
    double[,] errRow = new double[source.Width, 3];
    double[] right = new double[3];
    Bitmap result = new Bitmap(source.Width, source.Height);
    List<Color> palette = new List<Color>();
    palette.Add(Color.FromArgb(0, 0, 0));
    palette.Add(Color.FromArgb(255, 255, 255));
    for (int j = 0; j < source.Height; j++)
    {
        double[,] newErrRow = new double[source.Width, 3];
        bool forward = j % 2 == 0;
        for (int i = (forward ? 0 : source.Width - 1); forward ? i < source.Width : i >= 0; i += (forward ? 1 : -1))
        {
            int x = (int)((double)(i) / source.Width * source.Width);
            int y = (int)((double)(j) / source.Height * source.Height);
            Color c = source.GetPixel(x, y);
            double dR = c.R / 255d + right[0] + errRow[i, 0];
            double dG = c.G / 255d + right[1] + errRow[i, 1];
            double dB = c.B / 255d + right[2] + errRow[i, 2];
            Color curCol = Color.FromArgb((int)clamp(dR * 255, 0, 255), (int)clamp(dG * 255, 0, 255), (int)clamp(dB * 255, 0, 255));
            Color closestPalCol = closestPaletteColor(palette, curCol);
            result.SetPixel(i, j, closestPalCol);

            double qR = closestPalCol.R / 255d;
            double qG = closestPalCol.G / 255d;
            double qB = closestPalCol.B / 255d;

            double rDif = dR - qR;
            double gDif = dG - qG;
            double bDif = dB - qB;

            if (i < source.Width - 1)
            {
                right[0] = rDif * ri;
                right[1] = gDif * ri;
                right[2] = bDif * ri;

                newErrRow[i + 1, 0] += rDif * rd;
                newErrRow[i + 1, 1] += gDif * rd;
                newErrRow[i + 1, 2] += bDif * rd;
            }
            if (i > 0)
            {
                newErrRow[i - 1, 0] += rDif * ld;
                newErrRow[i - 1, 1] += gDif * ld;
                newErrRow[i - 1, 2] += bDif * ld;
            }

            newErrRow[i, 0] += rDif * d;
            newErrRow[i, 1] += gDif * d;
            newErrRow[i, 2] += bDif * d;
        }
        errRow = newErrRow;
    }
    result.Save(@"PATH_TO_SAVED_IMAGE", ImageFormat.Png);
}

public static Color closestPaletteColor(List<Color> palette, Color c)
{
    Color result = palette[0];
    int dif = 765;
    foreach(Color pc in palette)
    {
        int cdif = Math.Abs(pc.R - c.R) + Math.Abs(pc.G - c.G) + Math.Abs(pc.B - c.B);
        if (cdif < dif)
        {
            dif = cdif;
            result = pc;
        }
    }
    return result;
}

public static double clamp(double value, double min, double max)
{
    return (value < min) ? min : (value > max) ? max : value;
}
c# image-processing dithering
1个回答
0
投票

我想通了。我在错误的地方进行了夹紧。这是更正后的代码:

public static void ditheredImage(Bitmap source)
{
    double ri = 7 / 16d;
    double rd = 1 / 16d;
    double d = 5 / 16d;
    double ld = 3 / 16d;
    double[,] errRow = new double[source.Width, 3];
    double[] right = new double[3];
    Bitmap result = new Bitmap(source.Width, source.Height);
    List<Color> palette = new List<Color>();
    palette.Add(Color.FromArgb(0, 0, 0));
    palette.Add(Color.FromArgb(255, 255, 255));
    for (int j = 0; j < source.Height; j++)
    {
        double[,] newErrRow = new double[source.Width, 3];
        bool forward = j % 2 == 0;
        for (int i = (forward ? 0 : source.Width - 1); forward ? i < source.Width : i >= 0; i += (forward ? 1 : -1))
        {
            int x = (int)((double)(i) / source.Width * source.Width);
            int y = (int)((double)(j) / source.Height * source.Height);
            Color c = source.GetPixel(x, y);
            double dR = clamp(c.R / 255d + right[0] + errRow[i, 0], 0, 1); //CLAMPING SHIFTED HERE
            double dG = clamp(c.G / 255d + right[1] + errRow[i, 1], 0, 1); //AND HERE
            double dB = clamp(c.B / 255d + right[2] + errRow[i, 2], 0, 1); //AND HERE
            Color curCol = Color.FromArgb((int)(dR * 255), (int)(dG * 255), (int)(dB * 255)); // THIS IS WHERE THE MISTAKE WAS
            Color closestPalCol = closestPaletteColor(palette, curCol);
            result.SetPixel(i, j, closestPalCol);

            double qR = closestPalCol.R / 255d;
            double qG = closestPalCol.G / 255d;
            double qB = closestPalCol.B / 255d;

            double rDif = dR - qR;
            double gDif = dG - qG;
            double bDif = dB - qB;

            if (i < source.Width - 1)
            {
                right[0] = rDif * ri;
                right[1] = gDif * ri;
                right[2] = bDif * ri;

                newErrRow[i + 1, 0] += rDif * rd;
                newErrRow[i + 1, 1] += gDif * rd;
                newErrRow[i + 1, 2] += bDif * rd;
            }
            if (i > 0)
            {
                newErrRow[i - 1, 0] += rDif * ld;
                newErrRow[i - 1, 1] += gDif * ld;
                newErrRow[i - 1, 2] += bDif * ld;
            }

            newErrRow[i, 0] += rDif * d;
            newErrRow[i, 1] += gDif * d;
            newErrRow[i, 2] += bDif * d;
        }
        errRow = newErrRow;
    }
    result.Save(@"PATHTOSAVEDIMAGE", ImageFormat.Png);
}

public static Color closestPaletteColor(List<Color> palette, Color c)
{
    Color result = palette[0];
    int dif = 765;
    foreach(Color pc in palette)
    {
        int cdif = Math.Abs(pc.R - c.R) + Math.Abs(pc.G - c.G) + Math.Abs(pc.B - c.B);
        if (cdif < dif)
        {
            dif = cdif;
            result = pc;
        }
    }
    return result;
}

public static double clamp(double value, double min, double max)
{
    return (value < min) ? min : (value > max) ? max : value;
}

现在输出:

我仍然应该对点的重复模式做一些事情,显然有一种引入噪音的方法可以防止这种情况发生,但至少现在它没有做任何奇怪的事情。

© www.soinside.com 2019 - 2024. All rights reserved.