Pregunta

Estoy tratando de descubrir cómo probar todos los píxeles en una imagen y generar una paleta de colores, algo así como este o este. No tengo idea de por dónde empezar. ¿Alguien puede señalarme en la dirección correcta?

__EDITAR: __

Esto es con lo que he terminado hasta ahora:

Yo usé esto Pixelato Funcionar para obtener grandes secciones de bloques como sugirió Joe_Coolish. Funciona perfectamente y me da una muy buena muestra de colores para trabajar (esta es de la imagen de pescado de gelatina de muestra de Windows):

Ahora, si alguien pudiera ayudarme a obtener los 5 colores más distintos (azul más oscuro, azul más ligero, naranja, gris y durazno ()), te amaría para siempre. Realmente no entiendo como promedio o agregar colores juntos. Tampoco puedo entender cómo saber si un color es similar programáticamente, hay muchos números y variables en sus explicaciones de que me pierdo tratando de averiguar qué está haciendo a quién.

¿Fue útil?

Solución

Las respuestas que involucran el código le muestran cómo obtener la paleta completa. Si desea obtener los colores promedio como en los sitios web que publicó, así es como lo haría.

Imagen de origen:

Source

Primero, promediaría los colores aplicando un filtro de paso bajo (algo así como un desenfoque gaussiano)

enter image description here

De esa manera estás limitando la paleta total. A partir de ahí, dividiría la pantalla en n bloques (N es el número total de píxeles que desea en su paleta)

enter image description here

A partir de ahí, apunte a cada bloque e itera sobre cada píxel, y obtenga el píxel promedio para ese bloque y agréguelo a su índice de paleta. El resultado es algo como esto:

enter image description here

De esa manera, su paleta es limitada y obtienes los colores promedio de las diferentes regiones. Puedes hacer todo eso en el código, y si quieres ayuda con eso, avísame y publicaré algo. Este es solo el alto nivel "lo que haría".

Otros consejos

Primero, tome los píxeles en la imagen: (asume using System.Drawing.Imaging; y using System.Runtime.InteropServices)

Bitmap b = new Bitmap(myImage);
BitmapData bd = b.LockBits(new Rectangle(0, 0, b.Width, b.Height), ImageLockMode.ReadOnly, ImageFormat.Format32Bpp);
int[] arr = new int[bd.Width * bd.Height - 1];
Marshal.Copy(bd.Scan0, arr, 0, arr.Length);
b.UnlockBits(bd);

Entonces puedes crear tu paleta:

var distinctColors = arr.Distinct();

Opcionalmente: elimine colores similares hasta que tenga el tamaño de su paleta preferido. Así es como podrías hacer eso (aunque esta definitivamente no es la forma más eficiente o precisa, solo la más simple):

var dc = distinctColors.toArray(); // int dc[] = distinctColors.toArray() is what it used to be
int cmIndex1 = -1;
int cmIndex2 = -1;
int cmDiff = -1;
for (int i = 0; i < dc.length; i++) {
    Color c1 = Color.FromArgb(dc[i]);
    for (int j = i + 1; j < dc.length; j++) {
        Color c2 = Color.FromArgb(dc[j]);
        // Note: you might want to include alpha below
        int diff = Math.Abs(c1.R - c2.R) + Math.Abs(c1.G - c2.G) + Math.Abs(c1.B - c2.B);
        if (cmDiff < 0 || diff < cmDiff) {
            cmIndex1 = i;
            cmIndex2 = j;
            cmDiff = diff;
        }
    }
}
// Remove the colors, replace with average, repeat until you have the desired number of colors

Es probable que en cualquier imagen rica que la mayoría de sus colores sean únicos de alguna manera. Seguiría, entonces, que obtener colores distintos probablemente no lo ayudará a lograr su objetivo.

Recomiendo inspeccionar los valores de HSV para cada píxel en su imagen. Te dejo a innumerables ejemplos en línea de recuperación de imágenes como matrices de valores de HSV.

Con sus valores de HSV, puede calcular grupos de tonos prominentes creando una matriz entera de 256 recuentos de tonos, calculando un histograma de tonos en sus datos de imagen. Puede determinar los tonos prominentes al encontrar grupos de 4-6 tonos secuenciales con una alta suma de conteo.

