// // DeltavisionReader.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.IOException; import loci.formats.*; /** * DeltavisionReader is the file format reader for Deltavision files. * * <dl><dt><b>Source code:</b></dt> * <dd><a href="https://skyking.microscopy.wisc.edu/trac/java/browser/trunk/loci/formats/in/DeltavisionReader.java">Trac</a>, * <a href="https://skyking.microscopy.wisc.edu/svn/java/trunk/loci/formats/in/DeltavisionReader.java">SVN</a></dd></dl> * * @author Melissa Linkert linkert at wisc.edu */ public class DeltavisionReader extends FormatReader { // -- Constants -- private static final short LITTLE_ENDIAN = -16224; private static final int HEADER_LENGTH = 1024; // -- Fields -- /** Size of extended header. */ private int extSize; /** Bytes per pixel. */ private int bytesPerPixel; /** Size of one wave in the extended header. */ protected int wSize; /** Size of one z section in the extended header. */ protected int zSize; /** Size of one time element in the extended header. */ protected int tSize; /** * The number of ints in each extended header section. These fields appear * to be all blank but need to be skipped to get to the floats afterwards */ protected int numIntsPerSection; protected int numFloatsPerSection; /** Initialize an array of Extended Header Field structures. */ protected DVExtHdrFields[][][] extHdrFields = null; // -- Constructor -- /** Constructs a new Deltavision reader. */ public DeltavisionReader() { super("Deltavision", new String[] {"dv", "r3d", "r3d_d3d"}); } // -- IFormatReader API methods -- /* @see loci.formats.IFormatReader#isThisType(byte[]) */ public boolean isThisType(byte[] block) { return false; } /* @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); FormatTools.checkBufferSize(this, buf.length); // read the image plane's pixel data long offset = HEADER_LENGTH + extSize; long bytes = core.sizeX[0] * core.sizeY[0] * bytesPerPixel; in.seek(offset + bytes*no); in.read(buf); return buf; } // -- Internal FormatReader API methods -- /* @see loci.formats.FormatReader#initFile(String) */ protected void initFile(String id) throws FormatException, IOException { if (debug) debug("DeltavisionReader.initFile(" + id + ")"); super.initFile(id); in = new RandomAccessStream(id); status("Reading header"); // read in the image header data in.seek(96); in.order(true); core.littleEndian[0] = in.readShort() == LITTLE_ENDIAN; in.order(core.littleEndian[0]); in.seek(8); core.imageCount[0] = in.readInt(); in.seek(92); extSize = in.readInt(); in.seek(0); core.sizeX[0] = in.readInt(); core.sizeY[0] = in.readInt(); Integer xSize = new Integer(core.sizeX[0]); Integer ySize = new Integer(core.sizeY[0]); addMeta("ImageWidth", xSize); addMeta("ImageHeight", ySize); addMeta("NumberOfImages", new Integer(in.readInt())); int filePixelType = in.readInt(); String pixel; switch (filePixelType) { case 0: pixel = "8 bit unsigned integer"; core.pixelType[0] = FormatTools.UINT8; bytesPerPixel = 1; break; case 1: pixel = "16 bit signed integer"; core.pixelType[0] = FormatTools.UINT16; bytesPerPixel = 2; break; case 2: pixel = "32 bit floating point"; core.pixelType[0] = FormatTools.FLOAT; bytesPerPixel = 4; break; case 3: pixel = "32 bit complex"; core.pixelType[0] = FormatTools.UINT32; bytesPerPixel = 4; break; case 4: pixel = "64 bit complex"; core.pixelType[0] = FormatTools.FLOAT; bytesPerPixel = 8; break; case 6: pixel = "16 bit unsigned integer"; core.pixelType[0] = FormatTools.UINT16; bytesPerPixel = 2; break; default: pixel = "unknown"; core.pixelType[0] = FormatTools.UINT8; bytesPerPixel = 1; } addMeta("PixelType", pixel); addMeta("Sub-image starting point (X)", new Integer(in.readInt())); addMeta("Sub-image starting point (Y)", new Integer(in.readInt())); addMeta("Sub-image starting point (Z)", new Integer(in.readInt())); addMeta("Pixel sampling size (X)", new Integer(in.readInt())); addMeta("Pixel sampling size (Y)", new Integer(in.readInt())); addMeta("Pixel sampling size (Z)", new Integer(in.readInt())); float pixX = in.readFloat(); float pixY = in.readFloat(); float pixZ = in.readFloat(); addMeta("X element length (in um)", new Float(pixX)); addMeta("Y element length (in um)", new Float(pixY)); addMeta("Z element length (in um)", new Float(pixZ)); addMeta("X axis angle", new Float(in.readFloat())); addMeta("Y axis angle", new Float(in.readFloat())); addMeta("Z axis angle", new Float(in.readFloat())); addMeta("Column axis sequence", new Integer(in.readInt())); addMeta("Row axis sequence", new Integer(in.readInt())); addMeta("Section axis sequence", new Integer(in.readInt())); Float wave1Min = new Float(in.readFloat()); addMeta("Wavelength 1 min. intensity", wave1Min); Float wave1Max = new Float(in.readFloat()); addMeta("Wavelength 1 max. intensity", wave1Max); addMeta("Wavelength 1 mean intensity", new Float(in.readFloat())); addMeta("Space group number", new Integer(in.readInt())); in.seek(132); addMeta("Number of Sub-resolution sets", new Integer(in.readShort())); addMeta("Z axis reduction quotient", new Integer(in.readShort())); Float wave2Min = new Float(in.readFloat()); addMeta("Wavelength 2 min. intensity", wave2Min); Float wave2Max = new Float(in.readFloat()); addMeta("Wavelength 2 max. intensity", wave2Max); Float wave3Min = new Float(in.readFloat()); addMeta("Wavelength 3 min. intensity", wave3Min); Float wave3Max = new Float(in.readFloat()); addMeta("Wavelength 3 max. intensity", wave3Max); Float wave4Min = new Float(in.readFloat()); addMeta("Wavelength 4 min. intensity", wave4Min); Float wave4Max = new Float(in.readFloat()); addMeta("Wavelength 4 max. intensity", wave4Max); int type = in.readShort(); String imageType; switch (type) { case 0: imageType = "normal"; break; case 1: imageType = "Tilt-series"; break; case 2: imageType = "Stereo tilt-series"; break; case 3: imageType = "Averaged images"; break; case 4: imageType = "Averaged stereo pairs"; break; default: imageType = "unknown"; } addMeta("Image Type", imageType); addMeta("Lens ID Number", new Integer(in.readShort())); in.seek(172); Float wave5Min = new Float(in.readFloat()); addMeta("Wavelength 5 min. intensity", wave5Min); Float wave5Max = new Float(in.readFloat()); addMeta("Wavelength 5 max. intensity", wave5Max); core.sizeT[0] = in.readShort(); addMeta("Number of timepoints", new Integer(core.sizeT[0])); int sequence = in.readShort(); String imageSequence; switch (sequence) { case 0: imageSequence = "ZTW"; core.currentOrder[0] = "XYZTC"; break; case 1: imageSequence = "WZT"; core.currentOrder[0] = "XYCZT"; break; case 2: imageSequence = "ZWT"; core.currentOrder[0] = "XYZCT"; break; case 65536: imageSequence = "WZT"; core.currentOrder[0] = "XYCZT"; break; default: imageSequence = "unknown"; core.currentOrder[0] = "XYZTC"; } addMeta("Image sequence", imageSequence); addMeta("X axis tilt angle", new Float(in.readFloat())); addMeta("Y axis tilt angle", new Float(in.readFloat())); addMeta("Z axis tilt angle", new Float(in.readFloat())); core.sizeC[0] = in.readShort(); addMeta("Number of wavelengths", new Integer(core.sizeC[0])); core.sizeZ[0] = core.imageCount[0] / (core.sizeC[0] * core.sizeT[0]); addMeta("Number of focal planes", new Integer(core.sizeZ[0])); core.rgb[0] = false; core.interleaved[0] = false; core.metadataComplete[0] = true; core.indexed[0] = false; core.falseColor[0] = false; short[] waves = new short[5]; for (int i=0; i<waves.length; i++) waves[i] = in.readShort(); addMeta("Wavelength 1 (in nm)", new Integer(waves[0])); addMeta("Wavelength 2 (in nm)", new Integer(waves[1])); addMeta("Wavelength 3 (in nm)", new Integer(waves[2])); addMeta("Wavelength 4 (in nm)", new Integer(waves[3])); addMeta("Wavelength 5 (in nm)", new Integer(waves[4])); addMeta("X origin (in um)", new Float(in.readFloat())); addMeta("Y origin (in um)", new Float(in.readFloat())); addMeta("Z origin (in um)", new Float(in.readFloat())); // The metadata store we're working with. MetadataStore store = getMetadataStore(); in.seek(224); String title = null; for (int i=1; i<=10; i++) { // Make sure that "null" characters are stripped out title = in.readString(80).replaceAll("\0", ""); addMeta("Title " + i, title); } // ----- The Extended Header data handler begins here ------ status("Reading extended header"); in.seek(128); numIntsPerSection = in.readShort(); numFloatsPerSection = in.readShort(); setOffsetInfo(sequence, core.sizeZ[0], core.sizeC[0], core.sizeT[0]); extHdrFields = new DVExtHdrFields[core.sizeZ[0]][core.sizeC[0]][core.sizeT[0]]; FormatTools.populatePixels(store, this); store.setDimensions(new Float(pixX), new Float(pixY), new Float(pixZ), null, null, null); if (title == null) title = ""; title = title.length() == 0 ? null : title; store.setImage(id, null, title, null); // Run through every timeslice, for each wavelength, for each z section // and fill in the Extended Header information array for that image for (int z=0; z<core.sizeZ[0]; z++) { for (int t=0; t<core.sizeT[0]; t++) { for (int w=0; w<core.sizeC[0]; w++) { in.seek(HEADER_LENGTH); extHdrFields[z][w][t] = new DVExtHdrFields(getTotalOffset(z, w, t), numIntsPerSection, in, core.littleEndian[0]); store.setPlaneInfo(z, w, t, new Float(extHdrFields[z][w][t].getTimeStampSeconds()), new Float(extHdrFields[z][w][t].getExpTime()), null); } } } status("Populating metadata"); for (int w=0; w<core.sizeC[0]; w++) { store.setLogicalChannel(w, null, null, null, null, null, null, null, null, null, null, null, null, "Monochrome", "Wide-field", null, null, null, null, null, new Integer(waves[w]), new Integer((int) extHdrFields[0][w][0].getExFilter()), null, new Float(extHdrFields[0][w][0].getNdFilter()), null); } store.setStageLabel("ome", new Float(extHdrFields[0][0][0].getStageXCoord()), new Float(extHdrFields[0][0][0].getStageYCoord()), new Float(extHdrFields[0][0][0].getStageZCoord()), null); if (core.sizeC[0] > 0) { store.setChannelGlobalMinMax(0, new Double(wave1Min.floatValue()), new Double(wave1Max.floatValue()), null); } if (core.sizeC[0] > 1) { store.setChannelGlobalMinMax(1, new Double(wave2Min.floatValue()), new Double(wave2Max.floatValue()), null); } if (core.sizeC[0] > 2) { store.setChannelGlobalMinMax(2, new Double(wave3Min.floatValue()), new Double(wave3Max.floatValue()), null); } if (core.sizeC[0] > 3) { store.setChannelGlobalMinMax(3, new Double(wave4Min.floatValue()), new Double(wave4Max.floatValue()), null); } if (core.sizeC[0] > 4) { store.setChannelGlobalMinMax(4, new Double(wave5Min.floatValue()), new Double(wave5Max.floatValue()), null); } //store.setDefaultDisplaySettings(null); } // -- Helper methods -- /** * This method calculates the size of a w, t, z section depending on which * sequence is being used (either ZTW, WZT, or ZWT) * @param imgSequence * @param numZSections * @param numWaves * @param numTimes */ private void setOffsetInfo(int imgSequence, int numZSections, int numWaves, int numTimes) { int smallOffset = (numIntsPerSection + numFloatsPerSection) * 4; switch (imgSequence) { // ZTW sequence case 0: zSize = smallOffset; tSize = zSize * numZSections; wSize = tSize * numTimes; break; // WZT sequence case 1: wSize = smallOffset; zSize = wSize * numWaves; tSize = zSize * numZSections; break; // ZWT sequence case 2: zSize = smallOffset; wSize = zSize * numZSections; tSize = wSize * numWaves; break; } } /** * Given any specific Z, W, and T of a plane, determine the totalOffset from * the start of the extended header. * @param currentZ * @param currentW * @param currentT */ public int getTotalOffset(int currentZ, int currentW, int currentT) { return (zSize * currentZ) + (wSize * currentW) + (tSize * currentT); } /** * This method returns the a plane number from when given a Z, W * and T offsets. * @param currentZ * @param currentW * @param currentT */ public int getPlaneNumber(int currentZ, int currentW, int currentT) { int smallOffset = (numIntsPerSection + numFloatsPerSection) * 4; return getTotalOffset(currentZ, currentW, currentT) / smallOffset; } // -- Helper classes -- /** * This private class structure holds the details for the extended header * @author Brian W. Loranger */ private class DVExtHdrFields { private int offsetWithInts; private float oDFilter; /** Photosensor reading. Typically in mV. */ private float photosensorReading; /** Time stamp in seconds since the experiment began. */ private float timeStampSeconds; /** X stage coordinates. */ private float stageXCoord; /** Y stage coordinates. */ private float stageYCoord; /** Z stage coordinates. */ private float stageZCoord; /** Minimum intensity */ private float minInten; /** Maxiumum intensity. */ private float maxInten; /** Mean intesity. */ private float meanInten; /** Exposure time in seconds. */ private float expTime; /** Neutral density value. */ private float ndFilter; /** Excitation filter number. */ private float exFilter; /** Emiision filter number. */ private float emFilter; /** Excitation filter wavelength. */ private float exWavelen; /** Emission filter wavelength. */ private float emWavelen; /** Intensity scaling factor. Usually 1. */ private float intenScaling; /** Energy conversion factor. Usually 1. */ private float energyConvFactor; /** * Helper function which overrides toString, printing out the values in * the header section. */ public String toString() { String s = new String(); s += "photosensorReading: " + photosensorReading + "\n"; s += "timeStampSeconds: " + timeStampSeconds + "\n"; s += "stageXCoord: " + stageXCoord + "\n"; s += "stageYCoord: " + stageYCoord + "\n"; s += "stageZCoord: " + stageZCoord + "\n"; s += "minInten: " + minInten + "\n"; s += "maxInten: " + maxInten + "\n"; s += "meanInten: " + meanInten + "\n"; s += "expTime: " + expTime + "\n"; s += "ndFilter: " + ndFilter + "\n"; s += "exFilter: " + exFilter + "\n"; s += "emFilter: " + emFilter + "\n"; s += "exWavelen: " + exWavelen + "\n"; s += "emWavelen: " + emWavelen + "\n"; s += "intenScaling: " + intenScaling + "\n"; s += "energyConvFactor: " + energyConvFactor + "\n"; return s; } /** * Given the starting offset of a specific entry in the extended header * this method will go through each element in the entry and fill each * element's variable with its extended header value. * @param startingOffset * @param numIntsPerSection * @param in * @param little */ protected DVExtHdrFields(int startingOffset, int numIntsPerSection, RandomAccessStream in, boolean little) { try { long fp = in.getFilePointer(); // skip over the int values that have nothing in them offsetWithInts = startingOffset + (numIntsPerSection * 4); // DV files store the ND (neuatral density) Filter // (normally expressed as a %T (transmittance)) as an OD // (optical density) rating. // To convert from one to the other the formula is %T = 10^(-OD) X 100. in.skipBytes(offsetWithInts + 36); oDFilter = in.readFloat(); // fill in the extended header information for the floats in.seek(fp + offsetWithInts); photosensorReading = in.readFloat(); timeStampSeconds = in.readFloat(); stageXCoord = in.readFloat(); stageYCoord = in.readFloat(); stageZCoord = in.readFloat(); minInten = in.readFloat(); maxInten = in.readFloat(); meanInten = in.readFloat(); expTime = in.readFloat(); ndFilter = (float) Math.pow(10.0, -oDFilter); in.skipBytes(4); exFilter = in.readFloat(); emFilter = in.readFloat(); exWavelen = in.readFloat(); emWavelen = in.readFloat(); intenScaling = in.readFloat(); energyConvFactor = in.readFloat(); } catch (IOException e) { LogTools.trace(e); } } /** Various getters for the Extended header fields. */ public float getPhotosensorReading() { return photosensorReading; } public float getTimeStampSeconds() { return timeStampSeconds; } public float getStageXCoord() { return stageXCoord; } public float getStageYCoord() { return stageYCoord; } public float getStageZCoord() { return stageZCoord; } public float getMinInten() { return minInten; } public float getMaxInten() { return maxInten; } public float getMeanInten() { return meanInten; } public float getExpTime() { return expTime; } public float getNdFilter() { return ndFilter; } public float getExFilter() { return exFilter; } public float getEmFilter() { return emFilter; } public float getExWavelen() { return exWavelen; } public float getEmWavelen() { return emWavelen; } public float getIntenScaling() { return intenScaling; } } }