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

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

Исходное растровое изображение обычно представляет собой изображение 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 и убедиться, что он точно соответствует цвету?

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


person banjollity    schedule 07.03.2009    source источник
comment
Дупить? stackoverflow.com/questions/492211/   -  person bzlm    schedule 08.03.2009
comment
Для этого можно использовать imagemagick, imagemagick.org/Usage/quantize   -  person Daniel Wedlund    schedule 08.08.2009
comment
если ваши вышивки крестиком не нужно выравнивать, тогда вам подойдет дизеринг (поскольку у вас есть фиксированная палитра). Квантование обычно ограничивает количество цветов входного изображения, но ваша палитра может иметь цвета, которых нет в изображении, что приводит к странным искажениям цвета. Для правильного дизеринга вам потребуется как минимум: красный, зеленый, синий, голубой, пурпурный, желтый, черный, белый. Если у вас больше одной интенсивности, это лучше, но это также работает с одной интенсивностью для каждого цвета.   -  person Spektre    schedule 26.04.2016


Ответы (6)


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

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

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

Также ознакомьтесь с этот вопрос.

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

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

person bzlm    schedule 07.03.2009
comment
Вы предполагаете, что исходное изображение - фотография, но да. Я рассмотрел целый ряд других вещей для вышивки крестиком, но не хотел перегружать вопрос. - person banjollity; 08.03.2009
comment
Нет, на самом деле я этого не предполагал. Я только что говорил о том, что попиксельный подход не учитывает изображение в целом. Но если исходное изображение уже сильно квантовано, это, конечно, не проблема. (У меня была своя доля болезненных и суровых швов.) - person bzlm; 08.03.2009
comment
Не думаю, что подробности о ваших намерениях перегрузят вопрос. - person bzlm; 08.03.2009
comment
Обратите внимание: если вы используете пространство вроде HSL, вам нужно будет задать круговое евклидово расстояние на канале оттенка. - person Mr Fooz; 08.03.2009
comment
Я отредактировал ответ, чтобы прояснить это. Евклид был бы горд. - person bzlm; 08.03.2009
comment
Этот вопрос касался одной части проблемы. Сильно квантованные изображения, белый фон, как можно меньше цветов - все это способствует лучшей вышивке крестиком. Плюс другие уловки, такие как полустежки для элементарного сглаживания, сшивание линий для получения мелких деталей и т. Д. Слишком много для одного вопроса. - person banjollity; 08.03.2009

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

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

person unwind    schedule 07.03.2009
comment
Вместо самодельных схем взвешивания я бы предложил альтернативное цветовое пространство. - person bzlm; 08.03.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);
person Guffa    schedule 08.03.2009

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

person Alnitak    schedule 07.03.2009

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

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

person Mr Fooz    schedule 07.03.2009
comment
Кто думал, что для сшивания требуется степень в области информатики? - person bzlm; 08.03.2009
comment
Может быть, для диплома по информатике нужно немного прошить !? - person banjollity; 08.03.2009

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

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

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

Извините, если это перешло в сторону оффтопа

person Henrik Paul    schedule 07.03.2009
comment
Я согласен, здесь важно думать о оттенках (используя LAB или HSx). - person bzlm; 08.03.2009
comment
Википедия: Кроме того, многие «цвета» в пространстве Лаборатории выходят за рамки человеческого зрения и поэтому являются чисто воображаемыми; эти «цвета» не могут быть воспроизведены в физическом мире. - LAB просто голову сломала! - person banjollity; 08.03.2009
comment
Ну зачем ограничиваться физическим миром? В любом случае, вы все только в моей голове. - person bzlm; 08.03.2009