2 - Imagen

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