Rotar imágenes TIFF

2
33635

Manipulación de imágenes TIFF con Java JAI

En Autentia hemos
trabajado con casi toda la gama de formatos existentes para almacenar
información. Uno de los casos más peculiares ha sido la manipulación de ficheros TIFF.
TIFF es un formato casi obsoleto (frente al creciente uso de pdf y jpg),
utilizado principalmente por escáner y fax. Si bien hay un montón de utilidades
y tutoriales para leer y manipular imágenes TIFF, prácticamente no existen en la
Web recursos para modificar y guardar estas imágenes, fuera de foros privados.

En este tutorial vamos a crear un sencillo programa que recibirá una
imagen TIFF por parámetro, leerá sus parámetros, rotará la imagen 90 grados (en
caso de estar apaisada) y guardará la imagen modificada con el mismo nombre.


1. Introducción a JAI (Java Advanced Imaging)

Una de las primeras dificultades con las que nos hemos encontrado es con
la falta de herramientas apropiadas para manipular archivos TIFF.

Nos
explicamos: la mayoría de las API Java utilizadas (J2SDK y JIMI, en el caso de
Autentia) poseen las librerías de descompresión correspondientes a todo los
formatos de imagen utilizados, TIFF incluidos; esto permite abrir cualquier
imagen y manipularla a aplacer. Sin embargo, descubrimos con sorpresa que no
podemos guardar la imagen TIFF que hemos modificado: no existen funciones de
compresión para este formato.

Por ello, en este tutorial vamos a utilizar JAI (Java Advanced Imaging), una
extensión de la Java API de Sun Microsystems que sí implementa compresión y
escritura de imágenes TIFF. JAI es código abierto y es gratuito. Para descargar
JAI podéis utilizar este
enlace
.

Una vez descargado, hay que copiar las librerías jai_codec.jar y
jai_core.jar
en la ruta correspondiente al classpath a utilizar, como se
mostrará más adelante. Estas librerías implementan los modelos de datos y
funciones de acceso al formato TIFF.

2. Acceso a la imagen TIFF

Definimos una clase Rotar. Esta clase define las
siguientes librerías y variables privadas:


/**
 * Rotar.java
 *

 * Autor: Miguel Mena Sevilla



 *



 */

   

import org.w3c.dom.*;


import javax.imageio.*;



import java.awt.image.renderable.ParameterBlock;



import java.io.*;



import com.sun.media.jai.codec.*;



import com.sun.media.jai.codecimpl.*;



import javax.media.jai.*;



public class Rotar


{



    // variables de objeto



   


private

RenderedOp image;



   


private

RenderedOp resultado;



   


private

FileSeekableStream is;



   


private

FileOutputStream os;
    private int code;



    // constantes



    private final static int TIFF_RESOLUTION_INCHES = 2;

         

 Hemos definido 4 variables para los
ficheros de entrada y salida y para las imágenes original y modificada; también
definimos una constante para definir las pulgadas de resolución.

A continuación definimos el método para
cargar una imagen TIFF en memoria: el
método abrir
recibe por parámetro el fichero de la imagen, crea una instancia
FileSeekableStream que recibe el stream del fichero y lo pasa como parametro a
JAI.create para crear un objeto RenderedOp que recoge la imagen

         



   


public void abrir(String entrada) {



        try {



            is = new FileSeekableStream (entrada);



            image = JAI.create(«stream», is);



           


resultado = image;



        } catch (IOException e) {};



    }

    …

Ahora definimos algunas funciones para ver el contenido de los campos de
cabecera de la imagen: altura, anchura, resolución ,tipo de compresión, etc. En
futuras implementaciones de este programa, esta información puede ser utilizada
al guardar la imagen modificada:

         



     /**



     * ver compresion



    


*/



    public void verCompresion() {



        String[] formatos =
ImageCodec.getDecoderNames(is);



       


System.out.println(«Numero de compresores posibles: «+formatos.length);



       


for (int i=0; i<formatos.length; i++) {



           
System.out.println(«    Formato «+i+» : «+formatos[i]);



       

}

        System.out.println(«\n»);

    }


    /**

     * ver datos de la imagen

    

*/



