java algorithm gif animated-gif quantization

java - ¿GIF efectiva/cuantización de color de imagen?



algorithm animated-gif (3)

Aquí ... escribí esto y funciona un poco más rápido que Octree y parece producir mejores resultados en la mayoría de las imágenes (y fue muchísimo más fácil de codificar jajaja). Básicamente funciona como un Octree, pero al contrario ... crea una lista inicial de colores y luego divide la lista con el mayor número de colores únicos por bits ordenados (con un bit de disminución posterior) según sea necesario hasta que tenga tantos listas como colores deseados. Luego devuelve una matriz que contiene el color promedio de cada lista ...

using System; using System.Collections.Generic; using System.Drawing; using System.Linq; namespace SeelWorks.Libraries.Imaging.Quantization { public static class BitSplitQuantizer { public static Color[] CreatePalette(IEnumerable<Color> sourceColors, int maxColors = 256) { var collections = new List<Collection>(); collections.Add(new Collection()); foreach(var _ in sourceColors) collections[0].Add(_); var offset = 1; while(collections.Count < maxColors) { if(offset > collections.Count) { break; } else { collections = collections.OrderBy(_ => _.Colors.Count).ToList(); var split = collections[collections.Count - offset].Split(); if((split.Count == 1) || ((collections.Count + split.Count - 1) > maxColors)) { offset++; } else { offset = 1; collections.RemoveAt(collections.Count - 1); collections.AddRange(split); } } } return collections.Select(_ => _.GetAverageColor()).ToArray(); } private class Collection { public Dictionary<Color, int> Colors = new Dictionary<Color, int>(); public int Level = -1; public void Add(Color color) { if(!Colors.ContainsKey(color)) Colors.Add(color, 0); Colors[color]++; } public List<Collection> Split() { var colors = Colors.OrderBy(_ => _.Value).Select(_ => _.Key).ToList(); var level = (7 - Level - 1); var indexes = new int[8] { -1, -1, -1, -1, -1, -1, -1, -1 }; var ret = new List<Collection>(); foreach(var _ in colors) { var index_ = ((((_.R >> level) & 1) << 2) | (((_.G >> level) & 1) << 1) | ((_.B >> level) & 1)); if(indexes[index_] == -1) { ret.Add(new Collection()); indexes[index_] = (ret.Count - 1); ret[ret.Count - 1].Level = (Level + 1); } ret[indexes[index_]].Colors[_] = Colors[_]; } return ret; } public Color GetAverageColor() { var r = 0.0; var g = 0.0; var b = 0.0; var t = 0.0; foreach(var _ in Colors) { r += (_.Key.R * _.Value); g += (_.Key.G * _.Value); b += (_.Key.B * _.Value); t += _.Value; } return Color.FromArgb((int)Math.Round(r / t), (int)Math.Round(g / t), (int)Math.Round(b / t)); } } } }

Imagen original:

Octree cuantizado (0.145s):

BitSplit Quantized (0.100s):

Imagen original:

Octree cuantizado (0.233s):

BitSplit Quantized (0.213s):

Así que estoy tratando de codificar algunos archivos gif animados en mi aplicación Java. He estado usando algunas clases / algoritmos encontrados en línea, pero ninguno parece estar funcionando lo suficientemente bien.

En este momento estoy usando esta clase de cuantización para reducir los colores de una imagen a 256: http://www.java2s.com/Code/Java/2D-Graphics-GUI/Anefficientcolorquantizationalgorithm.htm

El problema es que no parece ser muy "inteligente".

Si paso una imagen con más de 256 colores, reduce el número de color, pero no muy bien. (Los rojos se vuelven azules, etc., errores muy obvios como este).

¿Hay otros algoritmos / bibliotecas para la cuantización del color en Java que pueda recomendar?

Nota: Conozco Neuquant, utilizado en este algoritmo: http://www.java2s.com/Code/Java/2D-Graphics-GUI/AnimatedGifEncoder.htm

