//
// OMETiffReader.java
//
/*
OME Bio-Formats package for reading and converting biological file formats.
Copyright (C) 2005-@year@ UW-Madison LOCI and Glencoe Software, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package loci.formats.in;
import java.io.File;
import java.io.IOException;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Vector;
import ome.xml.model.primitives.NonNegativeInteger;
import ome.xml.model.primitives.PositiveInteger;
import loci.common.Location;
import loci.common.RandomAccessInputStream;
import loci.common.services.DependencyException;
import loci.common.services.ServiceException;
import loci.common.services.ServiceFactory;
import loci.formats.CoreMetadata;
import loci.formats.FormatException;
import loci.formats.FormatReader;
import loci.formats.FormatTools;
import loci.formats.IFormatReader;
import loci.formats.MetadataTools;
import loci.formats.MissingLibraryException;
import loci.formats.meta.IMetadata;
import loci.formats.meta.MetadataStore;
import loci.formats.ome.OMEXMLMetadata;
import loci.formats.services.OMEXMLService;
import loci.formats.services.OMEXMLServiceImpl;
import loci.formats.tiff.IFD;
import loci.formats.tiff.IFDList;
import loci.formats.tiff.PhotoInterp;
import loci.formats.tiff.TiffParser;
/**
* OMETiffReader is the file format reader for
* <a href="http://ome-xml.org/wiki/OmeTiff">OME-TIFF</a> files.
*
* <dl><dt><b>Source code:</b></dt>
* <dd><a href="http://trac.openmicroscopy.org.uk/ome/browser/bioformats.git/components/bio-formats/src/loci/formats/in/OMETiffReader.java">Trac</a>,
* <a href="http://git.openmicroscopy.org/?p=bioformats.git;a=blob;f=components/bio-formats/src/loci/formats/in/OMETiffReader.java;hb=HEAD">Gitweb</a></dd></dl>
*/
public class OMETiffReader extends FormatReader {
// -- Fields --
/** Mapping from series and plane numbers to files and IFD entries. */
protected OMETiffPlane[][] info; // dimensioned [numSeries][numPlanes]
/** List of used files. */
protected String[] used;
private int lastPlane = 0;
private boolean hasSPW;
private int[] tileWidth;
private int[] tileHeight;
private OMEXMLService service;
// -- Constructor --
/** Constructs a new OME-TIFF reader. */
public OMETiffReader() {
super("OME-TIFF", new String[] {"ome.tif", "ome.tiff"});
suffixNecessary = false;
suffixSufficient = false;
domains = FormatTools.NON_GRAPHICS_DOMAINS;
hasCompanionFiles = true;
datasetDescription = "One or more .ome.tiff files";
}
// -- IFormatReader API methods --
/* @see loci.formats.IFormatReader#isSingleFile(String) */
public boolean isSingleFile(String id) throws FormatException, IOException {
// parse and populate OME-XML metadata
String fileName = new Location(id).getAbsoluteFile().getAbsolutePath();
RandomAccessInputStream ras = new RandomAccessInputStream(fileName);
TiffParser tp = new TiffParser(ras);
IFD ifd = tp.getFirstIFD();
long[] ifdOffsets = tp.getIFDOffsets();
ras.close();
String xml = ifd.getComment();
if (service == null) setupService();
OMEXMLMetadata meta;
try {
meta = service.createOMEXMLMetadata(xml);
}
catch (ServiceException se) {
throw new FormatException(se);
}
if (meta.getRoot() == null) {
throw new FormatException("Could not parse OME-XML from TIFF comment");
}
int nImages = 0;
for (int i=0; i<meta.getImageCount(); i++) {
int nChannels = meta.getChannelCount(i);
if (nChannels == 0) nChannels = 1;
int z = meta.getPixelsSizeZ(i).getValue().intValue();
int t = meta.getPixelsSizeT(i).getValue().intValue();
nImages += z * t * nChannels;
}
return nImages <= ifdOffsets.length;
}
/* @see loci.formats.IFormatReader#isThisType(RandomAccessInputStream) */
public boolean isThisType(RandomAccessInputStream stream) throws IOException {
TiffParser tp = new TiffParser(stream);
boolean validHeader = tp.isValidHeader();
if (!validHeader) return false;
// look for OME-XML in first IFD's comment
IFD ifd = tp.getFirstIFD();
if (ifd == null) return false;
String comment = ifd.getComment();
if (comment == null || comment.trim().length() == 0) return false;
comment = comment.trim();
// do a basic sanity check before attempting to parse the comment as XML
// the parsing step is a bit slow, so there is no sense in trying unless
// we are reasonably sure that the comment contains XML
if (!comment.startsWith("<") || !comment.endsWith(">")) {
return false;
}
try {
if (service == null) setupService();
IMetadata meta = service.createOMEXMLMetadata(comment);
for (int i=0; i<meta.getImageCount(); i++) {
meta.setPixelsBinDataBigEndian(Boolean.TRUE, i, 0);
MetadataTools.verifyMinimumPopulated(meta, i);
}
return meta.getImageCount() > 0;
}
catch (ServiceException se) { }
catch (NullPointerException e) { }
catch (FormatException e) { }
catch (IndexOutOfBoundsException e) { }
return false;
}
/* @see loci.formats.IFormatReader#getDomains() */
public String[] getDomains() {
FormatTools.assertId(currentId, true, 1);
return hasSPW ? new String[] {FormatTools.HCS_DOMAIN} :
FormatTools.NON_SPECIAL_DOMAINS;
}
/* @see loci.formats.IFormatReader#get8BitLookupTable() */
public byte[][] get8BitLookupTable() throws FormatException, IOException {
if (info[series][lastPlane] == null ||
info[series][lastPlane].reader == null ||
info[series][lastPlane].id == null)
{
return null;
}
info[series][lastPlane].reader.setId(info[series][lastPlane].id);
return info[series][lastPlane].reader.get8BitLookupTable();
}
/* @see loci.formats.IFormatReader#get16BitLookupTable() */
public short[][] get16BitLookupTable() throws FormatException, IOException {
if (info[series][lastPlane] == null ||
info[series][lastPlane].reader == null ||
info[series][lastPlane].id == null)
{
return null;
}
info[series][lastPlane].reader.setId(info[series][lastPlane].id);
return info[series][lastPlane].reader.get16BitLookupTable();
}
/*
* @see loci.formats.IFormatReader#openBytes(int, byte[], int, int, int, int)
*/
public byte[] openBytes(int no, byte[] buf, int x, int y, int w, int h)
throws FormatException, IOException
{
FormatTools.checkPlaneParameters(this, no, buf.length, x, y, w, h);
lastPlane = no;
int i = info[series][no].ifd;
MinimalTiffReader r = (MinimalTiffReader) info[series][no].reader;
if (r.getCurrentFile() == null) {
r.setId(info[series][no].id);
}
IFDList ifdList = r.getIFDs();
if (i >= ifdList.size()) {
LOGGER.warn("Error untangling IFDs; the OME-TIFF file may be malformed.");
return buf;
}
IFD ifd = ifdList.get(i);
RandomAccessInputStream s =
new RandomAccessInputStream(info[series][no].id);
TiffParser p = new TiffParser(s);
p.getSamples(ifd, buf, x, y, w, h);
s.close();
return buf;
}
/* @see loci.formats.IFormatReader#getSeriesUsedFiles(boolean) */
public String[] getSeriesUsedFiles(boolean noPixels) {
FormatTools.assertId(currentId, true, 1);
if (noPixels) return null;
Vector<String> usedFiles = new Vector<String>();
for (int i=0; i<info[series].length; i++) {
if (!usedFiles.contains(info[series][i].id)) {
usedFiles.add(info[series][i].id);
}
}
return usedFiles.toArray(new String[usedFiles.size()]);
}
/* @see loci.formats.IFormatReader#fileGroupOption() */
public int fileGroupOption(String id) {
try {
boolean single = isSingleFile(id);
return single ? FormatTools.CAN_GROUP : FormatTools.MUST_GROUP;
}
catch (FormatException e) {
LOGGER.debug("", e);
}
catch (IOException e) {
LOGGER.debug("", e);
}
return FormatTools.CAN_GROUP;
}
/* @see loci.formats.IFormatReader#close(boolean) */
public void close(boolean fileOnly) throws IOException {
super.close(fileOnly);
if (info != null) {
for (OMETiffPlane[] dimension : info) {
for (OMETiffPlane plane : dimension) {
if (plane.reader != null) {
try {
plane.reader.close();
}
catch (Exception e) {
LOGGER.error("Plane closure failure!", e);
}
}
}
}
}
if (!fileOnly) {
info = null;
used = null;
lastPlane = 0;
tileWidth = null;
tileHeight = null;
}
}
/* @see loci.formats.IFormatReader#getOptimalTileWidth() */
public int getOptimalTileWidth() {
FormatTools.assertId(currentId, true, 1);
return tileWidth[getSeries()];
}
/* @see loci.formats.IFormatReader#getOptimalTileHeight() */
public int getOptimalTileHeight() {
FormatTools.assertId(currentId, true, 1);
return tileHeight[getSeries()];
}
// -- Internal FormatReader API methods --
/* @see loci.formats.FormatReader#initFile(String) */
protected void initFile(String id) throws FormatException, IOException {
// normalize file name
super.initFile(normalizeFilename(null, id));
id = currentId;
String dir = new File(id).getParent();
// parse and populate OME-XML metadata
String fileName = new Location(id).getAbsoluteFile().getAbsolutePath();
RandomAccessInputStream ras = new RandomAccessInputStream(fileName);
String xml;
IFD firstIFD;
try {
TiffParser tp = new TiffParser(ras);
firstIFD = tp.getFirstIFD();
xml = firstIFD.getComment();
}
finally {
ras.close();
}
if (service == null) setupService();
OMEXMLMetadata meta;
try {
meta = service.createOMEXMLMetadata(xml);
}
catch (ServiceException se) {
throw new FormatException(se);
}
hasSPW = meta.getPlateCount() > 0;
for (int i=0; i<meta.getImageCount(); i++) {
int sizeC = meta.getPixelsSizeC(i).getValue().intValue();
service.removeChannels(meta, i, sizeC);
}
// TODO
//Hashtable originalMetadata = meta.getOriginalMetadata();
//if (originalMetadata != null) metadata = originalMetadata;
LOGGER.trace(xml);
if (meta.getRoot() == null) {
throw new FormatException("Could not parse OME-XML from TIFF comment");
}
String[] acquiredDates = new String[meta.getImageCount()];
for (int i=0; i<acquiredDates.length; i++) {
acquiredDates[i] = meta.getImageAcquiredDate(i);
}
String currentUUID = meta.getUUID();
service.convertMetadata(meta, metadataStore);
// determine series count from Image and Pixels elements
int seriesCount = meta.getImageCount();
core = new CoreMetadata[seriesCount];
for (int i=0; i<seriesCount; i++) {
core[i] = new CoreMetadata();
}
info = new OMETiffPlane[seriesCount][];
tileWidth = new int[seriesCount];
tileHeight = new int[seriesCount];
// compile list of file/UUID mappings
Hashtable<String, String> files = new Hashtable<String, String>();
boolean needSearch = false;
for (int i=0; i<seriesCount; i++) {
int tiffDataCount = meta.getTiffDataCount(i);
for (int td=0; td<tiffDataCount; td++) {
String uuid = null;
try {
uuid = meta.getUUIDValue(i, td);
}
catch (NullPointerException e) { }
String filename = null;
if (uuid == null) {
// no UUID means that TiffData element refers to this file
uuid = "";
filename = id;
}
else {
filename = meta.getUUIDFileName(i, td);
if (!new Location(dir, filename).exists()) filename = null;
if (filename == null) {
if (uuid.equals(currentUUID) || currentUUID == null) {
// UUID references this file
filename = id;
}
else {
// will need to search for this UUID
filename = "";
needSearch = true;
}
}
else filename = normalizeFilename(dir, filename);
}
String existing = files.get(uuid);
if (existing == null) files.put(uuid, filename);
else if (!existing.equals(filename)) {
throw new FormatException("Inconsistent UUID filenames");
}
}
}
// search for missing filenames
if (needSearch) {
Enumeration en = files.keys();
while (en.hasMoreElements()) {
String uuid = (String) en.nextElement();
String filename = files.get(uuid);
if (filename.equals("")) {
// TODO search...
// should scan only other .ome.tif files
// to make this work with OME server may be a little tricky?
throw new FormatException("Unmatched UUID: " + uuid);
}
}
}
// build list of used files
Enumeration en = files.keys();
int numUUIDs = files.size();
HashSet fileSet = new HashSet(); // ensure no duplicate filenames
for (int i=0; i<numUUIDs; i++) {
String uuid = (String) en.nextElement();
String filename = files.get(uuid);
fileSet.add(filename);
}
used = new String[fileSet.size()];
Iterator iter = fileSet.iterator();
for (int i=0; i<used.length; i++) used[i] = (String) iter.next();
// process TiffData elements
Hashtable<String, IFormatReader> readers =
new Hashtable<String, IFormatReader>();
for (int i=0; i<seriesCount; i++) {
int s = i;
LOGGER.debug("Image[{}] {", i);
LOGGER.debug(" id = {}", meta.getImageID(i));
String order = meta.getPixelsDimensionOrder(i).toString();
PositiveInteger samplesPerPixel = null;
if (meta.getChannelCount(i) > 0) {
samplesPerPixel = meta.getChannelSamplesPerPixel(i, 0);
}
int samples = samplesPerPixel == null ? -1 : samplesPerPixel.getValue();
int tiffSamples = firstIFD.getSamplesPerPixel();
boolean adjustedSamples = false;
if (samples != tiffSamples) {
LOGGER.warn("SamplesPerPixel mismatch: OME={}, TIFF={}",
samples, tiffSamples);
samples = tiffSamples;
adjustedSamples = true;
}
if (adjustedSamples && meta.getChannelCount(i) <= 1) {
adjustedSamples = false;
}
int effSizeC = meta.getPixelsSizeC(i).getValue().intValue();
if (!adjustedSamples) {
effSizeC /= samples;
}
if (effSizeC == 0) effSizeC = 1;
if (effSizeC * samples != meta.getPixelsSizeC(i).getValue().intValue()) {
effSizeC = meta.getPixelsSizeC(i).getValue().intValue();
}
int sizeT = meta.getPixelsSizeT(i).getValue().intValue();
int sizeZ = meta.getPixelsSizeZ(i).getValue().intValue();
int num = effSizeC * sizeT * sizeZ;
OMETiffPlane[] planes = new OMETiffPlane[num];
for (int no=0; no<num; no++) planes[no] = new OMETiffPlane();
int tiffDataCount = meta.getTiffDataCount(i);
boolean zOneIndexed = false;
boolean cOneIndexed = false;
boolean tOneIndexed = false;
// pre-scan TiffData indices to see if any of them are indexed from 1
for (int td=0; td<tiffDataCount; td++) {
NonNegativeInteger firstC = meta.getTiffDataFirstC(i, td);
NonNegativeInteger firstT = meta.getTiffDataFirstT(i, td);
NonNegativeInteger firstZ = meta.getTiffDataFirstZ(i, td);
int c = firstC == null ? 0 : firstC.getValue();
int t = firstT == null ? 0 : firstT.getValue();
int z = firstZ == null ? 0 : firstZ.getValue();
if (c >= effSizeC) cOneIndexed = true;
if (z >= sizeZ) zOneIndexed = true;
if (t >= sizeT) tOneIndexed = true;
}
for (int td=0; td<tiffDataCount; td++) {
LOGGER.debug(" TiffData[{}] {", td);
// extract TiffData parameters
String filename = null;
String uuid = null;
try {
filename = meta.getUUIDFileName(i, td);
} catch (NullPointerException e) {
LOGGER.debug("Ignoring null UUID object when retrieving filename.");
}
try {
uuid = meta.getUUIDValue(i, td);
} catch (NullPointerException e) {
LOGGER.debug("Ignoring null UUID object when retrieving value.");
}
NonNegativeInteger tdIFD = meta.getTiffDataIFD(i, td);
int ifd = tdIFD == null ? 0 : tdIFD.getValue();
NonNegativeInteger numPlanes = meta.getTiffDataPlaneCount(i, td);
NonNegativeInteger firstC = meta.getTiffDataFirstC(i, td);
NonNegativeInteger firstT = meta.getTiffDataFirstT(i, td);
NonNegativeInteger firstZ = meta.getTiffDataFirstZ(i, td);
int c = firstC == null ? 0 : firstC.getValue();
int t = firstT == null ? 0 : firstT.getValue();
int z = firstZ == null ? 0 : firstZ.getValue();
// NB: some writers index FirstC, FirstZ and FirstT from 1
if (cOneIndexed) c--;
if (zOneIndexed) z--;
if (tOneIndexed) t--;
int index = FormatTools.getIndex(order,
sizeZ, effSizeC, sizeT, num, z, c, t);
int count = numPlanes == null ? 1 : numPlanes.getValue();
if (count == 0) {
core[s] = null;
break;
}
// get reader object for this filename
if (filename == null) {
if (uuid == null) filename = id;
else filename = files.get(uuid);
}
else filename = normalizeFilename(dir, filename);
IFormatReader r = readers.get(filename);
if (r == null) {
r = new MinimalTiffReader();
readers.put(filename, r);
}
Location file = new Location(filename);
if (!file.exists()) {
// if this is an absolute file name, try using a relative name
// old versions of OMETiffWriter wrote an absolute path to
// UUID.FileName, which causes problems if the file is moved to
// a different directory
filename =
filename.substring(filename.lastIndexOf(File.separator) + 1);
filename = dir + File.separator + filename;
if (!new Location(filename).exists()) {
filename = currentId;
}
}
// populate plane index -> IFD mapping
for (int q=0; q<count; q++) {
int no = index + q;
planes[no].reader = r;
planes[no].id = filename;
planes[no].ifd = ifd + q;
planes[no].certain = true;
LOGGER.debug(" Plane[{}]: file={}, IFD={}",
new Object[] {no, planes[no].id, planes[no].ifd});
}
if (numPlanes == null) {
// unknown number of planes; fill down
for (int no=index+1; no<num; no++) {
if (planes[no].certain) break;
planes[no].reader = r;
planes[no].id = filename;
planes[no].ifd = planes[no - 1].ifd + 1;
LOGGER.debug(" Plane[{}]: FILLED", no);
}
}
else {
// known number of planes; clear anything subsequently filled
for (int no=index+count; no<num; no++) {
if (planes[no].certain) break;
planes[no].reader = null;
planes[no].id = null;
planes[no].ifd = -1;
LOGGER.debug(" Plane[{}]: CLEARED", no);
}
}
LOGGER.debug(" }");
}
if (core[s] == null) continue;
// verify that all planes are available
LOGGER.debug(" --------------------------------");
for (int no=0; no<num; no++) {
LOGGER.debug(" Plane[{}]: file={}, IFD={}",
new Object[] {no, planes[no].id, planes[no].ifd});
if (planes[no].reader == null) {
LOGGER.warn("Image ID '{}': missing plane #{}. " +
"Using TiffReader to determine the number of planes.",
meta.getImageID(i), no);
TiffReader r = new TiffReader();
r.setId(currentId);
try {
planes = new OMETiffPlane[r.getImageCount()];
for (int plane=0; plane<planes.length; plane++) {
planes[plane] = new OMETiffPlane();
planes[plane].id = currentId;
planes[plane].reader = r;
planes[plane].ifd = plane;
}
num = planes.length;
}
finally {
r.close();
}
}
}
LOGGER.debug(" }");
// populate core metadata
info[s] = planes;
try {
if (!info[s][0].reader.isThisType(info[s][0].id)) {
info[s][0].id = currentId;
}
for (int plane=0; plane<info[s].length; plane++) {
if (!info[s][plane].reader.isThisType(info[s][plane].id)) {
info[s][plane].id = info[s][0].id;
}
}
info[s][0].reader.setId(info[s][0].id);
tileWidth[s] = info[s][0].reader.getOptimalTileWidth();
tileHeight[s] = info[s][0].reader.getOptimalTileHeight();
core[s].sizeX = meta.getPixelsSizeX(i).getValue().intValue();
int tiffWidth = (int) firstIFD.getImageWidth();
if (core[s].sizeX != tiffWidth) {
LOGGER.warn("SizeX mismatch: OME={}, TIFF={}",
core[s].sizeX, tiffWidth);
}
core[s].sizeY = meta.getPixelsSizeY(i).getValue().intValue();
int tiffHeight = (int) firstIFD.getImageLength();
if (core[s].sizeY != tiffHeight) {
LOGGER.warn("SizeY mismatch: OME={}, TIFF={}",
core[s].sizeY, tiffHeight);
}
core[s].sizeZ = meta.getPixelsSizeZ(i).getValue().intValue();
core[s].sizeC = meta.getPixelsSizeC(i).getValue().intValue();
core[s].sizeT = meta.getPixelsSizeT(i).getValue().intValue();
core[s].pixelType = FormatTools.pixelTypeFromString(
meta.getPixelsType(i).toString());
int tiffPixelType = firstIFD.getPixelType();
if (core[s].pixelType != tiffPixelType) {
LOGGER.warn("PixelType mismatch: OME={}, TIFF={}",
core[s].pixelType, tiffPixelType);
core[s].pixelType = tiffPixelType;
}
core[s].imageCount = num;
core[s].dimensionOrder = meta.getPixelsDimensionOrder(i).toString();
// hackish workaround for files exported by OMERO that have an
// incorrect dimension order
String uuidFileName = "";
try {
if (meta.getTiffDataCount(i) > 0) {
uuidFileName = meta.getUUIDFileName(i, 0);
}
}
catch (NullPointerException e) { }
if (meta.getChannelCount(i) > 0 && meta.getChannelName(i, 0) == null &&
meta.getTiffDataCount(i) > 0 &&
uuidFileName.indexOf("__omero_export") != -1)
{
core[s].dimensionOrder = "XYZCT";
}
core[s].orderCertain = true;
PhotoInterp photo = firstIFD.getPhotometricInterpretation();
core[s].rgb = samples > 1 || photo == PhotoInterp.RGB;
if ((samples != core[s].sizeC && (samples % core[s].sizeC) != 0 &&
(core[s].sizeC % samples) != 0) || core[s].sizeC == 1 ||
adjustedSamples)
{
core[s].sizeC *= samples;
}
if (core[s].sizeZ * core[s].sizeT * core[s].sizeC >
core[s].imageCount && !core[s].rgb)
{
if (core[s].sizeZ == core[s].imageCount) {
core[s].sizeT = 1;
core[s].sizeC = 1;
}
else if (core[s].sizeT == core[s].imageCount) {
core[s].sizeZ = 1;
core[s].sizeC = 1;
}
else if (core[s].sizeC == core[s].imageCount) {
core[s].sizeT = 1;
core[s].sizeZ = 1;
}
}
if (meta.getPixelsBinDataCount(i) > 1) {
LOGGER.warn("OME-TIFF Pixels element contains BinData elements! " +
"Ignoring.");
}
core[s].littleEndian = firstIFD.isLittleEndian();
core[s].interleaved = false;
core[s].indexed = photo == PhotoInterp.RGB_PALETTE &&
firstIFD.getIFDValue(IFD.COLOR_MAP) != null;
if (core[s].indexed) {
core[s].rgb = false;
}
core[s].falseColor = true;
core[s].metadataComplete = true;
}
catch (NullPointerException exc) {
throw new FormatException("Incomplete Pixels metadata", exc);
}
}
// remove null CoreMetadata entries
Vector<CoreMetadata> series = new Vector<CoreMetadata>();
Vector<OMETiffPlane[]> planeInfo = new Vector<OMETiffPlane[]>();
for (int i=0; i<core.length; i++) {
if (core[i] != null) {
series.add(core[i]);
planeInfo.add(info[i]);
}
}
core = series.toArray(new CoreMetadata[series.size()]);
info = planeInfo.toArray(new OMETiffPlane[0][0]);
MetadataTools.populatePixels(metadataStore, this, false, false);
for (int i=0; i<acquiredDates.length; i++) {
if (acquiredDates[i] != null) {
metadataStore.setImageAcquiredDate(acquiredDates[i], i);
}
}
metadataStore = getMetadataStoreForConversion();
}
// -- OMETiffReader API methods --
/**
* Returns a MetadataStore that is populated in such a way as to
* produce valid OME-XML. The returned MetadataStore cannot be used
* by an IFormatWriter, as it will not contain the required
* BinData.BigEndian attributes.
*/
public MetadataStore getMetadataStoreForDisplay() {
MetadataStore store = getMetadataStore();
if (service.isOMEXMLMetadata(store)) {
service.removeBinData((OMEXMLMetadata) store);
for (int i=0; i<getSeriesCount(); i++) {
if (((OMEXMLMetadata) store).getTiffDataCount(i) == 0) {
service.addMetadataOnly((OMEXMLMetadata) store, i);
}
}
}
return store;
}
/**
* Returns a MetadataStore that is populated in such a way as to be
* usable by an IFormatWriter. Any OME-XML generated from this
* MetadataStore is <em>very unlikely</em> to be valid, as more than
* likely both BinData and TiffData element will be present.
*/
public MetadataStore getMetadataStoreForConversion() {
MetadataStore store = getMetadataStore();
int realSeries = getSeries();
for (int i=0; i<getSeriesCount(); i++) {
setSeries(i);
store.setPixelsBinDataBigEndian(new Boolean(!isLittleEndian()), i, 0);
}
setSeries(realSeries);
return store;
}
// -- Helper methods --
private String normalizeFilename(String dir, String name) {
File file = new File(dir, name);
if (file.exists()) return file.getAbsolutePath();
return new Location(name).getAbsolutePath();
}
private void setupService() throws FormatException {
try {
ServiceFactory factory = new ServiceFactory();
service = factory.getInstance(OMEXMLService.class);
}
catch (DependencyException de) {
throw new MissingLibraryException(OMEXMLServiceImpl.NO_OME_XML_MSG, de);
}
}
// -- Helper classes --
/** Structure containing details on where to find a particular image plane. */
private class OMETiffPlane {
/** Reader to use for accessing this plane. */
public IFormatReader reader;
/** File containing this plane. */
public String id;
/** IFD number of this plane. */
public int ifd = -1;
/** Certainty flag, for dealing with unspecified NumPlanes. */
public boolean certain = false;
}
}