Pergunta

Para um hobby que eu vou construir um programa que, quando dada uma imagem bitmap irá criar um padrão de ponto-cruz como um PDF. Eu vou estar usando Cocoa / Objective C em um Mac.

O bitmap de origem será tipicamente uma imagem de 24bpp, mas dos milhões de cores disponíveis, apenas existem poucos como threads de ponto de cruz. Threads vêm em vários tipos. DMC é o mais amplamente disponível, e quase a sua gama completa está disponível como valores RGB de várias webs. Aqui está um , por exemplo.

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

Meu primeiro problema, a meu ver, é a partir de um ponto de partida do RGB de um pixel da imagem escolhendo a cor mais próxima disponível a partir do conjunto DMC. Qual é a melhor maneira de encontrar a cor DMC mais próximo matematicamente, e garantir que ele é um bom ajuste como uma cor também?

Embora eu vou estar usando Cocoa, fique à vontade para usar pseudo-código (ou até mesmo Java!) Em qualquer código que você postar.

Foi útil?

Solução

Use a LAB espaço de cor e encontrar a cor com o mais próximo euclidiana distância . Fazer isso no espaço de cor RGB irá produzir resultados contra-intuitivos. (Ou use o HSL espaço de cor.)

Então, basta iterar cada pixel e encontrar a cor com a distância mais próxima dentro do espaço de cor que você escolher. Note-se que a distância deve ser calculado circularmente para alguns espaços de cor (por exemplo, as que empregam Hue ).

(A maioria gira cor quanization em torno de realmente escolher uma paleta, mas que já foi tomado cuidado no seu caso, então você não pode usar as técnicas de quantização mais populares.)

Além disso, confira esta questão .

Para encontrar o tom HSB em Cocoa, parece que você pode usar o método getHue declarou em NSColor.h

.

No entanto, se você apenas converter uma imagem para um design de ponto-cruz usando esta técnica, que vai ser muito difícil realmente costurar. Ele será cheio de campos de cor de um pixel, que tipo de derrotas a finalidade de cross-costura.

Outras dicas

Isso é chamado cor quantização , e existem muitos algoritmos disponíveis.

Um muito básico é apenas cores RGB tratar como pontos no espaço, e usar distância Euclidiana planície antiga entre as cores para descobrir como "fechar" eles são. Isso tem desvantagens, uma vez que os olhos humanos têm sensibilidade diferente em lugares diferentes neste espaço, de modo tal distância não correspondem bem à forma como os seres humanos percebem as cores. Você pode usar vários esquemas de ponderação para melhorar essa situação.

Interresting ...:)

Você não só identificar as cores mais próximas, você também quer reduzir o número de cores utilizadas. Você não quer acabar com um padrão de costura que utiliza centenas de cores diferentes ...

Eu coloquei algum código que faz isso em um nível básico. (Desculpe que está em C #, espero que ele pode ser de qualquer maneira um pouco útil.)

Há alguns mais ajustes que precisa ser feito antes que o método funciona bem, é claro. O método getDistance pesos a importância de matiz, saturação e brilho uns contra os outros, encontrar o melhor equilíbrio entre aqueles é naturalmente importante, a fim de encontrar a cor que fica mais próximo.

Há também muita coisa que pode ser feito com o método de reduzir a paleta. No exemplo que eu escolhi apenas as cores mais usadas, mas você provavelmente vai querer peso na forma como tanto as cores são na paleta. Isso pode ser feito, escolhendo a cor mais utilizada, reduzir a contagem para as cores restantes na lista, dependendo da distância para a cor escolhido, e depois recorrer a lista.

A classe Hsl que mantém uma cor DMC, pode calcular a distância para outra cor, e encontrar a cor mais próxima em uma lista de cores:

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 define uma lista (muito reduzida) de cores DMC, carrega uma imagem, conta as cores, reduz a paleta e converte a imagem. Você faria, claro, também deseja salvar as informações do lugar paleta reduzida.

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

obter o código fonte para a aplicação ppmquant do netpbm conjunto de utilitários

Outros têm apontado várias técnicas de quantização de cor. É possível usar técnicas como Campo aleatório de Markov para tentar penalizar o sistema para mudar cores da linha em locais de pixel vizinho. Existem algumas bibliotecas multi-rótulo MRF genéricos lá fora, incluindo Boykov.

Para usar um destes, os elementos de dados seriam as cores de entrada, os rótulos seria o conjunto de cores da linha, os termos de dados poderia ser algo como a distância Euclidiana no espaço LAB sugerido por bzlm, e os termos de vizinhança permitiria penalize para mudar cores da linha.

Dependendo da relevância da correção de suas operações de cor, lembre-se de tomar cor espaço s em conta. Embora eu tenha estudado este um pouco, devido ao meu hobby a fotografia, eu ainda estou um pouco confuso sobre tudo.

Mas, como mencionado anteriormente, o uso LAB, tanto quanto possível, porque (afaik)-lo do espaço de cor agnóstico, enquanto todos os outros métodos (RGB / HSL / CMYK) não significam nada (em teoria) sem um espaço de cor definido.

RGB, por exemplo, é de apenas três valores percentuais (0-255 => 0-100%, com profundidade de cor de 8 bits). Então, se você tem um RGB-trio de (0,255,0), isso se traduz em "somente verde, e tanto dele quanto possível". Assim, a pergunta é "como vermelho é vermelho?". Esta é a pergunta que uma cor respostas espaço - sRGB 100% -green não é tão verde como AdobeRGB 100% -green. Não é nem o mesmo matiz !

Desculpe se isso foi para o lado offtopic de coisas

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top