package edu.mbl.jif.imaging.tiff;
import com.sun.media.imageio.plugins.tiff.BaselineTIFFTagSet;
import com.sun.media.imageio.plugins.tiff.TIFFImageReadParam;
import com.sun.media.imageio.plugins.tiff.TIFFImageWriteParam;
import edu.mbl.jif.imaging.meta.TiffMetadata;
import edu.mbl.jif.imaging.api.SeriesFileListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.ImageTypeSpecifier;
import javax.imageio.ImageWriter;
import javax.imageio.event.IIOWriteProgressListener;
import javax.imageio.metadata.IIOInvalidTreeException;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.metadata.IIOMetadataNode;
import javax.imageio.spi.IIORegistry;
import javax.imageio.stream.ImageInputStream;
import javax.imageio.stream.ImageOutputStream;
import org.w3c.dom.Node;
//----------------------------------------------------------------
// MultipageTiffFile
// GBH Dec 2003 - Mar 2006
// Incremental Image Read/Write using javax.imageio
//
// static:
// ArrayList MultipageTiffFile.loadImageArrayList(filename);
// MultipageTiffFile.saveImageArrayList(imageArray, filename);
// MultipageTiffFile.appendToSeriesTiffFile(outImage, pathFile);
//----------------------------------------------------------------
// This object may be wrapped in a SeriesOfImages
// or a
/*
* To enable viewing series images during series acquisition,
* requires open, append, close, so that it can be opened, but... timing is tough
* ? Could write to tempSeriesAcqDir and create multipage file when done ?
* Then, written files can be viewed... then we need a seriesViewer that looks at dir.
*/// @todo ADD EXCEPTION HANDLING
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
/* File f = new File("c\\myimage.gif ");
ImageInputStream iis = null;
try {
iis = ImageIO.createImageInputStream(f);
}
catch (IIOException iioe1) {
System.out.println("Unable to create an input stream!");
return;
}
reader.setInput (stream);
try {
reader.read(0, param);
}
catch (IIOException iioe2) {
System.out.println("An error occurred during reading : " +
iioe2.getMessage());
Throwable t = iioe2.getCause();
if ((t != null) && (t instanceof IOException)) {
System.out.println("Caused by IOException : " +
t.getMessage());
}
}
}
*/
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
public class MultipageTiffFile {
public static final String TIFF_EXT = "tif";
String tiffFilename;
ImageOutputStream ios;
ImageWriter writer;
TIFFImageWriteParam paramWrite;
private boolean writable = false;
private boolean sequenceWritable = false;
ImageInputStream iis;
ImageReader reader;
TIFFImageReadParam paramRead;
private boolean readable = false;
int nextImg = -1;
static {
IIORegistry registry = IIORegistry.getDefaultInstance();
registry.registerServiceProvider(
new com.sun.media.imageioimpl.plugins.tiff.TIFFImageWriterSpi());
registry.registerServiceProvider(
new com.sun.media.imageioimpl.plugins.tiff.TIFFImageReaderSpi());
ImageIO.scanForPlugins();
}
public MultipageTiffFile() {
}
public MultipageTiffFile(String filename) {
tiffFilename = filenameWithTifExt(filename);
// check exists...
File f = new File(tiffFilename);
if (f.exists()) {
openRead(tiffFilename);
} else {
openWrite(tiffFilename);
}
}
// Open for write to new file - deletes if it already exists.
public MultipageTiffFile(String filename, boolean overWrite) {
tiffFilename = filenameWithTifExt(filename);
if (overWrite) {
if (exists(tiffFilename)) {
deleteFile(tiffFilename);
}
}
File f = new File(tiffFilename);
openWrite(tiffFilename);
}
public String getFilename() {
return tiffFilename;
}
public boolean isReadable() {
return readable;
}
public boolean isWritable() {
return writable;
}
public boolean canWriteSequence() {
return sequenceWritable;
}
///////////////////////////////////////////////////////////////////
// Writing
// Open file for writing
public void openWrite(String _filename) {
tiffFilename = filenameWithTifExt(_filename);
try {
//System.out.println("ext: " + ext);
Iterator writers = ImageIO.getImageWritersByFormatName(TIFF_EXT);
writer = (ImageWriter) writers.next();
File tifFile = new File(tiffFilename);
ios = ImageIO.createImageOutputStream(tifFile);
writer.setOutput(ios);
paramWrite = (TIFFImageWriteParam) writer.getDefaultWriteParam();
//org.pf.joi.Inspector.inspectWait(paramWrite);
// param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
// param.setCompressionType("JPEG");
// param.setCompressionQuality(0.7f);
//System.out.println("canWrite: " + tifFile.canWrite());
if (writer.canWriteSequence()) { // i.e tiff, sff(fax)
writer.prepareWriteSequence(null);
sequenceWritable = true;
} else {
System.err.println("Cannot WriteSequence");
sequenceWritable = false;
}
writable = true;
// remove this...
writer.addIIOWriteProgressListener(new WriteProgressListenerAdapter());
} catch (Exception e) {
System.err.println("Error opening to write: " + tiffFilename);
e.printStackTrace();
writable = false;
}
}
// TODO someday - This allows only one listener
SeriesFileListener listener;
public void addSeriesFileListener(SeriesFileListener listener) {
this.listener = listener;
}
public void removeSeriesFileListener() {
writer.removeAllIIOWriteProgressListeners();
this.listener = null;
}
public class WriteProgressListenerAdapter implements IIOWriteProgressListener {
@Override
public void imageStarted(ImageWriter source, int imageIndex) {
}
@Override
public void imageProgress(ImageWriter source, float percentageDone) {
}
@Override
public void imageComplete(ImageWriter source) {
//System.out.println("ImageComplete event");
}
@Override
public void thumbnailStarted(ImageWriter source, int imageIndex, int thumbnailIndex) {
}
@Override
public void thumbnailProgress(ImageWriter source, float percentageDone) {
}
@Override
public void thumbnailComplete(ImageWriter source) {
}
@Override
public void writeAborted(ImageWriter source) {
}
}
//-----------------------------------------------------------
// appendImage
//
// public void appendImage (BufferedImage image, IIOMetaData streamMeta) {
// if (writer == null) {
// System.err.println("Image append error: no MultipageTiff writer.");
// return;
// }
// try {
// IIOImage iioimg = new IIOImage(image, null, null);
// writer.write(streamMeta, iioimg, null);
// writer.writeToSequence(iioimg, null);
//
// }
// catch (Exception e) {
// System.err.println("Image append Error : " + e.getMessage());
// }
// }
public void appendImage(BufferedImage image) {
appendImage(image, null, null);
}
public void appendImage(BufferedImage image, List thumbnails, IIOMetadata meta) {
if (writer == null) {
System.err.println("Image append error: no MultipageTiff writer.");
return;
}
try {
IIOImage iioimg = new IIOImage(image, thumbnails, meta);
// a test, 7/30/08 {{
//writer.writeInsert(-1, iioimg, null);}
writer.writeToSequence(iioimg, null);
if (listener != null) {
listener.imageAdded();
}
} catch (Exception e) {
System.err.println("Image append Error : " + e.getMessage());
}
}
///////////////////////////////////////////////////////////////////
// Reading
public void openRead(String _filename) {
tiffFilename = filenameWithTifExt(_filename);
try {
Iterator readers = ImageIO.getImageReadersByFormatName(TIFF_EXT);
reader = (ImageReader) readers.next();
iis = ImageIO.createImageInputStream(new File(tiffFilename));
reader.setInput(iis, false);
paramRead = (TIFFImageReadParam) reader.getDefaultReadParam();
readable = true;
} catch (java.util.NoSuchElementException exx) {
System.err.println("No reader found.");
exx.printStackTrace();
readable = false;
} catch (Exception e) {
System.err.println("Error opening to read: " + tiffFilename);
e.printStackTrace();
readable = false;
}
}
public String getTiffFilename() {
return tiffFilename;
}
public int getNumImages() {
try {
return reader.getNumImages(true);
} catch (IOException ex) {
return 0;
}
}
public int getWidth(int n) {
try {
return reader.getWidth(n);
} catch (IOException ex) {
return 0;
}
}
public int getHeight(int n) {
try {
return reader.getHeight(n);
} catch (IOException ex) {
return 0;
}
}
//-----------------------------------------------------------
// get Image MetaData
public IIOMetadata getImageMetadata(int n) {
// if null for (n), use (0), if avail.
try {
return reader.getImageMetadata(n);
} catch (IOException ex) {
return null;
}
}
//-----------------------------------------------------------
// get Stream MetaData
public IIOMetadata getStreamMetadata() {
try {
return reader.getStreamMetadata();
} catch (IOException ex) {
return null;
}
}
//-----------------------------------------------------------
// getImage(n)
public BufferedImage getImage(int n) {
BufferedImage bImage = null;
try {
paramRead.setSourceSubsampling(1, 1, 0, 0);
bImage = reader.read(n, paramRead);
} catch (IndexOutOfBoundsException ioobe) {
System.out.println("getImageFailed");
//ioobe.printStackTrace();
} catch (Exception ex) {
System.out.println("getImage failed: n = " + n + " in " + getFilename());
ex.printStackTrace();
}
return bImage;
}
public IIOImage getIIOImage(int n) {
IIOImage image = null;
try {
image = new IIOImage(reader.read(n), null, reader.getImageMetadata(n));
//image.getMetadata();
} catch (IOException ex) {
}
return image;
}
//-----------------------------------------------------------
// getAsThumbnail(n, scale)
public BufferedImage getAsThumbnail(int n, int subsample) {
BufferedImage bImage = null;
try {
paramRead.setSourceSubsampling(subsample, subsample, 0, 0);
bImage = reader.read(n, paramRead);
} catch (IndexOutOfBoundsException ioobe) {
System.out.println("getAsThumbnail failed: n = " + n + " in " + getFilename());
//ioobe.printStackTrace();
} catch (Exception ex) {
System.out.println("getAsThumbnail failed: n = " + n + " in " + getFilename());
//ex.printStackTrace();
}
return bImage;
}
public BufferedImage getThumbnail(int n) {
if (reader.readerSupportsThumbnails()) {
BufferedImage bImage = null;
try {
bImage = reader.readThumbnail(n, 0);
} catch (Exception ex) {
System.out.println("getAsThumbnail failed: n = " + n + " in " + getFilename());
ex.printStackTrace();
}
return bImage;
} else {
return null;
}
}
//-----------------------------------------------------------
// close the file
public void close() {
closeWrite();
closeRead();
}
public void closeWrite() {
try {
if (writer != null) {
//writer.endWriteSequence();
writer.dispose();
}
if (ios != null) {
ios.close();
ios = null;
}
if (listener != null) {
listener = null;
}
} catch (IOException ex) {
System.err.println("Error closing tiff write");
ex.printStackTrace();
}
}
public void closeRead() {
try {
if (iis != null) {
iis.close();
iis = null;
}
if (reader != null) {
reader.dispose();
}
if (listener != null) {
listener = null;
}
} catch (IOException ex) {
System.err.println("Close");
ex.printStackTrace();
}
}
//=====================================================================
// >>> Static utility methods
public static String filenameWithTifExt(String filename) {
return removeExtension(filename) + "." + TIFF_EXT;
}
// loadImageArrayList
// Load all the pages from a TIFF file into an ArrayList of BufferedImages
public static ArrayList loadImageArrayList(String filename) {
String tifFilename = filenameWithTifExt(filename);
if (!(new File(tifFilename)).exists()) {
System.err.println("File not found in loadImageArrayList()");
return null;
} else {
//System.out.println("File found.");
}
ArrayList bImages = new ArrayList();
try {
long time = System.currentTimeMillis();
//String ext = filename.substring(filename.lastIndexOf('.') + 1);
//System.out.println("ext: " + ext);
Iterator readers = ImageIO.getImageReadersByFormatName(TIFF_EXT);
//System.out.println("readers: " + readers);
// if (readers != null) {
// while (readers.hasNext()) {
// Object item = (Object) readers.next();
// System.out.println("reader: " + item);
// }
// }
ImageReader reader = (ImageReader) readers.next();
ImageInputStream iis = ImageIO.createImageInputStream(new File(tifFilename));
reader.setInput(iis, true);
TIFFImageReadParam param = (TIFFImageReadParam) reader.getDefaultReadParam();
//param.setTIFFDecompressor();
//ImageReadParam param = reader.getDefaultReadParam();
//param.setSourceSubsampling(10, 10, 0, 0);
//BufferedImage img=null;
bImages.removeAll(bImages); // ??
try {
for (int i = 0; true; i++) {
BufferedImage img = reader.read(i, param);
//param.setDestinationOffset(new Point(w, 0));
//param.setDestination(img);
//reader.read(i, param);
bImages.add(img);
}
} catch (IndexOutOfBoundsException ioobe) {
System.out.println("done with loadImageArrayList");
} catch (java.util.NoSuchElementException exx) {
System.err.println("No reader found.");
} catch (Exception exxx) {
System.err.println("Exception in loadImageArrayList");
exxx.printStackTrace();
}
System.out.println("Opened : " + tifFilename);
} catch (Exception e) {
e.printStackTrace();
System.err.println("Error in loadImage(): " + e.getMessage());
}
return bImages;
}
//----------------------------------------------------------------------------
// saveImageArrayList: Writes the ArrayList of images to a file;
// If file exists, appends unless overWrite is specified
public static void saveImageArrayList(ArrayList imageArray, String _filename) {
saveImageArrayList(imageArray, _filename, true);
}
public static void saveImageArrayList(ArrayList imageArray, String _filename, boolean overWrite) {
String filename = filenameWithTifExt(_filename);
try {
//String ext = filename.substring(filename.lastIndexOf('.') + 1);
Iterator writers = ImageIO.getImageWritersByFormatName(TIFF_EXT);
ImageWriter writer = (ImageWriter) writers.next();
// Overwrite if exists
if (overWrite) {
if (exists(filename)) {
deleteFile(filename);
}
}
ImageOutputStream ios = ImageIO.createImageOutputStream(new File(filename));
writer.setOutput(ios);
//
if (writer.canWriteSequence()) { // i.e tiff, sff(fax)
writer.prepareWriteSequence(writer.getDefaultStreamMetadata(
writer.getDefaultWriteParam()));
ios.seek(ios.length() - 1);
for (int i = 0; i < imageArray.size(); i++) {
IIOImage iioimg = new IIOImage((BufferedImage) imageArray.get(i), null, null);
writer.writeToSequence(iioimg, null);
}
// IIOMetadata getDefaultStreamMetadata(ImageWriteParam param)
// writer.replaceStreamMetadata(IIOMetadata);
writer.endWriteSequence();
}
//time = edu.mbl.jif.utils.time.TimerHR.currentTimeMillis() - time;
ios.close();
ios = null;
writer.dispose();
writers = null;
System.out.println("Saved : " + filename);
//System.out.println("Time used to save images : " + time);
} catch (Exception e) {
System.err.println("Image Save Error : " + e.getMessage());
}
}
// appendImageToTiffFile (image, file): Append single image to TIFF
// public static void appendImageToTiffFile(BufferedImage outImage, String pathFile) {
// ArrayList imgList = new ArrayList();
// imgList.add(outImage);
// saveImageArrayList(imgList, pathFile + ".tif", false);
// imgList = null;
// }
public static void appendImageToTiffFile(BufferedImage outImage, String pathFile) {
MultipageTiffFile tif = new MultipageTiffFile();
tif.openWrite(pathFile);
tif.appendImage(outImage);
tif.close();
}
//---------------------------------------------------------------------
public static String removeExtension(String f) {
String ext = null;
if (f == null) {
return null;
}
int i = f.lastIndexOf('.');
if (i > 1) {
ext = f.substring(0, i);
return ext;
} else {
return f;
}
}
static boolean exists(String aFileName) {
File f = new File(aFileName);
return f.exists();
} // exists
static boolean deleteFile(String fileName) {
// A File object to represent the filename
File f = new File(fileName);
// Make sure the file or directory exists and isn't write protected
if (!f.exists()) {
System.err.println("deleteFile: cannot delete, no such file or directory: " + fileName);
return false;
}
if (!f.canWrite()) {
System.err.println("deleteFile: cannot delete - write protected: " + fileName);
return false;
}
// If it is a directory, make sure it is empty
if (f.isDirectory()) {
String[] files = f.list();
if (files.length > 0) {
System.err.println("Delete: directory not empty: " + fileName);
return false;
}
}
// Attempt to delete it
boolean success = f.delete();
if (!success) {
System.err.println("Delete: Delete failed!!: " + fileName);
return false;
//throw new IllegalArgumentException("Delete: deletion failed");
}
return success;
}
// <<< End of static utility methods
//======================================================================
public Node getMetadateAsTree(BufferedImage img) {
return null;
}
IIOMetadata createMetadata(BufferedImage img) {
ImageTypeSpecifier thisType = new ImageTypeSpecifier(img.getColorModel(),
img.getSampleModel());
IIOMetadata imageData = writer.getDefaultImageMetadata(thisType, paramWrite);
//Verify we're stuck with native tiff metadata format.
//Not as expected, c.f. the second table in
//http://java.sun.com/products/java-media/jai/forDevelopers/jai-
// imageio - 1_0 - rc - docs / index.html
System.out.print("Standard metadata format is available: ");
System.out.println(String.valueOf(imageData.isStandardMetadataFormatSupported()));
//Get the name for the native format.
String imageDataFormat = imageData.getNativeMetadataFormatName();
//Turn the metadata object into a tree.
Node iNode = imageData.getAsTree(imageDataFormat);
// Add ImageDescription field
String descStr = "PS___\n" + "zeroIntensity=10\n" + "this=that\n" + "swing=0.01\n"
+ "etc. etc. \n" + " \n";
// use ... addTIFFAsciiField(String tagNumber, String Name, String value){
//Create the little tree corresponding to the Tiff Tag "ImageDescription".
IIOMetadataNode fieldNode = new IIOMetadataNode("TIFFField");
fieldNode.setAttribute("number", "270");
fieldNode.setAttribute("name", "ImageDescription");
IIOMetadataNode asciisNode = new IIOMetadataNode("TIFFAsciis");
fieldNode.appendChild(asciisNode);
IIOMetadataNode asciiNode = new IIOMetadataNode("TIFFAscii");
asciiNode.setAttribute("value", descStr);
asciisNode.appendChild(asciiNode);
//Add this to the image metadata tree, down one level.
Node tagSetNode = iNode.getFirstChild();
tagSetNode.appendChild(fieldNode);
/*
<TIFFField number="258"
name="BitsPerSample">
<TIFFShorts>
<TIFFShort value="8"/>
</TIFFShorts>
</TIFFField>
*/
//Convert the tree back to metadata object
try {
imageData.setFromTree(imageDataFormat, iNode);
} catch (IIOInvalidTreeException e) {
System.out.println(e.getOffendingNode().toString());
}
edu.mbl.jif.imaging.meta.IIOMetadataDisplay.displayIIOMetadataNative(imageData);
return imageData;
}
public IIOMetadata modifyMetadata(BufferedImage img) {
// ImageTypeSpecifier thisType =
// new ImageTypeSpecifier(img.getColorModel(), img.getSampleModel());
ImageTypeSpecifier thisType = ImageTypeSpecifier.createFromBufferedImageType(img.getType());
IIOMetadata imageData = null;
// reader.getDefaultImageMetadata(thisType, reader.getDefaultReadParam());
edu.mbl.jif.imaging.meta.IIOMetadataDisplay.displayIIOMetadataNative(imageData);
TiffMetadata ts = new TiffMetadata(imageData);
int bps = ts.getNumericTag(BaselineTIFFTagSet.TAG_BITS_PER_SAMPLE);
// IIOMetadataNode bitsPerSampleNode =
// (IIOMetadataNode)ts.getTiffField(BaselineTIFFTagSet.TAG_BITS_PER_SAMPLE);
// bps = ts.getTiffShort (bitsPerSampleNode, 0);
System.out.println("bps: " + bps);
return imageData;
}
public static void main(String[] args) {
String inFile = "C:/MicroManagerData/PS-Synth/Synthetic2.tif";
//ArrayList<BufferedImage> images = MultipageTiffFile.loadImageArrayList(inFile);
MultipageTiffFile tiff = new MultipageTiffFile(inFile);
}
}