    public void verDatosTIFF() {



      try {



        TIFFDirectory td = new TIFFDirectory(is, 0);



        // ver resolucion de imagen



        float resX =
td.getFieldAsFloat(TIFFImageDecoder.TIFF_X_RESOLUTION);



        float resY =
td.getFieldAsFloat(TIFFImageDecoder.TIFF_Y_RESOLUTION);



        // ver tipo de compresion



        code =
(int)td.getFieldAsLong(TIFFImageDecoder.TIFF_COMPRESSION);



       


// ver altura y anchura de imagen



       


int anchura =
(int)td.getFieldAsLong(TIFFImageDecoder.TIFF_IMAGE_WIDTH);



        int altura =
(int)td.getFieldAsLong(TIFFImageDecoder.TIFF_IMAGE_LENGTH);



       


// imprime por pantalla los parametros



        System.out.println(«»);



        System.out.println(«Resolucion X: «+resX);



        System.out.println(«Resolucion Y: «+resY);



        imprimirCompresion();



        System.out.println(«      Altura: «+altura);



        
System.out.println(«    
Anchura: «+anchura);


        System.out.println(«»);


      } catch (IOException e) {};

   
}



    /**



     * imprimir tipo de compresion



     */



    public void imprimirCompresion() {



       


System.out.print(«  Compresion: «);



        switch (code) {



            case 1: System.out.println(«None»);



                    break;



            case 2: System.out.println(«Grupo 3-1D»);



                    break;



            case 3: System.out.println(«Grupo 3-2D»);



                    break;



            case 4: System.out.println(«Grupo 4»);



                    break;



            case 5: System.out.println(«LZW»);



                    break;



            case 7: System.out.println(«JPG-TTN2»);



                    break;



        case 32946: System.out.println(«Deflate»);



                    break;



           default: System.out.println(«No registrada»);



                    break;



        }



    }

    …

 

3. Rotando la imagen TIFF

Para rotar una imagen, basta con llamar al método rotarImagen; este
método rotará 90 grados una imagen. Para ello utiliza un objeto
ParameterBlock
que recibe toda la información referente a la transformación
de la imagen (imagen incluida):

         



    /**



     * rotar imagen 90 grados



     */



    public void rotarImagen(String entrada) {



        int anchura=image.getWidth();



        int altura=image.getHeight();



       


if (anchura != altura) {



            System.out.println(«Imagen «+entrada+» apaisada.


Girando…»);



            ParameterBlock params = new ParameterBlock();



            params.addSource(image);



            params.add((float)anchura/2);



            params.add((float)altura/2);



            params.add((float)Math.toRadians(270));



           
params.add(Interpolation.getInstance(Interpolation.INTERP_BILINEAR));



            resultado = JAI.create(«rotate», params);



        }



        else {



           


System.out.println(«Imagen «+entrada+» correcta.»);



        }



    }

    …

 

4. Guardando la imagen TIFF

Como el principal objetivo del programa
es el de guardar la imagen TIFF modificada, implementamos los métodos para
escribir imágenes TIFF en disco.

Para empezar, debemos definir los
parámetros de compresión de la imagen a almacenar: en un objeto
TIFFEncodeParam
definimos el tipo de compresión a utilizar (en este programa
utilizamos por defecto el grupo 4):

         



    /**



     * guardar imagen en fichero original



     */



    public void guardarImagen(String salida) {



      try {



        TIFFEncodeParam tiffEncoder = new TIFFEncodeParam();



       
tiffEncoder.setCompression(code);



        tiffEncoder.setExtraFields(nuevaResolucion());



        FileOutputStream os = new FileOutputStream(«temp.tif»);



       


ImageEncoder encoder = ImageCodec.createImageEncoder(«TIFF», os,
tiffEncoder);



       


encoder.encode(resultado);



        os.close();



        abrir(«temp.tif»);



        guardar(salida);



     


} catch (FileNotFoundException e) {



      } catch (IOException ee) {}



   

}

    …

 Como se puede ver en la imagen
anterior, llamamos al método nuevaResolucion() para definir la nueva
resolución de la imagen (200 ppp por defecto). Esto es debido a que, por defecto,
las funciones Java almacenan las imágenes con la resolución de la pantalla del
ordenador, de 96 ppp, con lo que se pierde resolución; hay que especificar la
resolución al guardar la imagen (en este caso, le pasamos imágenes de 200 ppp de
resolución, por lo que las imágenes de salida han de tener esa misma cifra).

         



 
  
/**


     * cambiar resolucion (ppp) del archivo TIFF


    


*/



    public TIFFField[] nuevaResolucion() {



        TIFFField[] tiffFields = new TIFFField[3];



        tiffFields[0] = new TIFFField(TIFFImageDecoder.TIFF_X_RESOLUTION,
TIFFField.TIFF_RATIONAL, 1, new long[][] {{200,1}});



        tiffFields[1] = new TIFFField(TIFFImageDecoder.TIFF_Y_RESOLUTION,
TIFFField.TIFF_RATIONAL, 1, new long[][] {{200,1}});



        tiffFields[2] = new TIFFField(TIFFImageDecoder.TIFF_RESOLUTION_UNIT,
TIFFField.TIFF_SHORT, 1, new char[] {TIFF_RESOLUTION_INCHES} );



        return tiffFields;



    }

