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));
}
}
}
}
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 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:
-
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
-
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
-
-
reordenar matrices para que los ceros en
his[]
estén al final de la matriztambién cuenta las entradas que no son cero en
his[]
y lo llamahists
-
(index) sort
hist[],idx[]
para quehist[]
se ordene descendente; -
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 -
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]
-
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 8pyx1[],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:
- reducir el tamaño máximo del diccionario de codificación
- usando la tabla de índice para el diccionario o tres estructuras para acelerar la búsqueda
- 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)
- para codificar la secuencia, puede usar una paleta única (si la escena no cambia demasiado el color)
- 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):