Domanda

Per un progetto hobby ho intenzione di creare un programma che, quando viene data un'immagine bitmap, creerà un motivo a punto croce come PDF. Userò Cocoa / Objective C su un Mac.

La bitmap di origine sarà in genere un'immagine a 24 bpp, ma dei milioni di colori disponibili, ne esistono solo pochi come fili a punto croce. Le discussioni sono disponibili in vari tipi. DMC è il più ampiamente disponibile e quasi l'intera gamma è disponibile come valori RGB da vari siti Web. Eccone uno , ad esempio.

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

Il mio primo problema, a mio modo di vedere, è da un punto di partenza dell'RGB da un pixel nell'immagine che sceglie il colore più vicino disponibile dal set DMC. Qual è il modo migliore per trovare matematicamente il colore DMC più vicino e garantire che si adatti anche al colore?

Anche se userò Cocoa, sentiti libero di usare lo pseudo-codice (o anche Java!) in qualsiasi codice che pubblichi.

È stato utile?

Soluzione

Usa lo LAB e trova il colore con distanza euclidea . In questo modo nello spazio colore RGB si ottengono risultati contro-intuitivi. (Oppure usa lo HSL ).

Quindi basta scorrere su ogni pixel e trovare il colore con la distanza più vicina all'interno dello spazio colore scelto. Nota che la distanza deve essere calcolata in modo circolare per alcuni spazi colore (ad esempio quelli che impiegano tonalità ).

(La maggior parte della quanizzazione del colore ruota attorno alla scelta effettiva di una tavolozza, ma questo è già stato curato nel tuo caso, quindi non puoi usare le tecniche di quantizzazione più popolari.)

Inoltre, controlla questa domanda .

Per trovare la tonalità HSB in Cocoa, sembra che tu possa usare metodo getHue dichiarato in NSColor.h .

Tuttavia, se converti un'immagine in un disegno a punto croce usando questa tecnica, sarà molto difficile effettivamente cucirla. Sarà pieno di campi di colore a pixel singolo, che una sorta di sconfitta allo scopo del punto croce.

Altri suggerimenti

Questo si chiama quantizzazione del colore e ci sono molti algoritmi disponibili.

Un modo molto semplice è quello di trattare semplicemente i colori RGB come punti nello spazio e usare la semplice vecchia distanza euclidea tra i colori per capire come "chiudere" loro sono. Ciò ha degli inconvenienti, poiché gli occhi umani hanno sensibilità diverse in luoghi diversi in questo spazio, quindi una tale distanza non corrisponderebbe bene a come gli umani percepiscono i colori. È possibile utilizzare vari schemi di ponderazione per migliorare tale situazione.

Interresting ... :)

Non solo identificheresti i colori più vicini, ma vorrai anche ridurre il numero di colori utilizzati. Non vuoi finire con un modello di cucitura che utilizza centinaia di colori diversi ...

Ho messo insieme un codice che lo fa a livello base. (Mi dispiace che sia in C #, spero che possa essere comunque utile in qualche modo.)

Ci sono alcune ulteriori modifiche che devono essere fatte prima che il metodo funzioni bene, ovviamente. Il metodo GetDistance valuta l'importanza di tonalità, saturazione e luminosità l'una contro l'altra, trovare il miglior equilibrio tra questi è ovviamente importante per trovare il colore che sembra più vicino.

C'è anche molto da fare con il metodo di riduzione della palette. Nell'esempio ho appena scelto i colori più usati, ma probabilmente vorrai ponderare quanto siano simili i colori nella tavolozza. Questo può essere fatto selezionando il colore più usato, riducendo il conteggio dei colori rimanenti nell'elenco a seconda della distanza dal colore selezionato e quindi ricorrendo all'elenco.

La classe Hsl che contiene un colore DMC, può calcolare la distanza da un altro colore e trovare il colore più vicino in un elenco di colori:

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

}

Questo codice imposta un elenco (fortemente ridotto) di colori DMC, carica un'immagine, conta i colori, riduce la tavolozza e converte l'immagine. Ovviamente vorresti anche salvare le informazioni dalla tavolozza ridotta da qualche parte.

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

ottieni la fonte per l'applicazione ppmquant dalla netpbm set di utilità

Altri hanno sottolineato varie tecniche per la quantizzazione del colore. È possibile utilizzare tecniche come Markov Random Fields per provare a penalizzare il sistema per cambiare i colori dei fili in punti di pixel vicini. Esistono alcune librerie MRF multi-etichetta generiche tra cui Boykov's .

Per usare uno di questi, gli elementi dei dati sarebbero i colori di input, le etichette sarebbero l'insieme dei colori dei fili, i termini dei dati potrebbero essere qualcosa di simile alla distanza euclidea nello spazio LAB suggerita da bzlm, e i termini di vicinato sarebbero penalizzare per cambiare i colori del filo.

A seconda della pertinenza della correttezza delle tue operazioni sui colori, ricorda di prendere spazio colore s in considerazione. Mentre l'ho studiato in qualche modo, a causa del mio hobby per la fotografia, sono ancora un po 'confuso su tutto.

Ma, come accennato in precedenza, usa LAB il più possibile, perché (afaik) è agnostico nello spazio colore, mentre tutti gli altri metodi (RGB / HSL / CMYK) non significano nulla (in teoria) senza uno spazio colore definito.

RGB, ad esempio, è solo tre valori percentuali (0-255 = > 0-100%, con profondità di colore a 8 bit). Quindi, se hai una tripletta RGB di (0,255,0), si traduce in "solo verde, e il più possibile". Quindi, la domanda è "quanto è rosso?" Questa è la domanda a cui risponde uno spazio colore: sRGB 100% -verde non è verde come AdobeRGB 100% verde. Non è nemmeno lo stesso hue !

Scusate se questo è andato al di fuori delle cose

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top