    …

Una vez definidos los parámetros de compresión, guardamos
la imagen que hemos leído y la sobreescribimos con la imagen modificada. Para
ello utilizamos un objeto de tipo ImageEncoder, el cual recibe los
parámetros de compresión que hemos detallado en el objeto TIFFEncodeParam
y con el cual invocamos el método encode(), que guarda la imagen:

         



 
  
/**



     * guardar imagen en fichero aparte



     */



   


public void guardar(String salida) {



      try {



        TIFFEncodeParam tiffEncoder = new TIFFEncodeParam();



       
tiffEncoder.setCompression(code);



        tiffEncoder.setExtraFields(nuevaResolucion());



        FileOutputStream os = new FileOutputStream(salida);



       


ImageEncoder encoder = ImageCodec.createImageEncoder(«TIFF», os,
tiffEncoder);



       


encoder.encode(resultado);



        os.close();



     


} catch (FileNotFoundException e) {



      } catch (IOException ee) {}



   

}

    …

 

5. Rotar.java

A continuación mostramos el programa completo Rotar.java:


/**
 * Rotar.java
 *

 * Autor: Miguel Mena Sevilla



 *



 * Descripcion:



 *    Este programa consulta los datos de un archivo TIFF, comprueba
si esta apaisado y,



 *    de ser asi, rotar la imagen 90 grados a la izquierda.



 *



 * Restricciones:



 *    Este programa está orientado específicamente para los archivos
TIFF de la base de



 *    datos de Archivus (compresión a dos niveles); produce
excepción aplicado a archivos



 *    comprimidos en un solo nivel.



 
*/

import org.w3c.dom.*;


import javax.imageio.*;



import java.awt.image.renderable.ParameterBlock;



import java.io.*;



import com.sun.media.jai.codec.*;



import com.sun.media.jai.codecimpl.*;



import javax.media.jai.*;



import java.awt.image.DataBuffer;




public class Rotar



{



    // variables de objeto



   


private

RenderedOp image;



   


private

RenderedOp resultado;



   


private

FileSeekableStream is;



   


private

FileOutputStream os;
   


private

int code;



    // constantes



    private final static int TIFF_RESOLUTION_INCHES     = 2;



   


/**



     * Constructor



     */



    public Rotar() {}



    /**



     * abrir imagen



     */



   


public void abrir(String entrada) {



        try {



            is = new FileSeekableStream (entrada);



            image = JAI.create(«stream», is);



           


resultado = image;



        } catch (IOException e) {};



    }



    /**



     * ver compresion



    


*/



    public void verCompresion() {



        String[] formatos =
ImageCodec.getDecoderNames(is);



       


System.out.println(«Numero de compresores posibles: «+formatos.length);



       


for (int i=0; i<formatos.length; i++) {



           
System.out.println(«    Formato «+i+» : «+formatos[i]);



       

}

        System.out.println(«\n»);

    }


    /**

     * ver datos de la imagen

    

*/



    public void verDatosTIFF() {



      try {



        TIFFDirectory td = new TIFFDirectory(is, 0);



        // ver resolucion de imagen



        float resX =
td.getFieldAsFloat(TIFFImageDecoder.TIFF_X_RESOLUTION);



        float resY =
td.getFieldAsFloat(TIFFImageDecoder.TIFF_Y_RESOLUTION);



        // ver tipo de compresion



        code =
(int)td.getFieldAsLong(TIFFImageDecoder.TIFF_COMPRESSION);



       


// ver altura y anchura de imagen



       


int anchura =
(int)td.getFieldAsLong(TIFFImageDecoder.TIFF_IMAGE_WIDTH);



        int altura =
(int)td.getFieldAsLong(TIFFImageDecoder.TIFF_IMAGE_LENGTH);



       


// imprime por pantalla los parametros



        System.out.println(«»);



        System.out.println(«Resolucion X: «+resX);



        System.out.println(«Resolucion Y: «+resY);



        imprimirCompresion();



        System.out.println(«      Altura: «+altura);



        
System.out.println(«    
Anchura: «+anchura);


        System.out.println(«»);


      } catch (IOException e) {};

   
}



    /**



     * imprimir tipo de compresion



     */



