对于一个业余爱好项目,我将构建一个程序,当给定图像位图时,它将创建一个 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!)。

有帮助吗?

解决方案

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

因此,只需迭代每个像素,找到所选颜色空间内距离最近的颜色。请注意,对于某些颜色空间(例如使用 hue 的那些颜色空间),必须计算循环的距离。

(大多数颜色都围绕着实际选择调色板,但在你的情况下已经处理过,所以你不能使用更流行的量化技术。)

另外,请查看这个问题

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

但是,如果您只是使用这种技术将图像转换为十字绣设计,实际拼接它将非常困难。它将充满单像素色域,有点挫败了十字绣的目的。

其他提示

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

一个非常基本的是将RGB颜色视为空间中的点,并使用颜色之间的普通旧欧几里德距离来确定如何“关闭”颜色。他们是。这具有缺点,因为人眼在该空间中的不同位置处具有不同的灵敏度,因此这种距离与人类感知颜色的方式不完全对应。您可以使用各种加权方案来改善这种情况。

有趣的......:)

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

我把一些在基本级别上执行此操作的代码放在一起。 (对不起,这是在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);

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

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

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

但是,如前所述,尽可能多地使用 LAB,因为(据我所知)它与颜色空间无关,而如果没有定义的颜色空间,所有其他方法(RGB/HSL/CMYK)(理论上)毫无意义。

例如,RGB 只是三个百分比值(0-255 => 0-100%,具有 8 位颜色深度)。因此,如果您的 RGB 三元组为 (0,255,0),则它会翻译为“仅绿色,并且尽可能多”。那么,问题是“多红才算红?”。这是色彩空间回答的问题 - sRGB 100%-绿色并不像 AdobeRGB 100%-绿色。甚至都不相同 色调!

抱歉,如果这涉及到了题外话

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top