Es muy lento y produce resultados "eh" (colores que parpadean entre cuadros).


podrías usar Gif89Encoder

Esta biblioteca de clases Java para codificar GIFs cubre más del conjunto extendido de características GIF89a, incluida la animación y los comentarios textuales incrustados, que cualquier otro codificador GIF GIF gratuito.

o http://imagej.nih.gov/ij/

o biblioteca GIF animada para Java

He usado la biblioteca GIF animada para Java con buenos resultados.


puedes buscar en Google otros algoritmos como corte medio, población, k-means, etc.

Hace poco también necesitaba esto, pero también tenía que ser atractivo y rápido (lo necesito para la captura de video en tiempo real), así que logré hacer algo como esto:

  1. convertir a rgb de 15 bits

    si ya tiene RGB, puede omitir este paso pero aplicar shift / y para que coincida con 5 bits por canal. También puede usar el esquema 5: 6: 5

  2. hacer un histograma

    15 bits rgb conducen a 32768 entradas, así que crea 2 matrices

    • his[32768] es el recuento de píxeles (histograma)
    • idx[32768] es index = valor de color

    Mientras cuenta los colores, asegúrese de que los contadores no se desborden si usa un conteo de bits bajo o resoluciones altas

  3. reordenar matrices para que los ceros en his[] estén al final de la matriz

    también cuenta las entradas que no son cero en his[] y lo llama hists

  4. (index) sort hist[],idx[] para que hist[] se ordene descendente;

  5. crear la paleta de colores N

    Tome el color idx[i] ( i={ 0,1,2,3,...,hists-1 } ) y observe si en su paleta no hay un color similar. ignore este color (configúrelo como el más cercano encontrado), de lo contrario, agréguelo a la paleta. si alcanzas los N colores para

  6. crear mapeo de color

    Así que toma cada color y encuentra el color más cercano en la paleta (esto se puede hacer parcialmente en el paso 5). A esta tabla la llamo recolor[32][32][32]

  7. volver a colorear la imagen

Esta es la fuente de C ++:

BYTE db,*p; AnsiString code; int e,b,bits,adr; int x0,x1,y0,y1,x,y,c; DWORD ix,cc,cm,i0,i,mask; union { DWORD dd; BYTE db[4]; } c0,c1; DWORD r,g,b; int a,aa,hists; DWORD his[32768]; DWORD idx[32768]; // 15bit histogram for (x=0;x<32768;x++) { his[x]=0; idx[x]=x; } for (y=0;y<ys;y++) for (x=0;x<xs;x++) { cc=pyx[y][x]; cc=((cc>>3)&0x1F)|((cc>>6)&0x3E0)|((cc>>9)&0x7C00); if (his[cc]<0xFFFFFFFF) his[cc]++; } // remove zeroes for (x=0,y=0;y<32768;y++) { his[x]=his[y]; idx[x]=idx[y]; if (his[x]) x++; } hists=x; // sort by hist for (i=1;i;) for (i=0,x=0,y=1;y<hists;x++,y++) if (his[x]<his[y]) { i=his[x]; his[x]=his[y]; his[y]=i; i=idx[x]; idx[x]=idx[y]; idx[y]=i; i=1; } // set lcolor color palete for (i0=0,x=0;x<hists;x++) // main colors { cc=idx[x]; b= cc &31; g=(cc>> 5)&31; r=(cc>>10)&31; c0.db[0]=b; c0.db[1]=g; c0.db[2]=r; c0.dd=(c0.dd<<3)&0x00F8F8F8; // skip if similar color already in lcolor[] for (a=0,i=0;i<i0;i++) { c1.dd=lcolor[i]; aa=int(BYTE(c1.db[0]))-int(BYTE(c0.db[0])); if (aa<=0) aa=-aa; a =aa; aa=int(BYTE(c1.db[1]))-int(BYTE(c0.db[1])); if (aa<=0) aa=-aa; a+=aa; aa=int(BYTE(c1.db[2]))-int(BYTE(c0.db[2])); if (aa<=0) aa=-aa; a+=aa; if (a<=16) { a=1; break; } a=0; // *** treshold *** } if (a) recolor[r][g][b]=i; else{ recolor[r][g][b]=i0; lcolor[i0]=c0.dd; i0++; if (i0>=DWORD(lcolors)) { x++; break; } } } // i0 = new color table size for (;x<hists;x++) // minor colors { cc=idx[x]; b= cc &31; g=(cc>> 5)&31; r=(cc>>10)&31; c0.db[0]=b; c0.db[1]=g; c0.db[2]=r; c0.dd=(c0.dd<<3)&0x00F8F8F8; // find closest color int dc=-1; DWORD ii=0; for (a=0,i=0;i<i0;i++) { c1.dd=lcolor[i]; aa=int(BYTE(c1.db[0]))-int(BYTE(c0.db[0])); if (aa<=0) aa=-aa; a =aa; aa=int(BYTE(c1.db[1]))-int(BYTE(c0.db[1])); if (aa<=0) aa=-aa; a+=aa; aa=int(BYTE(c1.db[2]))-int(BYTE(c0.db[2])); if (aa<=0) aa=-aa; a+=aa; if ((dc<0)||(dc>a)) { dc=a; ii=i; } } recolor[r][g][b]=ii; }