    public void imprimirCompresion() {



       


System.out.print(«  Compresion: «);



        switch (code) {



            case 1: System.out.println(«None»);



                    break;



            case 2: System.out.println(«Grupo 3-1D»);



                    break;



            case 3: System.out.println(«Grupo 3-2D»);



                    break;



            case 4: System.out.println(«Grupo 4»);



                    break;



            case 5: System.out.println(«LZW»);



                    break;



            case 7: System.out.println(«JPG-TTN2»);



                    break;



        case 32946: System.out.println(«Deflate»);



                    break;



           default: System.out.println(«No registrada»);



                    break;



        }



    }



    /**



     * rotar imagen 90 grados



     */



    public void rotarImagen(String entrada) {



        int anchura=image.getWidth();



        int altura=image.getHeight();



       


if (anchura > altura) {



            System.out.println(«Imagen «+entrada+» apaisada.


Girando…»);



            ParameterBlock params = new ParameterBlock();



            params.addSource(image);



            params.add((float)anchura/2);



            params.add((float)altura/2);



            params.add((float)Math.toRadians(270));



           
params.add(Interpolation.getInstance(Interpolation.INTERP_BILINEAR));



            resultado = JAI.create(«rotate», params);



        }



        else {



           


System.out.println(«Imagen «+entrada+» correcta.»);



        }



    }



    /**



     * cambiar resolucion (ppp) del archivo TIFF



    


*/



    public TIFFField[] nuevaResolucion() {



        TIFFField[] tiffFields = new TIFFField[3];



        tiffFields[0] = new TIFFField(TIFFImageDecoder.TIFF_X_RESOLUTION,
TIFFField.TIFF_RATIONAL, 1, new long[][] {{200,1}});



        tiffFields[1] = new TIFFField(TIFFImageDecoder.TIFF_Y_RESOLUTION,
TIFFField.TIFF_RATIONAL, 1, new long[][] {{200,1}});



        tiffFields[2] = new TIFFField(TIFFImageDecoder.TIFF_RESOLUTION_UNIT,
TIFFField.TIFF_SHORT, 1, new char[] {TIFF_RESOLUTION_INCHES} );



        return tiffFields;



    }



    /**



     * guardar imagen en fichero original



     */



    public void guardarImagen(String salida) {



      try {



        TIFFEncodeParam tiffEncoder = new TIFFEncodeParam();



       
tiffEncoder.setCompression(code);



        tiffEncoder.setExtraFields(nuevaResolucion());



        FileOutputStream os = new FileOutputStream(«temp.tif»);



       


ImageEncoder encoder = ImageCodec.createImageEncoder(«TIFF», os,
tiffEncoder);



       


encoder.encode(resultado);



        os.close();



        abrir(«temp.tif»);



        guardar(salida);



     


} catch (FileNotFoundException e) {



      } catch (IOException ee) {}



   

}



    /**



     * guardar imagen en fichero aparte



     */



   


public void guardar(String salida) {



      try {



        TIFFEncodeParam tiffEncoder = new TIFFEncodeParam();



       
tiffEncoder.setCompression(code);



        tiffEncoder.setExtraFields(nuevaResolucion());



        FileOutputStream os = new FileOutputStream(salida);



       


ImageEncoder encoder = ImageCodec.createImageEncoder(«TIFF», os,
tiffEncoder);



       


encoder.encode(resultado);



        os.close();



     


} catch (FileNotFoundException e) {



      } catch (IOException ee) {}



   

}



    /***************************************************************



     * programa principal



    
***************************************************************/



    public static void main(String[] args)



    {



      Rotar rotar = new Rotar();




      if (args.length != 1) {



          System.out.println(«Formato: Rotar <img_entrada>»);



          System.exit(0);



      }



      rotar.abrir(args[0]);



     


rotar.verDatosTIFF();



      rotar.rotarImagen(args[0]);



      rotar.
guardarImagen(args[0]);



      System.exit(0);



    }



}

 

6. Un ejemplo de ejecución

Para compilar y ejecutar el programa,
utilizar las siguientes líneas:

Utilizamos la siguiente imagen:

Y la imagen resultante es:

             

2 Comentarios

  1. Buen dia
    miren una aportacion, trabajamos para crear tiff para otros procesos, existen compresiones lzw, 2d, binivel escala de grisies y de color, para generar esas imagenes utilizamos la compresion.

    params2.setCompression(TIFFEncodeParam.COMPRESSION_PACKBITS);

    en lugar de utilizar :

    tiffEncoder.setCompression(code);

    ya que code, es una variable que obtiene la compresion actual del tiff, en la parte del codigo:
    code = (int)td.getFieldAsLong(TIFFImageDecoder.TIFF_COMPRESSION);

    el escenario es cuando se quiere mezclar compresiones diferentes en un nuevo multitiff

Dejar respuesta

Please enter your comment!
Please enter your name here