Cómo actúa una operación en la aplicación

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){

  parametros.insertarParametro(new ParametroOperador(
    OperadorRotacion.PARAMETRO_ANGULO_ROTACION, new Float(
     this.textoAnguloRotacion.getText())
     * new Float(Math.PI) / 180.0f));

  parametros.insertarParametro(new ParametroOperador(
     OperadorRotacion.PARAMETRO_CENTRO_ROTACION_X, new Float(
     this.textoCentroRotacionX.getText())));

  parametros.insertarParametro(new ParametroOperador(
     OperadorRotacion.PARAMETRO_CENTRO_ROTACION_Y, new Float(
     this.textoCentroRotacionY.getText())));

  parametros.insertarParametro(new ParametroOperador(
     OperadorRotacion.PARAMETRO_METODO_INTERPOLACION,
     this.listaMetodosInterpolacion.getInterpolacion()));

  parametros.insertarParametro(new ParametroOperador(
     OperadorRotacion.PARAMETRO_METODO_BORDE, this.listaMetodosBorde
     .getTratadorBorde()));

  parametros.insertarParametro(new ParametroOperador(
     OperadorRotacion.PARAMETRO_ROTAR_Y_CENTRAR,
     this.botonCentrarImagen.getSelection()));
}

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:

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.