// // LIFReader.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.IOException; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Stack; import java.util.StringTokenizer; import java.util.Vector; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import loci.common.DataTools; import loci.common.DateTools; import loci.common.RandomAccessInputStream; import loci.common.services.DependencyException; import loci.common.services.ServiceException; import loci.common.services.ServiceFactory; import loci.common.xml.XMLTools; import loci.formats.CoreMetadata; import loci.formats.FormatException; import loci.formats.FormatReader; import loci.formats.FormatTools; import loci.formats.ImageTools; import loci.formats.MetadataTools; import loci.formats.meta.IMetadata; import loci.formats.meta.MetadataStore; import ome.xml.model.primitives.PositiveFloat; import loci.formats.services.OMEXMLService; import ome.xml.model.enums.DetectorType; import ome.xml.model.enums.LaserMedium; import ome.xml.model.enums.LaserType; import ome.xml.model.primitives.NonNegativeInteger; import ome.xml.model.primitives.PercentFraction; import ome.xml.model.primitives.PositiveInteger; import org.xml.sax.SAXException; import org.w3c.dom.Attr; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; /** * LIFReader is the file format reader for Leica LIF 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/LIFReader.java">Trac</a>, * <a href="http://git.openmicroscopy.org/?p=bioformats.git;a=blob;f=components/bio-formats/src/loci/formats/in/LIFReader.java;hb=HEAD">Gitweb</a></dd></dl> * * @author Melissa Linkert melissa at glencoesoftware.com */ public class LIFReader extends FormatReader { // -- Constants -- public static final byte LIF_MAGIC_BYTE = 0x70; public static final byte LIF_MEMORY_BYTE = 0x2a; private static final HashMap<String, Integer> CHANNEL_PRIORITIES = createChannelPriorities(); private static HashMap<String, Integer> createChannelPriorities() { HashMap<String, Integer> h = new HashMap<String, Integer>(); h.put("red", new Integer(0)); h.put("green", new Integer(1)); h.put("blue", new Integer(2)); h.put("cyan", new Integer(3)); h.put("magenta", new Integer(4)); h.put("yellow", new Integer(5)); h.put("black", new Integer(6)); h.put("gray", new Integer(7)); h.put("", new Integer(8)); return h; } // -- Fields -- /** Offsets to memory blocks, paired with their corresponding description. */ private Vector<Long> offsets; private int[][] realChannel; private int lastChannel = 0; private Vector<String> lutNames = new Vector<String>(); private Vector<Double> physicalSizeXs = new Vector<Double>(); private Vector<Double> physicalSizeYs = new Vector<Double>(); private String[] descriptions, microscopeModels, serialNumber; private Double[] pinholes, zooms, zSteps, tSteps, lensNA; private Double[][] expTimes, gains; private Vector[] detectorOffsets; private String[][] channelNames; private Vector[] detectorModels, voltages; private Integer[][] exWaves; private Vector[] activeDetector; private HashMap[] detectorIndexes; private String[] immersions, corrections, objectiveModels; private Integer[] magnification; private Double[] posX, posY, posZ; private Double[] refractiveIndex; private Vector[] cutIns, cutOuts, filterModels; private double[][] timestamps; private Vector[] laserWavelength, laserIntensity; private ROI[][] imageROIs; private boolean alternateCenter = false; private String[] imageNames; private double[] acquiredDate; // -- Constructor -- /** Constructs a new Leica LIF reader. */ public LIFReader() { super("Leica Image File Format", "lif"); suffixNecessary = false; 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 = 1; if (!FormatTools.validStream(stream, blockLen, true)) return false; return stream.read() == LIF_MAGIC_BYTE; } /* @see loci.formats.IFormatReader#get8BitLookupTable() */ public byte[][] get8BitLookupTable() { FormatTools.assertId(currentId, true, 1); if (getPixelType() != FormatTools.UINT8 || !isIndexed()) return null; if (lastChannel < 0 || lastChannel >= 9) { return null; } byte[][] lut = new byte[3][256]; for (int i=0; i<256; i++) { switch (lastChannel) { case 0: // red lut[0][i] = (byte) (i & 0xff); break; case 1: // green lut[1][i] = (byte) (i & 0xff); break; case 2: // blue lut[2][i] = (byte) (i & 0xff); break; case 3: // cyan lut[1][i] = (byte) (i & 0xff); lut[2][i] = (byte) (i & 0xff); break; case 4: // magenta lut[0][i] = (byte) (i & 0xff); lut[2][i] = (byte) (i & 0xff); break; case 5: // yellow lut[0][i] = (byte) (i & 0xff); lut[1][i] = (byte) (i & 0xff); break; default: // gray lut[0][i] = (byte) (i & 0xff); lut[1][i] = (byte) (i & 0xff); lut[2][i] = (byte) (i & 0xff); } } return lut; } /* @see loci.formats.IFormatReader#get16BitLookupTable() */ public short[][] get16BitLookupTable() { FormatTools.assertId(currentId, true, 1); if (getPixelType() != FormatTools.UINT16 || !isIndexed()) return null; if (lastChannel < 0 || lastChannel >= 9) { return null; } short[][] lut = new short[3][65536]; for (int i=0; i<65536; i++) { switch (lastChannel) { case 0: // red lut[0][i] = (short) (i & 0xffff); break; case 1: // green lut[1][i] = (short) (i & 0xffff); break; case 2: // blue lut[2][i] = (short) (i & 0xffff); break; case 3: // cyan lut[1][i] = (short) (i & 0xffff); lut[2][i] = (short) (i & 0xffff); break; case 4: // magenta lut[0][i] = (short) (i & 0xffff); lut[2][i] = (short) (i & 0xffff); break; case 5: // yellow lut[0][i] = (short) (i & 0xffff); lut[1][i] = (short) (i & 0xffff); break; default: // gray lut[0][i] = (short) (i & 0xffff); lut[1][i] = (short) (i & 0xffff); lut[2][i] = (short) (i & 0xffff); } } 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); if (!isRGB()) { int[] pos = getZCTCoords(no); lastChannel = realChannel[series][pos[1]]; } if (series >= offsets.size()) { // truncated file; imitate LAS AF and return black planes Arrays.fill(buf, (byte) 0); return buf; } long offset = offsets.get(series).longValue(); int bytes = FormatTools.getBytesPerPixel(getPixelType()); int bpp = bytes * getRGBChannelCount(); long planeSize = (long) getSizeX() * getSizeY() * bpp; long nextOffset = series + 1 < offsets.size() ? offsets.get(series + 1).longValue() : in.length(); int bytesToSkip = (int) (nextOffset - offset - planeSize * getImageCount()); bytesToSkip /= getSizeY(); if ((getSizeX() % 4) == 0) bytesToSkip = 0; if (offset + (planeSize + bytesToSkip * getSizeY()) * no >= in.length()) { // truncated file; imitate LAS AF and return black planes Arrays.fill(buf, (byte) 0); return buf; } in.seek(offset + planeSize * no); in.skipBytes(bytesToSkip * getSizeY() * no); if (bytesToSkip == 0) { readPlane(in, x, y, w, h, buf); } else { in.skipBytes(y * (getSizeX() * bpp + bytesToSkip)); for (int row=0; row<h; row++) { in.skipBytes(x * bpp); in.read(buf, row * w * bpp, w * bpp); in.skipBytes(bpp * (getSizeX() - w - x) + bytesToSkip); } } // color planes are stored in BGR order if (getRGBChannelCount() == 3) { ImageTools.bgrToRgb(buf, isInterleaved(), bytes, getRGBChannelCount()); } return buf; } /* @see loci.formats.IFormatReader#close(boolean) */ public void close(boolean fileOnly) throws IOException { super.close(fileOnly); if (!fileOnly) { offsets = null; realChannel = null; lastChannel = 0; lutNames.clear(); physicalSizeXs.clear(); physicalSizeYs.clear(); descriptions = microscopeModels = serialNumber = null; pinholes = zooms = lensNA = null; zSteps = tSteps = null; expTimes = gains = null; detectorOffsets = null; channelNames = null; detectorModels = voltages = null; exWaves = null; activeDetector = null; immersions = corrections = null; magnification = null; objectiveModels = null; posX = posY = posZ = null; refractiveIndex = null; cutIns = cutOuts = filterModels = null; timestamps = null; laserWavelength = laserIntensity = null; imageROIs = null; alternateCenter = false; imageNames = null; acquiredDate = null; detectorIndexes = null; } } // -- Internal FormatReader API methods -- /* @see loci.formats.FormatReader#initFile(String) */ protected void initFile(String id) throws FormatException, IOException { super.initFile(id); in = new RandomAccessInputStream(id); offsets = new Vector<Long>(); in.order(true); // read the header LOGGER.info("Reading header"); byte checkOne = in.readByte(); in.skipBytes(2); byte checkTwo = in.readByte(); if (checkOne != LIF_MAGIC_BYTE && checkTwo != LIF_MAGIC_BYTE) { throw new FormatException(id + " is not a valid Leica LIF file"); } in.skipBytes(4); // read and parse the XML description if (in.read() != LIF_MEMORY_BYTE) { throw new FormatException("Invalid XML description"); } // number of Unicode characters in the XML block int nc = in.readInt(); String xml = DataTools.stripString(in.readString(nc * 2)); LOGGER.info("Finding image offsets"); while (in.getFilePointer() < in.length()) { LOGGER.debug("Looking for a block at {}; {} blocks read", in.getFilePointer(), offsets.size()); int check = in.readInt(); if (check != LIF_MAGIC_BYTE) { throw new FormatException("Invalid Memory Block: found magic bytes " + check + ", expected " + LIF_MAGIC_BYTE); } in.skipBytes(4); check = in.read(); if (check != LIF_MEMORY_BYTE) { throw new FormatException("Invalid Memory Description: found magic " + "byte " + check + ", expected " + LIF_MEMORY_BYTE); } long blockLength = in.readInt(); if (in.read() != LIF_MEMORY_BYTE) { in.seek(in.getFilePointer() - 5); blockLength = in.readLong(); check = in.read(); if (check != LIF_MEMORY_BYTE) { throw new FormatException("Invalid Memory Description: found magic " + "byte " + check + ", expected " + LIF_MEMORY_BYTE); } } int descrLength = in.readInt() * 2; if (blockLength > 0) { offsets.add(new Long(in.getFilePointer() + descrLength)); } in.seek(in.getFilePointer() + descrLength + blockLength); } initMetadata(xml); xml = null; // correct offsets, if necessary if (offsets.size() > getSeriesCount()) { Long[] storedOffsets = offsets.toArray(new Long[offsets.size()]); offsets.clear(); int index = 0; for (int i=0; i<getSeriesCount(); i++) { setSeries(i); long nBytes = (long) FormatTools.getPlaneSize(this) * getImageCount(); long start = storedOffsets[index]; long end = index == storedOffsets.length - 1 ? in.length() : storedOffsets[index + 1]; while (end - start < nBytes && ((end - start) / nBytes) != 1) { index++; start = storedOffsets[index]; end = index == storedOffsets.length - 1 ? in.length() : storedOffsets[index + 1]; } offsets.add(storedOffsets[index]); index++; } setSeries(0); } } // -- Helper methods -- /** Parses a string of XML and puts the values in a Hashtable. */ private void initMetadata(String xml) throws FormatException, IOException { IMetadata omexml = null; try { ServiceFactory factory = new ServiceFactory(); OMEXMLService service = factory.getInstance(OMEXMLService.class); omexml = service.createOMEXMLMetadata(); } catch (DependencyException exc) { throw new FormatException("Could not create OME-XML store.", exc); } catch (ServiceException exc) { throw new FormatException("Could not create OME-XML store.", exc); } MetadataStore store = makeFilterMetadata(); MetadataLevel level = getMetadataOptions().getMetadataLevel(); // the XML blocks stored in a LIF file are invalid, // because they don't have a root node xml = "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?><LEICA>" + xml + "</LEICA>"; xml = XMLTools.sanitizeXML(xml); translateMetadata(getMetadataRoot(xml)); for (int i=0; i<imageNames.length; i++) { setSeries(i); addSeriesMeta("Image name", imageNames[i]); } setSeries(0); // set up mapping to rearrange channels // for instance, the green channel may be #0, and the red channel may be #1 realChannel = new int[getSeriesCount()][]; int nextLut = 0; for (int i=0; i<getSeriesCount(); i++) { realChannel[i] = new int[core[i].sizeC]; for (int q=0; q<core[i].sizeC; q++) { String lut = lutNames.get(nextLut++).toLowerCase(); if (!CHANNEL_PRIORITIES.containsKey(lut)) lut = ""; realChannel[i][q] = CHANNEL_PRIORITIES.get(lut).intValue(); } int[] sorted = new int[core[i].sizeC]; Arrays.fill(sorted, -1); for (int q=0; q<sorted.length; q++) { int min = Integer.MAX_VALUE; int minIndex = -1; for (int n=0; n<core[i].sizeC; n++) { if (realChannel[i][n] < min && !DataTools.containsValue(sorted, n)) { min = realChannel[i][n]; minIndex = n; } } sorted[q] = minIndex; } } MetadataTools.populatePixels(store, this, true, false); for (int i=0; i<getSeriesCount(); i++) { setSeries(i); String instrumentID = MetadataTools.createLSID("Instrument", i); store.setInstrumentID(instrumentID, i); store.setMicroscopeModel(microscopeModels[i], i); store.setMicroscopeType(getMicroscopeType("Other"), i); String objectiveID = MetadataTools.createLSID("Objective", i, 0); store.setObjectiveID(objectiveID, i, 0); store.setObjectiveLensNA(lensNA[i], i, 0); store.setObjectiveSerialNumber(serialNumber[i], i, 0); if (magnification[i] != null && magnification[i] > 0) { store.setObjectiveNominalMagnification( new PositiveInteger(magnification[i]), i, 0); } store.setObjectiveImmersion(getImmersion(immersions[i]), i, 0); store.setObjectiveCorrection(getCorrection(corrections[i]), i, 0); store.setObjectiveModel(objectiveModels[i], i, 0); if (cutIns[i] != null) { for (int filter=0; filter<cutIns[i].size(); filter++) { String filterID = MetadataTools.createLSID("Filter", i, filter); store.setFilterID(filterID, i, filter); if (filter < filterModels[i].size()) { store.setFilterModel( (String) filterModels[i].get(filter), i, filter); } int channel = filter - (cutIns[i].size() - getEffectiveSizeC()); if (channel >= 0 && channel < getEffectiveSizeC()) { //store.setLightPathEmissionFilterRef(filterID, i, channel, 0); } store.setTransmittanceRangeCutIn( (PositiveInteger) cutIns[i].get(filter), i, filter); store.setTransmittanceRangeCutOut( (PositiveInteger) cutOuts[i].get(filter), i, filter); } } Vector lasers = laserWavelength[i]; Vector laserIntensities = laserIntensity[i]; int nextChannel = 0; if (lasers != null) { int laserIndex = 0; while (laserIndex < lasers.size()) { if ((Integer) lasers.get(laserIndex) == 0) { lasers.removeElementAt(laserIndex); } else { laserIndex++; } } for (int laser=0; laser<lasers.size(); laser++) { String id = MetadataTools.createLSID("LightSource", i, laser); store.setLaserID(id, i, laser); store.setLaserType(LaserType.OTHER, i, laser); store.setLaserLaserMedium(LaserMedium.OTHER, i, laser); Integer wavelength = (Integer) lasers.get(laser); if (wavelength > 0) { store.setLaserWavelength(new PositiveInteger(wavelength), i, laser); } } Vector<Integer> validIntensities = new Vector<Integer>(); for (int laser=0; laser<laserIntensities.size(); laser++) { double intensity = (Double) laserIntensities.get(laser); if (intensity < 100) { validIntensities.add(laser); } } int start = validIntensities.size() - getEffectiveSizeC(); if (start < 0) { start = 0; } boolean noNames = true; for (String name : channelNames[i]) { if (name != null && !name.equals("")) { noNames = false; break; } } for (int k=start; k<validIntensities.size(); k++, nextChannel++) { int index = validIntensities.get(k); double intensity = (Double) laserIntensities.get(index); int laser = index % lasers.size(); Integer wavelength = (Integer) lasers.get(laser); if (wavelength != 0) { while (channelNames != null && nextChannel < getEffectiveSizeC() && ((channelNames[i][nextChannel] == null || channelNames[i][nextChannel].equals("")) && !noNames)) { nextChannel++; } if (nextChannel < getEffectiveSizeC()) { String id = MetadataTools.createLSID("LightSource", i, laser); store.setChannelLightSourceSettingsID(id, i, nextChannel); store.setChannelLightSourceSettingsAttenuation( new PercentFraction((float) intensity / 100f), i, nextChannel); store.setChannelExcitationWavelength( new PositiveInteger(wavelength), i, nextChannel); } } } } store.setImageInstrumentRef(instrumentID, i); store.setImageObjectiveSettingsID(objectiveID, i); store.setImageObjectiveSettingsRefractiveIndex(refractiveIndex[i], i); store.setImageDescription(descriptions[i], i); if (acquiredDate[i] > 0) { store.setImageAcquiredDate(DateTools.convertDate( (long) (acquiredDate[i] * 1000), DateTools.COBOL), i); } store.setImageName(imageNames[i].trim(), i); if (physicalSizeXs.get(i) > 0) { store.setPixelsPhysicalSizeX( new PositiveFloat(physicalSizeXs.get(i)), i); } if (physicalSizeYs.get(i) > 0) { store.setPixelsPhysicalSizeY( new PositiveFloat(physicalSizeYs.get(i)), i); } if (zSteps[i] != null && zSteps[i] > 0) { store.setPixelsPhysicalSizeZ(new PositiveFloat(zSteps[i]), i); } store.setPixelsTimeIncrement(tSteps[i], i); Vector detectors = detectorModels[i]; if (detectors != null) { nextChannel = 0; for (int detector=0; detector<detectors.size(); detector++) { String detectorID = MetadataTools.createLSID("Detector", i, detector); store.setDetectorID(detectorID, i, detector); store.setDetectorModel((String) detectors.get(detector), i, detector); store.setDetectorZoom(zooms[i], i, detector); store.setDetectorType(DetectorType.PMT, i, detector); if (voltages[i] != null && detector < voltages[i].size()) { store.setDetectorVoltage( (Double) voltages[i].get(detector), i, detector); } if (activeDetector[i] != null) { if (detector < activeDetector[i].size() && (Boolean) activeDetector[i].get(detector) && detectorOffsets[i] != null && nextChannel < detectorOffsets[i].size()) { store.setDetectorOffset( (Double) detectorOffsets[i].get(nextChannel++), i, detector); } } } } Vector activeDetectors = activeDetector[i]; int firstDetector = activeDetectors == null ? 0 : activeDetectors.size() - getEffectiveSizeC(); int nextDetector = firstDetector; for (int c=0; c<getEffectiveSizeC(); c++) { if (activeDetectors != null) { while (nextDetector >= 0 && nextDetector < activeDetectors.size() && !(Boolean) activeDetectors.get(nextDetector)) { nextDetector++; } if (nextDetector < activeDetectors.size() && detectors != null && nextDetector - firstDetector < detectors.size()) { String detectorID = MetadataTools.createLSID( "Detector", i, nextDetector - firstDetector); store.setDetectorSettingsID(detectorID, i, c); nextDetector++; if (detectorOffsets[i] != null && c < detectorOffsets[i].size()) { store.setDetectorSettingsOffset( (Double) detectorOffsets[i].get(c), i, c); } if (gains[i] != null) { store.setDetectorSettingsGain(gains[i][c], i, c); } } } if (channelNames[i] != null) { store.setChannelName(channelNames[i][c], i, c); } store.setChannelPinholeSize(pinholes[i], i, c); if (exWaves[i] != null && exWaves[i][c] != null && exWaves[i][c] > 1) { store.setChannelExcitationWavelength( new PositiveInteger(exWaves[i][c]), i, c); } if (expTimes[i] != null) { store.setPlaneExposureTime(expTimes[i][c], i, c); } int channelColor = getChannelColor(realChannel[i][c]); store.setChannelColor(channelColor, i, c); } for (int image=0; image<getImageCount(); image++) { store.setPlanePositionX(posX[i], i, image); store.setPlanePositionY(posY[i], i, image); store.setPlanePositionZ(posZ[i], i, image); if (timestamps[i] != null) { double timestamp = timestamps[i][image]; if (timestamps[i][0] == acquiredDate[i]) { timestamp -= acquiredDate[i]; } else if (timestamp == acquiredDate[i] && image > 0) { timestamp = timestamps[i][0]; } store.setPlaneDeltaT(timestamp, i, image); } } if (imageROIs[i] != null) { for (int roi=0; roi<imageROIs[i].length; roi++) { if (imageROIs[i][roi] != null) { imageROIs[i][roi].storeROI(store, i, roi); } } } } } private Element getMetadataRoot(String xml) throws FormatException, IOException { try { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder parser = factory.newDocumentBuilder(); ByteArrayInputStream s = new ByteArrayInputStream(xml.getBytes()); Element root = parser.parse(s).getDocumentElement(); s.close(); return root; } catch (ParserConfigurationException e) { throw new FormatException(e); } catch (SAXException e) { throw new FormatException(e); } } private void translateMetadata(Element root) throws FormatException { Element realRoot = (Element) root.getChildNodes().item(0); NodeList toPrune = getNodes(realRoot, "LDM_Block_Sequential_Master"); if (toPrune != null) { for (int i=0; i<toPrune.getLength(); i++) { Element prune = (Element) toPrune.item(i); Element parent = (Element) prune.getParentNode(); parent.removeChild(prune); } } NodeList imageNodes = getNodes(realRoot, "Image"); core = new CoreMetadata[imageNodes.getLength()]; acquiredDate = new double[imageNodes.getLength()]; descriptions = new String[imageNodes.getLength()]; laserWavelength = new Vector[imageNodes.getLength()]; laserIntensity = new Vector[imageNodes.getLength()]; timestamps = new double[imageNodes.getLength()][]; activeDetector = new Vector[imageNodes.getLength()]; voltages = new Vector[imageNodes.getLength()]; detectorOffsets = new Vector[imageNodes.getLength()]; serialNumber = new String[imageNodes.getLength()]; lensNA = new Double[imageNodes.getLength()]; magnification = new Integer[imageNodes.getLength()]; immersions = new String[imageNodes.getLength()]; corrections = new String[imageNodes.getLength()]; objectiveModels = new String[imageNodes.getLength()]; posX = new Double[imageNodes.getLength()]; posY = new Double[imageNodes.getLength()]; posZ = new Double[imageNodes.getLength()]; refractiveIndex = new Double[imageNodes.getLength()]; cutIns = new Vector[imageNodes.getLength()]; cutOuts = new Vector[imageNodes.getLength()]; filterModels = new Vector[imageNodes.getLength()]; microscopeModels = new String[imageNodes.getLength()]; detectorModels = new Vector[imageNodes.getLength()]; detectorIndexes = new HashMap[imageNodes.getLength()]; zSteps = new Double[imageNodes.getLength()]; tSteps = new Double[imageNodes.getLength()]; pinholes = new Double[imageNodes.getLength()]; zooms = new Double[imageNodes.getLength()]; expTimes = new Double[imageNodes.getLength()][]; gains = new Double[imageNodes.getLength()][]; channelNames = new String[imageNodes.getLength()][]; exWaves = new Integer[imageNodes.getLength()][]; imageROIs = new ROI[imageNodes.getLength()][]; imageNames = new String[imageNodes.getLength()]; for (int i=0; i<imageNodes.getLength(); i++) { Element image = (Element) imageNodes.item(i); setSeries(i); translateImageNames(image, i); translateImageNodes(image, i); translateAttachmentNodes(image, i); translateScannerSettings(image, i); translateFilterSettings(image, i); translateTimestamps(image, i); translateLaserLines(image, i); translateROIs(image, i); translateSingleROIs(image, i); translateDetectors(image, i); Stack<String> nameStack = new Stack<String>(); HashMap<String, Integer> indexes = new HashMap<String, Integer>(); populateOriginalMetadata(image, nameStack, indexes); indexes.clear(); } setSeries(0); } private void populateOriginalMetadata(Element root, Stack<String> nameStack, HashMap<String, Integer> indexes) { String name = root.getNodeName(); if (root.hasAttributes() && !name.equals("Element") && !name.equals("Attachment") && !name.equals("LMSDataContainerHeader")) { nameStack.push(name); String suffix = root.getAttribute("Identifier"); String value = root.getAttribute("Variant"); if (suffix == null || suffix.trim().length() == 0) { suffix = root.getAttribute("Description"); } StringBuffer key = new StringBuffer(); for (String k : nameStack) { key.append(k); key.append("|"); } if (suffix != null && value != null && suffix.length() > 0 && value.length() > 0 && !suffix.equals("HighInteger") && !suffix.equals("LowInteger")) { Integer i = indexes.get(key.toString() + suffix); String storedKey = key.toString() + suffix + " " + (i == null ? 0 : i); indexes.put(key.toString() + suffix, i == null ? 1 : i + 1); addSeriesMeta(storedKey, value); } else { NamedNodeMap attributes = root.getAttributes(); for (int i=0; i<attributes.getLength(); i++) { Attr attr = (Attr) attributes.item(i); if (!attr.getName().equals("HighInteger") && !attr.getName().equals("LowInteger")) { Integer index = indexes.get(key.toString() + attr.getName()); if (index == null) { index = 0; } String storedKey = key.toString() + attr.getName() + " " + index; indexes.put(key.toString() + attr.getName(), index + 1); addSeriesMeta(storedKey, attr.getValue()); } } } } NodeList children = root.getChildNodes(); for (int i=0; i<children.getLength(); i++) { Object child = children.item(i); if (child instanceof Element) { populateOriginalMetadata((Element) child, nameStack, indexes); } } if (root.hasAttributes() && !name.equals("Element") && !name.equals("Attachment") && !name.equals("LMSDataContainerHeader")) { nameStack.pop(); } } private void translateImageNames(Element imageNode, int image) { Vector<String> names = new Vector<String>(); Element parent = imageNode; while (true) { parent = (Element) parent.getParentNode(); if (parent == null || parent.getNodeName().equals("LEICA")) { break; } if (parent.getNodeName().equals("Element")) { names.add(parent.getAttribute("Name")); } } imageNames[image] = ""; for (int i=names.size() - 2; i>=0; i--) { imageNames[image] += names.get(i); if (i > 0) imageNames[image] += "/"; } } private void translateDetectors(Element imageNode, int image) throws FormatException { NodeList definitions = getNodes(imageNode, "ATLConfocalSettingDefinition"); if (definitions == null) return; Vector<String> channels = new Vector<String>(); int nextChannel = 0; for (int definition=0; definition<definitions.getLength(); definition++) { Element definitionNode = (Element) definitions.item(definition); NodeList detectors = getNodes(definitionNode, "Detector"); if (detectors == null) return; for (int d=0; d<detectors.getLength(); d++) { Element detector = (Element) detectors.item(d); NodeList multibands = getNodes(definitionNode, "MultiBand"); String v = detector.getAttribute("Gain"); Double gain = v == null || v.trim().length() == 0 ? null : new Double(v); v = detector.getAttribute("Offset"); Double offset = v == null || v.trim().length() == 0 ? null : new Double(v); boolean active = "1".equals(detector.getAttribute("IsActive")); if (active) { String c = detector.getAttribute("Channel"); int channel = c == null ? 0 : Integer.parseInt(c); detectorModels[image].add(detectorIndexes[image].get(channel)); Element multiband = null; if (multibands != null) { for (int i=0; i<multibands.getLength(); i++) { Element mb = (Element) multibands.item(i); if (channel == Integer.parseInt(mb.getAttribute("Channel"))) { multiband = mb; break; } } } if (multiband != null) { channels.add(multiband.getAttribute("DyeName")); double cutIn = new Double(multiband.getAttribute("LeftWorld")); double cutOut = new Double(multiband.getAttribute("RightWorld")); cutIns[image].add(new PositiveInteger((int) Math.round(cutIn))); cutOuts[image].add(new PositiveInteger((int) Math.round(cutOut))); } else { channels.add(""); } if (nextChannel < getEffectiveSizeC()) { gains[image][nextChannel] = gain; detectorOffsets[image].add(offset); } nextChannel++; } if (active) { activeDetector[image].add(active); } } } for (int i=0; i<getEffectiveSizeC(); i++) { int index = i + channels.size() - getEffectiveSizeC(); if (index >= 0 && index < channels.size()) { channelNames[image][i] = channels.get(index); } } } private void translateROIs(Element imageNode, int image) throws FormatException { NodeList rois = getNodes(imageNode, "Annotation"); if (rois == null) return; imageROIs[image] = new ROI[rois.getLength()]; for (int r=0; r<rois.getLength(); r++) { Element roiNode = (Element) rois.item(r); ROI roi = new ROI(); String type = roiNode.getAttribute("type"); if (type != null) { roi.type = Integer.parseInt(type); } String color = roiNode.getAttribute("color"); if (color != null) { roi.color = Long.parseLong(color); } roi.name = roiNode.getAttribute("name"); roi.fontName = roiNode.getAttribute("fontName"); roi.fontSize = roiNode.getAttribute("fontSize"); roi.transX = parseDouble(roiNode.getAttribute("transTransX")); roi.transY = parseDouble(roiNode.getAttribute("transTransY")); roi.scaleX = parseDouble(roiNode.getAttribute("transScalingX")); roi.scaleY = parseDouble(roiNode.getAttribute("transScalingY")); roi.rotation = parseDouble(roiNode.getAttribute("transRotation")); String linewidth = roiNode.getAttribute("linewidth"); if (linewidth != null) { try { roi.linewidth = Integer.parseInt(linewidth); } catch (NumberFormatException e) { } } roi.text = roiNode.getAttribute("text"); NodeList vertices = getNodes(roiNode, "Vertex"); if (vertices == null) { continue; } for (int v=0; v<vertices.getLength(); v++) { Element vertex = (Element) vertices.item(v); String xx = vertex.getAttribute("x"); String yy = vertex.getAttribute("y"); if (xx != null) { roi.x.add(parseDouble(xx)); } if (yy != null) { roi.y.add(parseDouble(yy)); } } imageROIs[image][r] = roi; if (getNodes(imageNode, "ROI") != null) { alternateCenter = true; } } } private void translateSingleROIs(Element imageNode, int image) throws FormatException { if (imageROIs[image] != null) return; NodeList children = getNodes(imageNode, "ROI"); if (children == null) return; children = getNodes((Element) children.item(0), "Children"); if (children == null) return; children = getNodes((Element) children.item(0), "Element"); if (children == null) return; imageROIs[image] = new ROI[children.getLength()]; for (int r=0; r<children.getLength(); r++) { NodeList rois = getNodes((Element) children.item(r), "ROISingle"); Element roiNode = (Element) rois.item(0); ROI roi = new ROI(); String type = roiNode.getAttribute("RoiType"); if (type != null) { roi.type = Integer.parseInt(type); } String color = roiNode.getAttribute("Color"); if (color != null) { roi.color = Long.parseLong(color); } Element parent = (Element) roiNode.getParentNode(); parent = (Element) parent.getParentNode(); roi.name = parent.getAttribute("Name"); NodeList vertices = getNodes(roiNode, "P"); double sizeX = physicalSizeXs.get(image); double sizeY = physicalSizeYs.get(image); for (int v=0; v<vertices.getLength(); v++) { Element vertex = (Element) vertices.item(v); String xx = vertex.getAttribute("X"); String yy = vertex.getAttribute("Y"); if (xx != null) { roi.x.add(parseDouble(xx) / sizeX); } if (yy != null) { roi.y.add(parseDouble(yy) / sizeY); } } Element transform = (Element) getNodes(roiNode, "Transformation").item(0); roi.rotation = parseDouble(transform.getAttribute("Rotation")); Element scaling = (Element) getNodes(transform, "Scaling").item(0); roi.scaleX = parseDouble(scaling.getAttribute("XScale")); roi.scaleY = parseDouble(scaling.getAttribute("YScale")); Element translation = (Element) getNodes(transform, "Translation").item(0); roi.transX = parseDouble(translation.getAttribute("X")) / sizeX; roi.transY = parseDouble(translation.getAttribute("Y")) / sizeY; imageROIs[image][r] = roi; } } private void translateLaserLines(Element imageNode, int image) throws FormatException { NodeList aotfLists = getNodes(imageNode, "AotfList"); if (aotfLists == null) return; laserWavelength[image] = new Vector<Integer>(); laserIntensity[image] = new Vector<Double>(); int baseIntensityIndex = 0; for (int channel=0; channel<aotfLists.getLength(); channel++) { Element aotf = (Element) aotfLists.item(channel); NodeList laserLines = getNodes(aotf, "LaserLineSetting"); if (laserLines == null) return; for (int laser=0; laser<laserLines.getLength(); laser++) { Element laserLine = (Element) laserLines.item(laser); String lineIndex = laserLine.getAttribute("LineIndex"); String qual = laserLine.getAttribute("Qualifier"); int index = lineIndex == null ? 0 : Integer.parseInt(lineIndex); int qualifier = qual == null ? 0: Integer.parseInt(qual); index += (2 - (qualifier / 10)); if (index < 0) index = 0; Integer wavelength = new Integer(laserLine.getAttribute("LaserLine")); if (index < laserWavelength[image].size()) { laserWavelength[image].setElementAt(wavelength, index); } else { for (int i=laserWavelength[image].size(); i<index; i++) { laserWavelength[image].add(new Integer(0)); } laserWavelength[image].add(wavelength); } String intensity = laserLine.getAttribute("IntensityDev"); double realIntensity = intensity == null ? 0d : new Double(intensity); realIntensity = 100d - realIntensity; int realIndex = baseIntensityIndex + index; if (realIndex < laserIntensity[image].size()) { laserIntensity[image].setElementAt(realIntensity, realIndex); } else { while (realIndex < laserIntensity[image].size()) { laserIntensity[image].add(100d); } laserIntensity[image].add(realIntensity); } } baseIntensityIndex += laserWavelength[image].size(); } } private void translateTimestamps(Element imageNode, int image) throws FormatException { NodeList timestampNodes = getNodes(imageNode, "TimeStamp"); if (timestampNodes == null) return; timestamps[image] = new double[getImageCount()]; if (timestampNodes != null) { for (int stamp=0; stamp<timestampNodes.getLength(); stamp++) { if (stamp < getImageCount()) { Element timestamp = (Element) timestampNodes.item(stamp); String stampHigh = timestamp.getAttribute("HighInteger"); String stampLow = timestamp.getAttribute("LowInteger"); long high = stampHigh == null ? 0 : Long.parseLong(stampHigh); long low = stampLow == null ? 0 : Long.parseLong(stampLow); long ms = DateTools.getMillisFromTicks(high, low); timestamps[image][stamp] = ms / 1000.0; } } } acquiredDate[image] = timestamps[image][0]; NodeList relTimestampNodes = getNodes(imageNode, "RelTimeStamp"); if (relTimestampNodes != null) { for (int stamp=0; stamp<relTimestampNodes.getLength(); stamp++) { if (stamp < getImageCount()) { Element timestamp = (Element) relTimestampNodes.item(stamp); timestamps[image][stamp] = new Double(timestamp.getAttribute("Time")); } } } } private void translateFilterSettings(Element imageNode, int image) throws FormatException { NodeList filterSettings = getNodes(imageNode, "FilterSettingRecord"); if (filterSettings == null) return; activeDetector[image] = new Vector<Boolean>(); voltages[image] = new Vector<Double>(); detectorOffsets[image] = new Vector<Double>(); cutIns[image] = new Vector<PositiveInteger>(); cutOuts[image] = new Vector<PositiveInteger>(); filterModels[image] = new Vector<String>(); detectorIndexes[image] = new HashMap<Integer, String>(); for (int i=0; i<filterSettings.getLength(); i++) { Element filterSetting = (Element) filterSettings.item(i); String object = filterSetting.getAttribute("ObjectName"); String attribute = filterSetting.getAttribute("Attribute"); String objectClass = filterSetting.getAttribute("ClassName"); String variant = filterSetting.getAttribute("Variant"); String data = filterSetting.getAttribute("Data"); if (attribute.equals("NumericalAperture")) { lensNA[image] = new Double(variant); } else if (attribute.equals("OrderNumber")) { serialNumber[image] = variant; } else if (objectClass.equals("CDetectionUnit")) { if (attribute.equals("State")) { int channel = getChannelIndex(filterSetting); if (channel < 0) continue; detectorIndexes[image].put(new Integer(data), object); activeDetector[image].add(variant.equals("Active")); } else if (attribute.equals("HighVoltage")) { int channel = getChannelIndex(filterSetting); if (channel < 0) continue; if (channel < voltages[image].size()) { voltages[image].setElementAt(new Double(variant), channel); } else { while (channel > voltages[image].size()) { voltages[image].add(new Double(0)); } voltages[image].add(new Double(variant)); } } else if (attribute.equals("VideoOffset")) { int channel = getChannelIndex(filterSetting); if (channel < 0) continue; detectorOffsets[image].add(new Double(variant)); } } else if (attribute.equals("Objective")) { StringTokenizer tokens = new StringTokenizer(variant, " "); boolean foundMag = false; StringBuffer model = new StringBuffer(); while (!foundMag) { String token = tokens.nextToken(); int x = token.indexOf("x"); if (x != -1) { foundMag = true; int mag = (int) Double.parseDouble(token.substring(0, x)); String na = token.substring(x + 1); magnification[image] = mag; lensNA[image] = new Double(na); } else { model.append(token); model.append(" "); } } String immersion = "Other"; if (tokens.hasMoreTokens()) { immersion = tokens.nextToken(); if (immersion == null || immersion.trim().equals("")) { immersion = "Other"; } } immersions[image] = immersion; String correction = "Other"; if (tokens.hasMoreTokens()) { correction = tokens.nextToken(); if (correction == null || correction.trim().equals("")) { correction = "Other"; } } corrections[image] = correction; objectiveModels[image] = model.toString().trim(); } else if (attribute.equals("RefractionIndex")) { refractiveIndex[image] = new Double(variant); } else if (attribute.equals("XPos")) { posX[image] = new Double(variant); } else if (attribute.equals("YPos")) { posY[image] = new Double(variant); } else if (attribute.equals("ZPos")) { posZ[image] = new Double(variant); } else if (objectClass.equals("CSpectrophotometerUnit")) { Integer v = null; try { v = new Integer((int) Double.parseDouble(variant)); } catch (NumberFormatException e) { } String description = filterSetting.getAttribute("Description"); if (description.endsWith("(left)")) { filterModels[image].add(object); if (v != null) { cutIns[image].add(new PositiveInteger(v)); } } else if (description.endsWith("(right)")) { if (v != null) { cutOuts[image].add(new PositiveInteger(v)); } } } } } private void translateScannerSettings(Element imageNode, int image) throws FormatException { NodeList scannerSettings = getNodes(imageNode, "ScannerSettingRecord"); if (scannerSettings == null) return; expTimes[image] = new Double[getEffectiveSizeC()]; gains[image] = new Double[getEffectiveSizeC()]; channelNames[image] = new String[getEffectiveSizeC()]; exWaves[image] = new Integer[getEffectiveSizeC()]; detectorModels[image] = new Vector<String>(); for (int i=0; i<scannerSettings.getLength(); i++) { Element scannerSetting = (Element) scannerSettings.item(i); String id = scannerSetting.getAttribute("Identifier"); if (id == null) id = ""; String suffix = scannerSetting.getAttribute("Identifier"); String value = scannerSetting.getAttribute("Variant"); if (id.equals("SystemType")) { microscopeModels[image] = value; } else if (id.equals("dblPinhole")) { pinholes[image] = Double.parseDouble(value) * 1000000; } else if (id.equals("dblZoom")) { zooms[image] = new Double(value); } else if (id.equals("dblStepSize")) { zSteps[image] = Double.parseDouble(value) * 1000000; } else if (id.equals("nDelayTime_s")) { tSteps[image] = new Double(value); } else if (id.equals("CameraName")) { detectorModels[image].add(value); } else if (id.equals("eDirectional")) { addSeriesMeta("Reverse X orientation", value.equals("1")); } else if (id.equals("eDirectionalY")) { addSeriesMeta("Reverse Y orientation", value.equals("1")); } else if (id.indexOf("WFC") == 1) { int c = 0; try { c = Integer.parseInt(id.replaceAll("\\D", "")); } catch (NumberFormatException e) { } if (c < 0 || c >= getEffectiveSizeC()) { continue; } if (id.endsWith("ExposureTime")) { expTimes[image][c] = new Double(value); } else if (id.endsWith("Gain")) { gains[image][c] = new Double(value); } else if (id.endsWith("WaveLength")) { Integer exWave = new Integer(value); if (exWave > 0) { exWaves[image][c] = exWave; } } // NB: "UesrDefName" is not a typo. else if (id.endsWith("UesrDefName") && !value.equals("None")) { channelNames[image][c] = value; } } } } private void translateAttachmentNodes(Element imageNode, int image) throws FormatException { NodeList attachmentNodes = getNodes(imageNode, "Attachment"); if (attachmentNodes == null) return; for (int i=0; i<attachmentNodes.getLength(); i++) { Element attachment = (Element) attachmentNodes.item(i); if ("ContextDescription".equals(attachment.getAttribute("Name"))) { descriptions[image] = attachment.getAttribute("Content"); } } } private void translateImageNodes(Element imageNode, int i) throws FormatException { core[i] = new CoreMetadata(); core[i].orderCertain = true; core[i].metadataComplete = true; core[i].littleEndian = true; core[i].falseColor = true; NodeList channels = getChannelDescriptionNodes(imageNode); NodeList dimensions = getDimensionDescriptionNodes(imageNode); HashMap<Long, String> bytesPerAxis = new HashMap<Long, String>(); Double physicalSizeX = null; Double physicalSizeY = null; core[i].sizeC = channels.getLength(); for (int ch=0; ch<channels.getLength(); ch++) { Element channel = (Element) channels.item(ch); lutNames.add(channel.getAttribute("LUTName")); String bytesInc = channel.getAttribute("BytesInc"); long bytes = bytesInc == null ? 0 : Long.parseLong(bytesInc); if (bytes > 0) { bytesPerAxis.put(bytes, "C"); } } int extras = 1; for (int dim=0; dim<dimensions.getLength(); dim++) { Element dimension = (Element) dimensions.item(dim); int id = Integer.parseInt(dimension.getAttribute("DimID")); int len = Integer.parseInt(dimension.getAttribute("NumberOfElements")); long nBytes = Long.parseLong(dimension.getAttribute("BytesInc")); Double physicalLen = new Double(dimension.getAttribute("Length")); String unit = dimension.getAttribute("Unit"); physicalLen /= len; if (unit.equals("Ks")) { physicalLen /= 1000; } else if (unit.equals("m")) { physicalLen *= 1000000; } switch (id) { case 1: // X axis core[i].sizeX = len; core[i].rgb = (nBytes % 3) == 0; if (core[i].rgb) nBytes /= 3; core[i].pixelType = FormatTools.pixelTypeFromBytes((int) nBytes, false, true); physicalSizeX = physicalLen; break; case 2: // Y axis if (core[i].sizeY != 0) { if (core[i].sizeZ == 1) { core[i].sizeZ = len; bytesPerAxis.put(nBytes, "Z"); } else if (core[i].sizeT == 1) { core[i].sizeT = len; bytesPerAxis.put(nBytes, "T"); } } else { core[i].sizeY = len; physicalSizeY = physicalLen; } break; case 3: // Z axis if (core[i].sizeY == 0) { // XZ scan - swap Y and Z core[i].sizeY = len; core[i].sizeZ = 1; bytesPerAxis.put(nBytes, "Y"); physicalSizeY = physicalLen; } else { core[i].sizeZ = len; bytesPerAxis.put(nBytes, "Z"); } break; case 4: // T axis if (core[i].sizeY == 0) { // XT scan - swap Y and T core[i].sizeY = len; core[i].sizeT = 1; bytesPerAxis.put(nBytes, "Y"); physicalSizeY = physicalLen; } else { core[i].sizeT = len; bytesPerAxis.put(nBytes, "T"); } break; default: extras *= len; } } physicalSizeXs.add(physicalSizeX); physicalSizeYs.add(physicalSizeY); if (extras > 1) { if (core[i].sizeZ == 1) core[i].sizeZ = extras; else { if (core[i].sizeT == 0) core[i].sizeT = extras; else core[i].sizeT *= extras; } } if (core[i].sizeC == 0) core[i].sizeC = 1; if (core[i].sizeZ == 0) core[i].sizeZ = 1; if (core[i].sizeT == 0) core[i].sizeT = 1; core[i].interleaved = core[i].rgb; core[i].indexed = !core[i].rgb; core[i].imageCount = core[i].sizeZ * core[i].sizeT; if (!core[i].rgb) core[i].imageCount *= core[i].sizeC; Long[] bytes = bytesPerAxis.keySet().toArray(new Long[0]); Arrays.sort(bytes); core[i].dimensionOrder = "XY"; for (Long nBytes : bytes) { String axis = bytesPerAxis.get(nBytes); if (core[i].dimensionOrder.indexOf(axis) == -1) { core[i].dimensionOrder += axis; } } if (core[i].dimensionOrder.indexOf("Z") == -1) { core[i].dimensionOrder += "Z"; } if (core[i].dimensionOrder.indexOf("C") == -1) { core[i].dimensionOrder += "C"; } if (core[i].dimensionOrder.indexOf("T") == -1) { core[i].dimensionOrder += "T"; } } private NodeList getNodes(Element root, String nodeName) { NodeList nodes = root.getElementsByTagName(nodeName); if (nodes.getLength() == 0) { NodeList children = root.getChildNodes(); for (int i=0; i<children.getLength(); i++) { Object child = children.item(i); if (child instanceof Element) { NodeList childNodes = getNodes((Element) child, nodeName); if (childNodes != null) { return childNodes; } } } return null; } else return nodes; } private Element getImageDescription(Element root) { return (Element) root.getElementsByTagName("ImageDescription").item(0); } private NodeList getChannelDescriptionNodes(Element root) { Element imageDescription = getImageDescription(root); Element channels = (Element) imageDescription.getElementsByTagName("Channels").item(0); return channels.getElementsByTagName("ChannelDescription"); } private NodeList getDimensionDescriptionNodes(Element root) { Element imageDescription = getImageDescription(root); Element channels = (Element) imageDescription.getElementsByTagName("Dimensions").item(0); return channels.getElementsByTagName("DimensionDescription"); } private int getChannelIndex(Element filterSetting) { String data = filterSetting.getAttribute("data"); if (data == null || data.equals("")) { data = filterSetting.getAttribute("Data"); } int channel = data == null || data.equals("") ? 0 : Integer.parseInt(data); if (channel < 0) return -1; return channel - 1; } // -- Helper class -- class ROI { // -- Constants -- public static final int TEXT = 512; public static final int SCALE_BAR = 8192; public static final int POLYGON = 32; public static final int RECTANGLE = 16; public static final int LINE = 256; public static final int ARROW = 2; // -- Fields -- public int type; public Vector<Double> x = new Vector<Double>(); public Vector<Double> y = new Vector<Double>(); // center point of the ROI public double transX, transY; // transformation parameters public double scaleX, scaleY; public double rotation; public long color; public int linewidth; public String text; public String fontName; public String fontSize; public String name; private boolean normalized = false; // -- ROI API methods -- public void storeROI(MetadataStore store, int series, int roi) { MetadataLevel level = getMetadataOptions().getMetadataLevel(); if (level == MetadataLevel.NO_OVERLAYS || level == MetadataLevel.MINIMUM) { return; } // keep in mind that vertices are given relative to the center // point of the ROI and the transX/transY values are relative to // the center point of the image String roiID = MetadataTools.createLSID("ROI", roi); store.setImageROIRef(roiID, series, roi); store.setROIID(roiID, roi); store.setTextID(MetadataTools.createLSID("Shape", roi, 0), roi, 0); if (text == null) { text = name; } store.setTextValue(text, roi, 0); if (fontSize != null) { try { int size = (int) Double.parseDouble(fontSize); store.setTextFontSize(new NonNegativeInteger(size), roi, 0); } catch (NumberFormatException e) { } } store.setTextStrokeWidth(new Double(linewidth), roi, 0); if (!normalized) normalize(); double cornerX = x.get(0).doubleValue(); double cornerY = y.get(0).doubleValue(); store.setTextX(cornerX, roi, 0); store.setTextY(cornerY, roi, 0); int centerX = (core[series].sizeX / 2) - 1; int centerY = (core[series].sizeY / 2) - 1; double roiX = centerX + transX; double roiY = centerY + transY; if (alternateCenter) { roiX = transX - 2 * cornerX; roiY = transY - 2 * cornerY; } // TODO : rotation/scaling not populated String shapeID = MetadataTools.createLSID("Shape", roi, 1); switch (type) { case POLYGON: StringBuffer points = new StringBuffer(); for (int i=0; i<x.size(); i++) { points.append(x.get(i).doubleValue() + roiX); points.append(","); points.append(y.get(i).doubleValue() + roiY); if (i < x.size() - 1) points.append(" "); } store.setPolylineID(shapeID, roi, 1); store.setPolylinePoints(points.toString(), roi, 1); store.setPolylineClosed(Boolean.TRUE, roi, 1); break; case TEXT: case RECTANGLE: store.setRectangleID(shapeID, roi, 1); store.setRectangleX(roiX - Math.abs(cornerX), roi, 1); store.setRectangleY(roiY - Math.abs(cornerY), roi, 1); double width = 2 * Math.abs(cornerX); double height = 2 * Math.abs(cornerY); store.setRectangleWidth(width, roi, 1); store.setRectangleHeight(height, roi, 1); break; case SCALE_BAR: case ARROW: case LINE: store.setLineID(shapeID, roi, 1); store.setLineX1(roiX + x.get(0), roi, 1); store.setLineY1(roiY + y.get(0), roi, 1); store.setLineX2(roiX + x.get(1), roi, 1); store.setLineY2(roiY + y.get(1), roi, 1); break; } } // -- Helper methods -- /** * Vertices and transformation values are not stored in pixel coordinates. * We need to convert them from physical coordinates to pixel coordinates * so that they can be stored in a MetadataStore. */ private void normalize() { if (normalized) return; // coordinates are in meters transX *= 1000000; transY *= 1000000; transX *= 1; transY *= 1; for (int i=0; i<x.size(); i++) { double coordinate = x.get(i).doubleValue() * 1000000; coordinate *= 1; x.setElementAt(coordinate, i); } for (int i=0; i<y.size(); i++) { double coordinate = y.get(i).doubleValue() * 1000000; coordinate *= 1; y.setElementAt(coordinate, i); } normalized = true; } } private double parseDouble(String number) { if (number != null) { number = number.replaceAll(",", "."); try { return Double.parseDouble(number); } catch (NumberFormatException e) { } } return 0; } private int getChannelColor(int colorCode) { switch (colorCode) { case 0: return 0xff0000; case 1: return 0xff00; case 2: return 0xff; case 3: return 0xffff; case 4: return 0xff00ff; case 5: return 0xffff00; } return 0xffffff; } }