8

对于一个爱好项目,我将构建一个程序,当给定图像位图时,它将创建一个十字绣图案作为 PDF。我将在 Mac 上使用 Cocoa/Objective C。

源位图通常是 24bpp 图像,但在数百万种可用颜色中,只有少数以十字绣线的形式存在。线程有多种类型。DMC 是最广泛可用的,几乎它们的整个范围都可以从各种网站以 RGB 值的形式获得。例如,这是一个

DMC#  Name               R   G   B
----- ------------------ --- --- ---
blanc White              255 255 255
208   Lavender - vy dk   148  91 128
209   Lavender - dk      206 148 186
210   Lavender - md      236 207 225
211   Lavender - lt      243 218 228
      ...etc...

正如我所看到的,我的第一个问题是从图像中像素的 RGB 起点选择 DMC 集中可用的最接近的颜色。在数学上找到最接近的 DMC 颜色并确保它也适合作为颜色的最佳方法是什么?

尽管我将使用 Cocoa,但请随意在您发布的任何代码中使用伪代码(甚至是 Java!)。

4

6 回答 6

10

使用LAB颜色空间,找到距离最近的颜色。在 RGB 颜色空间中执行此操作会产生违反直觉的结果。(或使用HSL颜色空间。)

因此,只需遍历每个像素并在您选择的颜色空间内找到距离最近的颜色。请注意,对于某些颜色空间(例如使用色相的那些) ,必须循环计算距离。

(大多数颜色量化都围绕着实际选择调色板展开,但在您的情况下已经考虑到这一点,因此您不能使用更流行的量化技术。)

另外,看看这个问题

要在 Cocoa 中查找 HSB 色调,您可以使用NSColor.h 中声明的 getHue 方法

但是,如果您只是使用此技术将图像转换为十字绣设计,则实际缝合将非常困难。它将充满单像素色域,这有点违背了十字绣的目的。

于 2009-03-07T22:07:28.167 回答
3

这称为颜色量化,并且有许多可用的算法。

一个非常基本的方法是将 RGB 颜色视为空间中的点,并使用颜色之间的普通旧欧几里得距离来确定它们有多“接近”。这是有缺点的,因为人眼在这个空间的不同地方有不同的敏感度,所以这样的距离不能很好地对应人类对颜色的感知。您可以使用各种加权方案来改善这种情况。

于 2009-03-07T22:03:06.147 回答
2

有趣... :)

您不仅要识别最接近的颜色,还希望减少使用的颜色数量。你不想最终得到一个使用数百种不同颜色的拼接图案......

