2.2.1 - DexelHistogram
Asi como todos los dexels, el de histograma
es una forma alternativa de representar la información de una imagen,
con un criterio diferente al espacial que se tiene en la implementación.
Veamos una rápida descripción de que se
trata con el caso más clásico, el de una imagen 2D monocromática
que se clasifica por nivel de gris. Supongamos que cada nivel se representa
con un byte, con lo que podemos ver a los datos de la imagen ( la implementación
) como un array de números enteros que toman valores entre 0 y 255.
Para simplificar supongamos que se trata de una pequeña imagen 6
x 4 con valores entre 0 y 9 como se indica en la figura 2.2.1.1.
Figura 2.2.1.1
Hacer el histograma de esta imagen consiste simplemente
en contar el número de pixeles que tienen el mismo nivel de gris.
Con lo que la estructura necesaria para almacenar la información
es una tabla, donde en cada nodo se guarda la información sobre
el nivel de gris y el número de apariciones. La que podría
ordenarse crecientemente por el nivel de gris en este caso.
El histograma del ejemplo se muestra en la figura 2.2.1.2.
Figura 2.2.1.2
Es importante aclarar que no entraremos aquí en detalles sobre la elección de la estructura concreta para almacenar la información, no pretendemos encarar los detalles informáticos, sino tan solo ilustrar el concepto.
Dado que muchos algoritmos buscan modificar la imagen de forma de obtener un histograma con ciertas características, es necesario en estos casos reconstruir la imagen a partir del histograma. Lo más usual es recorrer la imagen y cambiar todos los pixeles por los valores nuevos correspondientes. En general se tendrá una relación del tipo : pixel = f ( pixel ) donde la función se puede evaluar mediante un algoritmo o una búsqueda en una tabla que tenga una relación pixel viejo - pixel nuevo. De cualquier forma si tenemos una imagen con N columnas y M filas se requiere evaluar la función NM veces. En el caso del ejemplo serán 24 veces.
Si cuando costruimos el histograma guardamos para cada
nivel de gris una lista de las coordenadas , relativas a la imagen, donde
aparecen cada uno de los pixeles con ese nivel, necesitaríamos
evaluar la función tantas veces cuanto niveles de gris existan,
que por lo general será bastante menor que NM.
El "histogrma extendido" del ejemplo se muestra en la
figura 2.2.1.3.
Figura 2.2.1.3
En este caso solo tengo que evaluar 8 veces la función,
en lugar de 24.
Puede ser un poco más costozo el recorrido, ya
que tengo que recorrer una lista principal y para cada uno de sus nodos
una lista de coordenadas. Pero por lo general lo más costozo en
este tipo de algoritmos es evaluar la fución, por lo que usar una
estructura de este tipo disminuirá sustancialmente el tiempo de
procesamiento.
Lo que si es claro es que el tiempo de creación del histograma será mayor que en el caso anterior, así como también se requiere más memoria para almacenar la información.
En Bicoti se provee la posibilidad de crear ambos tipos
de histogramas, definiéndose :
Histograma Usual - cuando no se almacenan las coordenadas.
Histograma Extendido - cuando se almacenan las coordenadas.
Generalización
Buscando dar un caracter más
amplio a lo descripto anteriormente, podemos abstraer algunos conceptos.
Podemos pensar que de una manera más amplia, un histograma es una
forma de agrupar los pixeles de una imagen, poniendo todos los que cumplen
cierta condición en un mismo grupo.
Dado un pixel, que puede ser un tipo básico o
uno estructurado definido por el usuario definimos un atributo del pixel
como alguna propiedad del mismo.
Es decir , suponemos que existe una función f
tal que :
Atributo = f ( Pixel )
Por ejemplo el nivel de gris es un caso particular de
atributo, cuando el pixel es un int o char es el propio valor, pero cuando
es un flotante habrá que redondearlo y cuando es un RGB deberá
tomarse un promedio ponderado entre los tres campos.
Podría tomarse como atributo de un pixel vectorial,
una combinación lineal de sus campos, por ejemplo en RGB podría
tomarse alguno de los colores como atributo.
En principio el atributo de un pixel podría ser
cualquier propiedad que a uno se le ocurra.
Existen en Bicoti clases llamadas bicotiPixelAttributeSelector que se encargan de seleccionar el atributo del pixel.
Para nosotros bicotiDexelHistogram
será una clase que almacenará una lista de pixeles, agrupando
en el mismo nodo todos los que tengan igual atributo. Se guardará
además el número de veces que aparece tal atributo así
como las coordenadas de los correspondientes pixeles.
La figura 2.2.1.4. nos muestra que bicotiDexelHistogram
tiene una referencia a la imagen, de donde lee los pixeles y a la clase
que selecciona el atributo, ambas deben setearse en la construcción
del dexel.
Figura 2.2.1.4
El proceso de construcción del dexel puede describirse
en pocas palabras de la siguiente manera.
Se recorre la imagen, para cada pixel se calcula el atributo
y se agregan las coordenadas de dicho punto a la lista del correspondiente
atributo, si no existe se crea un nuevo nodo.
La estructura interna de la clase se muestra en la figura 2.2.1.5.
Figura 2.2.1.5
La lista principal es de una clase interna HistoInfo que
guarda el valor del atributo ( color_value ) y la lista de coordenadas.
En estas clases están parametrizadas en tres tipos
( hay 3 templates ).
CoordType - Las coordenadas
ColorType - El atributo
PixelType - El Pixel de la imagen.
CoordType
Dado que esta
estructura sirve para cualquier dimensión, una vez elegida la imagen
hay que elegir las coordenadas de forma coherente. Por ejemplo si es una
imagen 2D se toma
CoorType = bicotiCoordinate2D< INTEGER > ,
de enteros porque es una estructura discreta.
ColorType
Es el tipo
del atributo, que no tiene porque ser el mismo que el del Pixel, por ejemplo
si el Pixel es RGB< int > y el atributo es el color rojo , el tipo del
atributo es int , es decir ColorType = int.
Se le llamó ColorType porque por lo general el
atributo es un color.
Si el pixel es int o char, se tendrá en el caso
usual ColorType = PixelType.
PixelType
Es el tipo
del pixel de la imagen a la que está asociado el dexel.
Por ejemplo si tenemos una imagen 2D de unsigned char y queremos hacer el histograma típico según el nivel de gris, la declaración sería :
bicotiDexelHistogram< bicotiCoordinate2D< INTEGER > , unsigned char, unsigned char > dexel
en este caso es lo mismo :
bicotiDexelHistogram< bicotiCoordinate2D< INTEGER > , unsigned char > dexel
por defecto se toma PixelType = ColorType.
Ahora si tenemos una imagen 3D de RGB< int > y el atributo es el color azul :
bicotiDexelHistogram< bicotiCoordinate3D< INTEGER > , int , RGB< int > > dexel