//
// ZeissZVIReader.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.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Hashtable;
import java.util.Vector;
import loci.common.DataTools;
import loci.common.DateTools;
import loci.common.Location;
import loci.common.RandomAccessInputStream;
import loci.common.services.DependencyException;
import loci.common.services.ServiceFactory;
import loci.formats.CoreMetadata;
import loci.formats.FormatException;
import loci.formats.FormatReader;
import loci.formats.FormatTools;
import loci.formats.MetadataTools;
import loci.formats.codec.Codec;
import loci.formats.codec.CodecOptions;
import loci.formats.codec.JPEGCodec;
import loci.formats.codec.ZlibCodec;
import loci.formats.meta.DummyMetadata;
import loci.formats.meta.MetadataStore;
import ome.xml.model.primitives.PositiveFloat;
import loci.formats.services.POIService;
import ome.xml.model.primitives.PositiveInteger;
/**
* ZeissZVIReader is the file format reader for Zeiss ZVI 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/ZeissZVIReader.java">Trac</a>,
* <a href="http://git.openmicroscopy.org/?p=bioformats.git;a=blob;f=components/bio-formats/src/loci/formats/in/ZeissZVIReader.java;hb=HEAD">Gitweb</a></dd></dl>
*
* @author Melissa Linkert melissa at glencoesoftware.com
*/
public class ZeissZVIReader extends FormatReader {
// -- Constants --
public static final int ZVI_MAGIC_BYTES = 0xd0cf11e0;
private static final long ROI_SIGNATURE = 0x21fff6977547000dL;
/** ROI types. */
private static final int ELLIPSE = 15;
private static final int CURVE = 12;
private static final int OUTLINE = 16;
private static final int RECTANGLE = 13;
private static final int LINE = 2;
private static final int TEXT = 17;
private static final int SCALE_BAR = 10;
private static final int OUTLINE_SPLINE = 50;
// -- Fields --
/** Number of bytes per pixel. */
private int bpp;
private String[] imageFiles;
private int[] offsets;
private int[][] coordinates;
private Hashtable<Integer, String> timestamps, exposureTime;
private int cIndex = -1;
private boolean isJPEG, isZlib;
private int realWidth, realHeight;
private POIService poi;
private Vector<String> tagsToParse;
private int nextEmWave = 0, nextExWave = 0, nextChName = 0;
private Hashtable<Integer, Double> stageX = new Hashtable<Integer, Double>();
private Hashtable<Integer, Double> stageY = new Hashtable<Integer, Double>();
private int timepoint = 0;
private int[] channelColors;
private int lastPlane = 0;
private Hashtable<Integer, Integer> tiles = new Hashtable<Integer, Integer>();
private Hashtable<Integer, Double> detectorGain =
new Hashtable<Integer, Double>();
private Hashtable<Integer, Double> detectorOffset =
new Hashtable<Integer, Double>();
private Hashtable<Integer, PositiveInteger> emWavelength =
new Hashtable<Integer, PositiveInteger>();
private Hashtable<Integer, PositiveInteger> exWavelength =
new Hashtable<Integer, PositiveInteger>();
private Hashtable<Integer, String> channelName =
new Hashtable<Integer, String>();
private Double physicalSizeX, physicalSizeY, physicalSizeZ;
private String imageDescription;
private Vector<String> roiIDs = new Vector<String>();
// -- Constructor --
/** Constructs a new ZeissZVI reader. */
public ZeissZVIReader() {
super("Zeiss Vision Image (ZVI)", "zvi");
domains = new String[] {FormatTools.LM_DOMAIN};
}
// -- IFormatReader API methods --
/* @see loci.formats.IFormatReader#getOptimalTileHeight() */
public int getOptimalTileHeight() {
FormatTools.assertId(currentId, true, 1);
return getSizeY();
}
/* @see loci.formats.IFormatReader#isThisType(RandomAccessInputStream) */
public boolean isThisType(RandomAccessInputStream stream) throws IOException {
final int blockLen = 65536;
if (!FormatTools.validStream(stream, blockLen, false)) return false;
int magic = stream.readInt();
if (magic != ZVI_MAGIC_BYTES) return false;
return true;
}
/* @see loci.formats.IFormatReader#get8BitLookupTable() */
public byte[][] get8BitLookupTable() throws FormatException, IOException {
int pixelType = getPixelType();
if ((pixelType != FormatTools.INT8 && pixelType != FormatTools.UINT8) ||
!isIndexed())
{
return null;
}
byte[][] lut = new byte[3][256];
int channel = getZCTCoords(lastPlane)[1];
if (channel >= channelColors.length) return null;
int color = channelColors[channel];
float red = (color & 0xff) / 255f;
float green = ((color & 0xff00) >> 8) / 255f;
float blue = ((color & 0xff0000) >> 16) / 255f;
for (int i=0; i<lut[0].length; i++) {
lut[0][i] = (byte) (red * i);
lut[1][i] = (byte) (green * i);
lut[2][i] = (byte) (blue * i);
}
return lut;
}
/* @see loci.formats.IFormatReader#get16BitLookupTable() */
public short[][] get16BitLookupTable() throws FormatException, IOException {
int pixelType = getPixelType();
if ((pixelType != FormatTools.INT16 && pixelType != FormatTools.UINT16) ||
!isIndexed())
{
return null;
}
short[][] lut = new short[3][65536];
int channel = getZCTCoords(lastPlane)[1];
if (channel >= channelColors.length) return null;
int color = channelColors[channel];
float red = (color & 0xff) / 255f;
float green = ((color & 0xff00) >> 8) / 255f;
float blue = ((color & 0xff0000) >> 16) / 255f;
for (int i=0; i<lut[0].length; i++) {
lut[0][i] = (short) (red * i);
lut[1][i] = (short) (green * i);
lut[2][i] = (short) (blue * i);
}
return lut;
}
/**
* @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 bytes = FormatTools.getBytesPerPixel(getPixelType());
int pixel = bytes * getRGBChannelCount();
CodecOptions options = new CodecOptions();
options.littleEndian = isLittleEndian();
options.interleaved = isInterleaved();
int index = no;
if (getSeriesCount() > 1) {
index += getSeries() * getImageCount();
}
if (index >= imageFiles.length) {
return buf;
}
RandomAccessInputStream s = poi.getDocumentStream(imageFiles[index]);
s.seek(offsets[index]);
int len = w * pixel;
int row = getSizeX() * pixel;
if (isJPEG) {
byte[] t = new JPEGCodec().decompress(s, options);
for (int yy=0; yy<h; yy++) {
System.arraycopy(t, (yy + y) * row + x * pixel, buf, yy*len, len);
}
}
else if (isZlib) {
byte[] t = new ZlibCodec().decompress(s, options);
for (int yy=0; yy<h; yy++) {
int src = (yy + y) * row + x * pixel;
int dest = yy * len;
if (src + len <= t.length && dest + len <= buf.length) {
System.arraycopy(t, src, buf, dest, len);
}
else break;
}
}
else {
readPlane(s, x, y, w, h, buf);
}
s.close();
if (isRGB() && !isJPEG) {
// reverse bytes in groups of 3 to account for BGR storage
byte[] bb = new byte[bytes];
for (int i=0; i<buf.length; i+=bpp) {
System.arraycopy(buf, i + 2*bytes, bb, 0, bytes);
System.arraycopy(buf, i, buf, i + 2*bytes, bytes);
System.arraycopy(bb, 0, buf, i, bytes);
}
}
return buf;
}
/* @see loci.formats.IFormatReader#close(boolean) */
public void close(boolean fileOnly) throws IOException {
super.close(fileOnly);
if (!fileOnly) {
timestamps = exposureTime = null;
offsets = null;
coordinates = null;
imageFiles = null;
cIndex = -1;
bpp = 0;
isJPEG = isZlib = false;
if (poi != null) poi.close();
poi = null;
tagsToParse = null;
nextEmWave = nextExWave = nextChName = 0;
realWidth = realHeight = 0;
stageX.clear();
stageY.clear();
channelColors = null;
lastPlane = 0;
tiles.clear();
detectorGain.clear();
detectorOffset.clear();
emWavelength.clear();
exWavelength.clear();
channelName.clear();
physicalSizeX = physicalSizeY = physicalSizeZ = null;
imageDescription = null;
timepoint = 0;
roiIDs.clear();
}
}
// -- Internal FormatReader API methods --
/* @see loci.formats.FormatReader#initFile(String) */
protected void initFile(String id) throws FormatException, IOException {
super.initFile(id);
try {
ServiceFactory factory = new ServiceFactory();
poi = factory.getInstance(POIService.class);
}
catch (DependencyException de) {
throw new FormatException("POI library not found", de);
}
poi.initialize(Location.getMappedId(id));
timestamps = new Hashtable<Integer, String>();
exposureTime = new Hashtable<Integer, String>();
tagsToParse = new Vector<String>();
// count number of images
String[] files = (String[]) poi.getDocumentList().toArray(new String[0]);
Arrays.sort(files, new Comparator() {
public int compare(Object o1, Object o2) {
int n1 = getImageNumber((String) o1, -1);
int n2 = getImageNumber((String) o2, -1);
return new Integer(n1).compareTo(new Integer(n2));
}
});
core[0].imageCount = 0;
for (String file : files) {
String uname = file.toUpperCase();
uname = uname.substring(uname.indexOf(File.separator) + 1);
if (uname.endsWith("CONTENTS") && (uname.startsWith("IMAGE") ||
uname.indexOf("ITEM") != -1) && poi.getFileSize(file) > 1024)
{
int imageNumber = getImageNumber(file, 0);
if (imageNumber >= getImageCount()) {
core[0].imageCount++;
}
}
}
offsets = new int[getImageCount()];
coordinates = new int[getImageCount()][3];
imageFiles = new String[getImageCount()];
// parse each embedded file
Vector<Integer> cIndices = new Vector<Integer>();
Vector<Integer> zIndices = new Vector<Integer>();
Vector<Integer> tIndices = new Vector<Integer>();
MetadataStore store = makeFilterMetadata();
for (String name : files) {
String relPath = name.substring(name.lastIndexOf(File.separator) + 1);
if (!relPath.toUpperCase().equals("CONTENTS")) continue;
String dirName = name.substring(0, name.lastIndexOf(File.separator));
if (dirName.indexOf(File.separator) != -1) {
dirName = dirName.substring(dirName.lastIndexOf(File.separator) + 1);
}
if (name.indexOf("Scaling") == -1 && dirName.equals("Tags")) {
int imageNum = getImageNumber(name, -1);
if (imageNum == -1) {
parseTags(imageNum, name, new DummyMetadata());
}
else tagsToParse.add(name);
}
else if (dirName.equals("Shapes") && name.indexOf("Item") != -1) {
int imageNum = getImageNumber(name, -1);
if (imageNum != -1) {
try {
parseROIs(imageNum, name, store);
}
catch (IOException e) {
LOGGER.debug("Could not parse all ROIs.", e);
}
}
}
else if (dirName.equals("Image") ||
dirName.toUpperCase().indexOf("ITEM") != -1)
{
int imageNum = getImageNumber(dirName, getImageCount() == 1 ? 0 : -1);
if (imageNum == -1) continue;
// found a valid image stream
RandomAccessInputStream s = poi.getDocumentStream(name);
s.order(true);
if (s.length() <= 1024) {
s.close();
continue;
}
for (int q=0; q<11; q++) {
getNextTag(s);
}
s.skipBytes(2);
int len = s.readInt() - 20;
s.skipBytes(8);
int zidx = s.readInt();
int cidx = s.readInt();
int tidx = s.readInt();
if (!zIndices.contains(zidx)) zIndices.add(zidx);
if (!tIndices.contains(tidx)) tIndices.add(tidx);
if (!cIndices.contains(cidx)) cIndices.add(cidx);
s.skipBytes(len);
for (int q=0; q<5; q++) {
getNextTag(s);
}
s.skipBytes(4);
if (getSizeX() == 0) {
core[0].sizeX = s.readInt();
core[0].sizeY = s.readInt();
}
else s.skipBytes(8);
s.skipBytes(4);
if (bpp == 0) {
bpp = s.readInt();
}
else s.skipBytes(4);
s.skipBytes(4);
int valid = s.readInt();
String check = s.readString(4).trim();
isZlib = (valid == 0 || valid == 1) && check.equals("WZL");
isJPEG = (valid == 0 || valid == 1) && !isZlib;
// save the offset to the pixel data
offsets[imageNum] = (int) s.getFilePointer() - 4;
if (isZlib) offsets[imageNum] += 8;
coordinates[imageNum][0] = zidx;
coordinates[imageNum][1] = cidx;
coordinates[imageNum][2] = tidx;
imageFiles[imageNum] = name;
s.close();
}
}
LOGGER.info("Populating metadata");
stageX.clear();
stageY.clear();
core[0].sizeZ = zIndices.size();
core[0].sizeT = tIndices.size();
core[0].sizeC = cIndices.size();
core[0].littleEndian = true;
core[0].interleaved = true;
core[0].falseColor = true;
core[0].metadataComplete = true;
core[0].imageCount = getSizeZ() * getSizeT() * getSizeC();
core[0].rgb = (bpp % 3) == 0;
if (isRGB()) core[0].sizeC *= 3;
for (String name : tagsToParse) {
int imageNum = getImageNumber(name, -1);
parseTags(imageNum, name, store);
}
// calculate tile dimensions and number of tiles
if (core.length > 1) {
Integer[] t = tiles.keySet().toArray(new Integer[tiles.size()]);
Arrays.sort(t);
Vector<Integer> tmpOffsets = new Vector<Integer>();
Vector<String> tmpFiles = new Vector<String>();
int index = 0;
for (Integer key : t) {
int nTiles = tiles.get(key).intValue();
if (nTiles < getImageCount()) {
tiles.remove(key);
}
else {
for (int p=0; p<nTiles; p++) {
tmpOffsets.add(new Integer(offsets[index + p]));
tmpFiles.add(imageFiles[index + p]);
}
}
index += nTiles;
}
offsets = new int[tmpOffsets.size()];
for (int i=0; i<offsets.length; i++) {
offsets[i] = tmpOffsets.get(i).intValue();
}
imageFiles = tmpFiles.toArray(new String[tmpFiles.size()]);
}
if (getSizeX() == 0) {
core[0].sizeX = 1;
}
if (getSizeY() == 0) {
core[0].sizeY = 1;
}
if (getImageCount() == 0) {
core[0].imageCount = 1;
core[0].sizeZ = 1;
core[0].sizeC = 1;
core[0].sizeT = 1;
}
int totalTiles = offsets.length / getImageCount();
int tileRows = realHeight / getSizeY();
int tileColumns = realWidth / getSizeX();
if (getSizeY() * tileRows != realHeight) tileRows++;
if (getSizeX() * tileColumns != realWidth) tileColumns++;
if (totalTiles <= 1) {
tileRows = 1;
tileColumns = 1;
}
if (tileRows == 0) tileRows = 1;
if (tileColumns == 0) tileColumns = 1;
if (tileColumns > 1 || tileRows > 1) {
CoreMetadata originalCore = core[0];
core = new CoreMetadata[tileRows * tileColumns];
core[0] = originalCore;
}
core[0].dimensionOrder = "XY";
if (isRGB()) core[0].dimensionOrder += "C";
for (int i=0; i<coordinates.length-1; i++) {
int[] zct1 = coordinates[i];
int[] zct2 = coordinates[i + 1];
int deltaZ = zct2[0] - zct1[0];
int deltaC = zct2[1] - zct1[1];
int deltaT = zct2[2] - zct1[2];
if (deltaZ > 0 && getDimensionOrder().indexOf("Z") == -1) {
core[0].dimensionOrder += "Z";
}
if (deltaC > 0 && getDimensionOrder().indexOf("C") == -1) {
core[0].dimensionOrder += "C";
}
if (deltaT > 0 && getDimensionOrder().indexOf("T") == -1) {
core[0].dimensionOrder += "T";
}
}
core[0].dimensionOrder =
MetadataTools.makeSaneDimensionOrder(getDimensionOrder());
if (bpp == 1 || bpp == 3) core[0].pixelType = FormatTools.UINT8;
else if (bpp == 2 || bpp == 6) core[0].pixelType = FormatTools.UINT16;
if (isJPEG) core[0].pixelType = FormatTools.UINT8;
core[0].indexed = !isRGB() && channelColors != null;
for (int i=1; i<core.length; i++) {
core[i] = new CoreMetadata(this, 0);
}
MetadataTools.populatePixels(store, this, true);
for (int i=0; i<getSeriesCount(); i++) {
long firstStamp = 0;
if (timestamps.size() > 0) {
String timestamp = timestamps.get(new Integer(0));
firstStamp = parseTimestamp(timestamp);
String date =
DateTools.convertDate((long) (firstStamp / 1600), DateTools.ZVI);
store.setImageAcquiredDate(date, i);
}
else MetadataTools.setDefaultCreationDate(store, getCurrentFile(), i);
}
if (getMetadataOptions().getMetadataLevel() != MetadataLevel.MINIMUM) {
// link Instrument and Image
String instrumentID = MetadataTools.createLSID("Instrument", 0);
store.setInstrumentID(instrumentID, 0);
String objectiveID = MetadataTools.createLSID("Objective", 0, 0);
store.setObjectiveID(objectiveID, 0, 0);
store.setObjectiveCorrection(getCorrection("Other"), 0, 0);
store.setObjectiveImmersion(getImmersion("Other"), 0, 0);
Integer[] channelKeys = channelName.keySet().toArray(new Integer[0]);
Arrays.sort(channelKeys);
// link DetectorSettings to an actual Detector
for (int i=0; i<getEffectiveSizeC(); i++) {
String detectorID = MetadataTools.createLSID("Detector", 0, i);
store.setDetectorID(detectorID, 0, i);
store.setDetectorType(getDetectorType("Other"), 0, i);
for (int s=0; s<getSeriesCount(); s++) {
int c = i;
if (i < channelKeys.length) {
c = channelKeys[i];
}
store.setDetectorSettingsID(detectorID, s, i);
store.setDetectorSettingsGain(detectorGain.get(c), s, i);
store.setDetectorSettingsOffset(detectorOffset.get(c), s, i);
store.setChannelName(channelName.get(c), s, i);
store.setChannelEmissionWavelength(emWavelength.get(c), s, i);
store.setChannelExcitationWavelength(exWavelength.get(c), s, i);
}
}
for (int i=0; i<getSeriesCount(); i++) {
store.setImageInstrumentRef(instrumentID, i);
store.setImageObjectiveSettingsID(objectiveID, i);
if (imageDescription != null) {
store.setImageDescription(imageDescription, i);
}
if (getSeriesCount() > 1) {
store.setImageName("Tile #" + (i + 1), i);
}
if (physicalSizeX != null && physicalSizeX > 0) {
store.setPixelsPhysicalSizeX(new PositiveFloat(physicalSizeX), i);
}
if (physicalSizeY != null && physicalSizeY > 0) {
store.setPixelsPhysicalSizeY(new PositiveFloat(physicalSizeY), i);
}
if (physicalSizeZ != null && physicalSizeZ > 0) {
store.setPixelsPhysicalSizeZ(new PositiveFloat(physicalSizeZ), i);
}
long firstStamp = parseTimestamp(timestamps.get(new Integer(0)));
for (int plane=0; plane<getImageCount(); plane++) {
int[] zct = getZCTCoords(plane);
int expIndex = zct[1];
if (channelKeys.length > 0) {
expIndex += channelKeys[0];
}
String exposure = exposureTime.get(expIndex);
if (exposure == null && exposureTime.size() == 1) {
exposure = exposureTime.get(exposureTime.keys().nextElement());
}
Double exp = new Double(0.0);
try { exp = new Double(exposure); }
catch (NumberFormatException e) { }
catch (NullPointerException e) { }
store.setPlaneExposureTime(exp, i, plane);
int posIndex = i * getImageCount() + plane;
if (posIndex < timestamps.size()) {
String timestamp = timestamps.get(new Integer(posIndex));
long stamp = parseTimestamp(timestamp);
stamp -= firstStamp;
store.setPlaneDeltaT(new Double(stamp / 1600000), i, plane);
}
if (stageX.get(posIndex) != null) {
store.setPlanePositionX(stageX.get(posIndex), i, plane);
}
if (stageY.get(posIndex) != null) {
store.setPlanePositionY(stageY.get(posIndex), i, plane);
}
}
}
for (int i=0; i<getSeriesCount(); i++) {
for (int roi=0; roi<roiIDs.size(); roi++) {
store.setImageROIRef(roiIDs.get(roi), i, roi);
}
}
}
}
private int getImageNumber(String dirName, int defaultNumber) {
if (dirName.toUpperCase().indexOf("ITEM") != -1) {
int open = dirName.indexOf("(");
int close = dirName.indexOf(")");
if (open < 0 || close < 0 || close < open) return defaultNumber;
return Integer.parseInt(dirName.substring(open + 1, close));
}
return defaultNumber;
}
private String getNextTag(RandomAccessInputStream s) throws IOException {
int type = s.readShort();
switch (type) {
case 0:
case 1:
return "";
case 2:
return String.valueOf(s.readShort());
case 3:
case 22:
case 23:
return String.valueOf(s.readInt());
case 4:
return String.valueOf(s.readFloat());
case 5:
return String.valueOf(s.readDouble());
case 7:
case 20:
case 21:
return String.valueOf(s.readLong());
case 8:
case 69:
int len = s.readInt();
return s.readString(len);
case 9:
case 13:
s.skipBytes(16);
return "";
case 63:
case 65:
len = s.readInt();
s.skipBytes(len);
return "";
case 66:
len = s.readShort();
return s.readString(len);
default:
long old = s.getFilePointer();
while (s.readShort() != 3 &&
s.getFilePointer() + 2 < s.length());
long fp = s.getFilePointer() - 2;
s.seek(old - 2);
return s.readString((int) (fp - old + 2));
}
}
/** Parse all of the tags in a stream. */
private void parseTags(int image, String file, MetadataStore store)
throws FormatException, IOException
{
RandomAccessInputStream s = poi.getDocumentStream(file);
s.order(true);
s.seek(8);
int count = s.readInt();
int effectiveSizeC = 0;
try {
effectiveSizeC = getEffectiveSizeC();
}
catch (ArithmeticException e) { }
for (int i=0; i<count; i++) {
if (s.getFilePointer() + 2 >= s.length()) break;
String value = DataTools.stripString(getNextTag(s));
s.skipBytes(2);
int tagID = s.readInt();
s.skipBytes(6);
try {
String key = getKey(tagID);
if (key.equals("Image Channel Index")) {
cIndex = Integer.parseInt(value);
int v = 0;
while (getGlobalMeta(key + " " + v) != null) v++;
if (!getGlobalMetadata().containsValue(cIndex)) {
addGlobalMeta(key + " " + v, cIndex);
}
continue;
}
else if (key.equals("ImageWidth")) {
int v = Integer.parseInt(value);
if (getSizeX() == 0 || v < getSizeX()) {
core[0].sizeX = v;
}
if (realWidth == 0 && v > realWidth) realWidth = v;
}
else if (key.equals("ImageHeight")) {
int v = Integer.parseInt(value);
if (getSizeY() == 0 || v < getSizeY()) core[0].sizeY = v;
if (realHeight == 0 || v > realHeight) realHeight = v;
}
if (cIndex != -1) key += " " + cIndex;
addGlobalMeta(key, value);
if (key.startsWith("ImageTile") && !(store instanceof DummyMetadata)) {
if (!tiles.containsKey(new Integer(value))) {
tiles.put(new Integer(value), new Integer(1));
}
else {
int v = tiles.get(new Integer(value)).intValue() + 1;
tiles.put(new Integer(value), new Integer(v));
}
}
if (key.startsWith("MultiChannel Color")) {
if (cIndex >= 0 && cIndex < effectiveSizeC) {
if (channelColors == null ||
effectiveSizeC > channelColors.length)
{
channelColors = new int[effectiveSizeC];
}
channelColors[cIndex] = Integer.parseInt(value);
}
}
else if (key.startsWith("Scale Factor for X") && physicalSizeX == null)
{
physicalSizeX = Double.parseDouble(value);
}
else if (key.startsWith("Scale Factor for Y") && physicalSizeY == null)
{
physicalSizeY = Double.parseDouble(value);
}
else if (key.startsWith("Scale Factor for Z") && physicalSizeZ == null)
{
physicalSizeZ = Double.parseDouble(value);
}
else if (key.startsWith("Emission Wavelength")) {
if (cIndex != -1) {
Integer wave = new Integer(value);
if (wave.intValue() > 0) {
emWavelength.put(cIndex, new PositiveInteger(wave));
}
}
}
else if (key.startsWith("Excitation Wavelength")) {
if (cIndex != -1) {
Integer wave = new Integer((int) Double.parseDouble(value));
if (wave.intValue() > 0) {
exWavelength.put(cIndex, new PositiveInteger(wave));
}
}
}
else if (key.startsWith("Channel Name")) {
if (cIndex != -1) {
channelName.put(cIndex, value);
}
}
else if (key.startsWith("Exposure Time [ms]")) {
if (exposureTime.get(new Integer(cIndex)) == null) {
double exp = Double.parseDouble(value) / 1000;
exposureTime.put(new Integer(cIndex), String.valueOf(exp));
}
}
else if (key.startsWith("User Name")) {
String[] username = value.split(" ");
if (username.length >= 2) {
store.setExperimenterFirstName(username[0], 0);
store.setExperimenterLastName(username[username.length - 1], 0);
}
}
else if (key.equals("User company")) {
store.setExperimenterInstitution(value, 0);
}
else if (key.startsWith("Objective Magnification")) {
int magnification = (int) Double.parseDouble(value);
if (magnification > 0) {
store.setObjectiveNominalMagnification(
new PositiveInteger(magnification), 0, 0);
}
}
else if (key.startsWith("Objective ID")) {
store.setObjectiveID("Objective:" + value, 0, 0);
store.setObjectiveCorrection(getCorrection("Other"), 0, 0);
store.setObjectiveImmersion(getImmersion("Other"), 0, 0);
}
else if (key.startsWith("Objective N.A.")) {
store.setObjectiveLensNA(new Double(value), 0, 0);
}
else if (key.startsWith("Objective Name")) {
String[] tokens = value.split(" ");
for (int q=0; q<tokens.length; q++) {
int slash = tokens[q].indexOf("/");
if (slash != -1 && slash - q > 0) {
int mag = (int)
Double.parseDouble(tokens[q].substring(0, slash - q));
String na = tokens[q].substring(slash + 1);
store.setObjectiveNominalMagnification(
new PositiveInteger(mag), 0, 0);
store.setObjectiveLensNA(new Double(na), 0, 0);
store.setObjectiveCorrection(getCorrection(tokens[q - 1]), 0, 0);
break;
}
}
}
else if (key.startsWith("Objective Working Distance")) {
store.setObjectiveWorkingDistance(new Double(value), 0, 0);
}
else if (key.startsWith("Objective Immersion Type")) {
String immersion = "Other";
switch (Integer.parseInt(value)) {
// case 1: no immersion
case 2:
immersion = "Oil";
break;
case 3:
immersion = "Water";
break;
}
store.setObjectiveImmersion(getImmersion(immersion), 0, 0);
}
else if (key.startsWith("Stage Position X")) {
stageX.put(image, new Double(value));
addGlobalMeta("X position for position #" + stageX.size(), value);
}
else if (key.startsWith("Stage Position Y")) {
stageY.put(image, new Double(value));
addGlobalMeta("Y position for position #" + stageY.size(), value);
}
else if (key.startsWith("Orca Analog Gain")) {
detectorGain.put(cIndex, new Double(value));
}
else if (key.startsWith("Orca Analog Offset")) {
detectorOffset.put(cIndex, new Double(value));
}
else if (key.startsWith("Comments")) {
imageDescription = value;
}
else if (key.startsWith("Acquisition Date")) {
if (timepoint > 0) {
timestamps.put(new Integer(timepoint - 1), value);
addGlobalMeta("Timestamp " + timepoint, value);
}
timepoint++;
}
}
catch (NumberFormatException e) { }
}
s.close();
}
/**
* Parse ROI data from the given RandomAccessInputStream and store it in the
* given MetadataStore.
*/
private void parseROIs(int imageNum, String name, MetadataStore store)
throws IOException
{
MetadataLevel level = getMetadataOptions().getMetadataLevel();
if (level == MetadataLevel.MINIMUM || level == MetadataLevel.NO_OVERLAYS) {
return;
}
RandomAccessInputStream s = poi.getDocumentStream(name);
s.order(true);
// scan stream for offsets to each ROI
Vector<Long> roiOffsets = new Vector<Long>();
s.seek(0);
while (s.getFilePointer() < s.length() - 8) {
// find next ROI signature
long signature = s.readLong() & 0xffffffffffffffffL;
while (signature != ROI_SIGNATURE) {
if (s.getFilePointer() >= s.length()) break;
s.seek(s.getFilePointer() - 6);
signature = s.readLong() & 0xffffffffffffffffL;
}
if (s.getFilePointer() < s.length()) {
roiOffsets.add(new Long(s.getFilePointer()));
}
}
int shapeIndex = 0;
for (int shape=0; shape<roiOffsets.size(); shape++) {
s.seek(roiOffsets.get(shape).longValue() + 18);
int length = s.readInt();
s.skipBytes(length + 10);
int roiType = s.readInt();
s.skipBytes(8);
// read the bounding box
int x = s.readInt();
int y = s.readInt();
int w = s.readInt() - x;
int h = s.readInt() - y;
// read text label and font data
long nextOffset = shape < roiOffsets.size() - 1 ?
roiOffsets.get(shape + 1).longValue() : s.length();
long nameBlock = s.getFilePointer();
long fontBlock = s.getFilePointer();
long lastBlock = s.getFilePointer();
while (s.getFilePointer() < nextOffset - 1) {
while (s.readShort() != 8) {
if (s.getFilePointer() >= nextOffset) break;
}
if (s.getFilePointer() >= nextOffset) break;
if (s.getFilePointer() - lastBlock > 64 && lastBlock != fontBlock) {
break;
}
nameBlock = fontBlock;
fontBlock = lastBlock;
lastBlock = s.getFilePointer();
}
s.seek(nameBlock);
int strlen = s.readInt();
if (strlen + s.getFilePointer() > s.length()) continue;
String roiName = DataTools.stripString(s.readString(strlen));
s.seek(fontBlock);
int fontLength = s.readInt();
String fontName = DataTools.stripString(s.readString(fontLength));
s.skipBytes(2);
int typeLength = s.readInt();
s.skipBytes(typeLength);
// read list of points that define this ROI
s.skipBytes(10);
int nPoints = s.readInt();
s.skipBytes(6);
String roiID = MetadataTools.createLSID("ROI", imageNum);
String shapeID = MetadataTools.createLSID("Shape", imageNum, shapeIndex);
roiIDs.add(roiID);
if (roiType == ELLIPSE) {
store.setROIID(roiID, imageNum);
store.setEllipseID(shapeID, imageNum, shapeIndex);
store.setEllipseX(new Double(x + (w / 2)), imageNum, shapeIndex);
store.setEllipseY(new Double(y + (h / 2)), imageNum, shapeIndex);
store.setEllipseRadiusX(new Double(w / 2), imageNum, shapeIndex);
store.setEllipseRadiusY(new Double(h / 2), imageNum, shapeIndex);
shapeIndex++;
}
else if (roiType == CURVE || roiType == OUTLINE ||
roiType == OUTLINE_SPLINE)
{
store.setROIID(roiID, imageNum);
StringBuffer points = new StringBuffer();
for (int p=0; p<nPoints; p++) {
double px = s.readDouble();
double py = s.readDouble();
points.append(px);
points.append(",");
points.append(py);
if (p < nPoints - 1) points.append(" ");
}
store.setPolylineID(shapeID, imageNum, shapeIndex);
store.setPolylinePoints(points.toString(), imageNum, shapeIndex);
store.setPolylineClosed(roiType != CURVE, imageNum, shapeIndex);
shapeIndex++;
}
else if (roiType == RECTANGLE || roiType == TEXT) {
store.setROIID(roiID, imageNum);
store.setRectangleID(shapeID, imageNum, shapeIndex);
store.setRectangleX(new Double(x), imageNum, shapeIndex);
store.setRectangleY(new Double(y), imageNum, shapeIndex);
store.setRectangleWidth(new Double(w), imageNum, shapeIndex);
store.setRectangleHeight(new Double(h), imageNum, shapeIndex);
shapeIndex++;
}
else if (roiType == LINE || roiType == SCALE_BAR) {
store.setROIID(roiID, imageNum);
double x1 = s.readDouble();
double y1 = s.readDouble();
double x2 = s.readDouble();
double y2 = s.readDouble();
store.setLineID(shapeID, imageNum, shapeIndex);
store.setLineX1(x1, imageNum, shapeIndex);
store.setLineY1(y1, imageNum, shapeIndex);
store.setLineX2(x2, imageNum, shapeIndex);
store.setLineY2(y2, imageNum, shapeIndex);
shapeIndex++;
}
}
s.close();
}
/** Return the string corresponding to the given ID. */
private String getKey(int tagID) {
switch (tagID) {
case 222: return "Compression";
case 258: return "BlackValue";
case 259: return "WhiteValue";
case 260: return "ImageDataMappingAutoRange";
case 261: return "Thumbnail";
case 262: return "GammaValue";
case 264: return "ImageOverExposure";
case 265: return "ImageRelativeTime1";
case 266: return "ImageRelativeTime2";
case 267: return "ImageRelativeTime3";
case 268: return "ImageRelativeTime4";
case 333: return "RelFocusPosition1";
case 334: return "RelFocusPosition2";
case 513: return "ObjectType";
case 515: return "ImageWidth";
case 516: return "ImageHeight";
case 517: return "Number Raw Count";
case 518: return "PixelType";
case 519: return "NumberOfRawImages";
case 520: return "ImageSize";
case 523: return "Acquisition pause annotation";
case 530: return "Document Subtype";
case 531: return "Acquisition Bit Depth";
case 532: return "Image Memory Usage (RAM)";
case 534: return "Z-Stack single representative";
case 769: return "Scale Factor for X";
case 770: return "Scale Unit for X";
case 771: return "Scale Width";
case 772: return "Scale Factor for Y";
case 773: return "Scale Unit for Y";
case 774: return "Scale Height";
case 775: return "Scale Factor for Z";
case 776: return "Scale Unit for Z";
case 777: return "Scale Depth";
case 778: return "Scaling Parent";
case 1001: return "Date";
case 1002: return "code";
case 1003: return "Source";
case 1004: return "Message";
case 1025: return "Acquisition Date";
case 1026: return "8-bit acquisition";
case 1027: return "Camera Bit Depth";
case 1029: return "MonoReferenceLow";
case 1030: return "MonoReferenceHigh";
case 1031: return "RedReferenceLow";
case 1032: return "RedReferenceHigh";
case 1033: return "GreenReferenceLow";
case 1034: return "GreenReferenceHigh";
case 1035: return "BlueReferenceLow";
case 1036: return "BlueReferenceHigh";
case 1041: return "FrameGrabber Name";
case 1042: return "Camera";
case 1044: return "CameraTriggerSignalType";
case 1045: return "CameraTriggerEnable";
case 1046: return "GrabberTimeout";
case 1281: return "MultiChannelEnabled";
case 1282: return "MultiChannel Color";
case 1283: return "MultiChannel Weight";
case 1284: return "Channel Name";
case 1536: return "DocumentInformationGroup";
case 1537: return "Title";
case 1538: return "Author";
case 1539: return "Keywords";
case 1540: return "Comments";
case 1541: return "SampleID";
case 1542: return "Subject";
case 1543: return "RevisionNumber";
case 1544: return "Save Folder";
case 1545: return "FileLink";
case 1546: return "Document Type";
case 1547: return "Storage Media";
case 1548: return "File ID";
case 1549: return "Reference";
case 1550: return "File Date";
case 1551: return "File Size";
case 1553: return "Filename";
case 1792: return "ProjectGroup";
case 1793: return "Acquisition Date";
case 1794: return "Last modified by";
case 1795: return "User company";
case 1796: return "User company logo";
case 1797: return "Image";
case 1800: return "User ID";
case 1801: return "User Name";
case 1802: return "User City";
case 1803: return "User Address";
case 1804: return "User Country";
case 1805: return "User Phone";
case 1806: return "User Fax";
case 2049: return "Objective Name";
case 2050: return "Optovar";
case 2051: return "Reflector";
case 2052: return "Condenser Contrast";
case 2053: return "Transmitted Light Filter 1";
case 2054: return "Transmitted Light Filter 2";
case 2055: return "Reflected Light Shutter";
case 2056: return "Condenser Front Lens";
case 2057: return "Excitation Filter Name";
case 2060: return "Transmitted Light Fieldstop Aperture";
case 2061: return "Reflected Light Aperture";
case 2062: return "Condenser N.A.";
case 2063: return "Light Path";
case 2064: return "HalogenLampOn";
case 2065: return "Halogen Lamp Mode";
case 2066: return "Halogen Lamp Voltage";
case 2068: return "Fluorescence Lamp Level";
case 2069: return "Fluorescence Lamp Intensity";
case 2070: return "LightManagerEnabled";
case 2071: return "tag_ID_2071";
case 2072: return "Focus Position";
case 2073: return "Stage Position X";
case 2074: return "Stage Position Y";
case 2075: return "Microscope Name";
case 2076: return "Objective Magnification";
case 2077: return "Objective N.A.";
case 2078: return "MicroscopeIllumination";
case 2079: return "External Shutter 1";
case 2080: return "External Shutter 2";
case 2081: return "External Shutter 3";
case 2082: return "External Filter Wheel 1 Name";
case 2083: return "External Filter Wheel 2 Name";
case 2084: return "Parfocal Correction";
case 2086: return "External Shutter 4";
case 2087: return "External Shutter 5";
case 2088: return "External Shutter 6";
case 2089: return "External Filter Wheel 3 Name";
case 2090: return "External Filter Wheel 4 Name";
case 2103: return "Objective Turret Position";
case 2104: return "Objective Contrast Method";
case 2105: return "Objective Immersion Type";
case 2107: return "Reflector Position";
case 2109: return "Transmitted Light Filter 1 Position";
case 2110: return "Transmitted Light Filter 2 Position";
case 2112: return "Excitation Filter Position";
case 2113: return "Lamp Mirror Position";
case 2114: return "External Filter Wheel 1 Position";
case 2115: return "External Filter Wheel 2 Position";
case 2116: return "External Filter Wheel 3 Position";
case 2117: return "External Filter Wheel 4 Position";
case 2118: return "Lightmanager Mode";
case 2119: return "Halogen Lamp Calibration";
case 2120: return "CondenserNAGoSpeed";
case 2121: return "TransmittedLightFieldstopGoSpeed";
case 2122: return "OptovarGoSpeed";
case 2123: return "Focus calibrated";
case 2124: return "FocusBasicPosition";
case 2125: return "FocusPower";
case 2126: return "FocusBacklash";
case 2127: return "FocusMeasurementOrigin";
case 2128: return "FocusMeasurementDistance";
case 2129: return "FocusSpeed";
case 2130: return "FocusGoSpeed";
case 2131: return "FocusDistance";
case 2132: return "FocusInitPosition";
case 2133: return "Stage calibrated";
case 2134: return "StagePower";
case 2135: return "StageXBacklash";
case 2136: return "StageYBacklash";
case 2137: return "StageSpeedX";
case 2138: return "StageSpeedY";
case 2139: return "StageSpeed";
case 2140: return "StageGoSpeedX";
case 2141: return "StageGoSpeedY";
case 2142: return "StageStepDistanceX";
case 2143: return "StageStepDistanceY";
case 2144: return "StageInitialisationPositionX";
case 2145: return "StageInitialisationPositionY";
case 2146: return "MicroscopeMagnification";
case 2147: return "ReflectorMagnification";
case 2148: return "LampMirrorPosition";
case 2149: return "FocusDepth";
case 2150: return "MicroscopeType";
case 2151: return "Objective Working Distance";
case 2152: return "ReflectedLightApertureGoSpeed";
case 2153: return "External Shutter";
case 2154: return "ObjectiveImmersionStop";
case 2155: return "Focus Start Speed";
case 2156: return "Focus Acceleration";
case 2157: return "ReflectedLightFieldstop";
case 2158: return "ReflectedLightFieldstopGoSpeed";
case 2159: return "ReflectedLightFilter 1";
case 2160: return "ReflectedLightFilter 2";
case 2161: return "ReflectedLightFilter1Position";
case 2162: return "ReflectedLightFilter2Position";
case 2163: return "TransmittedLightAttenuator";
case 2164: return "ReflectedLightAttenuator";
case 2165: return "Transmitted Light Shutter";
case 2166: return "TransmittedLightAttenuatorGoSpeed";
case 2167: return "ReflectedLightAttenuatorGoSpeed";
case 2176: return "TransmittedLightVirtualFilterPosition";
case 2177: return "TransmittedLightVirtualFilter";
case 2178: return "ReflectedLightVirtualFilterPosition";
case 2179: return "ReflectedLightVirtualFilter";
case 2180: return "ReflectedLightHalogenLampMode";
case 2181: return "ReflectedLightHalogenLampVoltage";
case 2182: return "ReflectedLightHalogenLampColorTemperature";
case 2183: return "ContrastManagerMode";
case 2184: return "Dazzle Protection Active";
case 2195: return "Zoom";
case 2196: return "ZoomGoSpeed";
case 2197: return "LightZoom";
case 2198: return "LightZoomGoSpeed";
case 2199: return "LightZoomCoupled";
case 2200: return "TransmittedLightHalogenLampMode";
case 2201: return "TransmittedLightHalogenLampVoltage";
case 2202: return "TransmittedLightHalogenLampColorTemperature";
case 2203: return "Reflected Coldlight Mode";
case 2204: return "Reflected Coldlight Intensity";
case 2205: return "Reflected Coldlight Color Temperature";
case 2206: return "Transmitted Coldlight Mode";
case 2207: return "Transmitted Coldlight Intensity";
case 2208: return "Transmitted Coldlight Color Temperature";
case 2209: return "Infinityspace Portchanger Position";
case 2210: return "Beamsplitter Infinity Space";
case 2211: return "TwoTv VisCamChanger Position";
case 2212: return "Beamsplitter Ocular";
case 2213: return "TwoTv CamerasChanger Position";
case 2214: return "Beamsplitter Cameras";
case 2215: return "Ocular Shutter";
case 2216: return "TwoTv CamerasChangerCube";
case 2218: return "Ocular Magnification";
case 2219: return "Camera Adapter Magnification";
case 2220: return "Microscope Port";
case 2221: return "Ocular Total Magnification";
case 2222: return "Field of View";
case 2223: return "Ocular";
case 2224: return "CameraAdapter";
case 2225: return "StageJoystickEnabled";
case 2226: return "ContrastManager Contrast Method";
case 2229: return "CamerasChanger Beamsplitter Type";
case 2235: return "Rearport Slider Position";
case 2236: return "Rearport Source";
case 2237: return "Beamsplitter Type Infinity Space";
case 2238: return "Fluorescence Attenuator";
case 2239: return "Fluorescence Attenuator Position";
case 2261: return "Objective ID";
case 2262: return "Reflector ID";
case 2307: return "Camera Framestart Left";
case 2308: return "Camera Framestart Top";
case 2309: return "Camera Frame Width";
case 2310: return "Camera Frame Height";
case 2311: return "Camera Binning";
case 2312: return "CameraFrameFull";
case 2313: return "CameraFramePixelDistance";
case 2318: return "DataFormatUseScaling";
case 2319: return "CameraFrameImageOrientation";
case 2320: return "VideoMonochromeSignalType";
case 2321: return "VideoColorSignalType";
case 2322: return "MeteorChannelInput";
case 2323: return "MeteorChannelSync";
case 2324: return "WhiteBalanceEnabled";
case 2325: return "CameraWhiteBalanceRed";
case 2326: return "CameraWhiteBalanceGreen";
case 2327: return "CameraWhiteBalanceBlue";
case 2331: return "CameraFrameScalingFactor";
case 2562: return "Meteor Camera Type";
case 2564: return "Exposure Time [ms]";
case 2568: return "CameraExposureTimeAutoCalculate";
case 2569: return "Meteor Gain Value";
case 2571: return "Meteor Gain Automatic";
case 2572: return "MeteorAdjustHue";
case 2573: return "MeteorAdjustSaturation";
case 2574: return "MeteorAdjustRedLow";
case 2575: return "MeteorAdjustGreenLow";
case 2576: return "Meteor Blue Low";
case 2577: return "MeteorAdjustRedHigh";
case 2578: return "MeteorAdjustGreenHigh";
case 2579: return "MeteorBlue High";
case 2582: return "CameraExposureTimeCalculationControl";
case 2585: return "AxioCamFadingCorrectionEnable";
case 2587: return "CameraLiveImage";
case 2588: return "CameraLiveEnabled";
case 2589: return "LiveImageSyncObjectName";
case 2590: return "CameraLiveSpeed";
case 2591: return "CameraImage";
case 2592: return "CameraImageWidth";
case 2593: return "CameraImageHeight";
case 2594: return "CameraImagePixelType";
case 2595: return "CameraImageShMemoryName";
case 2596: return "CameraLiveImageWidth";
case 2597: return "CameraLiveImageHeight";
case 2598: return "CameraLiveImagePixelType";
case 2599: return "CameraLiveImageShMemoryName";
case 2600: return "CameraLiveMaximumSpeed";
case 2601: return "CameraLiveBinning";
case 2602: return "CameraLiveGainValue";
case 2603: return "CameraLiveExposureTimeValue";
case 2604: return "CameraLiveScalingFactor";
case 2819: return "Image Index Z";
case 2820: return "Image Channel Index";
case 2821: return "Image Index T";
case 2822: return "ImageTile Index";
case 2823: return "Image acquisition Index";
case 2827: return "Image IndexS";
case 2841: return "Original Stage Position X";
case 2842: return "Original Stage Position Y";
case 3088: return "LayerDrawFlags";
case 3334: return "RemainingTime";
case 3585: return "User Field 1";
case 3586: return "User Field 2";
case 3587: return "User Field 3";
case 3588: return "User Field 4";
case 3589: return "User Field 5";
case 3590: return "User Field 6";
case 3591: return "User Field 7";
case 3592: return "User Field 8";
case 3593: return "User Field 9";
case 3594: return "User Field 10";
case 3840: return "ID";
case 3841: return "Name";
case 3842: return "Value";
case 5501: return "PvCamClockingMode";
case 8193: return "Autofocus Status Report";
case 8194: return "Autofocus Position";
case 8195: return "Autofocus Position Offset";
case 8196: return "Autofocus Empty Field Threshold";
case 8197: return "Autofocus Calibration Name";
case 8198: return "Autofocus Current Calibration Item";
case 20478: return "tag_ID_20478";
case 65537: return "CameraFrameFullWidth";
case 65538: return "CameraFrameFullHeight";
case 65541: return "AxioCam Shutter Signal";
case 65542: return "AxioCam Delay Time";
case 65543: return "AxioCam Shutter Control";
case 65544: return "AxioCam BlackRefIsCalculated";
case 65545: return "AxioCam Black Reference";
case 65547: return "Camera Shading Correction";
case 65550: return "AxioCam Enhance Color";
case 65551: return "AxioCam NIR Mode";
case 65552: return "CameraShutterCloseDelay";
case 65553: return "CameraWhiteBalanceAutoCalculate";
case 65556: return "AxioCam NIR Mode Available";
case 65557: return "AxioCam Fading Correction Available";
case 65559: return "AxioCam Enhance Color Available";
case 65565: return "MeteorVideoNorm";
case 65566: return "MeteorAdjustWhiteReference";
case 65567: return "MeteorBlackReference";
case 65568: return "MeteorChannelInputCountMono";
case 65570: return "MeteorChannelInputCountRGB";
case 65571: return "MeteorEnableVCR";
case 65572: return "Meteor Brightness";
case 65573: return "Meteor Contrast";
case 65575: return "AxioCam Selector";
case 65576: return "AxioCam Type";
case 65577: return "AxioCam Info";
case 65580: return "AxioCam Resolution";
case 65581: return "AxioCam Color Model";
case 65582: return "AxioCam MicroScanning";
case 65585: return "Amplification Index";
case 65586: return "Device Command";
case 65587: return "BeamLocation";
case 65588: return "ComponentType";
case 65589: return "ControllerType";
case 65590: return "CameraWhiteBalanceCalculationRedPaint";
case 65591: return "CameraWhiteBalanceCalculationBluePaint";
case 65592: return "CameraWhiteBalanceSetRed";
case 65593: return "CameraWhiteBalanceSetGreen";
case 65594: return "CameraWhiteBalanceSetBlue";
case 65595: return "CameraWhiteBalanceSetTargetRed";
case 65596: return "CameraWhiteBalanceSetTargetGreen";
case 65597: return "CameraWhiteBalanceSetTargetBlue";
case 65598: return "ApotomeCamCalibrationMode";
case 65599: return "ApoTome Grid Position";
case 65600: return "ApotomeCamScannerPosition";
case 65601: return "ApoTome Full Phase Shift";
case 65602: return "ApoTome Grid Name";
case 65603: return "ApoTome Staining";
case 65604: return "ApoTome Processing Mode";
case 65605: return "ApotomeCamLiveCombineMode";
case 65606: return "ApoTome Filter Name";
case 65607: return "Apotome Filter Strength";
case 65608: return "ApotomeCamFilterHarmonics";
case 65609: return "ApoTome Grating Period";
case 65610: return "ApoTome Auto Shutter Used";
case 65611: return "Apotome Cam Status";
case 65612: return "ApotomeCamNormalize";
case 65613: return "ApotomeCamSettingsManager";
case 65614: return "DeepviewCamSupervisorMode";
case 65615: return "DeepView Processing";
case 65616: return "DeepviewCamFilterName";
case 65617: return "DeepviewCamStatus";
case 65618: return "DeepviewCamSettingsManager";
case 65619: return "DeviceScalingName";
case 65620: return "CameraShadingIsCalculated";
case 65621: return "CameraShadingCalculationName";
case 65622: return "CameraShadingAutoCalculate";
case 65623: return "CameraTriggerAvailable";
case 65626: return "CameraShutterAvailable";
case 65627: return "AxioCam ShutterMicroScanningEnable";
case 65628: return "ApotomeCamLiveFocus";
case 65629: return "DeviceInitStatus";
case 65630: return "DeviceErrorStatus";
case 65631: return "ApotomeCamSliderInGridPosition";
case 65632: return "Orca NIR Mode Used";
case 65633: return "Orca Analog Gain";
case 65634: return "Orca Analog Offset";
case 65635: return "Orca Binning";
case 65636: return "Orca Bit Depth";
case 65637: return "ApoTome Averaging Count";
case 65638: return "DeepView DoF";
case 65639: return "DeepView EDoF";
case 65643: return "DeepView Slider Name";
case 65655: return "DeepView Slider Name";
case 5439491: return "Acquisition Sofware";
case 16777488: return "Excitation Wavelength";
case 16777489: return "Emission Wavelength";
case 101515267: return "File Name";
case 101253123:
case 101777411:
return "Image Name";
default: return "" + tagID;
}
}
private long parseTimestamp(String s) {
long stamp = 0;
try {
stamp = Long.parseLong(s);
}
catch (NumberFormatException exc) {
if (s != null) {
stamp = DateTools.getTime(s, "M/d/y h:mm:ss aa");
}
}
return stamp;
}
}