BenCole
BenCole

Reputation: 2112

Java ImageIO-ext TIF File Corrupt when Read

I am attempting to display a .tif in Java using a minimal number of additional libraries:

import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.WindowConstants;

import javax.media.jai.widget.*;
import it.geosolutions.imageio.utilities.*;
import it.geosolutions.imageioimpl.plugins.tiff.*;
import com.sun.media.imageioimpl.common.*;

public static void main(String[] args) {
    try {
        File f = new File("image.tif");  
        BufferedImage tif = ImageIO.read(f);  
        ImageIcon ic = new ImageIcon(tif);  
        JFrame frame = new JFrame();  
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);  
        JLabel label = new JLabel(ic);  
        frame.add(label);  
        frame.setVisible(true);  
    } catch (IOException e) {
        e.printStackTrace();
    }
}

The libraries I'm using are:

 jai-core-1.1.3.jar
 jai-imageio-1.1.jar
 imageio-ext-tiff.1.1.3.jar
 imageio-ext-utilities.1.1.3.jar

From here: http://java.net/projects/imageio-ext (Downloads link on right side)

However, the displayed image is: corrupted-tif which is decidedly not the original image. Nor are any errors being thrown that I know of. Furthermore, the original image is fine, and doesn't change.

However, the original code is small. I don't actually use the imageio-ext imports, but the program will fail without them. I also haven't used imageio-ext before either.

Please help! I need to be able to use .tif images in Java without installing software.

Upvotes: 1

Views: 4775

Answers (3)

BenCole
BenCole

Reputation: 2112

I ended up going with the most-recent version of Apache-Commons Imaging (formerly Sanselan). Imaging offers out of the box support for TIFF files (I had as little bit of trouble at first, but that was solved by switching from the older Sanselan to the newer Commons Imaging).

There was a little bit of functionality I had to reverse-engineer myself (loading a single sub-TIFF at a specified width while maintaining aspect ratio):

/**
 * Load a scaled sub-TIFF image.  Loads nth sub-image and scales to given width; preserves aspect ratio.
 * 
 * @param fileName String filename
 * @param index Index of sub-TIFF; will throw ArrayIndexOutOfBoundsException if sub-image doesn't exist
 * @param w Desired width of image; height will scale
 * @return Image (BufferedImage)
 * @throws IOException
 * @throws ImageReadException
 */
public static Image loadScaledSubTIFF(String fileName, int index, int w) throws IOException, ImageReadException {
    File imageFile = new File(fileName);
    ByteSourceFile bsf = new ByteSourceFile(imageFile);
    FormatCompliance formatCompliance = FormatCompliance.getDefault();
    TiffReader tiffReader = new TiffReader(true);
    TiffContents contents = tiffReader.readDirectories(bsf, true, formatCompliance);
    TiffDirectory td = contents.directories.get(index);
    Image bi = td.getTiffImage(tiffReader.getByteOrder(), null);
    Object width = td.getFieldValue(new TagInfo("", 256, TiffFieldTypeConstants.FIELD_TYPE_SHORT) {/**/});
    Object height = td.getFieldValue(new TagInfo("", 257, TiffFieldTypeConstants.FIELD_TYPE_SHORT) {/**/});
    int newWidth = w;
    int newHeight = (int) ((newWidth * ((Number)height).doubleValue()) / (((Number)width).doubleValue()));

    bi = bi.getScaledInstance(w, newHeight, java.awt.Image.SCALE_FAST);
    height = null;
    width = null;
    td = null;
    contents = null;
    tiffReader = null;
    formatCompliance = null;
    bsf = null;
    return bi;
}

Upvotes: 0

Matthieu
Matthieu

Reputation: 3097

If you already use all JAI/ImageIO libraries, you might want to try the following (which works fine for me):

import com.sun.media.jai.codec.FileSeekableStream;
import com.sun.media.jai.codec.ImageCodec;
import com.sun.media.jai.codec.ImageDecoder;

// This function is minimal, you should add exceptions and error handling
public RenderedImage read(String filename)
    FileSeekableStream fss = new FileSeekableStream(filename);
    ImageDecoder decoder = ImageCodec.createImageDecoder("tiff", fss, null);
    RenderedImage image = decoder.decodeAsRenderedImage()
    fss.close();
    return image;
}

If you need a BufferedImage instead of a RenderedImage, the only solution I found is to use this function:

public static BufferedImage Rendered2Buffered(RenderedImage image) {
    BufferedImage bi = new BufferedImage(image.getWidth(), image.getHeight(), image.getSampleModel().getDataType());
    bi.setData(image.getData());
    return bi;
}

Be careful though, the image.getSampleModel().getDataType() usually returns a BufferedImage.TYPE_CUSTOM, which makes it impossible for the BufferedImage to be created! In my case I had to "guess" the type according to the sample size returned by image.getSampleModel().getSampleSize(0) (because I know the image format I'm working with). If you know a better way to transform a RenderedImage to a BufferedImage, please enlighten me :)

Upvotes: 2

Jamie
Jamie

Reputation: 4078

You're correct in thinking that you need the JAI libraries to decode and use TIFF files, but even though you've imported them, you aren't actually using them!

Here is a short tutorial showing how you to create a TIFFDecodeParam object (from the JAI library), and then use that to decode (and display) a TIFF image.

You might also find the JAI API Library useful too.

Upvotes: 0

Related Questions