Pregunta

Para un proyecto de hobby, voy a construir un programa que, cuando se le presente un mapa de bits de imagen, creará un patrón de punto de cruz como un PDF. Usaré Cocoa / Objective C en una Mac.

El mapa de bits de origen normalmente será una imagen de 24 bpp, pero de los millones de colores disponibles, solo unos pocos existen como hilos de punto de cruz. Los hilos vienen en varios tipos. DMC es el más disponible, y casi su rango completo está disponible como valores RGB de varios sitios web. Aquí hay uno , por ejemplo.

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...

Mi primer problema, tal como lo veo, es desde el punto de inicio del RGB desde un píxel en la imagen que elige el color más cercano disponible desde el conjunto DMC. ¿Cuál es la mejor manera de encontrar matemáticamente el color DMC más cercano y garantizar que también se ajuste a un color?

Aunque usaré Cocoa, siéntase libre de usar pseudocódigo (¡o incluso Java!) en cualquier código que publique.

¿Fue útil?

Solución

Use el LAB y encuentre el color más cercano distancia euclidiana . Hacer esto en el espacio de color RGB dará resultados contraintuitivos. (O use el HSL espacio de color).

Así que simplemente itere sobre cada píxel y encuentre el color con la distancia más cercana dentro del espacio de color que elija. Tenga en cuenta que la distancia debe calcularse circularmente para algunos espacios de color (por ejemplo, aquellos que emplean tono ).

(La mayoría de la quanización de color gira en torno a la elección de una paleta, pero eso ya se ha solucionado en su caso, por lo que no puede utilizar las técnicas de cuantización más populares).

También, consulte esta pregunta .

Para encontrar el tono HSB en Cocoa, parece que puede usar método getHue declarado en NSColor.h .

Sin embargo, si simplemente convierte una imagen a un diseño de punto de cruz utilizando esta técnica, será muy difícil coserla. Estará llena de campos de color de un solo píxel, que tipo de derrotas el propósito de la costura cruzada.

Otros consejos

Esto se llama cuantización de color , y hay muchos algoritmos disponibles.

Uno muy básico es simplemente tratar los colores RGB como puntos en el espacio, y usar la distancia euclidiana tradicional entre los colores para descubrir cómo " cerrar " son. Esto tiene inconvenientes, ya que los ojos humanos tienen una sensibilidad diferente en diferentes lugares de este espacio, por lo que tal distancia no correspondería bien a cómo los humanos perciben los colores. Puede utilizar varios esquemas de ponderación para mejorar esa situación.

Interresting ... :)

No solo identificaría los colores más cercanos, sino que también desearía reducir la cantidad de colores utilizados. No querrás terminar con un patrón de costura que usa cientos de colores diferentes ...

Reuní un código que hace esto en un nivel básico. (Lamento que esté en C #, espero que pueda ser algo útil de todos modos).

Hay algunos ajustes adicionales que deben hacerse antes de que el método funcione bien, por supuesto. El método GetDistance compara la importancia del tono, la saturación y el brillo entre sí, por supuesto, encontrar el mejor equilibrio entre ellos es importante para encontrar el color más parecido.

También se puede hacer mucho con el método de reducir la paleta. En el ejemplo, acabo de seleccionar los colores más utilizados, pero es probable que desee ponderar la similitud de los colores en la paleta. Esto se puede hacer seleccionando el color más utilizado, reduciendo el recuento de los colores restantes en la lista dependiendo de la distancia al color elegido, y luego recurra a la lista.

La clase Hsl que tiene un color DMC, puede calcular la distancia a otro color y encontrar el color más cercano en una lista de colores:

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;
    }

}

Este código configura una lista (muy reducida) de colores DMC, carga una imagen, cuenta los colores, reduce la paleta y convierte la imagen. Por supuesto, también querrá guardar la información de la paleta reducida en algún lugar.

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);

obtenga el código fuente de la aplicación ppmquant de netpbm conjunto de utilidades

Otros han señalado diversas técnicas para la cuantificación del color. Es posible usar técnicas como Markov Random Fields para intentar penalizar al sistema por cambiar los colores de hilos en las ubicaciones de píxeles adyacentes. Hay algunas bibliotecas genéricas de MRF de etiquetas múltiples que incluyen Boykov's .

Para usar uno de estos, los elementos de datos serían los colores de entrada, las etiquetas serían el conjunto de colores de hilos, los términos de datos podrían ser algo como la distancia euclidiana en el espacio LAB sugerido por bzlm, y los términos de vecindad penalizar por cambiar colores de hilo.

Dependiendo de la relevancia de la corrección de sus operaciones de color, recuerde tomar espacio de color s en cuenta. Aunque he estudiado esto un poco, debido a mi afición a la fotografía, todavía estoy un poco confundido acerca de todo.

Pero, como se mencionó anteriormente, use LAB tanto como sea posible, porque (afaik) es agnóstico al espacio de color, mientras que todos los otros métodos (RGB / HSL / CMYK) no significan nada (en teoría) sin un espacio de color definido.

RGB, por ejemplo, es solo tres valores porcentuales (0-255 = > 0-100%, con profundidad de color de 8 bits). Por lo tanto, si tiene un triplete RGB de (0,255,0), se traduce a "solo verde", y tanto como sea posible ". Entonces, la pregunta es " ¿qué tan rojo es rojo? & Quot ;. Esta es la pregunta a la que responde un espacio de color: sRGB 100% - verde no es tan verde como AdobeRGB 100% - verde. ¡Ni siquiera es el mismo hue !

Lo siento si esto fue al lado opaco de las cosas

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top