Как уменьшить растровое изображение до известного набора цветов RGB

StackOverflow https://stackoverflow.com/questions/622573

  •  05-07-2019
  •  | 
  •  

Вопрос

Для хобби-проекта я собираюсь создать программу, которая при наличии растрового изображения создаст рисунок в виде крестика в формате 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!) в любом коде, который вы публикуете.

Это было полезно?

Решение

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

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

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

Кроме того, ознакомьтесь с этот вопрос .

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

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

Другие советы

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

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

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

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

Я собрал некоторый код, который делает это на базовом уровне. (Извините, что это в 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);

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

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

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

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top