Как уменьшить растровое изображение до известного набора цветов RGB - PullRequest
8 голосов
/ 08 марта 2009

Для хобби-проекта я собираюсь создать программу, которая при наличии растрового изображения создаст рисунок в виде крестика в формате PDF. Я буду использовать Какао / Objective C на Mac.

Исходное растровое изображение обычно представляет собой изображение с разрешением 24 бит / с, но из миллионов доступных цветов лишь немногие существуют в виде нитей для вышивания крестиком. Нити бывают разных типов. 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 и убедиться, что он точно соответствует цвету?

Хотя я буду использовать Какао, не стесняйтесь использовать псевдокод (или даже Java!) В любом коде, который вы публикуете.

Ответы [ 6 ]

10 голосов
/ 08 марта 2009

Используйте цветовое пространство LAB и найдите цвет с ближайшим евклидовым расстоянием . Выполнение этого в цветовом пространстве RGB даст нелогичные результаты. (Или используйте цветовое пространство HSL .)

Так что просто переберите каждый пиксель и найдите цвет с ближайшим расстоянием в пределах выбранного вами цветового пространства. Обратите внимание, что расстояние должно быть вычислено по кругу для некоторых цветовых пространств (например, использующих оттенок ).

(Большая часть цветового квантования вращается вокруг фактического выбора палитры, но об этом уже позаботились в вашем случае, поэтому вы не можете использовать более популярные методы квантования.)

Также, проверьте этот вопрос .

Чтобы найти оттенок HSB в Какао, похоже, что вы можете использовать метод getHue, объявленный в NSColor.h .

Однако, если вы просто конвертируете изображение в дизайн для вышивания крестиком, используя эту технику, на самом деле будет очень трудно его прошить. Оно будет полно однопиксельных цветовых полей, поражений с целью вышивания крестиком.

3 голосов
/ 08 марта 2009

Это называется квантование цвета , и есть много доступных алгоритмов.

Один из основных принципов - просто рассматривать цвета RGB как точки в пространстве и использовать простое евклидово расстояние между цветами, чтобы определить, насколько они «близки». Это имеет недостатки, поскольку человеческие глаза имеют разную чувствительность в разных местах в этом пространстве, поэтому такое расстояние не будет хорошо соответствовать тому, как люди воспринимают цвета. Вы можете использовать различные схемы взвешивания, чтобы улучшить эту ситуацию.

2 голосов
/ 08 марта 2009

Интересно ...:)

Вы бы не только идентифицировали ближайшие цвета, но и хотели бы уменьшить количество используемых цветов. Вы не хотите, чтобы получился узор сшивания, который использует сотни разных цветов ...

Я собрал некоторый код, который делает это на базовом уровне. (Извините, что это в C #, я надеюсь, что это все равно может быть полезно.)

Конечно, перед тем, как метод заработает, нужно еще кое-что изменить. Метод GetDistance взвешивает важность оттенка, насыщенности и яркости по отношению друг к другу, и, конечно, важно найти наилучший баланс между ними, чтобы найти цвет, который выглядит ближе всего.

Существует также многое, что можно сделать с помощью метода уменьшения палитры. В примере я только что выбрал наиболее используемые цвета, но вы, вероятно, хотите оценить, насколько похожи цвета в палитре. Это можно сделать, выбрав наиболее используемый цвет, уменьшив количество оставшихся цветов в списке в зависимости от расстояния до выбранного цвета, а затем прибавив список.

Класс Hsl, который содержит цвет DMC, может рассчитать расстояние до другого цвета и найти ближайший цвет в списке цветов:

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);
1 голос
/ 08 марта 2009

Другие указали на различные методы квантования цвета. Можно использовать такие методы, как марковские случайные поля, чтобы попытаться наказать систему за переключение цветов нитей в соседних пикселях. Существует несколько универсальных библиотек MRF с несколькими метками, включая Boykov's .

Чтобы использовать один из них, элементы данных были бы входными цветами, метки были бы набором цветов нитей, термины данных могли бы быть чем-то вроде евклидова расстояния в пространстве LAB, предложенного bzlm, а термины соседства штраф за переключение цветов нитей.

1 голос
/ 08 марта 2009

получить исходный код приложения ppmquant из набора netpbm

0 голосов
/ 08 марта 2009

В зависимости от релевантности правильности ваших цветовых операций, не забывайте принимать во внимание цветовое пространство с. Хотя я немного изучил это из-за моего увлечения фотографией, я все еще немного запутался во всем.

Но, как упоминалось ранее, используйте LAB в максимально возможной степени, потому что (afaik) он не зависит от цветового пространства, тогда как все другие методы (RGB / HSL / CMYK) ничего не значат (в теории) без определенного цветового пространства.

RGB, например, это всего три процентных значения (0-255 => 0-100%, с 8-битной глубиной цвета). Итак, если у вас есть RGB-триплет (0,255,0), он переводится как «только зеленый, и как можно больше его». Итак, вопрос «насколько красный красный?». На этот вопрос отвечает цветовое пространство - sRGB 100% -зеленый не такой зеленый, как AdobeRGB 100% -зеленый. Это даже не то же самое оттенок !

Извините, если это пошло на оффтопическую сторону вещей

...