Histograma de una Imagen a Color.
El histograma de una imagen contiene la información de la probabilidad de aparición de las distintas tonalidades de color que se pueden dar.
Con un Histograma de una imagen se pueden corregir fallas en la distribución de los colores de la imagen. Es muy importante en la corrección de fallas en los tonos.
En el caso de una imagen en color, no podemos hablar de un único histograma que caracterice a la imagen sino de tres histogramas, uno para cada color (RGB).
Al elaborar el histograma, el algoritmo debe separar el color correspondiente a cada pixel en sus componentes RGB (rojo, verde y azul).
Ejemplo de una imagen cualquiera con sus respectivos Histogramas
clase que obtiene el color promedio de una imagen a través de su histograma de color.
- Es un sistema de votación, tenemos una matriz tridimensional
histograma[r][g][b] inicializada a ceros.
- Recorremos cada pixel de la imagen y obtenemos su componente r, g y b.
- Con esas 3 componentes incrementaremos el valor de la casilla
correspondiente: histograma[r][g][b]++
- Finalmente, la casilla que mayor valor tenga será la del color dominante
Puntualizaciones:
- Para sacar el color con error cero, habría que crear una matriz
histograma[256][256][256], que consumiría demasiada memoria y tiempo
para rellenarla.
- Lo que se hace para reducir tiempo y memoria es discretizar el
histograma a, por ejemplo, [64][64][64], de forma que el pixel con
valores de rgb 32,64,128 en lugar de votar por la casilla [32][64][128],
votaría por la casilla [8][16][32], ya que hemos dividido el espacio de
colores entre 4. Esto significa que el color 33,65,129 votaría por la
misma casilla que el color anterior, pero este error es admisible ya que
a simple vista no podremos diferenciar un color de otro.
- Varios ejemplos de color promedio dependiendo del número de niveles en
el que hayamos dividido el histograma:
- [20][20][20] -> rgb = 26 64 13
- [40][40][40] -> rgb = 45 51 32
- [50][50][50] -> rgb = 46 51 36
- [80][80][80] -> rgb = 48 51 35
- [150][150][150] -> rgb = 49 53 38
- [180][180][180] -> rgb = 50 53 38
- Como se puede observar, a mayor número de niveles más preciso es el
algoritmo, pero mucho más tiempo y memoria consume. Con 50 niveles ya
obtenemos un color aceptable que no dista mucho del promedio real, y a
simple vista es inapreciable.
A continuación la clase sin optimizaciones para que sea más legible:
public static function getColorPromedioSinOptimizar(bitmap:BitmapData,
niveles:Number):Number {
var histograma:Array = [];
for (var i:Number=0; i histograma[i] = [];
for (var j:Number=0; j histograma[i][j] = [];
for (var k:Number=0; k histograma[i][j][k] = 0;
}
}
}
var rango:Number = 256 / niveles;
var colorPromedio:Number = 0;
var mayor:Number = 0;
var w:Number = bitmap.width;
var h:Number = bitmap.height;
var totalPixeles:Number = w*h;
var valorVotacion:Number = 1 / totalPixeles;
for (var i:Number=0; i for (var j:Number=0; j var color:Number = bitmap.getPixel(i, j);
var r:Number = (color >> 16) & 0xFF;
var g:Number = (color >> 8) & 0xFF;
var b:Number = color & 0xFF;
r = Math.floor(r / rango);
g = Math.floor(g / rango);
b = Math.floor(b / rango);
histograma[r][g][b] += valorVotacion;
if (histograma[r][g][b] > mayor) {
mayor = histograma[r][g][b];
colorPromedio = color;
}
}
}
return colorPromedio;
}
Y a continuación la clase optimizada, que es menos legible pero funciona
casi el doble de rápido:
public static function getColorPromedio(bitmap:BitmapData,
niveles:Number):Number {
var histo:Array = [];
for (var i:Number=niveles-1; i>=0; i--) {
histo[i] = [];
var histo1:Array = histo[i];
for (var j:Number=niveles-1; j>=0; j--) {
histo1[j] = [];
var histo2:Array = histo1[j];
for (var k:Number=niveles-1; k>=0; k--) {
histo2[k] = 0;
}
}
}
var rango:Number = 256 / niveles;
var rangos:Array = [];
var f:Function = Math.floor;
for (var i:Number=0; i<256; i++) {
rangos[i] = f(i / rango);
}
var w:Number = bitmap.width;
var h:Number = bitmap.height;
var valorVotacion:Number = 1 / (w*h);
var colorPromedio:Number = 0;
var mayor:Number = 0;
for (var i:Number=0; i for (var j:Number=0; j var color:Number = bitmap.getPixel(i, j);
var r:Number = rangos[(color >> 16) & 0xFF];
var g:Number = rangos[(color >> 8) & 0xFF];
var b:Number = rangos[(color & 0xFF)];
histo[r][g][b] += valorVotacion;
if (histo[r][g][b] > mayor) {
mayor = histo[r][g][b];
colorPromedio = color;
}
}
}
return colorPromedio;
}
Es un método estático que se pude incluir en vuestras clases ColorUtils o
similares. El primer parámetro es un objeto BitmapData con la imagen y
el segundo es el número de niveles en el que vamos a dividir el histograma.
Un ejemplo de uso podría ser este:
//imagen es un MovieClip con una imagen dentro en su coordenada (0,0)
var bmp:BitmapData = new BitmapData(imagen._width, imagen._height);
bmp.draw(imagen);
var colorPromedio:Number = ColorUtil.getColorPromedio(bmp, 50);
//resultado es un MovieClip con un cuadrado negro
//lo coloreo para ver el color promedio de la imagen
var c:Color = new Color(timeline.resultado);
c.setRGB(colorPromedio);
Para imágenes más pequeñas o menos niveles, evidentemente menos tiempo.