Después de elegir varios tonos prominentes, subdividir los píxeles de esos tonos en otro histograma que mide la saturación, y elija grupos prominentes, y así sucesivamente.

Ejemplo difícil

El siguiente código hace algún intento para ayudar a identificar tonos prominentes. Lo más probable es que hay otras formas increíbles de hacer esto; Sin embargo, esto puede proporcionar algunas ideas.

Primero, obtengo todos los colores de la imagen como una variedad de Color objetos, como así:

private static Color[] GetImageData(Image image)
{
    using (var b = new Bitmap(image))
    {
        var bd = b.LockBits(new Rectangle(0, 0, b.Width, b.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
        byte[] arr = new byte[bd.Width * bd.Height * 3];
        Color[] colors = new Color[bd.Width * bd.Height];
        Marshal.Copy(bd.Scan0, arr, 0, arr.Length);
        b.UnlockBits(bd);

        for (int i = 0; i < colors.Length; i++)
        {
            var start = i*3;
            colors[i] = Color.FromArgb(arr[start], arr[start + 1], arr[start + 2]);
        }

        return colors;
    }
}

Puede considerar validar que obtuve el orden de RGB en el Color.FromArgb Llamada de método en el orden correcto.

A continuación, aparto un método de utilidad para convertir a HSV. En mi ejemplo, solo trabajaré con tonos, pero aquí hay un ejemplo de trabajo completo de la conversión:

private static void ColorToHSV(Color color, out int hue, out int saturation, out int value)
{
    int max = Math.Max(color.R, Math.Max(color.G, color.B));
    int min = Math.Min(color.R, Math.Min(color.G, color.B));

    hue = (int)(color.GetHue() * 256f / 360f);
    saturation = (max == 0) ? 0 : (int)(1d - (1d * min / max));
    value = (int)(max / 255d);
}

Finalmente, construyo el histograma del tono, defino un ancho de tonos (digamos, 9 tonos) en el que agregar cuenta juntos, y luego denuncio los recuentos a la consola.

private static void ProcessImage(Color[] imagecolors)
{
    var hues = new int[256];
    var hueclusters = new int[256];
    int hue, saturation, value;

    // build hue histogram.
    foreach (var color in imagecolors) {
        ColorToHSV(color, out hue, out saturation, out value);
        hues[hue]++;
    }

    // calculate counts for clusters of colors.
    for (int i = 0; i < 256; i++) {
        int huecluster = 0;
        for (int count = 0, j = i; count < 9; count++, j++) {
            huecluster += hues[j % 256];
        }

        hueclusters[(i + 5) % 256] = huecluster;
    }

    // Print clusters on the console
    for (int i = 0; i < 256; i++) {
        Console.WriteLine("Hue {0}, Score {1}.", i, hueclusters[i]);
    }
}

No he intentado filtrar a cual Hues para elegir. Es posible que deba considerar algunas heurísticas en lugar de elegir ciegamente los mejores conteos, porque probablemente desee elegir tonos que estén un poco separados en el espectro de color. No tengo tiempo para explorar esto más, pero espero que esto proporcione una idea de una estrategia que pueda considerar.

Empecé aquí:

System.Drawing.Image img = System.Drawing.Bitmap.FromFile("file");
System.Drawing.Imaging.ColorPalette palette = img.Palette;
foreach (Color color in palette.Entries)
{
  //...
}

Voy a describir el mejor enfoque en un nivel muy alto.

Primero construye un histograma de colores en la imagen y su frecuencia.

Termina con una lista de todos los colores en la imagen, puede usar la agrupación de datos para encontrar colores candidatos para fusionar. Los colores que se fusionan en un promedio ponderado basado en la frecuencia de los colores originales.

De esta manera, puede disminuir incrementalmente la paleta a una fidelidad deseada, al tiempo que preserva los detalles de alto contraste pero solo los detalles y solo perder fidelidad donde los gradientes son mucho más sutiles.

Una vez que tenga la paleta reducida, recolore la imagen usando el color vecino más cercano que está en la paleta.

los K-medias El algoritmo de agrupación funciona bien para este problema. Hace un gran trabajo al extraer centroides de grupos de color de imagen, pero tenga en cuenta que su comportamiento no determinista dificulta la determinación de la prominencia real de cada grupo.

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