Toda operación que quiera añadirse a los menús de operaciones de esta aplicación está representada mediante una clase que implementa la interfaz
IOperadorAplicacion.
Dicha interfaz contiene una serie de métodos básicos que representan qué necesita una operación para poder ser incrustada dentro de ésta aplicación.
Toda operación tiene tres componentes principales, a saber el panel de la operación, la operación en sí misma e información
adicional requerida para el funcionamiento de la operación.
Explicaremos estos tres conceptos de forma más detallada.
Cuando se hace referencia al panel de la operación nos referimos realmente (aunque no de forma exacta) al panel que se muestra en la aplicación cuando se hace click en alguna operación del menú de operaciones de tratamiento o del menú de operaciones de caracterización de imágenes.
Toda operación debe contener un panel donde el usuario debe ser capaz de seleccionar los parámetros necesarios para llevar a cabo la operación. Para realizar una convolución, por ejemplo, se necesita saber tanto cuál es el kernel de convolución como qué método es usado a la hora de tratar los bordes de la imagen. En el caso de una operación de rotación hay que especificar tanto el ángulo de rotación como el método de interpolación usado para corregir los errores que surgen cuando la imagen no se rota un múltiplo de 90 grados. Como se ve, cada operación contiene una serie de parámetros necesarios que especifican cómo dicha operación ha de operar sobre la imagen o imágenes de entrada. Por supuesto, también existen operaciones que no necesitan parámetros. Una operación NOT, por ejemplo, no requiere especificar un "ángulo de rotación", ni nada parecido.
La clase
PanelInfoOperador
es una clase abstracta que define la semántica del panel de datos de una operación concreta.
Por ejemplo, el PanelInfoOperadorRotacion
(panel perteneciente a la operación Rotación)
es un panel (Composite) que define una serie de campos que especifican cómo la operación de rotación debe
funcionar; dichos campos son: el ángulo de rotación; el centro de rotación; el método de interpolación usado para
corregir los errores que surgen a consecuencia de realizar la rotación; el método de extensión del borde para tratar
los píxeles que caen fuera de la imagen, y un campo que indica si se quiere que la imagen sea centrada tras la
rotación. Como se observa, el PanelInfoOperadorRotacion
contiene una serie de campos que especifican cómo será
llevada a cabo la rotación. El PanelInfoOperadorRotacion
, como hemos dicho, extiende a la clase PanelInfoOperador
,
lo cual le proporciona la capacidad de ser un panel de operaciones que podrá ser insertado en la aplicación.
Esperamos que hasta aquí esté claro cómo funcionan los paneles asociados a las operaciones. En resumen, toda operación tiene
asociado un PanelInfoOperador
que recoge todos aquellos parámetros que especifican cómo la operación ha de ser
llevada a cabo. Pasemos ahora a explicar cómo funciona esta clase.
La clase PanelInfoOperador
define un método llamado public void getParametros(ConjuntoParametroOperador parametros)
.
Este método es un punto clave dentro de la clase PanelInfoOperador
. La idea es que dicho método debe insertar en el objeto
parametros
los parámetros de la operación que se pueden extraer del panel. Debe tenerse en cuenta que la clase
ConjuntoParametroOperador
representa un conjunto de parámetros
asociados a una determinada operación. El método getParametros(ConjuntoParametroOperador parametros)
debe insertar en el
objeto parametros
los parámetros que pueden extraerse del panel y que sean necesarios para la realización de la operación. Debe
tenerse en cuenta que cada parámetro está representado por un objeto de la clase
ParametroOperador
(para más información acerca de cómo funcionan las clases ConjuntoParametroOperador
y ParametroOperador
, se
recomienda acceder al javadoc de ambas clases). Por ejemplo, el PanelInfoOperadorRotacion
, en su método
getParametros(ConjuntoParametroOperador parametros)
, inserta en parametros
los parámetros asociados al operador de
rotación y que pueden ser extraídos del panel en sí, es decir: el ángulo de rotación, el centro de rotación, etc. En el siguiente extracto de código se puede
ver la implementación de dicho método para la clase PanelInfoOperadorRotacion
:
public void getParametros(ConjuntoParametroOperador parametros){
|
Este método es el más importante de la clase PanelInfoOperador
, ya que él es el que se encarga de recuperar
los parámetros necesarios para la realización de la operación. Analicemos con mayor detalle el código fuente de arriba.
En el objeto parametros
se insertan todos los parámetros que se pueden extraer del panel de la rotación. Como
puede apreciarse, cada parámetro insertado (mediante la función insertarParametro()
) es un objeto de tipo
ParametroOperador
. Cada parámetro tiene asociado dos elementos: un identificador del parámetro (valor int
)
y un Object
que representa el valor del parámetro. Por ejemplo, el primer parámetro que se inserta se corresponde con el
ángulo de rotación de la imagen. El valor numérico del ángulo de rotación es extraído del campo textual del panel que permite
al usuario introducir el ángulo de rotación. Dicho valor numérico se corresponde con el valor del parámetro.
El parámetro, aparte de un valor, tiene asociado un identificador en forma de entero. Así por ejemplo, el primer parámetro
insertado en el ejemplo de arriba tiene un identificador cuyo valor es la constante OperadorRotacion.PARAMETRO_ANGULO_ROTACION
.
El identificador del parámetro no es más que un valor entero
que el usuario, de forma arbitraria, asigna a cada uno de los parámetros que requiere su operación para ejecutarse. Por ejemplo,
el usuario podría asignar el identificador 100 al ángulo de rotación o el identificador 101 al centro de rotación. Es importante
que, sin embargo, no haya parámetros con identificadores repetidos. Así pues, el usuario no podría asignar tanto al ángulo
de rotación como al centro de rotación identificadores ambos de 100. Es también importante notar que el usuario no puede hacer
uso de los identificadores que van del 0 al 99. Dichos identificadores están reservados por la aplicación, y si el usuario
hace uso de ellos, el resultado de la operación puede ser inesperado.
Como habrá pensado, el método getParametros()
de la clase PanelInfoOperador
realmente no recupera todos los parámetros necesarios para hacer la operación. Por ejemplo, habrá visto en el código
fuente de arriba que el método getParametros()
de la clase PanelInfoOperadorRotacion
no inserta en el objeto
parámetros
de entrada el parámetro correspondiente a la imagen a rotar. Para que el OperadorRotacionAplicacion
(que implementa la interfaz IOperadorAplicacion
) pudiera operar de forma correcta, sería necesario que éste recibiera,
además de los parámetros indicados ahí arriba, otro parámetro que contuviera la imagen a rotar, y que sería el siguiente:
new ParametroOperador(OperadorRotacion.PARAMETRO_IMAGEN_FUENTE, "objeto
Imagen
")
Se puede entender que, como parece ser, la clase PanelInfoOperador
no es capaz de proporcionar al operador subyacente
todos los parámetros que necesita para su funcionamiento. Más tarde aclararemos este pequeño detalle.
¿Dónde aparece la clase PanelInfoOperador
dentro de la estructura de la aplicación?. Es aquí cuando
es necesario regresar a la interfaz IOperadorAplicacion
. La interfaz IOperadorAplicacion
define un método,
getPanelInfoOperador()
que simplemente devuelve el PanelInfoOperador
asociado al operador que implementa dicho
IOperadorAplicacion
. La aplicación, en última instancia, hará que se visualice dicho PanelInfoOperador
cuando
el usuario haga uso de la operación, y a partir de él, obtendrá los parámetros necesarios para la ejecución de la operación (pero no todos;
recuerde que las imágenes necesarias para la ejecución de la operación no son extraídas mediante el PanelInfoOperador
).
La interfaz IOperadorAplicacion
define otro método importante, a saber, public void insertarParametros
(ConjuntoParametroOperador parametros)
. Este método es llamado desde la aplicación para registrar en el
IOperadorAplicacion
todos los parámetros que éste necesita para llevar a cabo la operación. La idea es que, registrados
todos los parámetros que el operador necesita para su ejecución, se pueda llamar posteriormente al método operar()
,
para obtener así el resultado de la operación. Así, por ejemplo, en el OperadorRotacionAplicacion
, la función
insertarParametros(ConjuntoParametroOperador parametros)
se debe encargar de, "de algún modo", guardar una copia de
todos los parámetros que se le pasan mediante la función insertarParametros()
. Cuando se llame al método
operar()
del OperadorRotacionAplicacion
, dicho operador se encargará de, usando los parámetros que se
le han pasado mediante el método insertarParametros
, realizar la operación de rotación, y devolver el resultado.
Cada operación puede necesitar de una o varias imágenes de entrada para obtener el resultado. Por ejemplo, una operación de inversión
de imagen, la NOT, requiere una única imagen para operar. Hay operaciones, sin embargo, que requieren de varias imágenes: una suma
de imágenes, por ejemplo, requiere de dos imágenes para obtener la imagen resultado. El operador debe indicar a la aplicación, de algún
modo, cuántas imágenes necesita para obtener su resultado. Ha de tenerse en cuenta que, dentro del paradigma del IOperadorAplicacion
,
las imágenes de entrada son otros parámetros de la operación. La diferencia es que dichas imágenes no se extraen del
PanelInfoOperador
. Como parámetros que son, el usuario, además, deberá informar a la aplicación de cuáles son los
identificadores de parámetro asociados a las imágenes de entrada. La clase
InfoOperadorAplicacion
se encarga de ello.
El método IOperadorAplicacion.getInfoOperador()
debe devolver un objeto de la clase InfoOperadorAplicacion
, el cual
indique tanto cuántas imágenes requiere el operador para obtener el resultado, como cuáles son los identificadores de parámetro asociados a
dichas imágenes. La clase InfoOperadorAplicacion
, además, proporciona un nombre para el operador y una breve descripción. Ambos
podrán ser usados por la aplicación cuándo ésta estime oportuno.
La parte más importante del InfoOperadorAplicacion
es la relativa a la información de las imágenes requeridas por la operación.
El método getInfoImagenes()
de esta clase devuelve un objeto de tipo
ConjuntoInformacionImagenesOperador
. Dicho objeto informa de cuántas imágenes se requieren para llevar a cabo la operación
así como de cuál es su función y cuál es el identificador del parámetro asociado a cada una de esas imágenes. Así, la aplicación es capaz de extraer
los últimos parámetros que faltan para la ejecución de una determinada operación, a saber, las imágenes sobre las que se aplica.
Así pues, una visión general de cómo funciona un operador dentro de la aplicación es la siguiente; usaremos el
ejemplo del OperadorRotacionAplicacion
:
getPanelInfoOperador()
de la clase OperadorRotacionAplicacion
devuelve un objeto de la clase
PanelInfoOperadorRotacion
. Dicho panel, que extiende a la clase PanelInfoOperador
, es el que se muestra
dentro de la aplicación cuando el usuario quiere hacer uso de la operación, y el cual le permite al
usuario introducir todos los parámetros de la rotación (ángulo de rotación, centro de rotación, etc.).
PanelInfoOperadorRotacion
los parámetros necesarios
para la ejecución de la operación de rotación. Para ello, llama al método getParametros(ConjuntoParametroOperador parametros)
. Si
la operación requiere imágenes de entrada, la aplicación construirá los parámetros asociados a la imágenes de la operación. Para ello, hará
uso de la información proporcionada por el InfoOperadorAplicacion
devuelto por el método getInfoOperador()
. Todos
estos parámetros se almacenan en un objeto ConjuntoParametroOperador
insertarParametros()
del OperadorRotacionAplicacion
con el objeto ConjuntoParametroOperador
anteriormente construido. El OperadorRotacionAplicacion
, entonces, almacena todos los
parámetros necesarios para la ejecución de la operación de rotación.
operar()
del OperadorRotacionAplicacion
. Dicho método, internamente,
hace uso de los parámetros que se le registraron mediante insertarParametros()
, y obtiene el resultado de la
rotación, que devuelve.
Observe que lo bueno de la interfaz IOperadorAplicacion
es que es independiente de cómo se realiza la operación a bajo nivel.
El usuario no tiene que preocuparse más que de definir un PanelInfoOperador
concreto, y un objeto InfoOperadorAplicacion
.
Del PanelInfoOperador
, la aplicación extrae los parámetros fundamentales que son necesarios para la ejecución de la operación.
el objeto InfoOperadorAplicacion
, por otro lado, es usado para construir los parámetros de tipo Imagen que el operador requiera.
Una vez que el IOperadorAplicacion
ha recibido los parámetros que necesita
para la operación, los usa como desee en su método operar()
, para devolver posteriormente el resultado. En el caso del
OperadorRotatacionAplicacion
, simplemente se llevará a cabo la rotación de la imagen, que se devolverá como resultado de
la operación.
Para finalizar esta sección, comentar que se proporcionan los códigos fuentes de la operación
de rotación aquí explicada (OperadorRotacion.java,
PanelInfoOperadorRotacion.java,
OperadorRotacionAplicacion.java). Recordar también, que el
OperadorRotacion
hace uso de la biblioteca Java Advanced Imaging (JAI). Si está interesado en utilizar esta
biblioteca para sus operaciones, puede encontrar algunos enlaces de interés en la sección
Bibliografía -> Java Advanced Imaging (JAI). Señalar también que se proporciona una clase,
PanelInfoOperadorVacio
,
que representa un PanelInfoOperador vacío, sin capacidad de proporcionar parámetros. Esta clase puede ser
usada en operaciones que no requieran parámetros, como la NOT, una suma de imágenes, etc.