//
// LeicaReader.java
//
/*
LOCI Bio-Formats package for reading and converting biological file formats.
Copyright (C) 2005-@year@ Melissa Linkert, Curtis Rueden, Chris Allan,
Eric Kjellman and Brian Loranger.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Library 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 Library General Public License for more details.
You should have received a copy of the GNU Library 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.*;
import java.text.*;
import java.util.*;
import loci.formats.*;
/**
* LeicaReader is the file format reader for Leica files.
*
* <dl><dt><b>Source code:</b></dt>
* <dd><a href="https://skyking.microscopy.wisc.edu/trac/java/browser/trunk/loci/formats/in/LeicaReader.java">Trac</a>,
* <a href="https://skyking.microscopy.wisc.edu/svn/java/trunk/loci/formats/in/LeicaReader.java">SVN</a></dd></dl>
*
* @author Melissa Linkert linkert at wisc.edu
*/
public class LeicaReader extends FormatReader {
// -- Constants -
/** All Leica TIFFs have this tag. */
private static final int LEICA_MAGIC_TAG = 33923;
// -- Fields --
protected Hashtable[] ifds;
/** Array of IFD-like structures containing metadata. */
protected Hashtable[] headerIFDs;
/** Helper readers. */
protected TiffReader[][] tiff;
/** Array of image file names. */
protected Vector[] files;
/** Number of series in the file. */
private int numSeries;
/** Name of current LEI file */
private String leiFilename;
private int bpp;
private Vector seriesNames;
// -- Constructor --
/** Constructs a new Leica reader. */
public LeicaReader() {
super("Leica", new String[] {"lei", "tif", "tiff"});
}
// -- IFormatReader API methods --
/* @see loci.formats.IFormatReader#isThisType(byte[]) */
public boolean isThisType(byte[] block) {
if (block.length < 4) return false;
if (block.length < 8) {
// we can only check whether it is a TIFF
return (block[0] == 0x49 && block[1] == 0x49 && block[2] == 0x49 &&
block[3] == 0x49) || (block[0] == 0x4d && block[1] == 0x4d &&
block[2] == 0x4d && block[3] == 0x4d);
}
int ifdlocation = DataTools.bytesToInt(block, 4, true);
if (ifdlocation < 0 || ifdlocation + 1 > block.length) {
return false;
}
else {
int ifdnumber = DataTools.bytesToInt(block, ifdlocation, 2, true);
for (int i=0; i<ifdnumber; i++) {
if (ifdlocation + 3 + (i*12) > block.length) return false;
else {
int ifdtag = DataTools.bytesToInt(block,
ifdlocation + 2 + (i*12), 2, true);
if (ifdtag == LEICA_MAGIC_TAG) return true;
}
}
return false;
}
}
/* @see loci.formats.IFormatReader#get8BitLookupTable() */
public byte[][] get8BitLookupTable() throws FormatException, IOException {
FormatTools.assertId(currentId, true, 1);
tiff[0][0].setId((String) files[0].get(0));
return tiff[0][0].get8BitLookupTable();
}
/* @see loci.formats.IFormatReader#get16BitLookupTable() */
public short[][] get16BitLookupTable() throws FormatException, IOException {
FormatTools.assertId(currentId, true, 1);
tiff[0][0].setId((String) files[0].get(0));
return tiff[0][0].get16BitLookupTable();
}
/* @see loci.formats.IFormatReader#fileGroupOption(String) */
public int fileGroupOption(String id) throws FormatException, IOException {
return id.toLowerCase().endsWith(".lei") ? FormatTools.MUST_GROUP :
FormatTools.CAN_GROUP;
}
/* @see loci.formats.IFormatReader#openBytes(int, byte[]) */
public byte[] openBytes(int no, byte[] buf)
throws FormatException, IOException
{
FormatTools.assertId(currentId, true, 1);
FormatTools.checkPlaneNumber(this, no);
tiff[series][no].setId((String) files[series].get(no));
return tiff[series][no].openBytes(0, buf);
}
/* @see loci.formats.IFormatReader#getUsedFiles() */
public String[] getUsedFiles() {
FormatTools.assertId(currentId, true, 1);
Vector v = new Vector();
v.add(leiFilename);
for (int i=0; i<files.length; i++) {
for (int j=0; j<files[i].size(); j++) {
v.add(files[i].get(j));
}
}
return (String[]) v.toArray(new String[0]);
}
/* @see loci.formats.IFormatReader#close(boolean) */
public void close(boolean fileOnly) throws IOException {
if (fileOnly) {
if (in != null) in.close();
if (tiff != null) {
for (int i=0; i<tiff.length; i++) {
if (tiff[i] != null) {
for (int j=0; j<tiff[i].length; j++) {
if (tiff[i][j] != null) tiff[i][j].close(fileOnly);
}
}
}
}
}
else close();
}
// -- IFormatHandler API methods --
/* @see loci.formats.IFormatHandler#isThisType(String, boolean) */
public boolean isThisType(String name, boolean open) {
String lname = name.toLowerCase();
if (lname.endsWith(".lei")) return true;
else if (!lname.endsWith(".tif") && !lname.endsWith(".tiff")) return false;
if (!open) return true; // not allowed to check the file contents
if (!isGroupFiles()) return false;
// just checking the filename isn't enough to differentiate between
// Leica and regular TIFF; open the file and check more thoroughly
Location file = new Location(name);
if (!file.exists()) return false;
long len = file.length();
if (len < 4) return false;
try {
RandomAccessStream ras = new RandomAccessStream(name);
Hashtable ifd = TiffTools.getFirstIFD(ras);
ras.close();
if (ifd == null) return false;
String descr = (String) ifd.get(new Integer(TiffTools.IMAGE_DESCRIPTION));
int ndx = descr == null ? -1 : descr.indexOf("Series Name");
if (ndx == -1) return false;
File f = new File(name).getAbsoluteFile();
String[] listing = null;
if (f.exists()) listing = f.getParentFile().list();
else {
listing =
(String[]) Location.getIdMap().keySet().toArray(new String[0]);
}
for (int i=0; i<listing.length; i++) {
if (listing[i].toLowerCase().endsWith(".lei")) return true;
}
return false;
}
catch (IOException exc) {
if (debug) trace(exc);
}
catch (ClassCastException exc) {
if (debug) trace(exc);
}
return false;
}
/* @see loci.formats.IFormatHandler#close() */
public void close() throws IOException {
super.close();
leiFilename = null;
files = null;
if (tiff != null) {
for (int i=0; i<tiff.length; i++) {
if (tiff[i] != null) {
for (int j=0; j<tiff[i].length; j++) {
if (tiff[i][j] != null) tiff[i][j].close();
}
}
}
}
}
// -- Internal FormatReader API methods --
/* @see loci.formats.FormatReader#initFile(String) */
protected void initFile(String id) throws FormatException, IOException {
if (debug) debug("LeicaReader.initFile(" + id + ")");
String idLow = id.toLowerCase();
close();
if (idLow.endsWith("tif") || idLow.endsWith("tiff")) {
if (ifds == null) super.initFile(id);
in = new RandomAccessStream(id);
if (in.readShort() == 0x4949) {
in.order(true);
}
in.seek(0);
status("Finding companion file name");
// open the TIFF file and look for the "Image Description" field
ifds = TiffTools.getIFDs(in);
if (ifds == null) throw new FormatException("No IFDs found");
String descr = (String) TiffTools.getIFDValue(ifds[0],
TiffTools.IMAGE_DESCRIPTION);
int ndx = descr.indexOf("Series Name");
// should be more graceful about this
if (ndx == -1) throw new FormatException("LEI file not found");
String lei = descr.substring(descr.indexOf("=", ndx) + 1);
int newLineNdx = lei.indexOf("\n");
lei = lei.substring(0, newLineNdx == -1 ? lei.length() : newLineNdx);
lei = lei.trim();
String dir = id.substring(0, id.lastIndexOf("/") + 1);
lei = dir + lei;
// parse key/value pairs in ImageDescription
// first thing is to remove anything of the form "[blah]"
String first;
String last;
while (descr.indexOf("[") != -1) {
first = descr.substring(0, descr.indexOf("["));
last = descr.substring(descr.indexOf("\n", descr.indexOf("[")));
descr = first + last;
}
// each remaining line in descr is a (key, value) pair,
// where '=' separates the key from the value
String key;
String value;
int eqIndex = descr.indexOf("=");
while (eqIndex != -1) {
key = descr.substring(0, eqIndex);
newLineNdx = descr.indexOf("\n", eqIndex);
if (newLineNdx == -1) newLineNdx = descr.length();
value = descr.substring(eqIndex+1, newLineNdx);
addMeta(key.trim(), value.trim());
newLineNdx = descr.indexOf("\n", eqIndex);
if (newLineNdx == -1) newLineNdx = descr.length();
descr = descr.substring(newLineNdx);
eqIndex = descr.indexOf("=");
}
// now open the LEI file
Location l = new Location(lei);
if (l.getAbsoluteFile().exists()) initFile(lei);
else {
l = l.getAbsoluteFile().getParentFile();
String[] list = l.list();
for (int i=0; i<list.length; i++) {
if (list[i].toLowerCase().endsWith("lei")) {
initFile(list[i]);
return;
}
}
}
}
else {
// parse the LEI file
super.initFile(id);
leiFilename = id;
in = new RandomAccessStream(id);
seriesNames = new Vector();
byte[] fourBytes = new byte[4];
in.read(fourBytes);
core.littleEndian[0] = (fourBytes[0] == TiffTools.LITTLE &&
fourBytes[1] == TiffTools.LITTLE &&
fourBytes[2] == TiffTools.LITTLE &&
fourBytes[3] == TiffTools.LITTLE);
in.order(core.littleEndian[0]);
status("Reading metadata blocks");
in.skipBytes(8);
int addr = in.readInt();
Vector v = new Vector();
while (addr != 0) {
numSeries++;
Hashtable ifd = new Hashtable();
v.add(ifd);
in.seek(addr + 4);
int tag = in.readInt();
while (tag != 0) {
// create the IFD structure
int offset = in.readInt();
long pos = in.getFilePointer();
in.seek(offset + 12);
int size = in.readInt();
byte[] data = new byte[size];
in.read(data);
ifd.put(new Integer(tag), (Object) data);
in.seek(pos);
tag = in.readInt();
}
addr = in.readInt();
}
if (v.size() < numSeries) numSeries = v.size();
core = new CoreMetadata(numSeries);
headerIFDs = new Hashtable[numSeries];
files = new Vector[numSeries];
v.copyInto(headerIFDs);
// determine the length of a filename
int nameLength = 0;
int maxPlanes = 0;
status("Parsing metadata blocks");
core.littleEndian[0] = !core.littleEndian[0];
for (int i=0; i<headerIFDs.length; i++) {
if (headerIFDs[i].get(new Integer(10)) != null) {
byte[] temp = (byte[]) headerIFDs[i].get(new Integer(10));
nameLength = DataTools.bytesToInt(temp, 8, 4, core.littleEndian[0]);
}
Vector f = new Vector();
byte[] tempData = (byte[]) headerIFDs[i].get(new Integer(15));
int tempImages = DataTools.bytesToInt(tempData, 0, 4,
core.littleEndian[0]);
File dirFile = new File(id).getAbsoluteFile();
String[] listing = null;
String dirPrefix = "";
if (dirFile.exists()) {
listing = dirFile.getParentFile().list();
dirPrefix = dirFile.getParent();
}
else {
listing =
(String[]) Location.getIdMap().keySet().toArray(new String[0]);
}
Vector list = new Vector();
for (int k=0; k<listing.length; k++) {
if (listing[k].toLowerCase().endsWith(".tif") ||
listing[k].toLowerCase().endsWith(".tiff"))
{
list.add(listing[k]);
}
}
listing = (String[]) list.toArray(new String[0]);
boolean tiffsExist = true;
String prefix = "";
for (int j=0; j<tempImages; j++) {
// read in each filename
prefix = DataTools.stripString(new String(tempData,
20 + 2*(j*nameLength), 2*nameLength));
f.add(dirPrefix + File.separator + prefix);
// test to make sure the path is valid
Location test = new Location((String) f.get(f.size() - 1));
if (tiffsExist) tiffsExist = test.exists();
// get the series name from the stored file name
int firstUnderscore = prefix.indexOf("_") + 1;
int secondUnderscore = prefix.indexOf("_", firstUnderscore);
String name = null;
if (firstUnderscore < 0 || secondUnderscore < 0) name = prefix;
else {
String s = prefix.substring(firstUnderscore, secondUnderscore);
if (seriesNames.contains(s)) {
int suffix = 2;
do {
name = s + "-" + suffix;
suffix++;
}
while (seriesNames.contains(name));
}
else name = s;
}
seriesNames.add(name);
}
// at least one of the TIFF files was renamed
if (!tiffsExist) {
status("Handling renamed TIFF files");
// first thing is to get original LEI name associate with each TIFF
// this lets us figure out which TIFFs we need for this dataset
Hashtable leiMapping = new Hashtable();
int numLeis = 0;
for (int j=0; j<listing.length; j++) {
RandomAccessStream ras = new RandomAccessStream(
new Location(dirPrefix, listing[j]).getAbsolutePath());
Hashtable ifd = TiffTools.getFirstIFD(ras);
ras.close();
String descr =
(String) ifd.get(new Integer(TiffTools.IMAGE_DESCRIPTION));
int ndx = descr.indexOf("=", descr.indexOf("Series Name"));
String leiFile = descr.substring(ndx + 1, descr.indexOf("\n", ndx));
leiFile = leiFile.trim();
if (!leiMapping.contains(leiFile)) numLeis++;
leiMapping.put(listing[j], leiFile);
}
// compare original TIFF prefix with original LEI prefix
f.clear();
String[] keys = (String[]) leiMapping.keySet().toArray(new String[0]);
for (int j=0; j<keys.length; j++) {
String lei = (String) leiMapping.get(keys[j]);
if (DataTools.samePrefix(lei, prefix)) {
f.add(keys[j]);
}
}
// now that we have our list of files, all that remains is to figure
// out how they should be ordered
// we'll try looking for a naming convention, using FilePattern
String[] usedFiles = null;
for (int j=0; j<f.size(); j++) {
if (usedFiles != null) {
for (int k=0; k<usedFiles.length; k++) {
if (usedFiles[k].equals((String) f.get(j)) ||
usedFile((String) f.get(j)))
{
k = 0;
j++;
}
}
}
if (j >= f.size()) break;
FilePattern fp = new FilePattern(new Location((String) f.get(j)));
if (fp != null) usedFiles = fp.getFiles();
if (usedFiles != null && usedFiles.length == tempImages) {
files[i] = new Vector();
for (int k=0; k<usedFiles.length; k++) {
files[i].add(new Location(usedFiles[k]).getAbsolutePath());
}
break;
}
}
// failing that, we can check the datestamp in each TIFF file
// note that this is not guaranteed to work - some versions of
// the Leica software will write a blank datestamp
if (files[i] == null || files[i].size() == 0) {
files[i] = new Vector();
Hashtable h = new Hashtable();
for (int j=0; j<listing.length; j++) {
RandomAccessStream ras = new RandomAccessStream(
new Location(dirPrefix, listing[j]).getAbsolutePath());
Hashtable fd = TiffTools.getFirstIFD(ras);
String stamp =
(String) TiffTools.getIFDValue(fd, TiffTools.DATE_TIME);
if (h.size() == tempImages) {
String[] ks = (String[]) h.keySet().toArray(new String[0]);
Arrays.sort(ks);
for (int k=0; k<ks.length; k++) {
files[i].add(new Location(dirPrefix,
(String) h.get(ks[k])).getAbsolutePath());
}
h.clear();
break;
}
else {
if (!h.contains(stamp)) h.put(stamp, listing[j]);
else {
h.clear();
h.put(stamp, listing[j]);
}
}
ras.close();
}
if (h.size() == tempImages) {
String[] ks = (String[]) h.keySet().toArray(new String[0]);
Arrays.sort(ks);
for (int k=0; k<ks.length; k++) {
files[i].add(new Location(dirPrefix,
(String) h.get(ks[k])).getAbsolutePath());
}
}
}
// Our final effort is to just sort the filenames lexicographically.
// This gives us a pretty good chance of getting the order right,
// but it's not perfect. Basically covers the (hopefully) unlikely
// case where filenames are nonsensical, and datestamps are invalid.
if (files[i] == null || files[i].size() == 0) {
if (debug) debug("File ordering is not obvious.");
files[i] = new Vector();
Arrays.sort(listing);
int ndx = 0;
for (int j=0; j<i; j++) ndx += files[j].size();
for (int j=ndx; j<ndx+tempImages; j++) {
files[i].add(new Location(dirPrefix,
listing[j]).getAbsolutePath());
}
}
// Ways to break the renaming heuristics:
//
// 1) Don't use a detectable naming convention, and remove datestamps
// from TIFF files.
// 2) Use a naming convention such as plane 0 -> "5.tif",
// plane 1 -> "4.tif", plane 2 -> "3.tif", etc.
// 3) Place two datasets in the same folder:
// a) swap the two LEI file names
// b) use the same naming convention for both sets of TIFF files
// c) use the same naming convention AND make sure the datestamps
// are the same between TIFF files
}
else files[i] = f;
core.imageCount[i] = files[i].size();
if (core.imageCount[i] > maxPlanes) maxPlanes = core.imageCount[i];
}
tiff = new TiffReader[numSeries][maxPlanes];
for (int i=0; i<tiff.length; i++) {
for (int j=0; j<tiff[i].length; j++) {
tiff[i][j] = new TiffReader();
if (j > 0) tiff[i][j].setMetadataCollected(false);
}
}
status("Populating metadata");
initMetadata();
}
}
// -- Helper methods --
protected void initMetadata() throws FormatException, IOException {
if (headerIFDs == null) headerIFDs = ifds;
for (int i=0; i<headerIFDs.length; i++) {
byte[] temp = (byte[]) headerIFDs[i].get(new Integer(10));
if (temp != null) {
// the series data
// ID_SERIES
addMeta("Version",
new Integer(DataTools.bytesToInt(temp, 0, 4, core.littleEndian[0])));
addMeta("Number of Series",
new Integer(DataTools.bytesToInt(temp, 4, 4, core.littleEndian[0])));
addMeta("Length of filename",
new Integer(DataTools.bytesToInt(temp, 8, 4, core.littleEndian[0])));
Integer fileExtLen =
new Integer(DataTools.bytesToInt(temp, 12, 4, core.littleEndian[0]));
addMeta("Length of file extension", fileExtLen);
addMeta("Image file extension",
DataTools.stripString(new String(temp, 16, fileExtLen.intValue())));
}
temp = (byte[]) headerIFDs[i].get(new Integer(15));
if (temp != null) {
// the image data
// ID_IMAGES
core.sizeZ[i] = DataTools.bytesToInt(temp, 0, 4, core.littleEndian[0]);
core.sizeX[i] = DataTools.bytesToInt(temp, 4, 4, core.littleEndian[0]);
core.sizeY[i] = DataTools.bytesToInt(temp, 8, 4, core.littleEndian[0]);
addMeta("Number of images", new Integer(core.sizeZ[i]));
addMeta("Image width", new Integer(core.sizeX[i]));
addMeta("Image height", new Integer(core.sizeY[i]));
addMeta("Bits per Sample",
new Integer(DataTools.bytesToInt(temp, 12, 4, core.littleEndian[0])));
addMeta("Samples per pixel",
new Integer(DataTools.bytesToInt(temp, 16, 4, core.littleEndian[0])));
}
temp = (byte[]) headerIFDs[i].get(new Integer(20));
if (temp != null) {
// dimension description
// ID_DIMDESCR
int pt = 0;
addMeta("Voxel Version", new Integer(
DataTools.bytesToInt(temp, 0, 4, core.littleEndian[0])));
int voxelType = DataTools.bytesToInt(temp, 4, 4, core.littleEndian[0]);
String type = "";
switch (voxelType) {
case 0:
type = "undefined";
break;
case 10:
type = "gray normal";
break;
case 20:
type = "RGB";
break;
}
addMeta("VoxelType", type);
bpp = DataTools.bytesToInt(temp, 8, 4, core.littleEndian[0]);
addMeta("Bytes per pixel", new Integer(bpp));
addMeta("Real world resolution",
new Integer(DataTools.bytesToInt(temp, 12, 4, core.littleEndian[0])));
int length = DataTools.bytesToInt(temp, 16, 4, core.littleEndian[0]);
addMeta("Maximum voxel intensity",
DataTools.stripString(new String(temp, 20, length)));
pt = 20 + length;
pt += 4;
addMeta("Minimum voxel intensity",
DataTools.stripString(new String(temp, pt, length)));
pt += length;
length = DataTools.bytesToInt(temp, pt, 4, core.littleEndian[0]);
pt += 4 + length + 4;
length = DataTools.bytesToInt(temp, pt, 4, core.littleEndian[0]);
for (int j=0; j<length; j++) {
int dimId = DataTools.bytesToInt(temp, pt, 4, core.littleEndian[0]);
String dimType = "";
switch (dimId) {
case 0:
dimType = "undefined";
break;
case 120:
dimType = "x";
break;
case 121:
dimType = "y";
break;
case 122:
dimType = "z";
break;
case 116:
dimType = "t";
break;
case 6815843:
dimType = "channel";
break;
case 6357100:
dimType = "wave length";
break;
case 7602290:
dimType = "rotation";
break;
case 7798904:
dimType = "x-wide for the motorized xy-stage";
break;
case 7798905:
dimType = "y-wide for the motorized xy-stage";
break;
case 7798906:
dimType = "z-wide for the z-stage-drive";
break;
case 4259957:
dimType = "user1 - unspecified";
break;
case 4325493:
dimType = "user2 - unspecified";
break;
case 4391029:
dimType = "user3 - unspecified";
break;
case 6357095:
dimType = "graylevel";
break;
case 6422631:
dimType = "graylevel1";
break;
case 6488167:
dimType = "graylevel2";
break;
case 6553703:
dimType = "graylevel3";
break;
case 7864398:
dimType = "logical x";
break;
case 7929934:
dimType = "logical y";
break;
case 7995470:
dimType = "logical z";
break;
case 7602254:
dimType = "logical t";
break;
case 7077966:
dimType = "logical lambda";
break;
case 7471182:
dimType = "logical rotation";
break;
case 5767246:
dimType = "logical x-wide";
break;
case 5832782:
dimType = "logical y-wide";
break;
case 5898318:
dimType = "logical z-wide";
break;
}
//if (dimType.equals("channel")) numChannels++;
addMeta("Dim" + j + " type", dimType);
pt += 4;
addMeta("Dim" + j + " size", new Integer(
DataTools.bytesToInt(temp, pt, 4, core.littleEndian[0])));
pt += 4;
int dist = DataTools.bytesToInt(temp, pt, 4, core.littleEndian[0]);
addMeta("Dim" + j + " distance between sub-dimensions",
new Integer(dist));
pt += 4;
int len = DataTools.bytesToInt(temp, pt, 4, core.littleEndian[0]);
pt += 4;
addMeta("Dim" + j + " physical length",
DataTools.stripString(new String(temp, pt, len)));
pt += len;
len = DataTools.bytesToInt(temp, pt, 4, core.littleEndian[0]);
pt += 4;
addMeta("Dim" + j + " physical origin",
DataTools.stripString(new String(temp, pt, len)));
pt += len;
len = DataTools.bytesToInt(temp, pt, 4, core.littleEndian[0]);
pt += 4;
addMeta("Dim" + j + " name",
DataTools.stripString(new String(temp, pt, len)));
pt += len;
len = DataTools.bytesToInt(temp, pt, 4, core.littleEndian[0]);
pt += 4;
addMeta("Dim" + j + " description",
DataTools.stripString(new String(temp, pt, len)));
}
}
temp = (byte[]) headerIFDs[i].get(new Integer(30));
if (temp != null) {
// filter data
// ID_FILTERSET
// not currently used
}
temp = (byte[]) headerIFDs[i].get(new Integer(40));
if (temp != null) {
// time data
// ID_TIMEINFO
int nDims = DataTools.bytesToInt(temp, 0, 4, core.littleEndian[0]);
addMeta("Number of time-stamped dimensions", new Integer(nDims));
addMeta("Time-stamped dimension",
new Integer(DataTools.bytesToInt(temp, 4, 4, core.littleEndian[0])));
int pt = 8;
for (int j=0; j < nDims; j++) {
int v = DataTools.bytesToInt(temp, pt, 4, core.littleEndian[0]);
addMeta("Dimension " + j + " ID", new Integer(v));
pt += 4;
v = DataTools.bytesToInt(temp, pt, 4, core.littleEndian[0]);
addMeta("Dimension " + j + " size", new Integer(v));
pt += 4;
v = DataTools.bytesToInt(temp, pt, 4, core.littleEndian[0]);
addMeta("Dimension " + j + " distance between dimensions",
new Integer(v));
pt += 4;
}
int numStamps = DataTools.bytesToInt(temp, pt, 4, core.littleEndian[0]);
pt += 4;
addMeta("Number of time-stamps", new Integer(numStamps));
for (int j=0; j<numStamps; j++) {
addMeta("Timestamp " + j,
DataTools.stripString(new String(temp, pt, 64)));
pt += 64;
}
int numTMs = DataTools.bytesToInt(temp, pt, 4, core.littleEndian[0]);
pt += 4;
addMeta("Number of time-markers", new Integer(numTMs));
for (int j=0; j<numTMs; j++) {
int numDims = DataTools.bytesToInt(temp, pt, 4, core.littleEndian[0]);
pt += 4;
for (int k=0; k<numDims; k++) {
int v = DataTools.bytesToInt(temp, pt, 4, core.littleEndian[0]);
addMeta("Time-marker " + j +
" Dimension " + k + " coordinate", new Integer(v));
pt += 4;
}
addMeta("Time-marker " + j,
DataTools.stripString(new String(temp, pt, 64)));
pt += 64;
}
}
temp = (byte[]) headerIFDs[i].get(new Integer(50));
if (temp != null) {
// scanner data
// ID_SCANNERSET
// not currently used
}
temp = (byte[]) headerIFDs[i].get(new Integer(60));
if (temp != null) {
// experiment data
// ID_EXPERIMENT
int pt = 8;
int len = DataTools.bytesToInt(temp, pt, 4, core.littleEndian[0]);
pt += 4;
addMeta("Image Description",
DataTools.stripString(new String(temp, pt, 2*len)));
pt += 2*len;
len = DataTools.bytesToInt(temp, pt, 4, core.littleEndian[0]);
pt += 4;
addMeta("Main file extension",
DataTools.stripString(new String(temp, pt, 2*len)));
pt += 2*len;
len = DataTools.bytesToInt(temp, pt, 4, core.littleEndian[0]);
pt += 4;
addMeta("Single image format identifier",
DataTools.stripString(new String(temp, pt, 2*len)));
pt += 2*len;
len = DataTools.bytesToInt(temp, pt, 4, core.littleEndian[0]);
pt += 4;
addMeta("Single image extension",
DataTools.stripString(new String(temp, pt, 2*len)));
}
temp = (byte[]) headerIFDs[i].get(new Integer(70));
if (temp != null) {
// LUT data
// ID_LUTDESC
int pt = 0;
int nChannels = DataTools.bytesToInt(temp, pt, 4, core.littleEndian[0]);
pt += 4;
addMeta("Number of LUT channels", new Integer(nChannels));
addMeta("ID of colored dimension",
new Integer(DataTools.bytesToInt(temp, pt, 4, core.littleEndian[0])));
pt += 4;
if (nChannels > 4) nChannels = 3;
core.sizeC[i] = nChannels;
for (int j=0; j<nChannels; j++) {
int v = DataTools.bytesToInt(temp, pt, 4, core.littleEndian[0]);
addMeta("LUT Channel " + j + " version", new Integer(v));
pt += 4;
int invert = DataTools.bytesToInt(temp, pt, 1, core.littleEndian[0]);
pt += 1;
boolean inverted = invert == 1;
addMeta("LUT Channel " + j + " inverted?",
new Boolean(inverted).toString());
int length = DataTools.bytesToInt(temp, pt, 4, core.littleEndian[0]);
pt += 4;
addMeta("LUT Channel " + j + " description",
DataTools.stripString(new String(temp, pt, length)));
pt += length;
length = DataTools.bytesToInt(temp, pt, 4, core.littleEndian[0]);
pt += 4;
addMeta("LUT Channel " + j + " filename",
DataTools.stripString(new String(temp, pt, length)));
pt += length;
length = DataTools.bytesToInt(temp, pt, 4, core.littleEndian[0]);
pt += 4;
String name = DataTools.stripString(new String(temp, pt, length));
addMeta("LUT Channel " + j + " name", name);
pt += length;
pt += 8;
}
}
}
//core = new CoreMetadata(numSeries);
Arrays.fill(core.orderCertain, true);
// sizeC is null here if the file we opened was a TIFF.
// However, the sizeC field will be adjusted anyway by
// a later call to initMetadata.
if (core.sizeC != null) {
int oldSeries = getSeries();
for (int i=0; i<core.sizeC.length; i++) {
setSeries(i);
core.sizeZ[i] /= core.sizeC[i];
}
setSeries(oldSeries);
}
Integer v = (Integer) getMeta("Real world resolution");
// the metadata store we're working with
MetadataStore store = getMetadataStore();
byte[] f = new byte[4];
for (int i=0; i<numSeries; i++) {
core.orderCertain[i] = true;
core.interleaved[i] = true;
in.seek(0);
in.read(f);
core.littleEndian[i] = (f[0] == TiffTools.LITTLE &&
f[1] == TiffTools.LITTLE && f[2] == TiffTools.LITTLE &&
f[3] == TiffTools.LITTLE);
if (core.sizeC[i] == 0) core.sizeC[i] = 1;
core.sizeT[i] += 1;
core.currentOrder[i] = core.sizeC[i] == 1 ? "XYZTC" : "XYCZT";
if (core.sizeZ[i] == 0) core.sizeZ[i] = 1;
switch (bpp) {
case 1:
core.pixelType[i] = FormatTools.UINT8;
break;
case 2:
core.pixelType[i] = FormatTools.UINT16;
break;
case 3:
core.pixelType[i] = FormatTools.UINT8;
break;
case 4:
core.pixelType[i] = FormatTools.INT32;
break;
case 6:
core.pixelType[i] = FormatTools.INT16;
break;
case 8:
core.pixelType[i] = FormatTools.DOUBLE;
break;
}
core.rgb[i] =
core.imageCount[i] != core.sizeC[i] * core.sizeZ[i] * core.sizeT[i];
Integer ii = new Integer(i);
core.rgb[i] = false;
//core.sizeC[i] *= 3;
core.indexed[i] = true;
core.falseColor[i] = true;
core.metadataComplete[i] = true;
String timestamp = (String) getMeta("Timestamp " + (i+1));
String description = (String) getMeta("Image Description");
if (timestamp != null) {
SimpleDateFormat parse =
new SimpleDateFormat("yyyy:MM:dd,HH:mm:ss:SSS");
Date date = parse.parse(timestamp, new ParsePosition(0));
SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
timestamp = fmt.format(date);
}
store.setImage((String) seriesNames.get(i), timestamp, description, ii);
}
FormatTools.populatePixels(store, this);
for (int i=0; i<core.sizeC.length; i++) {
for (int j=0; j<core.sizeC[i]; j++) {
store.setLogicalChannel(j, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, new Integer(i));
// TODO: get channel min/max from metadata
// store.setChannelGlobalMinMax(j, getChannelGlobalMinimum(currentId, j),
// getChannelGlobalMaximum(currentId, j), ii);
}
}
}
private boolean usedFile(String s) {
if (files == null) return false;
for (int i=0; i<files.length; i++) {
if (files[i] == null) continue;
for (int j=0; j<files[i].size(); j++) {
if (((String) files[i].get(j)).endsWith(s)) return true;
}
}
return false;
}
}