我整理了一些在基本层面上执行此操作的代码。(对不起,它是在 C# 中的,我希望它无论如何都能有点用处。)

当然,在该方法运行良好之前,还需要进行一些进一步的调整。GetDistance 方法权衡色调、饱和度和亮度的重要性,找到它们之间的最佳平衡对于找到看起来最接近的颜色当然很重要。

减少调色板的方法也可以做很多事情。在示例中,我只选择了最常用的颜色,但您可能想要衡量调色板中颜色的相似程度。这可以通过选择最常用的颜色来完成,根据与所选择颜色的距离减少列表中剩余颜色的计数,然后重新选择列表。

持有 DMC 颜色的 Hsl 类,可以计算到另一种颜色的距离,并在颜色列表中找到最近的颜色:

public class Hsl {

    public string DmcNumber { get; private set; }
    public Color Color { get; private set; }
    public float Hue { get; private set; }
    public float Saturation { get; private set; }
    public float Brightness { get; private set; }
    public int Count { get; set; }

    public Hsl(Color c) {
        DmcNumber = "unknown";
        Color = c;
        Hue = c.GetHue();
        Saturation = c.GetSaturation();
        Brightness = c.GetBrightness();
        Count = 0;
    }

    public Hsl(string dmc, int r, int g, int b)
        : this(Color.FromArgb(r, g, b))
    {
        DmcNumber = dmc;
    }

    private static float AngleDifference(float a1, float a2) {
        float a = Math.Abs(a1 - a2);
        if (a > 180f) {
            a = 360f - a;
        }
        return a / 180f;
    }

    public float GetDistance(Hsl other) {
        return
            AngleDifference(Hue, other.Hue) * 3.0f +
            Math.Abs(Saturation - other.Saturation) +
            Math.Abs(Brightness - other.Brightness) * 4.0f;
    }

    public Hsl GetNearest(IEnumerable<Hsl> dmcColors) {
        Hsl nearest = null;
        float nearestDistance = float.MaxValue;
        foreach (Hsl dmc in dmcColors) {
            float distance = GetDistance(dmc);
            if (distance < nearestDistance) {
                nearestDistance = distance;
                nearest = dmc;
            }
        }
        return nearest;
    }

}

此代码设置 DMC 颜色(大大减少)列表,加载图像,计算颜色,减少调色板并转换图像。您当然还希望将缩减调色板中的信息保存在某处。

Hsl[] dmcColors = {
    new Hsl("blanc", 255, 255, 255),
    new Hsl("310", 0, 0, 0),
    new Hsl("317", 167, 139, 136),
    new Hsl("318", 197, 198, 190),
    new Hsl("322", 81, 109, 135),
    new Hsl("336", 36, 73, 103),
    new Hsl("413", 109, 95, 95),
    new Hsl("414", 167, 139, 136),
    new Hsl("415", 221, 221, 218),
    new Hsl("451", 179, 151, 143),
    new Hsl("452", 210, 185, 175),
    new Hsl("453", 235, 207, 185),
    new Hsl("503", 195, 206, 183),
    new Hsl("504", 206, 221, 193),
    new Hsl("535", 85, 85, 89)
};

Bitmap image = (Bitmap)Image.FromFile(@"d:\temp\pattern.jpg");

// count colors used
List<Hsl> usage = new List<Hsl>();
for (int y = 0; y < image.Height; y++) {
    for (int x = 0; x < image.Width; x++) {
        Hsl color = new Hsl(image.GetPixel(x, y));
        Hsl nearest = color.GetNearest(dmcColors);
        int index = usage.FindIndex(h => h.Color.Equals(nearest.Color));
        if (index != -1) {
            usage[index].Count++;
        } else {
            nearest.Count = 1;
            usage.Add(nearest);
        }
    }
}

// reduce number of colors by picking the most used
Hsl[] reduced = usage.OrderBy(c => -c.Count).Take(5).ToArray();

// convert image
for (int y = 0; y < image.Height; y++) {
    for (int x = 0; x < image.Width; x++) {
        Hsl color = new Hsl(image.GetPixel(x, y));
        Hsl nearest = color.GetNearest(reduced);
        image.SetPixel(x, y, nearest.Color);
    }
}

image.Save(@"d:\temp\pattern.png", System.Drawing.Imaging.ImageFormat.Png);
于 2009-03-08T02:23:20.813 回答
1

从实用程序集中获取ppmquant应用程序的源代码netpbm

于 2009-03-07T22:17:22.057 回答
1

其他人指出了颜色量化的各种技术。可以使用诸如马尔可夫随机场之类的技术来尝试惩罚系统在相邻像素位置切换线色。有一些通用的多标签 MRF 库,包括Boykov 的.

要使用其中之一,数据元素将是输入颜色,标签将是一组线色,数据项可能类似于 bzlm 建议的 LAB 空间中的欧几里德距离,并且邻域项会因切换而受到惩罚线颜色。

于 2009-03-07T22:37:16.067 回答
-1

根据您的颜色操作正确性的相关性,请记住将颜色空间s 考虑在内。虽然我对此有所研究,但由于我的摄影爱好,我仍然对一切感到有些困惑。

但是,如前所述,尽可能使用 LAB,因为(afaik)它与色彩空间无关,而所有其他方法(RGB/HSL/CMYK)在没有定义色彩空间的情况下(理论上)毫无意义。

例如,RGB 只是三个百分比值(0-255 => 0-100%,具有 8 位色深)。因此,如果您有一个 (0,255,0) 的 RGB 三元组,则它转换为“只有绿色,并且尽可能多”。所以,问题是“红色有多红?”。这是色彩空间回答的问题 - sRGB 100%-green 不如 Adob​​eRGB 100 %-green 绿。它甚至不是相同的色调

对不起,如果这事离题了

于 2009-03-07T22:36:32.590 回答