3.1.1 - Image Iterators.
Iteradores
Los iteradores
son objetos ampliamente usados en esta y otras bibliotecas ( en la STL
por ejemplo ), dado que son clases que permiten recorrer en forma estandar
estructuras muy distintas.
La idea básica consiste en que si se tienen estructuras
que contienen elementos, un array un vector, una lista, una tabla hash
por ejemplo, donde se puede definir cierta precedencia, no es necesarios
saber como
se organiza internamente la estructura para recorrerla.
Si pensamos en un iterador como un puntero a algún elemento, basta
con saber como recuperar el elemneto al que estamos apuntando y como movenos
al siguiente.
Por ejemplo si tenemos tres clases bien diferentes, un
vector , una lista y un arbol, todas derivadas de
una clase base, que llamamos contenedor.
Para cada una de estas estructuras, definimos un iterador, con lo que tendremos un nuevo arbol de clases como se muestra en la figura 3.1.3
Es necesaria la clase base para definir una interfáz
estandard. Las cuatro funciones que aparecen en los iteradores son las
fundamentales, las más típicas.
First ( ) - posiciona al iterador en el primer lugar
Next ( ) - mueve el iterador al siguiente lugar
GetCurrent( ) - devuelve el elemento actual
IsDone( ) - indica si se llegó al final del contenedor.
Con estas funciones se puede hacer, con un simple bucle
for, un recorrido por la estructura.
for ( iterator.First( ) ; ! iterator.IsDone( ) ; iterator.Next(
) )
{
elemento = iterator.GetCurrent( ) ;
...
// se hace algo sobre el elemento
}
En la clase base las funciones son virtuales puras y se
definen adecuadamente en cada clase concreta.
Un iterador de array conoce el formato del array, con
lo que sabe como recorrerlo, pero el usuario
solo necesita conocer las funciones del iterador para
recorrer la estructura, sin importar cual es.
Si usamos un Factory Method para crear los iteradores,
es decir le agregamos a los contenedores una
función para que cree su correspondiente iterador,
podremos hacer muchos algoritmos genéricos, sin
importar de que contenedor se trata.
Si le agregamos a las clase Contenedor una función
:
ContenedorIterator * CreateIterator( )
Virtual en esta clase pero implementada en Array, Lista
y Arbol.
Crea un iterador y devuelve una referencia al mismo.
Notese que es necesario devolver una referencia
( un puntero ) ya que en realidad se crea un iterador
concreto ( ArrayIterator, ListaIterator o ArbolIterator) pero para poder
hacerlo polimorficamente hay que devolver un puntero a la clase base.
De forma que podremos hacer algoritmos que reciban punteros a la clase base y la recorran usando un iterador. Los algoritmos serán de la forma :
Algoritmo ( Contenedor * ptr_contenedor ) // le paso
un puntero a contenedor
{
ContenedorIterator * ptr_iterator ;
ptr_iterator = ptr_contenedor->CreateIterator( ) ; // Creo
un iterador
for ( ptr_iterator->First( ) ; ! ptr_iterator->IsDone( ) ;
ptr_iterator->Next( ) )
{
elemento = ptr_iterator->GetCurrent( ) ;
...
// se hace algo sobre el elemento
}
}
Cuando se quiere usar este algoritmo para una Lista por ejemplo, debemos crear la lista como :
Contenedor * ptr_lista;
ptr_lista = new Lista ( ... );
La creo con un puntero a la clase base , pero instancio una clase concreta, así puedo hacer :
Algoritmo ( ptr_lista );
Este tipo de algoritmo es totalmente independiente del contenedor, y se verá mucho en la biblioteca, con algunas complicaciones adicionales que se iran explicando.
Iteradores de Imágenes
Los iteradores para imagenes siguen la filosofía descripta anteriromente, donde la estructura de contenedores es la de las implementaciones de imágenes, que se muestra en la figura 1.2.1
En paralelo se tendrá una estructura de iteradores como se indica en la figura 3.1.1
Cada uno de los iteradores del nivel más bajo está
asociado a una de las implementaciones concretas.
Por ejemplo bicotiImageIterator2DArray< PixelType
> es un iterador para la clase bicotiImageImplementation2DArray< PixelType
>
Las implementaciones de imágenes tienen funciones
para crear iteradores ( Factory Method ).
Se crea un iterador concreto y se devuelve un puntero
a la clase base.
Con lo que si tenemos un puntero a una imagen ptr_image,
podemos crear un iterador como :
bicotiImageIterator<
PixelType > * ptr_iterator;
ptr_iterator
= ptr_image->CreateIterator( );
y podemos usar la interfáz estándar del iterador para recorrer la imagen, sin importar de que dimensión o tipo se trata. Lo que si es necesario saber es el tipo del pixel, ya que como la imagen es template del pixel, el iterador también lo será.
La relación Imagen - Iterador es la que se muestra en la figura 3.1.4
El iterador mantiene una referencia ( un puntero ) a la
imagen sobre la cual "itera". Por otro lado, la imagen crea al iterador
y luego pierde referencia a el.
Un iterador solo puede tener referencia a una imagen,
pero una imagen puede tener más de un iterador "iterando" sobre
ella , inclusive pueden estar en puntos diferentes. Toda la información
referente a la posición se almacena en el iterador, con lo que dos
iteradores pueden moverse en forma totalmente independiente sobre una misma
imagen.
Recorrido
Las imágenes son estructuras
multidimensionales ( 2D , 3D , etc ), con lo que el sentido del recorrido
no es tan obvio como en una estructura lineal como un array por ejemplo.
En el caso de una imagen 2D por ejemplo la de la figura
3.1.10
El iterador está ubicado en el punto de coordenadas
( x_0 = 4 , x_1=1 ). La función Next( ) puede definirse de dos formas
:
Por filas : Incrementando x_0
Por columnas : Incrementando x_1
Por este motivo, en los iteradores, definimos un operador
++ ( el equivalente al Next( ) ) que se moverá en la dirección
predeterminada y funciones que nos dejen movernos en otros sentidos.
Por ejemplo en 2D.
X0Increment(
) - aumenta la columna.
X1Increment(
) - aumenta la fila.
X0Decrement(
) - disminuye la columna.
X1Decrement(
) - disminuye la fila.
La dirección predeterminada depende de la estrategia de borde que se elija. Ver estrategias de borde.
La forma típica de recorrer una imagen es:
bicotiImageIterator<
PixelType > * ptr_iterator;
ptr_iterator
= ptr_image->CreateIterator( ); // Creo el iterador
for
( ptr_iterator->First() ; ! (ptr_iterator->IsDone()) ; ( * ptr_iterator
)++ )
pixel = * * ptr_iterator ;
Este tipo de loops se verá mucho en los algoritmos de la biblioteca.
Se pueden ver todos los detalles sobre las funciones en
:
bicotiImageIterator
bicotiImageIterator2D
bicotiImageIterator2DArray
bicotiImageIterator2DSparse
bicotiImageImplementation3D
bicotiImageIterator3DArray
bicotiImageIterator3DLinear