Y la clase de imagen del propietario contiene esto:

// image data Graphics::TBitmap *bmp,*bmp0,*bmp1; // actual and restore to 32bit frames,and 8bit input conversion frame int xs,ys; // resolution int *py; // interlace table DWORD **pyx,**pyx0; // ScanLine[] of bmp,bmp0 BYTE **pyx1; // colors (colors are computed from color_bits) DWORD gcolor[256]; //hdr DWORD lcolor[256]; //img BYTE recolor[32][32][32]; //encode reduce color table int scolors,scolor_bits; //hdr screen color depth int gcolors,gcolor_bits; //hdr global pallete int lcolors,lcolor_bits; //img/hdr local palette

  • el pyx[],bmp contiene la imagen fuente de 32 bits
  • pyx1[],bmp1 es una imagen temporal de 8 pyx1[],bmp1 para codificar

Así es como se realiza el cambio de color:

// recolor to lcolors for (y=0;y<ys;y++) for (x=0;x<xs;x++) { int r,g,b; c0.dd=(pyx[y][x]>>3)&0x001F1F1F; b=c0.db[0]; g=c0.db[1]; r=c0.db[2]; i=recolor[r][g][b]; // pyx [y][x]=lcolor[i]; // 32 bit output (visual) pyx1[y][x]=i; // 8 bit output (encoding) }

Aquí algunos ejemplos de salida:

Esta es una comparación entre la reducción de color VCL / GDI, mi enfoque y la imagen original)

En la parte superior está el dibujo de la paleta de colores (la imagen original contiene la paleta de la imagen central)

aquí foto en color verdadero:

y reducido a 256 colores:

Esto tomó ~ 185 ms para codificar en GIF (reducción de color incluida). Estoy muy satisfecho con el resultado, pero como pueden ver, las imágenes no son las mismas. Los grupos de hierba verde son un poco diferentes después de volver a colorear (¿menos área / intensidad?)

[Notas]

El código aún no está optimizado, por lo que debería ser una forma de hacerlo más rápido. Puede aumentar la velocidad de codificación al:

  1. reducir el tamaño máximo del diccionario de codificación
  2. usando la tabla de índice para el diccionario o tres estructuras para acelerar la búsqueda
  3. puede cambiar el orden de burbuja del histograma a algo más rápido (pero esa parte del código está lejos de ser crítica)
  4. para codificar la secuencia, puede usar una paleta única (si la escena no cambia demasiado el color)
  5. Si desea aún más velocidad, cree una paleta estática y use tramado en su lugar todo esto

Aquí un ejemplo de video capturado por RT (la fuente era 50 fps, así que reduje la resolución para que coincida con la velocidad):