diff --git a/components/formats-api/src/loci/formats/readers.txt b/components/formats-api/src/loci/formats/readers.txt index c98b7b21164..9672f31e4e2 100644 --- a/components/formats-api/src/loci/formats/readers.txt +++ b/components/formats-api/src/loci/formats/readers.txt @@ -36,7 +36,7 @@ loci.formats.in.GatanDM2Reader # dm2 loci.formats.in.ImarisReader # ims loci.formats.in.OpenlabRawReader # raw loci.formats.in.OMEXMLReader # ome -loci.formats.in.LIFReader # lif +loci.formats.in.LIFDelegateReader # lif loci.formats.in.AVIReader # avi loci.formats.in.PictReader # pict, pct loci.formats.in.SDTReader # sdt diff --git a/components/formats-gpl/src/loci/formats/in/LIFDelegateReader.java b/components/formats-gpl/src/loci/formats/in/LIFDelegateReader.java new file mode 100644 index 00000000000..f7f065c43b9 --- /dev/null +++ b/components/formats-gpl/src/loci/formats/in/LIFDelegateReader.java @@ -0,0 +1,56 @@ +package loci.formats.in; + +import java.io.IOException; +import java.util.ArrayList; + +import loci.formats.CoreMetadata; +import loci.formats.DelegateReader; +import loci.formats.FormatException; +import loci.formats.FormatTools; +import loci.formats.IFormatReader; + +public class LIFDelegateReader extends DelegateReader { + + public LIFDelegateReader() { + super("Leica Image File Format", "lif"); + nativeReader = new LMSLIFReader(); + legacyReader = new LIFReader(); + nativeReaderInitialized = false; + legacyReaderInitialized = false; + suffixNecessary = false; + hasCompanionFiles = true; + domains = new String[] { FormatTools.LM_DOMAIN }; + useLegacy = false; + } + + @Override + public byte[] openBytes(int no, byte[] buf, int x, int y, int w, int h) + throws FormatException, IOException + { + IFormatReader reader = isLegacy() ? legacyReader : nativeReader; + return reader.openBytes(no, buf, x, y, w, h); + } + + @Override + public void setId(String id) throws FormatException, IOException { + IFormatReader reader = isLegacy() ? legacyReader : nativeReader; + reader.setId(id); + + if (isLegacy()) + legacyReaderInitialized = true; + else + nativeReaderInitialized = true; + + currentId = reader.getCurrentFile(); + core = new ArrayList(reader.getCoreMetadataList()); + metadata = reader.getGlobalMetadata(); + metadataStore = reader.getMetadataStore(); + } + + /* @see loci.formats.IFormatReader#isThisType(String, boolean) */ + @Override + public boolean isThisType(String name, boolean open) { + IFormatReader reader = isLegacy() ? legacyReader : nativeReader; + return reader.isThisType(name, open); + } +} diff --git a/components/formats-gpl/src/loci/formats/in/LMSLIFReader.java b/components/formats-gpl/src/loci/formats/in/LMSLIFReader.java new file mode 100644 index 00000000000..c6dfd165c1b --- /dev/null +++ b/components/formats-gpl/src/loci/formats/in/LMSLIFReader.java @@ -0,0 +1,499 @@ +/* + * #%L + * OME Bio-Formats package for reading and converting biological file formats. + * %% + * Copyright (C) 2005 - 2017 Open Microscopy Environment: + * - Board of Regents of the University of Wisconsin-Madison + * - Glencoe Software, Inc. + * - University of Dundee + * %% + * 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, see + * . + * #L% + */ + +package loci.formats.in; + +import java.io.IOException; +import java.util.Arrays; +import java.util.ArrayList; +import java.util.List; + +import loci.common.DataTools; +import loci.common.Location; +import loci.common.RandomAccessInputStream; +import loci.common.services.DependencyException; +import loci.common.services.ServiceException; +import loci.common.services.ServiceFactory; +import loci.common.xml.XMLTools; +import loci.formats.FormatException; +import loci.formats.FormatTools; +import loci.formats.ImageTools; +import loci.formats.in.LeicaMicrosystemsMetadata.LMSFileReader; +import loci.formats.in.LeicaMicrosystemsMetadata.helpers.Tuple; +import loci.formats.in.LeicaMicrosystemsMetadata.model.Channel; +import loci.formats.in.LeicaMicrosystemsMetadata.xml.LMSImageXmlDocument; +import loci.formats.in.LeicaMicrosystemsMetadata.xml.LifImageXmlDocument; +import loci.formats.in.LeicaMicrosystemsMetadata.xml.LifXmlDocument; +import loci.formats.services.OMEXMLService; + +/** + * LMSLIFReader is the new file format reader for Leica LIF files. + * + * @author Melissa Linkert melissa at glencoesoftware.com + * @author Constanze Wendlandt constanze.wendlandt at leica-microsystems.com + */ +public class LMSLIFReader extends LMSFileReader { + + // -- Constants -- + + public static final String OLD_PHYSICAL_SIZE_KEY = "leicalif.old_physical_size"; + public static final boolean OLD_PHYSICAL_SIZE_DEFAULT = false; + + public static final byte LIF_MAGIC_BYTE = 0x70; + public static final byte LIF_MEMORY_BYTE = 0x2a; + + /** The encoding used in this file. */ + private static final String ENCODING = "ISO-8859-1"; + + private static final String LOGO_FILE = "LeicaLogo.jpg"; + private static final String STYLESHEET_FILE = "LASAF_CIP.xsl"; + + // -- Fields -- + + /** Offsets to memory blocks, paired with their corresponding description. */ + private List offsets; + private List memoryBlockIds; + + private int lastChannelLutColorIndex = 0; + private long endPointer; + List imageXmls = new ArrayList<>(); + + // -- Constructor -- + + /** Constructs a new Leica LIF reader. */ + public LMSLIFReader() { + super("Leica Image File Format", "lif"); + suffixNecessary = false; + hasCompanionFiles = true; + domains = new String[] { FormatTools.LM_DOMAIN }; + } + + // -- IFormatReader API methods -- + + /* @see loci.formats.IFormatReader#getOptimalTileHeight() */ + @Override + public int getOptimalTileHeight() { + FormatTools.assertId(currentId, true, 1); + return getSizeY(); + } + + /* @see loci.formats.IFormatReader#isThisType(RandomAccessInputStream) */ + @Override + public boolean isThisType(RandomAccessInputStream stream) throws IOException { + // return false; + final int blockLen = 1; + if (!FormatTools.validStream(stream, blockLen, true)) + return false; + if (stream.read() != LIF_MAGIC_BYTE) + return false; + stream.skipBytes(7); + if (stream.readByte() != LIF_MEMORY_BYTE) + return false; + int nc = stream.readInt(); + String desc = DataTools.stripString(stream.readString(nc * 2)); + if (desc.equals("LMS_Object_File")) + return false; // LOF file + return true; + } + + /* @see loci.formats.IFormatReader#get8BitLookupTable() */ + @Override + public byte[][] get8BitLookupTable() { + FormatTools.assertId(currentId, true, 1); + if (getPixelType() != FormatTools.UINT8 || !isIndexed()) + return null; + + if (lastChannelLutColorIndex < Channel.RED || lastChannelLutColorIndex > Channel.GREY) { + return null; + } + + byte[][] lut = new byte[3][256]; + for (int i = 0; i < 256; i++) { + switch (lastChannelLutColorIndex) { + case Channel.RED: + lut[0][i] = (byte) (i & 0xff); + break; + case Channel.GREEN: + lut[1][i] = (byte) (i & 0xff); + break; + case Channel.BLUE: + lut[2][i] = (byte) (i & 0xff); + break; + case Channel.CYAN: + lut[1][i] = (byte) (i & 0xff); + lut[2][i] = (byte) (i & 0xff); + break; + case Channel.MAGENTA: + lut[0][i] = (byte) (i & 0xff); + lut[2][i] = (byte) (i & 0xff); + break; + case Channel.YELLOW: + lut[0][i] = (byte) (i & 0xff); + lut[1][i] = (byte) (i & 0xff); + break; + case Channel.GREY: + default: + 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() */ + @Override + public short[][] get16BitLookupTable() { + FormatTools.assertId(currentId, true, 1); + if (getPixelType() != FormatTools.UINT16 || !isIndexed()) + return null; + + if (lastChannelLutColorIndex < Channel.RED || lastChannelLutColorIndex > Channel.GREY) { + return null; + } + + short[][] lut = new short[3][65536]; + for (int i = 0; i < 65536; i++) { + switch (lastChannelLutColorIndex) { + case Channel.RED: + lut[0][i] = (short) (i & 0xffff); + break; + case Channel.GREEN: + lut[1][i] = (short) (i & 0xffff); + break; + case Channel.BLUE: + lut[2][i] = (short) (i & 0xffff); + break; + case Channel.CYAN: + lut[1][i] = (short) (i & 0xffff); + lut[2][i] = (short) (i & 0xffff); + break; + case Channel.MAGENTA: + lut[0][i] = (short) (i & 0xffff); + lut[2][i] = (short) (i & 0xffff); + break; + case Channel.YELLOW: + lut[0][i] = (short) (i & 0xffff); + lut[1][i] = (short) (i & 0xffff); + break; + case Channel.GREY: + default: + 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) + */ + @Override + 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); + lastChannelLutColorIndex = metadataTranslators.get(getTileIndex(series)).dimensionStore.channels.get(pos[1]).lutColorIndex; + } + + int index = getTileIndex(series); + if (index >= offsets.size()) { + // truncated file; imitate LAS AF and return black planes + Arrays.fill(buf, (byte) 0); + return buf; + } + + long offset = offsets.get(index).longValue(); + int bytes = FormatTools.getBytesPerPixel(getPixelType()); + int bpp = bytes * getRGBChannelCount(); + + long planeSize = (long) getSizeX() * getSizeY() * bpp; + long nextOffset = index + 1 < offsets.size() ? offsets.get(index + 1).longValue() : endPointer; + long bytesToSkip = 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; + } + + seekStartOfPlane(no, offset, planeSize); + + if (bytesToSkip == 0) { + readPlane(in, x, y, w, h, buf); + } else { + in.skipBytes(bytesToSkip * getSizeY() * no); + 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); + } + } + + if (getRGBChannelCount() == 3 && metadataTranslators.get(index).dimensionStore.inverseRgb) { + ImageTools.bgrToRgb(buf, isInterleaved(), bytes, getRGBChannelCount()); + } + + return buf; + } + + private void seekStartOfPlane(int no, long dataOffset, long planeSize) + throws IOException { + int index = getTileIndex(series); + long posInFile; + + int numberOfTiles = metadataTranslators.get(index).dimensionStore.tileCount; + if (numberOfTiles > 1) { + // LAS AF treats tiles just like any other dimension, while we do not. + // Hence we need to take the tiles into account for a frame's position. + long bytesIncPerTile = metadataTranslators.get(index).dimensionStore.tileBytesInc; + long framesPerTile = bytesIncPerTile / planeSize; + + if (framesPerTile > Integer.MAX_VALUE) { + throw new IOException("Could not read frame due to int overflow"); + } + + int noOutsideTiles = no / (int) framesPerTile; + int noInsideTiles = no % (int) framesPerTile; + + int tile = series; + for (int i = 0; i < index; i++) { + tile -= metadataTranslators.get(i).dimensionStore.tileCount; + } + + posInFile = dataOffset; + posInFile += noOutsideTiles * bytesIncPerTile * numberOfTiles; + posInFile += tile * bytesIncPerTile; + posInFile += noInsideTiles * planeSize; + } else { + posInFile = dataOffset + no * planeSize; + } + + // seek instead of skipBytes to prevent dangerous int cast + in.seek(posInFile); + } + + /* @see loci.formats.IFormatReader#getSeriesUsedFiles(boolean) */ + @Override + public String[] getSeriesUsedFiles(boolean noPixels) { + FormatTools.assertId(currentId, true, 1); + final List files = new ArrayList(); + files.add(currentId); + + Location currentFile = new Location(currentId).getAbsoluteFile(); + Location parent = currentFile.getParentFile(); + if (parent != null && getSeries() < metadataTranslators.size()) { + // look for an XML file with the same name as this series + Location xmlFile = new Location(parent, metadataTranslators.get(getSeries()).imageDetails.originalImageName.trim() + ".xml"); + if (xmlFile.exists()) { + files.add(xmlFile.getAbsolutePath()); + } + + Location logoFile = new Location(parent, LOGO_FILE); + if (logoFile.exists()) { + files.add(logoFile.getAbsolutePath()); + } + + Location stylesheetFile = new Location(parent, STYLESHEET_FILE); + if (stylesheetFile.exists()) { + files.add(stylesheetFile.getAbsolutePath()); + } + } + + return files.toArray(new String[files.size()]); + } + + /* @see loci.formats.IFormatReader#close(boolean) */ + @Override + public void close(boolean fileOnly) throws IOException { + super.close(fileOnly); + if (!fileOnly) { + offsets = null; + lastChannelLutColorIndex = 0; + endPointer = 0; + } + } + + // -- Internal FormatReader API methods -- + + /* @see loci.formats.FormatReader#getAvailableOptions() */ + @Override + protected ArrayList getAvailableOptions() { + ArrayList optionsList = super.getAvailableOptions(); + optionsList.add(OLD_PHYSICAL_SIZE_KEY); + return optionsList; + } + + /* @see loci.formats.FormatReader#initFile(String) */ + @Override + protected void initFile(String id) throws FormatException, IOException { + super.initFile(id); + in = new RandomAccessInputStream(id); + in.setEncoding(ENCODING); + offsets = new ArrayList(); + memoryBlockIds = new ArrayList(); + + in.order(true); + + // read the header + + LOGGER.info("Reading header"); + + // --- 1: XML --- + // 4 bytes: test value 0x70 + if (in.readInt() != LIF_MAGIC_BYTE) + throw new FormatException(id + " is not a valid Leica LIF file"); + + // 4 bytes: length of the following binary chunk to read (1.1). we don't use it + in.skipBytes(4); + + // --- 1.1: XML content --- + // 1 byte: test value 0x2A + if (in.readByte() != LIF_MEMORY_BYTE) + throw new FormatException(id + " is not a valid Leica LIF file"); + + // 4 bytes: number of unicode characters in the XML block (NC) + int nc = in.readInt(); + // NC * 2 bytes: XML object description + String xml = DataTools.stripString(in.readString(nc * 2)); + + LOGGER.info("Finding image offsets"); + + // --- 2: Object memory block(s) --- + while (in.getFilePointer() < in.length()) { + LOGGER.debug("Looking for a block at {}; {} blocks read", + in.getFilePointer(), offsets.size()); + + // 4 bytes: test value 0x70 + int check = in.readInt(); + if (check != LIF_MAGIC_BYTE) { + if (check == 0 && offsets.size() > 0) { + // newer .lif file; the remainder of the file is all 0s + endPointer = in.getFilePointer(); + break; + } + throw new FormatException("Invalid Memory Block: found magic bytes " + + check + ", expected " + LIF_MAGIC_BYTE); + } + + // 4 bytes: length of the following binary chunk to read (2.1). we don't use it + in.skipBytes(4); + + // --- 2.1: memory description --- + + // 1 byte: test value 0x2A + byte checkByte = in.readByte(); + if (checkByte != LIF_MEMORY_BYTE) { + throw new FormatException("Invalid Memory Description: found magic " + + "byte " + checkByte + ", expected " + LIF_MEMORY_BYTE); + } + + // LIF version 1: 4 bytes: size of memory (2.2) + // LIF version 2: 8 bytes: size of memory (2.2) + // 1 byte: test value 0x2A + + 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); + } + } + + // 4 bytes: number of unicode characters + int descrLength = in.readInt(); + + // NC * 2 bytes: XML object description + String description = DataTools.stripString(in.readString(descrLength * 2)); + + memoryBlockIds.add(description); + offsets.add(in.getFilePointer()); + + in.seek(in.getFilePointer() + blockLength); + } + + String name = id.replaceAll("^.*[\\/\\\\]", "").replace(".lof", ""); + initMetadata(xml, name); + xml = null; + + if (endPointer == 0) { + endPointer = in.length(); + } + + // remove offsets of unused memory blocks (e.g. frame properties) + List temp = new ArrayList<>(); + + for (LifImageXmlDocument imageXml : imageXmls){ + String memoryBlockId = imageXml.getMemoryBlockId(); + for (int i = 0; i < memoryBlockIds.size(); i++){ + if (memoryBlockIds.get(i).equals(memoryBlockId)){ + temp.add(offsets.get(i)); + break; + } + } + } + + offsets = temp; + } + + // -- LeicaFileReader methods -- + @Override + public ImageFormat getImageFormat() { + return ImageFormat.LIF; + } + + // -- Helper methods -- + + /** Parses a string of XML and puts the values in a Hashtable. */ + private void initMetadata(String xml, String name) throws FormatException, IOException { + try { + ServiceFactory factory = new ServiceFactory(); + OMEXMLService service = factory.getInstance(OMEXMLService.class); + 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); + } + + xml = XMLTools.sanitizeXML(xml); + LOGGER.trace(xml); + + LifXmlDocument doc = new LifXmlDocument(xml); + imageXmls = doc.getImageXmlDocuments(); + + translateMetadata((List) (List) imageXmls); + } +} diff --git a/components/formats-gpl/src/loci/formats/in/LOFReader.java b/components/formats-gpl/src/loci/formats/in/LOFReader.java index fccda2a1622..f3bfd9f07eb 100644 --- a/components/formats-gpl/src/loci/formats/in/LOFReader.java +++ b/components/formats-gpl/src/loci/formats/in/LOFReader.java @@ -35,6 +35,9 @@ import loci.formats.FormatTools; import loci.formats.ImageTools; import loci.formats.in.LeicaMicrosystemsMetadata.*; +import loci.formats.in.LeicaMicrosystemsMetadata.model.Channel; +import loci.formats.in.LeicaMicrosystemsMetadata.xml.LofXmlDocument; +import loci.formats.in.LeicaMicrosystemsMetadata.xml.XlifDocument; /** * LOFReader is the file format reader for Leica Microsystems' LOF files. @@ -55,7 +58,7 @@ public class LOFReader extends LMSFileReader { /** Offsets to memory blocks, paired with their corresponding description. */ private List offsets; - private int lastChannel = 0; + private int lastChannelLutColorIndex = 0; private long endPointer; public enum MetadataSource { @@ -152,12 +155,20 @@ public boolean isThisType(RandomAccessInputStream stream) throws IOException { lofXml += stream.readChar(); } LofXmlDocument doc = new LofXmlDocument(lofXml, ""); - if (doc.getImageNode() != null) { - return true; - } else { + + if (!doc.isValid()){ + LOGGER.error("LOF file cannot be opened due to invalid XML. "+ + "Please try opening the corresponding XLEF file instead, or try "+ + "opening and re-saving this image with a current LASX version."); + return false; + } + + if (doc.getImageNode() == null){ LOGGER.info("This LOF does not contain image data, it cannot be opened directly."); return false; } + + return true; } /* @see loci.formats.IFormatReader#get8BitLookupTable() */ @@ -167,42 +178,36 @@ public byte[][] get8BitLookupTable() { if (getPixelType() != FormatTools.UINT8 || !isIndexed()) return null; - if (lastChannel < 0 || lastChannel >= 9) { + if (lastChannelLutColorIndex < Channel.RED || lastChannelLutColorIndex > Channel.GREY) { return null; } byte[][] lut = new byte[3][256]; for (int i = 0; i < 256; i++) { - switch (lastChannel) { - case 0: - // red + switch (lastChannelLutColorIndex) { + case Channel.RED: lut[0][i] = (byte) (i & 0xff); break; - case 1: - // green + case Channel.GREEN: lut[1][i] = (byte) (i & 0xff); break; - case 2: - // blue + case Channel.BLUE: lut[2][i] = (byte) (i & 0xff); break; - case 3: - // cyan + case Channel.CYAN: lut[1][i] = (byte) (i & 0xff); lut[2][i] = (byte) (i & 0xff); break; - case 4: - // magenta + case Channel.MAGENTA: lut[0][i] = (byte) (i & 0xff); lut[2][i] = (byte) (i & 0xff); break; - case 5: - // yellow + case Channel.YELLOW: lut[0][i] = (byte) (i & 0xff); lut[1][i] = (byte) (i & 0xff); break; + case Channel.GREY: default: - // gray lut[0][i] = (byte) (i & 0xff); lut[1][i] = (byte) (i & 0xff); lut[2][i] = (byte) (i & 0xff); @@ -218,42 +223,36 @@ public short[][] get16BitLookupTable() { if (getPixelType() != FormatTools.UINT16 || !isIndexed()) return null; - if (lastChannel < 0 || lastChannel >= 9) { + if (lastChannelLutColorIndex < Channel.RED || lastChannelLutColorIndex > Channel.GREY) { return null; } short[][] lut = new short[3][65536]; for (int i = 0; i < 65536; i++) { - switch (lastChannel) { - case 0: - // red + switch (lastChannelLutColorIndex) { + case Channel.RED: lut[0][i] = (short) (i & 0xffff); break; - case 1: - // green + case Channel.GREEN: lut[1][i] = (short) (i & 0xffff); break; - case 2: - // blue + case Channel.BLUE: lut[2][i] = (short) (i & 0xffff); break; - case 3: - // cyan + case Channel.CYAN: lut[1][i] = (short) (i & 0xffff); lut[2][i] = (short) (i & 0xffff); break; - case 4: - // magenta + case Channel.MAGENTA: lut[0][i] = (short) (i & 0xffff); lut[2][i] = (short) (i & 0xffff); break; - case 5: - // yellow + case Channel.YELLOW: lut[0][i] = (short) (i & 0xffff); lut[1][i] = (short) (i & 0xffff); break; + case Channel.GREY: default: - // gray lut[0][i] = (short) (i & 0xffff); lut[1][i] = (short) (i & 0xffff); lut[2][i] = (short) (i & 0xffff); @@ -271,7 +270,7 @@ public byte[] openBytes(int no, byte[] buf, int x, int y, int w, int h) throws F if (!isRGB()) { int[] pos = getZCTCoords(no); - lastChannel = metaTemp.channelPrios[getTileIndex(series)][pos[1]]; + lastChannelLutColorIndex = metadataTranslators.get(getTileIndex(series)).dimensionStore.channels.get(pos[1]).lutColorIndex; } int tileIndex = getTileIndex(series); @@ -317,7 +316,7 @@ public byte[] openBytes(int no, byte[] buf, int x, int y, int w, int h) throws F } // rearrange if color planes are stored in BGR order - if (getRGBChannelCount() == 3 && metaTemp.inverseRgb[0]) { + if (getRGBChannelCount() == 3 && metadataTranslators.get(tileIndex).dimensionStore.inverseRgb) { ImageTools.bgrToRgb(buf, isInterleaved(), bytes, getRGBChannelCount()); } @@ -339,7 +338,7 @@ public void close(boolean fileOnly) throws IOException { super.close(fileOnly); if (!fileOnly) { offsets = null; - lastChannel = 0; + lastChannelLutColorIndex = 0; endPointer = 0; } } @@ -474,11 +473,11 @@ private void seekStartOfPlane(int no, long dataOffset, long planeSize) throws IO int index = getTileIndex(series); long posInFile; - int numberOfTiles = metaTemp.tileCount[index]; + int numberOfTiles = metadataTranslators.get(index).dimensionStore.tileCount; if (numberOfTiles > 1) { // LAS AF treats tiles just like any other dimension, while we do not. // Hence we need to take the tiles into account for a frame's position. - long bytesIncPerTile = metaTemp.tileBytesInc[index]; + long bytesIncPerTile = metadataTranslators.get(index).dimensionStore.tileBytesInc; long framesPerTile = bytesIncPerTile / planeSize; if (framesPerTile > Integer.MAX_VALUE) { @@ -490,7 +489,7 @@ private void seekStartOfPlane(int no, long dataOffset, long planeSize) throws IO int tile = series; for (int i = 0; i < index; i++) { - tile -= metaTemp.tileCount[i]; + tile -= metadataTranslators.get(i).dimensionStore.tileCount; } posInFile = dataOffset; @@ -505,17 +504,6 @@ private void seekStartOfPlane(int no, long dataOffset, long planeSize) throws IO in.seek(posInFile); } - private int getTileIndex(int coreIndex) { - int count = 0; - for (int tile = 0; tile < metaTemp.tileCount.length; tile++) { - if (coreIndex < count + metaTemp.tileCount[tile]) { - return tile; - } - count += metaTemp.tileCount[tile]; - } - return -1; - } - /** * Inits the LOFReader using passed xml (from XLIF) for metadata initiation, * instead of reading the LOF's included xml diff --git a/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/LMSFileReader.java b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/LMSFileReader.java index 1fe51b16e6c..dcad01f3cec 100644 --- a/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/LMSFileReader.java +++ b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/LMSFileReader.java @@ -36,6 +36,8 @@ import loci.formats.FormatException; import loci.formats.meta.MetadataStore; import loci.formats.in.MetadataOptions; +import loci.formats.in.LeicaMicrosystemsMetadata.xml.LMSImageXmlDocument; +import loci.formats.in.LeicaMicrosystemsMetadata.xml.LMSXmlDocument; import loci.formats.in.DynamicMetadataOptions; /** @@ -52,12 +54,12 @@ public abstract class LMSFileReader extends FormatReader { // -- Fields -- public static Logger log; - public MetadataTempBuffer metaTemp; public LMSXmlDocument associatedXmlDoc; //an optional LMS xml file that references the file(s) that are read by this reader + public List metadataTranslators = new ArrayList(); /** file format in which actual image bytes are stored */ public enum ImageFormat { - LOF, TIF, BMP, JPEG, PNG, UNKNOWN + LOF, TIF, BMP, JPEG, PNG, LIF, UNKNOWN } // -- Constructor -- @@ -74,7 +76,7 @@ public void close(boolean fileOnly) throws IOException { super.close(fileOnly); if (!fileOnly){ - metaTemp = null; + metadataTranslators = new ArrayList(); } } @@ -122,14 +124,8 @@ public MetadataStore makeFilterMetadata() { * @throws IOException */ public void translateMetadata(List docs) throws FormatException, IOException { - int len = docs.size(); - metaTemp = new MetadataTempBuffer(len); - - LMSMetadataExtractor extractor = new LMSMetadataExtractor(this); - extractor.translateMetadata(docs); - - MetadataStoreInitializer initializer = new MetadataStoreInitializer(this); - initializer.initMetadataStore(); + LMSMetadataTranslator translator = new LMSMetadataTranslator(this); + translator.translateMetadata(docs); } public void translateMetadata(LMSImageXmlDocument doc) throws FormatException, IOException{ @@ -158,4 +154,15 @@ public static boolean fileExists(String path) { } return false; } + + protected int getTileIndex(int coreIndex) { + int count = 0; + for (int tile = 0; tile < metadataTranslators.size(); tile++) { + if (coreIndex < count + metadataTranslators.get(tile).dimensionStore.tileCount) { + return tile; + } + count += metadataTranslators.get(tile).dimensionStore.tileCount; + } + return -1; + } } diff --git a/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/LMSMetadataExtractor.java b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/LMSMetadataExtractor.java deleted file mode 100644 index 58bae0de703..00000000000 --- a/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/LMSMetadataExtractor.java +++ /dev/null @@ -1,1349 +0,0 @@ -/* - * #%L - * OME Bio-Formats package for reading and converting biological file formats. - * %% - * Copyright (C) 2005 - 2017 Open Microscopy Environment: - * - Board of Regents of the University of Wisconsin-Madison - * - Glencoe Software, Inc. - * - University of Dundee - * %% - * 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, see - * . - * #L% - */ - -package loci.formats.in.LeicaMicrosystemsMetadata; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.StringTokenizer; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import java.io.IOException; -import java.util.ArrayDeque; -import java.util.Deque; -import java.util.Iterator; - -import org.apache.commons.lang.StringUtils; -import org.w3c.dom.Element; -import org.w3c.dom.NodeList; -import org.w3c.dom.Node; -import org.w3c.dom.NamedNodeMap; -import org.w3c.dom.Attr; - -import loci.formats.CoreMetadata; -import loci.formats.FormatException; -import loci.formats.FormatTools; -import loci.formats.in.LeicaMicrosystemsMetadata.Dimension.DimensionKey; -import loci.formats.in.LeicaMicrosystemsMetadata.LMSFileReader.ImageFormat; -import loci.common.DataTools; -import loci.common.DateTools; -import ome.units.UNITS; -import ome.units.quantity.Length; -import ome.xml.model.primitives.Color; - -/** - * This class extracts metadata information from Leica Microsystems image XML and stores it in the reader's CoreMetadata and MetadataTempBuffer - * - * @author Melissa Linkert melissa at glencoesoftware.com - * @author Constanze Wendlandt constanze.wendlandt at leica-microsystems.com - */ -public class LMSMetadataExtractor { - // -- Constants -- - private static final long METER_MULTIPLY = 1000000; - - // -- Fields -- - private LMSFileReader r; - List channelBytesIncs = new ArrayList(); - int extras = 1; - - // -- Constructor -- - public LMSMetadataExtractor(LMSFileReader reader){ - this.r = reader; - } - - // -- Methods -- - /** - * Extracts all information from Leica image xml Documents and writes it to reader's {@link CoreMetadata} and {@link MetadataTempBuffer} - * @param docs list of Leica xml documents - * @throws FormatException - * @throws IOException - */ - public void translateMetadata(List docs) throws FormatException, IOException{ - int len = docs.size(); - r.setCore(new ArrayList(len)); - r.getCore().clear(); - - //create CoreMetadata for each xml referenced image (=series) - for (int i = 0; i < docs.size(); i++) { - CoreMetadata ms = new CoreMetadata(); - r.getCore().add(ms); - r.setSeries(i); - - Node image = docs.get(i).getImageNode(); - r.metaTemp.imageNames[i] = docs.get(i).getImageName(); - translateImage((Element)image, i); - } - r.setSeries(0); - - //after this section, we don't have 1 CoreMetadata per xlif-referenced image, - //but 1 CoreMetadata per series ( = tile )! - ArrayList newCore = new ArrayList(); - for (int i = 0; i < r.getCore().size(); i++) { - for (int tile = 0; tile < r.metaTemp.tileCount[i]; tile++) { - newCore.add(r.getCore().get(i)); - } - } - r.setCore(newCore); - } - - /** - * Extracts information from element node and writes it to reader's {@link CoreMetadata} and {@link MetadataTempBuffer} - * @param img node with tag name "element" from Leica XML - * @param i image /core index - * @throws FormatException - */ - public void translateImage(Element image, int i) throws FormatException{ - CoreMetadata ms = r.getCore().get(i); - ms.orderCertain = true; - ms.metadataComplete = true; - ms.littleEndian = true; - ms.falseColor = true; - - translateChannelDescriptions(image, i); - translateDimensionDescriptions(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); - - final Deque nameStack = new ArrayDeque(); - populateOriginalMetadata(image, nameStack); - addUserCommentMeta(image, i); - } - - /*** - * Extracts i.a. channel number and luts from channel descriptions and writes it to reader's {@link CoreMetadata} and {@link MetadataTempBuffer} - * @param imageNode Image node from Leica xml - * @param coreIndex - * @throws FormatException - */ - public void translateChannelDescriptions(Element imageNode, int coreIndex) - throws FormatException - { - CoreMetadata ms = r.getCore().get(coreIndex); - - NodeList channels = getChannelDescriptionNodes(imageNode); - ms.sizeC = channels.getLength(); - - List luts = new ArrayList(); - r.metaTemp.channelPrios = new int[r.metaTemp.tileCount.length][]; - - for (int ch=0; ch= 3 && - ((Element)channels.item(0)).getAttribute("LUTName").equals("Red") && - ((Element)channels.item(1)).getAttribute("LUTName").equals("Green") && - ((Element)channels.item(2)).getAttribute("LUTName").equals("Blue")); - - translateLuts(luts, coreIndex); - } - - /*** - * Translates raw channel luts of an image to Colors and writes it to reader's {@link MetadataTempBuffer} - * @param luts list of raw lut names / values from Leica XML - */ - private void translateLuts(List luts, int imgIndex){ - CoreMetadata ms = r.getCore().get(imgIndex); - ArrayList channelColors = new ArrayList(ms.sizeC); - r.metaTemp.channelPrios[imgIndex] = new int[ms.sizeC]; - - int nextLut = 0; - //foreach channel of image - for (int channel=0; channel 1) { - if (cmd.sizeZ == 1) cmd.sizeZ = extras; - else { - if (cmd.sizeT == 0) cmd.sizeT = extras; - else cmd.sizeT *= extras; - } - } - - if (cmd.sizeX == 0) cmd.sizeX = 1; - if (cmd.sizeY == 0) cmd.sizeY = 1; - if (cmd.sizeC == 0) cmd.sizeC = 1; - if (cmd.sizeZ == 0) cmd.sizeZ = 1; - if (cmd.sizeT == 0) cmd.sizeT = 1; - } - - /** - * Sets CoreMetadata.pixelType depending on extracted x bytesInc - * @param coreIndex - * @throws FormatException - */ - private void setPixelType(int coreIndex) throws FormatException { - CoreMetadata cmd = r.getCore().get(coreIndex); - long xBytesInc = r.metaTemp.getDimension(coreIndex, DimensionKey.X).bytesInc; - cmd.pixelType = - FormatTools.pixelTypeFromBytes((int) xBytesInc, false, true); - } - - /** - * Extracts information from dimension descriptions and writes it to reader's {@link MetadataTempBuffer} - * @param imageNode Image node from Leica xml - * @param image image / core index - * @throws FormatException - */ - public void translateAttachmentNodes(Element imageNode, int image) - throws FormatException - { - boolean tilesAttachmentFound = false; - NodeList attachmentNodes = getNodes(imageNode, "Attachment"); - if (attachmentNodes == null) return; - for (int i=0; i= r.getEffectiveSizeC()) { - continue; - } - - if (id.endsWith("ExposureTime")) { - r.metaTemp.expTimes[image][c] = DataTools.parseDouble(value.trim()); - } - else if (id.endsWith("Gain")) { - r.metaTemp.gains[image][c] = DataTools.parseDouble(value.trim()); - } - else if (id.endsWith("WaveLength")) { - Double exWave = DataTools.parseDouble(value.trim()); - if (exWave != null && exWave > 0) { - r.metaTemp.exWaves[image][c] = exWave; - } - } - // NB: "UesrDefName" is not a typo. - else if ((id.endsWith("UesrDefName") || id.endsWith("UserDefName")) && - !value.equals("None")) - { - if (r.metaTemp.channelNames[image][c] == null || - r.metaTemp.channelNames[image][c].trim().isEmpty()) - { - r.metaTemp.channelNames[image][c] = value; - } - } - } - } - } - - if (confocalSettings != null) { - for (int i=0; i 0) { - Length in = FormatTools.getCutIn(v); - if (in != null) { - r.metaTemp.cutIns.get(image).add(in); - } - } - } - else if (description.endsWith("(right)")) { - if (v != null && v > 0) { - Length out = FormatTools.getCutOut(v); - if (out != null) { - r.metaTemp.cutOuts.get(image).add(out); - } - } - } - else if (attribute.equals("Stain")) { - if (nextChannel < r.metaTemp.channelNames[image].length) { - r.metaTemp.channelNames[image][nextChannel++] = variant; - } - } - } - } - } - - /** - * Extracts timestamps and writes them to reader's {@link MetadataTempBuffer} - * @param imageNode Image node from Leica xml - * @param image image / core index - * @throws FormatException - */ - public void translateTimestamps(Element imageNode, int image) - throws FormatException - { - NodeList timeStampLists = getNodes(imageNode, "TimeStampList"); - if (timeStampLists == null) return; - - Element timeStampList = (Element)timeStampLists.item(0); - r.metaTemp.timestamps[image] = new Double[r.getImageCount()]; - - // probe if timestamps are saved in the format of LAS AF 3.1 or newer - String numberOfTimeStamps = timeStampList.getAttribute("NumberOfTimeStamps"); - if (numberOfTimeStamps != null && !numberOfTimeStamps.isEmpty()) { - // LAS AF 3.1 (or newer) timestamps are available - String timeStampsRaw = timeStampList.getTextContent(); - List timeStamps = Arrays.asList(timeStampsRaw.split(" ")); - for (int stamp = 0; stamp < timeStamps.size(); stamp++) { - if (stamp < r.getImageCount()) { - String timestamp = timeStamps.get(stamp); - r.metaTemp.timestamps[image][stamp] = translateSingleTimestamp(timestamp); - } - } - } - else { - // probe if timestamps are saved in the format of LAS AF 3.0 or older - NodeList timestampNodes = getNodes(imageNode, "TimeStamp"); - if (timestampNodes != null) { - // LAS AF 3.0 (or older) timestamps are available - for (int stamp = 0; stamp < timestampNodes.getLength(); stamp++) { - if (stamp < r.getImageCount()) { - Element timestamp = (Element) timestampNodes.item(stamp); - r.metaTemp.timestamps[image][stamp] = translateSingleTimestamp(timestamp); - } - } - } - else { - return; - } - } - - r.metaTemp.acquiredDate[image] = r.metaTemp.timestamps[image][0]; - } - - /** - * Extracts laser information and writes it to reader's {@link MetadataTempBuffer} - * @param imageNode Image node from Leica xml - * @param image image / core index - * @throws FormatException - */ - public void translateLaserLines(Element imageNode, int image) - throws FormatException - { - NodeList aotfLists = getNodes(imageNode, "AotfList"); - if (aotfLists == null || aotfLists.getLength() == 0) return; - - int baseIntensityIndex = 0; - - for (int channel=0; channel channels = new ArrayList(); - int nextChannel = 0; - for (int definition=0; definition 0) { - Length in = - FormatTools.getCutIn((double) Math.round(cutIn)); - if (in != null) { - r.metaTemp.cutIns.get(image).add(in); - } - } - if (cutOut != null && cutOut.intValue() > 0) { - Length out = - FormatTools.getCutOut((double) Math.round(cutOut)); - if (out != null) { - r.metaTemp.cutOuts.get(image).add(out); - } - } - } - else { - channels.add(""); - } - - if (!isMaster) { - if (channel < nextChannel) { - nextChannel = 0; - } - - if (nextChannel < r.getEffectiveSizeC()) { - if (r.metaTemp.gains[image] != null) { - r.metaTemp.gains[image][nextChannel] = gain; - } - if (r.metaTemp.detectorOffsets[image] != null) { - r.metaTemp.detectorOffsets[image][nextChannel] = offset; - } - } - - nextChannel++; - } - } else { - count++; - } - if (active && r.metaTemp.activeDetector.get(image) != null) { - r.metaTemp.activeDetector.get(image).add(active); - } - } - //Store values to check if actually it is active. - if (!isMaster) { - r.metaTemp.laserActive.get(image).add(count < detectors.getLength()); - } - } - - if (channels != null && r.metaTemp.channelNames[image] != null) { - for (int i=0; i= 0 && index < channels.size()) { - if (r.metaTemp.channelNames[image][i] == null || - r.metaTemp.channelNames[image][i].trim().isEmpty()) - { - r.metaTemp.channelNames[image][i] = channels.get(index); - } - } - } - } - } - - /** - Translates hex timestamp string to double value - * @param timestamp timestamp in format - * @return number of seconds - */ - private double translateSingleTimestamp(String timestamp) { - timestamp = timestamp.trim(); - int stampLowStart = Math.max(0, timestamp.length() - 8); - int stampHighEnd = Math.max(0, stampLowStart); - String stampHigh = timestamp.substring(0, stampHighEnd); - String stampLow = timestamp.substring(stampLowStart, timestamp.length()); - long high - = stampHigh == null || stampHigh.trim().isEmpty() ? 0 - : Long.parseLong(stampHigh.trim(), 16); - long low - = stampLow == null || stampLow.trim().isEmpty() ? 0 - : Long.parseLong(stampLow.trim(), 16); - long milliseconds = DateTools.getMillisFromTicks(high, low); - double seconds = (double)milliseconds / 1000; - return seconds; - } - - /** - * Translates the content of a timestamp node to double value - * @param imageNode timestamp node - * @param image image / core index - * @throws FormatException - */ - private double translateSingleTimestamp(Element timestamp) { - String stampHigh = timestamp.getAttribute("HighInteger"); - String stampLow = timestamp.getAttribute("LowInteger"); - long high - = stampHigh == null || stampHigh.trim().isEmpty() ? 0 - : Long.parseLong(stampHigh.trim()); - long low - = stampLow == null || stampLow.trim().isEmpty() ? 0 - : Long.parseLong(stampLow.trim()); - - long milliseconds = DateTools.getMillisFromTicks(high, low); - double seconds = (double)milliseconds / 1000; - return seconds; - } - - /** - * Extracts user comments and adds them to the reader's {@link CoreMetadata} - * @param imageNode - * @param image - * @throws FormatException - */ - private void addUserCommentMeta(Element imageNode, int image) throws FormatException { - NodeList attachmentNodes = getNodes(imageNode, "User-Comment"); - if (attachmentNodes == null) - return; - for (int i = 0; i < attachmentNodes.getLength(); i++) { - Node attachment = attachmentNodes.item(i); - r.addSeriesMeta("User-Comment[" + i + "]", attachment.getTextContent()); - if (i == 0 && r.metaTemp.descriptions[image] == null) { - r.metaTemp.descriptions[image] = attachment.getTextContent(); - } - } - } - - /** - * Creates key value pairs from attributes of the root's child nodes (tag | attribute name : attribute value) and adds them to reader's {@link CoreMetadata} - * @param root xml node - * @param nameStack list of node names to be prepended to key name - */ - private void populateOriginalMetadata(Element root, Deque nameStack) { - 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"); - } - final StringBuilder key = new StringBuilder(); - final Iterator nameStackIterator = nameStack.descendingIterator(); - while (nameStackIterator.hasNext()) { - final String k = nameStackIterator.next(); - key.append(k); - key.append("|"); - } - if (suffix != null && value != null && suffix.length() > 0 && value.length() > 0 && !suffix.equals("HighInteger") - && !suffix.equals("LowInteger")) { - r.addSeriesMetaList(key.toString() + suffix, 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")) { - r.addSeriesMeta(key.toString() + attr.getName(), 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); - } - } - - if (root.hasAttributes() && !name.equals("Element") && !name.equals("Attachment") - && !name.equals("LMSDataContainerHeader")) { - nameStack.pop(); - } - } - - // -- Helper functions -- - - public long parseLong(String value){ - return value == null || value.trim().isEmpty() ? 0 : Long.parseLong(value.trim()); - } - - public int parseInt(String value){ - return value == null || value.trim().isEmpty() ? 0 : Integer.parseInt(value.trim()); - } - - public double parseDouble(String value){ - return StringUtils.isBlank(value) ? 0d : DataTools.parseDouble(value.trim()); - } - - 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"); - } - - /** - * Returns all (grand*n)children nodes with given node name - * @param root root node - * @param nodeName name of children that shall be searched - * @return list of child nodes with given name - */ - 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. + * #L% + */ + +package loci.formats.in.LeicaMicrosystemsMetadata; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import loci.formats.CoreMetadata; +import loci.formats.FormatException; +import loci.formats.MetadataTools; +import loci.formats.in.LeicaMicrosystemsMetadata.xml.LMSImageXmlDocument; +import loci.formats.meta.MetadataStore; + +/** + * LMSMetadataTranslator sets up the whole metadata translation for all images of an LMSFileReader, + * translating LMS image XML to the reader's CoreMetadata and MetadataStore, and mapping LMS image and + * instrument metadata to OME metadata. + * + * @author Constanze Wendlandt constanze.wendlandt at leica-microsystems.com + */ +public class LMSMetadataTranslator { + // -- Fields -- + private LMSFileReader r; + MetadataStore store; + + // -- Constructor -- + public LMSMetadataTranslator(LMSFileReader reader) { + this.r = reader; + store = r.makeFilterMetadata(); + } + + public void translateMetadata(List docs) throws FormatException, IOException { + // extract metadata info + for (int i = 0; i < docs.size(); i++) { + SingleImageTranslator translator = new SingleImageTranslator(docs.get(i), i, docs.size(), r); + r.metadataTranslators.add(translator); + translator.extract(); + } + + // get series count (one per image tile) + int seriesCount = 0; + for (SingleImageTranslator translator : r.metadataTranslators){ + seriesCount += translator.dimensionStore.tileCount; + } + + initCoreMetadata(seriesCount); + + int seriesIndex = 0; + int translatorIndex = 0; + while (seriesIndex < seriesCount){ + SingleImageTranslator translator = r.metadataTranslators.get(translatorIndex); + for (int tileIndex = 0; tileIndex < translator.dimensionStore.tileCount; tileIndex++){ + String imageName = translator.imageDetails.originalImageName; + if (translator.dimensionStore.tileCount > 1) + imageName += " - tile " + (tileIndex + 1); + + translator.setTarget(seriesIndex + tileIndex, translator.imageDetails.collectionPrefix + imageName, tileIndex); + translator.write(); + } + + seriesIndex += translator.dimensionStore.tileCount; + translatorIndex++; + } + + r.setSeries(0); + + MetadataTools.populatePixels(store, this.r, true, false); + } + + private void initCoreMetadata(int len) { + r.setCore(new ArrayList(len)); + r.getCore().clear(); + + for (int i = 0; i < len; i++) { + CoreMetadata ms = new CoreMetadata(); + ms.orderCertain = true; + ms.metadataComplete = true; + ms.littleEndian = true; + ms.falseColor = true; + r.getCore().add(ms); + } + } +} diff --git a/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/MetadataStoreInitializer.java b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/MetadataStoreInitializer.java deleted file mode 100644 index 1874be6ce18..00000000000 --- a/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/MetadataStoreInitializer.java +++ /dev/null @@ -1,568 +0,0 @@ -/* - * #%L - * OME Bio-Formats package for reading and converting biological file formats. - * %% - * Copyright (C) 2005 - 2017 Open Microscopy Environment: - * - Board of Regents of the University of Wisconsin-Madison - * - Glencoe Software, Inc. - * - University of Dundee - * %% - * 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, see - * . - * #L% - */ - -package loci.formats.in.LeicaMicrosystemsMetadata; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import loci.common.DateTools; -import loci.formats.FormatException; -import loci.formats.FormatTools; -import loci.formats.MetadataTools; -import ome.units.UNITS; -import ome.units.quantity.Length; -import ome.units.quantity.Time; -import ome.xml.model.enums.DetectorType; -import ome.xml.model.enums.LaserMedium; -import ome.xml.model.enums.LaserType; -import ome.xml.model.primitives.Color; -import ome.xml.model.primitives.PercentFraction; -import ome.xml.model.primitives.Timestamp; -import loci.formats.meta.MetadataStore; - -/** - * This class initializes the {@link MetadataStore} of a given {@link LMSFileReader}, using its {@link MetadataTempBuffer} - * - * @author Melissa Linkert melissa at glencoesoftware.com - * @author Constanze Wendlandt constanze.wendlandt at leica-microsystems.com - */ -public class MetadataStoreInitializer { - // -- Fields -- - private LMSFileReader r; - MetadataStore store; - private int nextChannel = 0; - - // -- Constructor -- - public MetadataStoreInitializer(LMSFileReader reader){ - this.r = reader; - store = r.makeFilterMetadata(); - } - - // -- Methods -- - - /** - * Adds microscope and objective details from the reader's {@link MetadataTempBuffer} to its {@link MetadataStore} - * @param series index of image series / metadata store - * @throws FormatException - */ - public void initStandDetails(int series) throws FormatException { - String instrumentID = MetadataTools.createLSID("Instrument", series); - store.setInstrumentID(instrumentID, series); - - int index = getTileIndex(series); - - store.setMicroscopeModel(r.metaTemp.microscopeModels[index], series); - store.setMicroscopeType(MetadataTools.getMicroscopeType("Other"), series); - - String objectiveID = MetadataTools.createLSID("Objective", series, 0); - store.setObjectiveID(objectiveID, series, 0); - store.setObjectiveLensNA(r.metaTemp.lensNA[index], series, 0); - store.setObjectiveSerialNumber(r.metaTemp.serialNumber[index], series, 0); - if (r.metaTemp.magnification[index] != null) { - store.setObjectiveNominalMagnification(r.metaTemp.magnification[index], series, 0); - } - if (r.metaTemp.immersions[index] != null) - store.setObjectiveImmersion(MetadataTools.getImmersion(r.metaTemp.immersions[index]), series, 0); - if (r.metaTemp.corrections[index] != null) - store.setObjectiveCorrection(MetadataTools.getCorrection(r.metaTemp.corrections[index]), series, 0); - store.setObjectiveModel(r.metaTemp.objectiveModels[index], series, 0); - - store.setImageInstrumentRef(instrumentID, series); - store.setObjectiveSettingsID(objectiveID, series); - store.setObjectiveSettingsRefractiveIndex(r.metaTemp.refractiveIndex[index], series); - } - - /** - * Adds filter details from the reader's {@link MetadataTempBuffer} to its {@link MetadataStore} - * @param series index of image series / metadata store - * @throws FormatException - */ - public void initFilterModels(int series){ - int index = getTileIndex(series); - if (r.metaTemp.cutIns.get(index) != null && r.metaTemp.filterModels.get(index) != null) { - // int channel = 0; - if (r.metaTemp.cutIns.get(index).size() >= r.metaTemp.filterModels.get(index).size() * 2) { - int diff = r.metaTemp.cutIns.get(index).size() - r.metaTemp.filterModels.get(index).size(); - for (int q=0; q lasers = r.metaTemp.laserWavelength.size() > index ? r.metaTemp.laserWavelength.get(index) : null; - final List laserIntensities = r.metaTemp.laserIntensity.size() > index ? r.metaTemp.laserIntensity.get(index) : null; - - final List active = r.metaTemp.laserActive.size() > index ? r.metaTemp.laserActive.get(index) : null; - final List frap = r.metaTemp.laserFrap.size() > index ? r.metaTemp.laserFrap.get(index) : null; - - if (lasers != null) { - int laserIndex = 0; - while (laserIndex < lasers.size()) { - if ((Double) lasers.get(laserIndex) == 0) { - lasers.remove(laserIndex); - } - else { - laserIndex++; - } - } - - for (int laser=0; laser ignoredChannels = new HashSet(); - final List validIntensities = new ArrayList(); - int size = lasers.size(); - int channel = 0; - Set channels = new HashSet(); - - for (int laser=0; laser toRemove = new HashSet(); - - int as = active.size(); - for (int j = 0; j < s; j++) { - if (j < as && !(Boolean) active.get(j)) { - toRemove.add(validIntensities.get(j)); - } - jj = j+1; - if (jj < s) { - int v = validIntensities.get(j)/size; - int vv = validIntensities.get(jj)/size; - if (vv == v) {//do not consider that channel. - toRemove.add(validIntensities.get(j)); - toRemove.add(validIntensities.get(jj)); - ignoredChannels.add(j); - } - } - } - if (toRemove.size() > 0) { - validIntensities.removeAll(toRemove); - } - - boolean noNames = true; - if (r.metaTemp.channelNames[index] != null) { - for (String name : r.metaTemp.channelNames[index]) { - if (name != null && !name.equals("")) { - noNames = false; - break; - } - } - } - if (!noNames && frap != null) { //only use name for frap. - for (int k = 0; k < frap.size(); k++) { - if (!frap.get(k)) { - noNames = true; - break; - } - } - } - - int nextFilter = 0; - //int nextFilter = cutIns[i].size() - getEffectiveSizeC(); - for (int k=0; k 0) { - if (r.metaTemp.cutIns.get(index) == null || nextFilter >= r.metaTemp.cutIns.get(index).size()) - { - continue; - } - Double cutIn = - ((Length) r.metaTemp.cutIns.get(index).get(nextFilter)).value(UNITS.NANOMETER).doubleValue(); - while (cutIn - wavelength > 20) { - nextFilter++; - if (nextFilter < r.metaTemp.cutIns.get(index).size()) { - cutIn = ((Length) - r.metaTemp.cutIns.get(index).get(nextFilter)).value(UNITS.NANOMETER).doubleValue(); - } - else { - break; - } - } - if (nextFilter < r.metaTemp.cutIns.get(index).size()) { - // String fid = MetadataTools.createLSID("Filter", series, nextFilter); - // store.setLightPathEmissionFilterRef(fid, index, nextChannel, 0); - nextFilter++; - } - } - } - } - } - } - } - - /** - * Adds detector and channel color details from the reader's {@link MetadataTempBuffer} to its {@link MetadataStore} - * @param series index of image series / metadata store - * @throws FormatException - */ - public void initDetectorModels(int series){ - int index = getTileIndex(series); - - final List detectors = r.metaTemp.detectorModels.size() > index ? r.metaTemp.detectorModels.get(index) : null; - if (detectors != null) { - nextChannel = 0; - int start = detectors.size() - r.getEffectiveSizeC(); - if (start < 0) { - start = 0; - } - for (int detector=start; detector= 0 && - detectorIndex < r.metaTemp.activeDetector.get(index).size() && - (Boolean) r.metaTemp.activeDetector.get(index).get(detectorIndex) && - r.metaTemp.detectorOffsets[index] != null && - nextChannel < r.metaTemp.detectorOffsets[index].length) - { - store.setDetectorOffset( - r.metaTemp.detectorOffsets[index][nextChannel++], series, dIndex); - } - } - } - } - - final List activeDetectors = r.metaTemp.activeDetector.size() > index ? r.metaTemp.activeDetector.get(index) : null; - int firstDetector = activeDetectors == null ? 0 : - activeDetectors.size() - r.getEffectiveSizeC(); - int nextDetector = firstDetector; - - int nextFilter = 0; - int nextFilterDetector = 0; - - if (activeDetectors != null && - activeDetectors.size() > r.metaTemp.cutIns.get(index).size() && - (Boolean) activeDetectors.get(activeDetectors.size() - 1) && - (Boolean) activeDetectors.get(activeDetectors.size() - 2)) - { - nextFilterDetector = activeDetectors.size() - r.metaTemp.cutIns.get(index).size(); - - if (r.metaTemp.cutIns.get(index).size() > r.metaTemp.filterModels.get(index).size()) { - nextFilterDetector += r.metaTemp.filterModels.get(index).size(); - nextFilter += r.metaTemp.filterModels.get(index).size(); - } - } - - for (int c=0; c= 0 && nextDetector < activeDetectors.size() && - !(Boolean) activeDetectors.get(nextDetector)) - { - nextDetector++; - } - if (nextDetector < activeDetectors.size() && detectors != null && - nextDetector - firstDetector < detectors.size()) - { - String detectorID = MetadataTools.createLSID( - "Detector", series, nextDetector - firstDetector); - store.setDetectorSettingsID(detectorID, series, c); - nextDetector++; - - if (r.metaTemp.detectorOffsets[index] != null && - c < r.metaTemp.detectorOffsets[index].length) - { - store.setDetectorSettingsOffset(r.metaTemp.detectorOffsets[index][c], series, c); - } - - if (r.metaTemp.gains[index] != null) { - store.setDetectorSettingsGain(r.metaTemp.gains[index][c], series, c); - } - } - } - - if (r.metaTemp.channelNames[index] != null) { - store.setChannelName(r.metaTemp.channelNames[index][c], series, c); - } - if (r.metaTemp.pinholes[index] != null) { - store.setChannelPinholeSize(new Length(r.metaTemp.pinholes[index], UNITS.MICROMETER), series, c); - } - if (r.metaTemp.exWaves[index] != null) { - if (r.metaTemp.exWaves[index][c] != null && r.metaTemp.exWaves[index][c] > 1) { - Length ex = - FormatTools.getExcitationWavelength(r.metaTemp.exWaves[index][c]); - if (ex != null) { - store.setChannelExcitationWavelength(ex, series, c); - } - } - } - // channel coloring is implicit if the image is stored as RGB - Color channelColor = r.metaTemp.channelColors.get(index).get(c); - if (!r.isRGB()) { - store.setChannelColor(channelColor, series, c); - } - - if (channelColor.getValue() != -1 && nextFilter >= 0) { - if (nextDetector - firstDetector != r.getSizeC() && - r.metaTemp.cutIns.get(index) != null && nextDetector >= r.metaTemp.cutIns.get(index).size()) - { - while (nextFilterDetector < firstDetector) { - String filterID = - MetadataTools.createLSID("Filter", series, nextFilter); - store.setFilterID(filterID, series, nextFilter); - - nextFilterDetector++; - nextFilter++; - } - } - while (activeDetectors != null && - nextFilterDetector < activeDetectors.size() && - !(Boolean) activeDetectors.get(nextFilterDetector)) - { - String filterID = MetadataTools.createLSID("Filter", series, nextFilter); - store.setFilterID(filterID, series, nextFilter); - nextFilterDetector++; - nextFilter++; - } - String filterID = MetadataTools.createLSID("Filter", series, nextFilter); - store.setFilterID(filterID, series, nextFilter); - store.setLightPathEmissionFilterRef(filterID, series, c, 0); - nextFilterDetector++; - nextFilter++; - } - } - } - - /** - * Adds image and ROI details from the reader's {@link MetadataTempBuffer} to its {@link MetadataStore} - * @param series index of image series / metadata store - * @throws FormatException - */ - public void initImageDetails(int series){ - int index = getTileIndex(series); - - store.setImageDescription(r.metaTemp.descriptions[index], series); - if (r.metaTemp.acquiredDate[index] > 0) { - store.setImageAcquisitionDate(new Timestamp(DateTools.convertDate( - (long) (r.metaTemp.acquiredDate[index] * 1000), DateTools.COBOL, - DateTools.ISO8601_FORMAT, false)), series); - } - store.setImageName(r.metaTemp.imageNames[index].trim(), series); - - Length sizeX = - FormatTools.getPhysicalSizeX(r.metaTemp.physicalSizeXs.get(index)); - Length sizeY = - FormatTools.getPhysicalSizeY(r.metaTemp.physicalSizeYs.get(index)); - Length sizeZ = FormatTools.getPhysicalSizeZ(r.metaTemp.zSteps[index]); - - if (sizeX != null) { - store.setPixelsPhysicalSizeX(sizeX, series); - } - if (sizeY != null) { - store.setPixelsPhysicalSizeY(sizeY, series); - } - if (sizeZ != null) { - store.setPixelsPhysicalSizeZ(sizeZ, series); - } - if (r.metaTemp.tSteps[index] != null) { - store.setPixelsTimeIncrement(new Time(r.metaTemp.tSteps[index], UNITS.SECOND), series); - } - - int roiCount = 0; - for (int image=0; image 0) { - timestamp = r.metaTemp.timestamps[index][0]; - } - store.setPlaneDeltaT(new Time(timestamp, UNITS.SECOND), series, image); - } - } - - if (r.metaTemp.expTimes[index] != null) { - int c = r.getZCTCoords(image)[1]; - if (r.metaTemp.expTimes[index][c] != null) - { - store.setPlaneExposureTime(new Time(r.metaTemp.expTimes[index][c], UNITS.SECOND), series, image); - } - } - } - - if (r.metaTemp.imageROIs[index] != null) { - for (int roi=0; roi. - * #L% - */ - -package loci.formats.in.LeicaMicrosystemsMetadata; - -import java.util.List; - -import loci.formats.in.LeicaMicrosystemsMetadata.Dimension.DimensionKey; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Arrays; - -import ome.units.quantity.Length; -import ome.xml.model.primitives.Color; - -/** - * This class is used to temporarily store metadata information extracted from - * Leica XML before it is written to the reader's MetadataStore - * - * @author Melissa Linkert melissa at glencoesoftware.com - * @author Constanze Wendlandt constanze.wendlandt at leica-microsystems.com - */ -public class MetadataTempBuffer { - // -- Fields -- - public List> channelColors = new ArrayList>(); - public int[][] channelPrios; - - public List physicalSizeXs = new ArrayList(); - public List physicalSizeYs = new ArrayList(); - public List fieldPosX = new ArrayList(); - public List fieldPosY = new ArrayList(); - - public String[] descriptions, microscopeModels, serialNumber; - public Double[] pinholes, zooms, zSteps, tSteps, lensNA; - public boolean[] flipX, flipY, swapXY; - public Double[][] expTimes, gains, detectorOffsets; - public String[][] channelNames; - public ArrayList> detectorModels; - public Double[][] exWaves; - public ArrayList> activeDetector; - public ArrayList> detectorIndexes; - - public String[] immersions, corrections, objectiveModels; - public Double[] magnification; - public Length[] posX, posY, posZ; - public Double[] refractiveIndex; - public ArrayList> cutIns, cutOuts; - public ArrayList> filterModels; - public Double[][] timestamps; - public ArrayList> laserIntensity, laserWavelength; - public ArrayList> laserActive, laserFrap; - - public ROI[][] imageROIs; - public boolean alternateCenter = false; - public String[] imageNames; - public double[] acquiredDate; - - public int[] tileCount; - public long[] tileBytesInc; - public boolean[] inverseRgb; // true if channels are in BGR order - private ArrayList> dimensions; - public ArrayList> channels; - - // -- Constructor -- - /** - * Constructs a MetadataTempBuffer for a given number of images. - * - * @param len Number of images (one per XLIF) - */ - public MetadataTempBuffer(int len) { - tileCount = new int[len]; - Arrays.fill(tileCount, 1); - tileBytesInc = new long[len]; - acquiredDate = new double[len]; - descriptions = new String[len]; - timestamps = new Double[len][]; - serialNumber = new String[len]; - lensNA = new Double[len]; - magnification = new Double[len]; - immersions = new String[len]; - corrections = new String[len]; - objectiveModels = new String[len]; - posX = new Length[len]; - posY = new Length[len]; - posZ = new Length[len]; - refractiveIndex = new Double[len]; - microscopeModels = new String[len]; - zSteps = new Double[len]; - tSteps = new Double[len]; - pinholes = new Double[len]; - zooms = new Double[len]; - flipX = new boolean[len]; - flipY = new boolean[len]; - swapXY = new boolean[len]; - expTimes = new Double[len][]; - gains = new Double[len][]; - detectorOffsets = new Double[len][]; - channelNames = new String[len][]; - exWaves = new Double[len][]; - imageROIs = new ROI[len][]; - imageNames = new String[len]; - inverseRgb = new boolean[len]; - - laserWavelength = ArrayListOfArrayLists(len, Double.class); - activeDetector = ArrayListOfArrayLists(len, Boolean.class); - cutIns = ArrayListOfArrayLists(len, Length.class); - cutOuts = ArrayListOfArrayLists(len, Length.class); - filterModels = ArrayListOfArrayLists(len, String.class); - detectorModels = ArrayListOfArrayLists(len, String.class); - detectorIndexes = ArrayListOfHashMaps(len, Integer.class, String.class); - laserIntensity = ArrayListOfArrayLists(len, Double.class); - laserActive = ArrayListOfArrayLists(len, Boolean.class); - laserFrap = ArrayListOfArrayLists(len, Boolean.class); - dimensions = ArrayListOfArrayLists(len, Dimension.class); - channels = ArrayListOfArrayLists(len, Channel.class); - } - - // -- Methods -- - public Dimension getDimension(int imageIndex, DimensionKey key){ - for (Dimension dimension : dimensions.get(imageIndex)){ - if (dimension.key == key) - return dimension; - } - return null; - } - - /** - * Inserts dimension to buffer and optionally adapts other dimension-dependent values - * @param imageIndex - * @param dimension - */ - public void addDimension(int imageIndex, Dimension dimension){ - dimensions.get(imageIndex).add(dimension); - if (dimension.key == DimensionKey.X){ - physicalSizeXs.add(dimension.getLength()); - } else if (dimension.key == DimensionKey.Y) { - physicalSizeYs.add(dimension.getLength()); - } else if (dimension.key == DimensionKey.Z){ - if (zSteps[imageIndex] == null && dimension.getLength() != null) { - zSteps[imageIndex] = Math.abs(dimension.getLength()); - } - } else if (dimension.key == DimensionKey.S){ - tileCount[imageIndex] *= dimension.size; - tileBytesInc[imageIndex] = dimension.bytesInc; - } - } - - /** - * Returns the dimension order as a string - * @param imageIndex - * @return dimension order string, as it is expected in CoreMetadata.dimensionOrder - */ - public String getDimensionOrder(int imageIndex){ - sortDimensions(imageIndex); - - String dimensionOrder = ""; - List standardDimensions = new ArrayList<>(Arrays.asList(DimensionKey.X, DimensionKey.Y, DimensionKey.Z, - DimensionKey.C, DimensionKey.T)); - - for (Dimension dimension : dimensions.get(imageIndex)){ - if (standardDimensions.contains(dimension.key)){ - dimensionOrder += dimension.key.token; - } - } - return dimensionOrder; - } - - /** - * Sorts list of existing dimensions by increasing bytesInc, beginning with X and Y, ending with stage position - * @param coreIndex - */ - private void sortDimensions(int coreIndex){ - List dims = dimensions.get(coreIndex); - dims.sort((Dimension dim1, Dimension dim2) -> Long.compare(dim1.bytesInc, dim2.bytesInc)); - - //move X and Y to the start - Dimension dimX = getDimension(coreIndex, DimensionKey.X); - Dimension dimY = getDimension(coreIndex, DimensionKey.Y); - dimensions.get(coreIndex).remove(dimX); - dimensions.get(coreIndex).remove(dimY); - - //XY - if (dimX.bytesInc < dimY.bytesInc){ - dimensions.get(coreIndex).add(0, dimX); - dimensions.get(coreIndex).add(1, dimY); - } else { - //YX - dimensions.get(coreIndex).add(0, dimY); - dimensions.get(coreIndex).add(1, dimX); - } - - //move dimension S to the end to sort images by stage position, since tiles are accessed as separate series - Dimension dimS = getDimension(coreIndex, DimensionKey.S); - dimensions.get(coreIndex).remove(dimS); - dimensions.get(coreIndex).add(dimS); - } - - public ArrayList getDimensions(int imageIndex){ - sortDimensions(imageIndex); - return dimensions.get(imageIndex); - } - - /** - * Adds Z, T and S dimension if they haven't been added already - * @param imageIndex - */ - public void addMissingDimensions(int imageIndex){ - dimensions.get(imageIndex).sort((dim1, dim2) -> Long.compare(dim1.bytesInc, dim2.bytesInc)); - Dimension lastDimension = dimensions.get(imageIndex).get(dimensions.get(imageIndex).size() - 1); - if (getDimension(imageIndex, DimensionKey.Z) == null){ - addDimension(imageIndex, new Dimension(DimensionKey.Z, 1, lastDimension.bytesInc, "m", 1.0, lastDimension.oldPhysicalSize)); - } - if (getDimension(imageIndex, DimensionKey.T) == null){ - addDimension(imageIndex, new Dimension(DimensionKey.T, 1, lastDimension.bytesInc, "s", 1.0, lastDimension.oldPhysicalSize)); - } - if (getDimension(imageIndex, DimensionKey.S) == null){ - addDimension(imageIndex, new Dimension(DimensionKey.S, 1, lastDimension.bytesInc, "", 1.0, lastDimension.oldPhysicalSize)); - } - } - - /** - * Adds channel dimension - * @param coreIndex - * @param sizeC total number of channels - * @param bytesInc - */ - public void addChannelDimension(int coreIndex){ - boolean rgb = (getDimension(coreIndex, DimensionKey.X).bytesInc % 3) == 0; - int sizeC = rgb ? channels.get(coreIndex).size() / 3 : channels.get(coreIndex).size(); - - long channelBytesInc = getChannelDimensionBytesInc(coreIndex); - - addDimension(coreIndex, Dimension.createChannelDimension(sizeC, channelBytesInc)); - } - - private long getChannelDimensionBytesInc(int coreIndex){ - boolean rgb = (getDimension(coreIndex, DimensionKey.X).bytesInc % 3) == 0; - long maxBytesInc = 0; - - if (rgb){ - for (int i = 0; i < channels.get(coreIndex).size(); i++){ - Channel channel = channels.get(coreIndex).get(i); - if (channel.channelTag == 3){ - maxBytesInc = channel.bytesInc > maxBytesInc ? channel.bytesInc : maxBytesInc; - } - } - } else { - for (Channel channel : channels.get(coreIndex)){ - maxBytesInc = channel.bytesInc > maxBytesInc ? channel.bytesInc : maxBytesInc; - } - } - - if (maxBytesInc == 0){ - Dimension yDim = getDimension(coreIndex, DimensionKey.Y); - maxBytesInc = yDim.bytesInc * yDim.size; - } - return maxBytesInc; - } - - // -- Helper functions -- - private ArrayList> ArrayListOfArrayLists(int rows, Class type) { - ArrayList> lst = new ArrayList<>(); - for (int i = 0; i < rows; i++) { - lst.add(new ArrayList()); - } - return lst; - } - - private ArrayList> ArrayListOfHashMaps(int rows, Class key, Class value) { - ArrayList> lst = new ArrayList<>(); - for (int i = 0; i < rows; i++) { - lst.add(new HashMap()); - } - return lst; - } -} diff --git a/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/MultipleImagesReader.java b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/MultipleImagesReader.java index 5a5be6fd477..b2405f979a3 100644 --- a/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/MultipleImagesReader.java +++ b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/MultipleImagesReader.java @@ -35,7 +35,9 @@ import loci.formats.in.LOFReader; import loci.formats.in.JPEGReader; import loci.formats.in.TiffDelegateReader; -import loci.formats.in.LeicaMicrosystemsMetadata.Dimension.DimensionKey; +import loci.formats.in.LeicaMicrosystemsMetadata.model.Dimension; +import loci.formats.in.LeicaMicrosystemsMetadata.model.Dimension.DimensionKey; +import loci.formats.in.LeicaMicrosystemsMetadata.xml.XlifDocument; import loci.formats.in.BMPReader; import loci.formats.in.APNGReader; @@ -183,10 +185,6 @@ public ImageFormat getImageFormat() { return imageFormat; } - public void setMetadataTempBuffer(MetadataTempBuffer metaTemp){ - this.metaTemp = metaTemp; - } - public void setCoreMetadata(CoreMetadata cmd){ if (core == null){ core = new ArrayList(); @@ -207,12 +205,12 @@ public void swapDimensions() throws FormatException{ int sizeS = tileCount; List newOrder = new ArrayList(); - List dimensions = metaTemp.getDimensions(imageIndex); + List dimensions = metadataTranslators.get(imageIndex).dimensionStore.getDimensions(); - Dimension dimZ = metaTemp.getDimension(imageIndex, DimensionKey.Z); - Dimension dimC = metaTemp.getDimension(imageIndex, DimensionKey.C); - Dimension dimT = metaTemp.getDimension(imageIndex, DimensionKey.T); - Dimension dimS = metaTemp.getDimension(imageIndex, DimensionKey.S); + Dimension dimZ = metadataTranslators.get(imageIndex).dimensionStore.getDimension(DimensionKey.Z); + Dimension dimC = metadataTranslators.get(imageIndex).dimensionStore.getDimension(DimensionKey.C); + Dimension dimT = metadataTranslators.get(imageIndex).dimensionStore.getDimension(DimensionKey.T); + Dimension dimS = metadataTranslators.get(imageIndex).dimensionStore.getDimension(DimensionKey.S); //iterating through dimensions in desired order for (int indexDim5 = 0; indexDim5 < dimensions.get(5).size; indexDim5++){ diff --git a/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/SingleImageTranslator.java b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/SingleImageTranslator.java new file mode 100644 index 00000000000..9d463bed9ac --- /dev/null +++ b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/SingleImageTranslator.java @@ -0,0 +1,262 @@ +/* + * #%L + * OME Bio-Formats package for reading and converting biological file formats. + * %% + * Copyright (C) 2005 - 2017 Open Microscopy Environment: + * - Board of Regents of the University of Wisconsin-Madison + * - Glencoe Software, Inc. + * - University of Dundee + * %% + * 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, see + * . + * #L% + */ + +package loci.formats.in.LeicaMicrosystemsMetadata; + +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Deque; +import java.util.List; + +import org.w3c.dom.Element; + +import loci.formats.CoreMetadata; +import loci.formats.MetadataTools; +import loci.formats.in.LeicaMicrosystemsMetadata.extract.ChannelExtractor; +import loci.formats.in.LeicaMicrosystemsMetadata.extract.DimensionExtractor; +import loci.formats.in.LeicaMicrosystemsMetadata.extract.Extractor; +import loci.formats.in.LeicaMicrosystemsMetadata.extract.confocal.ConfocalSettingsExtractor; +import loci.formats.in.LeicaMicrosystemsMetadata.extract.widefield.WidefieldSettingsExtractor; +import loci.formats.in.LeicaMicrosystemsMetadata.extract.HardwareSettingsExtractor; +import loci.formats.in.LeicaMicrosystemsMetadata.extract.ImageSettingsExtractor; +import loci.formats.in.LeicaMicrosystemsMetadata.extract.MicroscopeExtractor; +import loci.formats.in.LeicaMicrosystemsMetadata.extract.PositionExtractor; +import loci.formats.in.LeicaMicrosystemsMetadata.extract.ROIExtractor; +import loci.formats.in.LeicaMicrosystemsMetadata.extract.TimestampExtractor; +import loci.formats.in.LeicaMicrosystemsMetadata.helpers.LMSMainXmlNodes; +import loci.formats.in.LeicaMicrosystemsMetadata.helpers.LMSMainXmlNodes.AtlSettingLayout; +import loci.formats.in.LeicaMicrosystemsMetadata.helpers.LMSMainXmlNodes.DataSourceType; +import loci.formats.in.LeicaMicrosystemsMetadata.model.DimensionStore; +import loci.formats.in.LeicaMicrosystemsMetadata.model.Dye; +import loci.formats.in.LeicaMicrosystemsMetadata.model.Filter; +import loci.formats.in.LeicaMicrosystemsMetadata.model.ImageDetails; +import loci.formats.in.LeicaMicrosystemsMetadata.model.MicroscopeDetails; +import loci.formats.in.LeicaMicrosystemsMetadata.model.ROI; +import loci.formats.in.LeicaMicrosystemsMetadata.model.confocal.ConfocalAcquisitionSettings; +import loci.formats.in.LeicaMicrosystemsMetadata.model.confocal.Detector; +import loci.formats.in.LeicaMicrosystemsMetadata.model.confocal.DetectorSetting; +import loci.formats.in.LeicaMicrosystemsMetadata.model.confocal.Laser; +import loci.formats.in.LeicaMicrosystemsMetadata.model.confocal.LaserSetting; +import loci.formats.in.LeicaMicrosystemsMetadata.write.ConfocalSettingsWriter; +import loci.formats.in.LeicaMicrosystemsMetadata.write.DimensionWriter; +import loci.formats.in.LeicaMicrosystemsMetadata.write.ImageSettingsWriter; +import loci.formats.in.LeicaMicrosystemsMetadata.write.InstrumentWriter; +import loci.formats.in.LeicaMicrosystemsMetadata.write.WidefieldSettingsWriter; +import loci.formats.in.LeicaMicrosystemsMetadata.xml.LMSImageXmlDocument; +import loci.formats.meta.MetadataStore; + +/** + * SingleImageTranslator translates image and instrument metadata of one LMS image to + * core (CoreMetadata) and OME metadata (MetadataStore). + * + * @author Constanze Wendlandt constanze.wendlandt at leica-microsystems.com + */ +public class SingleImageTranslator { + //XML nodes + LMSMainXmlNodes xmlNodes = new LMSMainXmlNodes(); + + //extracted data + public DimensionStore dimensionStore = new DimensionStore(); + List lasers = new ArrayList(); + List laserSettings = new ArrayList(); + List detectors = new ArrayList(); + List detectorSettings = new ArrayList(); + List filters = new ArrayList(); + List rois = new ArrayList(); + List singleRois = new ArrayList(); + List timestamps = new ArrayList(); + List dyes = new ArrayList<>(); + + MicroscopeDetails microscopeDetails = new MicroscopeDetails(); + public ImageDetails imageDetails = new ImageDetails(); + ConfocalAcquisitionSettings confocalAcquisitionSettings = new ConfocalAcquisitionSettings(); + boolean alternateCenter = false; + + int imageCount; + boolean useOldPhysicalSizeCalculation; + public int currentTileIndex = 0; + + int seriesIndex; + LMSFileReader reader; + MetadataStore store; + CoreMetadata core; + + public SingleImageTranslator(LMSImageXmlDocument doc, int seriesIndex, int imageCount, LMSFileReader reader){ + this.xmlNodes.imageNode = (Element)doc.getImageNode(); + this.imageDetails.originalImageName = doc.getImageName(); + imageDetails.targetImageName = this.imageDetails.originalImageName; + imageDetails.collectionPrefix = doc.getCollectionPath(); + + this.reader = reader; + + this.seriesIndex = seriesIndex; + this.store = reader.getMetadataStore(); + this.imageCount = imageCount; + this.useOldPhysicalSizeCalculation = reader.useOldPhysicalSizeCalculation(); + } + + public void setTarget(int seriesIndex, String imageName, int tileIndex){ + imageDetails.targetImageName = imageName; + this.seriesIndex = seriesIndex; + currentTileIndex = tileIndex; + + reader.setSeries(seriesIndex); + reader.addSeriesMeta("Image name", imageDetails.targetImageName); + this.core = reader.getCore().get(seriesIndex); + } + + public void extract(){ + getMainNodes(); + + //image metadata + ImageSettingsExtractor.extractImageDetails(xmlNodes, imageDetails); + DimensionExtractor.extractChannels(xmlNodes, dimensionStore); + DimensionExtractor.extractDimensions(xmlNodes.imageDescription, useOldPhysicalSizeCalculation, dimensionStore); + timestamps = TimestampExtractor.extractTimestamps(xmlNodes.imageNode, dimensionStore.getNumberOfPlanesPerTile() * dimensionStore.tileCount); + + if (xmlNodes.hardwareSetting == null) + return; + + microscopeDetails = MicroscopeExtractor.extractMicroscopeDetails(xmlNodes); + PositionExtractor.extractFieldPositions(xmlNodes, dimensionStore); + DimensionExtractor.extractExposureTimes(xmlNodes, dimensionStore); + + switch(xmlNodes.atlSettingLayout){ + case CONFOCAL_OLD: + case CONFOCAL_NEW: + ConfocalSettingsExtractor.extractInstrumentSettings(xmlNodes, confocalAcquisitionSettings); + ConfocalSettingsExtractor.extractChannelSettings(xmlNodes, confocalAcquisitionSettings); + break; + case WIDEFIELD: + filters = WidefieldSettingsExtractor.extractWidefieldFilters(xmlNodes); + break; + case MICA_CONFOCAL: + case MICA_WIDEFIELD: + case MICA_WIDEFOCAL: + ConfocalSettingsExtractor.extractInstrumentSettings(xmlNodes, confocalAcquisitionSettings); + dyes = ChannelExtractor.extractMicaDyes(xmlNodes); + break; + default: break; + } + + extractROIs(); + } + + public void write(){ + ImageSettingsWriter.writeImageDetails(store, reader, imageDetails, seriesIndex); + DimensionWriter.writeChannels(core, store, dimensionStore, reader.getImageFormat(), seriesIndex); + DimensionWriter.writeDimensions(core, dimensionStore); + DimensionWriter.writeTimestamps(store, reader, dimensionStore, timestamps, seriesIndex); + + if (xmlNodes.hardwareSetting == null){ + //an empty instrument is created even when there are no hardware settings for the image (e.g. depth map image, EDF image), + //since this is bioformats' expectation (see OMEXMLMetadataImpl, line 7801) + String instrumentID = MetadataTools.createLSID("Instrument", seriesIndex); + store.setInstrumentID(instrumentID, seriesIndex); + store.setImageInstrumentRef(instrumentID, seriesIndex); + return; + } + + DimensionWriter.writePhysicalSizes(store, dimensionStore, seriesIndex); + DimensionWriter.writeFieldPositions(store, dimensionStore, reader, seriesIndex, currentTileIndex); + DimensionWriter.writeExposureTimes(store, dimensionStore, reader, seriesIndex); + + //instrument metadata + InstrumentWriter.writeMicroscopeDetails(store, xmlNodes, microscopeDetails, seriesIndex); + + switch(xmlNodes.atlSettingLayout){ + case CONFOCAL_OLD: + case CONFOCAL_NEW: + ConfocalSettingsWriter.initConfocalInstrumentSettings(confocalAcquisitionSettings, seriesIndex, store); + ConfocalSettingsWriter.initConfocalChannelSettings(confocalAcquisitionSettings, seriesIndex, store); + break; + case WIDEFIELD: + WidefieldSettingsWriter.initFilters(filters, dimensionStore.channels.size(), seriesIndex, store); + break; + case MICA_CONFOCAL: + case MICA_WIDEFIELD: + case MICA_WIDEFOCAL: + ConfocalSettingsWriter.initConfocalInstrumentSettings(confocalAcquisitionSettings, seriesIndex, store); + DimensionWriter.addDyeInfosToChannels(store, dyes, dimensionStore, seriesIndex); + break; + default: break; + } + + writeROIs(); + + final Deque nameStack = new ArrayDeque(); + ImageSettingsWriter.populateOriginalMetadata(xmlNodes.imageNode, nameStack, reader); + } + + public void translate(){ + extract(); + write(); + } + + private void getMainNodes(){ + xmlNodes.imageDescription = (Element) xmlNodes.imageNode.getElementsByTagName("ImageDescription").item(0); + xmlNodes.attachments = Extractor.getDescendantNodesWithName(xmlNodes.imageNode, "Attachment"); + + HardwareSettingsExtractor.extractHardwareSetting(xmlNodes); + + if (xmlNodes.hardwareSetting == null) return; + + HardwareSettingsExtractor.extractDataSourceType(xmlNodes); + if (xmlNodes.dataSourceType == null || xmlNodes.dataSourceType == DataSourceType.SPIM || + xmlNodes.dataSourceType == DataSourceType.UNDEFINED){ + System.out.println("Image data source type currently not supported!"); + return; + } + + String microscopeModel = MicroscopeExtractor.extractMicroscopeModel(xmlNodes); + xmlNodes.isMicaImage = microscopeModel.equals("MICA"); + + HardwareSettingsExtractor.extractAtlSettingLayout(xmlNodes); + if (xmlNodes.atlSettingLayout == AtlSettingLayout.UNKNOWN) return; + + HardwareSettingsExtractor.extractHardwareSettingChildNodes(xmlNodes); + } + + + private void extractROIs(){ + if (Extractor.getDescendantNodesWithName(xmlNodes.imageNode, "ROI") != null) { + alternateCenter = true; + } + + rois = ROIExtractor.translateROIs(xmlNodes.imageNode, dimensionStore.physicalSizeX, dimensionStore.physicalSizeY); + singleRois = ROIExtractor.translateSingleROIs(xmlNodes.imageNode, dimensionStore.physicalSizeX, dimensionStore.physicalSizeY); + } + + private void writeROIs(){ + int roiCount = 0; + for (int planeIndex = 0; planeIndex < reader.getImageCount(); planeIndex++){ + for (int roi = 0; roi < rois.size(); roi++) { + rois.get(roi).storeROI(store, seriesIndex, roiCount++, roi, + reader.getCore().get(seriesIndex).sizeX, reader.getCore().get(seriesIndex).sizeY, alternateCenter, + reader.getMetadataOptions().getMetadataLevel()); + } + } + } +} diff --git a/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/extract/ChannelExtractor.java b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/extract/ChannelExtractor.java new file mode 100644 index 00000000000..fe460eda10a --- /dev/null +++ b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/extract/ChannelExtractor.java @@ -0,0 +1,197 @@ +/* + * #%L + * OME Bio-Formats package for reading and converting biological file formats. + * %% + * Copyright (C) 2005 - 2017 Open Microscopy Environment: + * - Board of Regents of the University of Wisconsin-Madison + * - Glencoe Software, Inc. + * - University of Dundee + * %% + * 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, see + * . + * #L% + */ + +package loci.formats.in.LeicaMicrosystemsMetadata.extract; + +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; + +import loci.formats.in.LeicaMicrosystemsMetadata.helpers.LMSMainXmlNodes; +import loci.formats.in.LeicaMicrosystemsMetadata.model.Channel; +import loci.formats.in.LeicaMicrosystemsMetadata.model.Dye; +import ome.xml.model.primitives.Color; + +/** + * ChannelExtractor is a helper class for extracting channel information from LMS XML files. + * + * @author Constanze Wendlandt constanze.wendlandt at leica-microsystems.com + */ +public class ChannelExtractor extends Extractor { + + /*** + * Creates and returns Channels with information extracted from channel description nodes + */ + public static List extractChannels(NodeList channelDescNodes) { + List channels = new ArrayList(); + + for (int ch = 0; ch < channelDescNodes.getLength(); ch++) { + Element channelElement = (Element) channelDescNodes.item(ch); + + Channel channel = new Channel(); + channel.channelTag = Integer.parseInt(channelElement.getAttribute("ChannelTag")); + channel.resolution = Integer.parseInt(channelElement.getAttribute("Resolution")); + channel.min = parseDouble(channelElement.getAttribute("Min")); + channel.max = parseDouble(channelElement.getAttribute("Max")); + channel.unit = channelElement.getAttribute("Unit"); + channel.lutName = channelElement.getAttribute("LUTName"); + channel.bytesInc = parseLong(channelElement.getAttribute("BytesInc")); + channel.setChannelType(); + + List channelProperties = Extractor.getChildNodesWithNameAsElement(channelElement, "ChannelProperty"); + for (Element channelProperty : channelProperties){ + Element key = Extractor.getChildNodeWithNameAsElement(channelProperty, "Key"); + String keyS = key.getTextContent(); + Element value = Extractor.getChildNodeWithNameAsElement(channelProperty, "Value"); + String valueS = value.getTextContent(); + + if (keyS.equals("ChannelGroup")) + channel.channelProperties.channelGroup = Extractor.parseInt(valueS); + else if (keyS.equals("ChannelType")) + channel.channelProperties.channelType = valueS; + else if (keyS.equals("BeamRoute")) + channel.channelProperties.beamRoute = valueS; + else if (keyS.equals("DetectorName")) + channel.channelProperties.detectorName = valueS; + else if (keyS.equals("DyeName")) + channel.channelProperties.dyeName = valueS; + else if (keyS.equals("SequentialSettingIndex")) + channel.channelProperties.sequentialSettingIndex = Extractor.parseInt(valueS); + else if (keyS.equals("DigitalGatingMode")) + channel.channelProperties.digitalGatingMode = valueS; + else if (keyS.equals("TauScanLine")) + channel.channelProperties.tauScanLine = Extractor.parseDouble(valueS); + } + + channels.add(channel); + } + + translateLuts(channels); + + return channels; + } + + /*** + * Translates raw channel luts of an image to Colors and lut color indices + */ + public static void translateLuts(List channels) { + for (Channel channel : channels){ + channel.lutColor = translateLut(channel.lutName); + channel.lutColorIndex = getLutColorIndex(channel.lutName); + } + } + + public static List extractMicaDyes(LMSMainXmlNodes xmlNodes){ + Element sampleData = Extractor.getChildNodeWithNameAsElement(xmlNodes.widefocalExperimentSettings, "SampleData"); + Element samplePattern = Extractor.getChildNodeWithNameAsElement(sampleData, "SamplePattern"); + Element structuresElement = Extractor.getChildNodeWithNameAsElement(samplePattern, "Structures"); + List structures = Extractor.getChildNodesWithNameAsElement(structuresElement, "Structure"); + + List dyes = new ArrayList(); + + for (Element structure : structures){ + Dye dye = new Dye(); + Element name = Extractor.getChildNodeWithNameAsElement(structure, "Name"); + Element fluochrome = Extractor.getChildNodeWithNameAsElement(structure, "Fluochrome"); + Element lutName = Extractor.getChildNodeWithNameAsElement(structure, "LutName"); + Element emissionWavelength = Extractor.getChildNodeWithNameAsElement(structure, "EmissionWavelength"); + Element excitationWavelength = Extractor.getChildNodeWithNameAsElement(structure, "ExcitationWavelength"); + Element isAutofluo = Extractor.getChildNodeWithNameAsElement(structure, "IsAutoFluorescence"); + dye.name = name.getTextContent(); + dye.fluochromeName = fluochrome.getTextContent(); + dye.lutName = lutName.getTextContent(); + dye.emissionWavelength = Extractor.parseDouble(emissionWavelength.getTextContent()); + dye.excitationWavelength = Extractor.parseDouble(excitationWavelength.getTextContent()); + dye.isAutofluorescence = isAutofluo.getTextContent().equals("true"); + dyes.add(dye); + } + + return dyes; + } + + /** + * Translates LeicaXML lut name to lut color index + */ + private static int getLutColorIndex(String lutName) { + switch (lutName) { + case "red": + return Channel.RED; + case "green": + return Channel.GREEN; + case "blue": + return Channel.BLUE; + case "cyan": + return Channel.CYAN; + case "magenta": + return Channel.MAGENTA; + case "yellow": + return Channel.YELLOW; + case "black": + case "gray": + case "": + default: + return Channel.GREY; + } + } + + /*** + * Translates Leica XML lut name / value to Color + */ + private static Color translateLut(String lutName) { + lutName = lutName.replaceAll("\\s+", ""); + // some LUTs are stored as gradients + Pattern pattern = Pattern.compile("Gradient\\(\\d+,\\d+,\\d+\\)", Pattern.CASE_INSENSITIVE); + Matcher matcher = pattern.matcher(lutName); + if (matcher.find()) { + String[] rgb = lutName.substring(9, lutName.length() - 1).split(","); + return new Color(Integer.parseInt(rgb[2]), + Integer.parseInt(rgb[1]), + Integer.parseInt(rgb[0]), + 255); + } else { + switch (lutName.toLowerCase()) { + case "red": + return new Color(255, 0, 0, 255); + case "green": + return new Color(0, 255, 0, 255); + case "blue": + return new Color(0, 0, 255, 255); + case "cyan": + return new Color(0, 255, 255, 255); + case "magenta": + return new Color(255, 0, 255, 255); + case "yellow": + return new Color(255, 255, 0, 255); + default: + return new Color(255, 255, 255, 255); + } + } + // TODO: numeric lut handling + } +} diff --git a/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/extract/DimensionExtractor.java b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/extract/DimensionExtractor.java new file mode 100644 index 00000000000..826eb50253b --- /dev/null +++ b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/extract/DimensionExtractor.java @@ -0,0 +1,142 @@ +/* + * #%L + * OME Bio-Formats package for reading and converting biological file formats. + * %% + * Copyright (C) 2005 - 2017 Open Microscopy Environment: + * - Board of Regents of the University of Wisconsin-Madison + * - Glencoe Software, Inc. + * - University of Dundee + * %% + * 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, see + * . + * #L% + */ + +package loci.formats.in.LeicaMicrosystemsMetadata.extract; + +import java.util.ArrayList; +import java.util.List; + +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; + +import loci.formats.in.LeicaMicrosystemsMetadata.helpers.LMSMainXmlNodes; +import loci.formats.in.LeicaMicrosystemsMetadata.helpers.LMSMainXmlNodes.DataSourceType; +import loci.formats.in.LeicaMicrosystemsMetadata.model.Channel; +import loci.formats.in.LeicaMicrosystemsMetadata.model.Dimension; +import loci.formats.in.LeicaMicrosystemsMetadata.model.DimensionStore; +import loci.formats.in.LeicaMicrosystemsMetadata.model.Dimension.DimensionKey; + +/** + * DimensionExtractor is a helper class for extracting image dimension information from LMS XML files. + * + * @author Constanze Wendlandt constanze.wendlandt at leica-microsystems.com + */ +public class DimensionExtractor extends Extractor { + + /** + * Returns a list of {@link Dimension}s it extracts from LMS XML image description + */ + public static List extractDimensions(Element imageDescription, boolean useOldPhysicalSizeCalculation) { + List dimensions = new ArrayList(); + + Element dimensionsNode = (Element) imageDescription.getElementsByTagName("Dimensions").item(0); + NodeList dimensionNodes = dimensionsNode.getElementsByTagName("DimensionDescription"); + + // add dimensions + for (int i = 0; i < dimensionNodes.getLength(); i++) { + Element dimensionElement = (Element) dimensionNodes.item(i); + + int id = parseInt(dimensionElement.getAttribute("DimID")); + int size = parseInt(dimensionElement.getAttribute("NumberOfElements")); + long bytesInc = parseLong(dimensionElement.getAttribute("BytesInc")); + Double length = parseDouble(dimensionElement.getAttribute("Length")); + String unit = dimensionElement.getAttribute("Unit"); + double origin = parseDouble(dimensionElement.getAttribute("Origin")); + + Dimension dimension = new Dimension(DimensionKey.with(id), size, bytesInc, unit, length, origin, useOldPhysicalSizeCalculation); + dimensions.add(dimension); + } + + return dimensions; + } + + /** + * Extracts dimension info from LMS XML + */ + public static void extractDimensions(Element imageDescription, boolean useOldPhysicalSizeCalculation, DimensionStore dimensionStore){ + List dimensions = new ArrayList(); + + Element dimensionsNode = (Element) imageDescription.getElementsByTagName("Dimensions").item(0); + NodeList dimensionNodes = dimensionsNode.getElementsByTagName("DimensionDescription"); + + // add dimensions + for (int i = 0; i < dimensionNodes.getLength(); i++) { + Element dimensionElement = (Element) dimensionNodes.item(i); + + int id = parseInt(dimensionElement.getAttribute("DimID")); + int size = parseInt(dimensionElement.getAttribute("NumberOfElements")); + long bytesInc = parseLong(dimensionElement.getAttribute("BytesInc")); + Double length = parseDouble(dimensionElement.getAttribute("Length")); + String unit = dimensionElement.getAttribute("Unit"); + double origin = parseDouble(dimensionElement.getAttribute("Origin")); + + Dimension dimension = new Dimension(DimensionKey.with(id), size, bytesInc, unit, length, origin, useOldPhysicalSizeCalculation); + dimensions.add(dimension); + } + + for (Dimension dimension : dimensions){ + dimensionStore.addDimension(dimension); + if(dimension.key == null) + dimensionStore.extras *= dimension.size; + } + + if (dimensionStore.getDimension(DimensionKey.X) == null) { + dimensionStore.addDimension(new Dimension(DimensionKey.X, 1, 1, "m", 1.0, 0.0, useOldPhysicalSizeCalculation)); + } + if (dimensionStore.getDimension(DimensionKey.Y) == null) { + dimensionStore.addDimension(new Dimension(DimensionKey.Y, 1, 1, "m", 1.0, 0.0, useOldPhysicalSizeCalculation)); + } + + if (dimensionStore.getDimension(DimensionKey.C) == null) + dimensionStore.addChannelDimension(); + + dimensionStore.addMissingDimensions(); + } + + /** + * Extracts exposure times from LMS XML + */ + public static void extractExposureTimes(LMSMainXmlNodes xmlNodes, DimensionStore dimensionStore){ + if (xmlNodes.dataSourceType != DataSourceType.CAMERA && xmlNodes.dataSourceType != DataSourceType.WIDEFOCAL) return; + + for (int channelIndex = 0; channelIndex < dimensionStore.channels.size(); channelIndex++){ + int logicalChannelIndex = dimensionStore.rgb ? channelIndex / 3 : channelIndex; + if (xmlNodes.widefieldChannelInfos.size() <= logicalChannelIndex) break; + + String exposureTimeS = Extractor.getAttributeValue(xmlNodes.widefieldChannelInfos.get(logicalChannelIndex), "ExposureTime"); + dimensionStore.channels.get(channelIndex).exposureTime = Extractor.parseDouble(exposureTimeS); + } + } + + /** + * Extracts channel information from LMS XML + */ + public static void extractChannels(LMSMainXmlNodes xmlNodes, DimensionStore dimensionStore){ + Element channelsNode = (Element) xmlNodes.imageDescription.getElementsByTagName("Channels").item(0); + NodeList channelNodes = channelsNode.getElementsByTagName("ChannelDescription"); + List channels = ChannelExtractor.extractChannels(channelNodes); + dimensionStore.setChannels(channels); + } +} diff --git a/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/extract/Extractor.java b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/extract/Extractor.java new file mode 100644 index 00000000000..f1428e93fd7 --- /dev/null +++ b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/extract/Extractor.java @@ -0,0 +1,217 @@ +/* + * #%L + * OME Bio-Formats package for reading and converting biological file formats. + * %% + * Copyright (C) 2005 - 2017 Open Microscopy Environment: + * - Board of Regents of the University of Wisconsin-Madison + * - Glencoe Software, Inc. + * - University of Dundee + * %% + * 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, see + * . + * #L% + */ + +package loci.formats.in.LeicaMicrosystemsMetadata.extract; + +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.List; + +import javax.xml.transform.OutputKeys; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; + +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import loci.common.DataTools; +import ome.units.quantity.Length; +import ome.units.unit.Unit; + +/** + * Extractor is a helper class for navigating, reading, parsing and printing XML node contents. + * + * @author Constanze Wendlandt constanze.wendlandt at leica-microsystems.com + */ +public class Extractor { + protected static final long METER_MULTIPLY = 1000000; + + /** + * Returns the first direct child node with passed name that is found + */ + public static Node getChildNodeWithName(Node node, String nodeName) { + if (node == null || nodeName == null || nodeName.isEmpty()) return null; + + NodeList children = node.getChildNodes(); + for (int i = 0; i < children.getLength(); i++) { + if (children.item(i).getNodeName().equals(nodeName)) + return children.item(i); + } + return null; + } + + /** + * Returns the first direct child node with passed name that is found and tries to cast it to Element + */ + public static Element getChildNodeWithNameAsElement(Node node, String nodeName){ + Node child = getChildNodeWithName(node, nodeName); + Element element = null; + try { + element = (Element)child; + } catch (Exception e){} + + return element; + } + + /** + * Returns all direct child nodes with passed name and tries to cast them to Element + */ + public static List getChildNodesWithNameAsElement(Node node, String nodeName){ + if (node == null || nodeName == null || nodeName.isEmpty()) return null; + + List children = new ArrayList(); + NodeList childNodes = node.getChildNodes(); + + for (int i = 0; i < childNodes.getLength(); i++){ + Element element = null; + try { + element = (Element)childNodes.item(i); + children.add(element); + } catch (Exception e){} + } + + return children; + } + + /** + * Returns the first node of a NodeList that has an attribute with a certain value + */ + public static Node getNodeWithAttribute(NodeList nodes, String attributeName, String attributeValue) { + if (nodes == null) return null; + + for (int i = 0; i < nodes.getLength(); i++) { + Node node = nodes.item(i); + Node attribute = node.getAttributes().getNamedItem(attributeName); + if (attribute != null && attribute.getTextContent().equals(attributeValue)) + return node; + } + return null; + } + + /** + * Returns the first node of a NodeList that has an attribute with a certain value + */ + public static List getNodesWithAttributeAsElements(NodeList nodes, String attributeName, String attributeValue) { + if (nodes == null) return null; + + List childNodes = new ArrayList(); + + for (int i = 0; i < nodes.getLength(); i++) { + Node node = nodes.item(i); + Node attribute = node.getAttributes().getNamedItem(attributeName); + if (attribute != null && attribute.getTextContent().equals(attributeValue)) + try { + childNodes.add((Element)node); + } catch (Exception e){} + } + + return childNodes; + } + + /** + * Returns the value of an attribute of a node + */ + public static String getAttributeValue(Node node, String attributeName) { + if (node == null || attributeName.isEmpty()) return ""; + Node attribute = node.getAttributes().getNamedItem(attributeName); + if (attribute != null) + return attribute.getTextContent(); + else + return ""; + } + + /** + * Returns all (grand*n)children nodes with given node name + * + * @param root + * root node + * @param nodeName + * name of children that shall be searched + * @return list of child nodes with given name + */ + public static NodeList getDescendantNodesWithName(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 = getDescendantNodesWithName((Element) child, nodeName); + if (childNodes != null) { + return childNodes; + } + } + } + return null; + } else + return nodes; + } + + public static long parseLong(String value) { + return value == null || value.trim().isEmpty() ? 0 : Long.parseLong(value.trim()); + } + + public static int parseInt(String value) { + return value == null || value.trim().isEmpty() ? 0 : Integer.parseInt(value.trim()); + } + + public static double parseDouble(String value) { + return value == null || value.trim().isEmpty() ? 0d : DataTools.parseDouble(value.trim()); + } + + public static Length parseLength(String value, Unit unit){ + double valueD = parseDouble(value); + return new Length(valueD, unit); + } + + /** + * Prints the complete XML of a node + */ + public static void printNode(Node node) { + try { + // Set up the output transformer + TransformerFactory transfac = TransformerFactory.newInstance(); + Transformer trans = transfac.newTransformer(); + trans.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); + trans.setOutputProperty(OutputKeys.INDENT, "yes"); + + // Print the DOM node + + StringWriter sw = new StringWriter(); + StreamResult result = new StreamResult(sw); + DOMSource source = new DOMSource(node); + trans.transform(source, result); + String xmlString = sw.toString(); + + System.out.println(xmlString); + } catch (TransformerException e) { + e.printStackTrace(); + } + } +} diff --git a/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/extract/HardwareSettingsExtractor.java b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/extract/HardwareSettingsExtractor.java new file mode 100644 index 00000000000..90369f431a3 --- /dev/null +++ b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/extract/HardwareSettingsExtractor.java @@ -0,0 +1,301 @@ +/* + * #%L + * OME Bio-Formats package for reading and converting biological file formats. + * %% + * Copyright (C) 2005 - 2017 Open Microscopy Environment: + * - Board of Regents of the University of Wisconsin-Madison + * - Glencoe Software, Inc. + * - University of Dundee + * %% + * 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, see + * . + * #L% + */ + +package loci.formats.in.LeicaMicrosystemsMetadata.extract; + +import java.util.List; + +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; + +import loci.formats.in.LeicaMicrosystemsMetadata.extract.confocal.ConfocalSettingRecordsExtractor; +import loci.formats.in.LeicaMicrosystemsMetadata.helpers.LMSMainXmlNodes; +import loci.formats.in.LeicaMicrosystemsMetadata.helpers.LMSMainXmlNodes.AtlSettingLayout; +import loci.formats.in.LeicaMicrosystemsMetadata.helpers.LMSMainXmlNodes.DataSourceType; +import loci.formats.in.LeicaMicrosystemsMetadata.helpers.LMSMainXmlNodes.HardwareSettingLayout; + +/** + * HardwareSettingsExtractor is a helper class for extracting hardware setting nodes from LMS XML files with different layouts. + * + * @author Constanze Wendlandt constanze.wendlandt at leica-microsystems.com + */ +public class HardwareSettingsExtractor extends Extractor { + +/** + * Adds the hardware setting node to the passed LMSMainXmlNodes + * @param xmlNodes has to contain the imageNode, its hardwareSetting will be updated + */ + public static void extractHardwareSetting(LMSMainXmlNodes xmlNodes){ + //common hardware setting node for "newer" images + xmlNodes.hardwareSetting = (Element)Extractor.getNodeWithAttribute(xmlNodes.attachments, "Name", "HardwareSetting"); + + if (xmlNodes.hardwareSetting != null){ + //new hardware settings layout + xmlNodes.hardwareSettingLayout = HardwareSettingLayout.NEW; + } else { + //look for old hardware setting layout + Element hardwareSettingParent = (Element)Extractor.getNodeWithAttribute(xmlNodes.attachments, "Name", "HardwareSettingList"); + xmlNodes.hardwareSetting = hardwareSettingParent != null ? (Element)Extractor.getChildNodeWithNameAsElement(hardwareSettingParent, "HardwareSetting") : null; + + // no hardware setting found until here: it is assumed that it doesn't exist (e.g. multifocus / depth map images) + xmlNodes.hardwareSettingLayout = xmlNodes.hardwareSetting != null ? HardwareSettingLayout.OLD : HardwareSettingLayout.NONE; + } + } + + /** + * Adds the data source type to the LMSMainXmlNode + * @param xmlNodes has to contain hardwareSetting and hardwareSettingLayout + */ + public static void extractDataSourceType(LMSMainXmlNodes xmlNodes){ + int dataSourceType = -1; + + if (xmlNodes.hardwareSettingLayout == HardwareSettingLayout.OLD){ + Element scannerSetting = Extractor.getChildNodeWithNameAsElement(xmlNodes.hardwareSetting, "ScannerSetting"); + List scannerSettingRecords = Extractor.getChildNodesWithNameAsElement(scannerSetting, "ScannerSettingRecord"); + + String systemType = ""; + + for (Element scannerSettingRecord : scannerSettingRecords){ + String identifier = scannerSettingRecord.getAttribute("Identifier"); + if (identifier.equals("eDataSource")){ + String variant = scannerSettingRecord.getAttribute("Variant"); + dataSourceType = Extractor.parseInt(variant); + break; + } + //while navigating scanner setting records, look up system type in case the eDataSource record doesn't exist + if (systemType.isEmpty() && identifier.equals("SystemType")){ + systemType = scannerSettingRecord.getAttribute("Variant"); + } + } + // older SP5 images do not have the eDataSource record + if (dataSourceType == -1){ + dataSourceType = systemType.contains("SP5") ? 0 : -1; + } + } else { + String dataSourceTypeS = Extractor.getAttributeValue(xmlNodes.hardwareSetting, "DataSourceType"); + dataSourceType = Extractor.parseInt(dataSourceTypeS); + } + + switch(dataSourceType){ + case 0: + xmlNodes.dataSourceType = DataSourceType.CONFOCAL; + break; + case 1: + xmlNodes.dataSourceType = DataSourceType.CAMERA; + break; + case 2: + xmlNodes.dataSourceType = DataSourceType.SPIM; + break; + case 3: + xmlNodes.dataSourceType = DataSourceType.WIDEFOCAL; + break; + default: + xmlNodes.dataSourceType = DataSourceType.UNDEFINED; + break; + } + } + + public static void extractAtlSettingLayout(LMSMainXmlNodes xmlNodes){ + if (xmlNodes.isMicaImage){ + if (xmlNodes.dataSourceType == DataSourceType.CONFOCAL) + xmlNodes.atlSettingLayout = AtlSettingLayout.MICA_CONFOCAL; + else if (xmlNodes.dataSourceType == DataSourceType.CAMERA) + xmlNodes.atlSettingLayout = AtlSettingLayout.MICA_WIDEFIELD; + else if (xmlNodes.dataSourceType == DataSourceType.WIDEFOCAL) + xmlNodes.atlSettingLayout = AtlSettingLayout.MICA_WIDEFOCAL; + else + xmlNodes.atlSettingLayout = AtlSettingLayout.UNKNOWN; + } else { + if (xmlNodes.dataSourceType == DataSourceType.CONFOCAL) + xmlNodes.atlSettingLayout = xmlNodes.hardwareSettingLayout == HardwareSettingLayout.OLD ? AtlSettingLayout.CONFOCAL_OLD : AtlSettingLayout.CONFOCAL_NEW; + else if (xmlNodes.dataSourceType == DataSourceType.CAMERA) + xmlNodes.atlSettingLayout = AtlSettingLayout.WIDEFIELD; + else + xmlNodes.atlSettingLayout = AtlSettingLayout.UNKNOWN; + } + } + + /** + * Extracts main and LDM ATL settings and other nodes for different ATL settings layouts + * @param xmlNodes has to contain atlSettingLayout + */ + public static void extractHardwareSettingChildNodes(LMSMainXmlNodes xmlNodes){ + switch (xmlNodes.atlSettingLayout){ + case CONFOCAL_OLD: + extractLDMConfocalSettings(xmlNodes); + ConfocalSettingRecordsExtractor.extractSettingRecords(xmlNodes); + break; + case CONFOCAL_NEW: + extractMainConfocalSetting(xmlNodes); + extractLDMConfocalSettings(xmlNodes); + break; + case WIDEFIELD: + extractMainCameraSetting(xmlNodes); + extractLDMCameraSettings(xmlNodes); + extractWidefieldChannelInfos(xmlNodes); + break; + case MICA_CONFOCAL: + extractMainConfocalSetting(xmlNodes); + extractLDMConfocalSettings(xmlNodes); + extractLDMCameraSettings(xmlNodes); + extractWidefocalExperimentSettings(xmlNodes); + break; + case MICA_WIDEFIELD: + case MICA_WIDEFOCAL: + extractMainCameraSetting(xmlNodes); + extractLDMCameraSettings(xmlNodes); + extractLDMConfocalSettings(xmlNodes); + extractWidefieldChannelInfos(xmlNodes); + extractWidefocalExperimentSettings(xmlNodes); + break; + default: break; + } + } + + + /** + * Adds main confocal setting to passed LMSMainXmlNodes + * @param xmlNodes has to contain hardware setting + */ + public static void extractMainConfocalSetting(LMSMainXmlNodes xmlNodes){ + xmlNodes.mainConfocalSetting = Extractor.getChildNodeWithNameAsElement(xmlNodes.hardwareSetting, "ATLConfocalSettingDefinition"); + } + + + /** + * Adds main camera setting to passed LMSMainXmlNodes + * @param xmlNodes has to contain hardware setting + */ + public static void extractMainCameraSetting(LMSMainXmlNodes xmlNodes){ + xmlNodes.mainCameraSetting = Extractor.getChildNodeWithNameAsElement(xmlNodes.hardwareSetting, "ATLCameraSettingDefinition"); + Element seeSequentialBlock = Extractor.getChildNodeWithNameAsElement(xmlNodes.mainCameraSetting, "SEE_SEQUENTIAL_BLOCK"); + if (seeSequentialBlock != null) + xmlNodes.mainCameraSetting = null; + } + + + /** + * Adds master and sequential confocal settings, if existing, to passed LMSMainXmlNodes + * @param xmlNodes has to contain hardware setting + */ + public static void extractLDMConfocalSettings(LMSMainXmlNodes xmlNodes){ + // get LDM_Block_Sequential + Element ldmBlockSequential; + if (xmlNodes.isMicaImage){ + Element blockWidefocal = Extractor.getChildNodeWithNameAsElement(xmlNodes.hardwareSetting, "Block_Widefocal"); + ldmBlockSequential = Extractor.getChildNodeWithNameAsElement(blockWidefocal, "LDM_Block_Sequential"); + } else { + ldmBlockSequential = Extractor.getChildNodeWithNameAsElement(xmlNodes.hardwareSetting, "LDM_Block_Sequential"); + } + + // get master and sequential confocal settings + if (ldmBlockSequential != null){ + Element ldmBlockSequentialMaster = Extractor.getChildNodeWithNameAsElement(ldmBlockSequential, "LDM_Block_Sequential_Master"); + xmlNodes.masterConfocalSetting = Extractor.getChildNodeWithNameAsElement(ldmBlockSequentialMaster, "ATLConfocalSettingDefinition"); + + Element ldmBlockSequentialList = Extractor.getChildNodeWithNameAsElement(ldmBlockSequential, "LDM_Block_Sequential_List"); + NodeList sequentialConfocalSettings = ldmBlockSequentialList.getChildNodes(); + + for (int channelIndex = 0; channelIndex < sequentialConfocalSettings.getLength(); channelIndex++){ + Element sequentialConfocalSetting; + try { + sequentialConfocalSetting = (Element)sequentialConfocalSettings.item(channelIndex); + } catch (Exception e){ + continue; + } + xmlNodes.sequentialConfocalSettings.add(sequentialConfocalSetting); + } + } + } + + + /** + * Adds master and sequential camera settings, if existing, to passed LMSMainXmlNodes + * @param xmlNodes has to contain hardware setting + */ + public static void extractLDMCameraSettings(LMSMainXmlNodes xmlNodes){ + // get LDM_Block_Widefield_Sequential + Element ldmBlockWidefieldSequential; + if (xmlNodes.isMicaImage){ + Element blockWidefocal = Extractor.getChildNodeWithNameAsElement(xmlNodes.hardwareSetting, "Block_Widefocal"); + ldmBlockWidefieldSequential = Extractor.getChildNodeWithNameAsElement(blockWidefocal, "LDM_Block_Widefield_Sequential"); + } else { + ldmBlockWidefieldSequential = Extractor.getChildNodeWithNameAsElement(xmlNodes.hardwareSetting, "LDM_Block_Widefield_Sequential"); + } + + // get master and sequential camera settings + if (ldmBlockWidefieldSequential != null){ + Element ldmBlockSequentialMaster = Extractor.getChildNodeWithNameAsElement(ldmBlockWidefieldSequential, "LDM_Block_Sequential_Master"); + xmlNodes.masterCameraSetting = Extractor.getChildNodeWithNameAsElement(ldmBlockSequentialMaster, "ATLCameraSettingDefinition"); + if (xmlNodes.mainCameraSetting == null) + xmlNodes.mainCameraSetting = xmlNodes.masterCameraSetting; + + Element ldmBlockSequentialList = Extractor.getChildNodeWithNameAsElement(ldmBlockWidefieldSequential, "LDM_Block_Sequential_List"); + NodeList sequentialCameraSettings = ldmBlockSequentialList.getChildNodes(); + + for (int channelIndex = 0; channelIndex < sequentialCameraSettings.getLength(); channelIndex++){ + Element sequentialCameraSetting; + try { + sequentialCameraSetting = (Element)sequentialCameraSettings.item(channelIndex); + } catch (Exception e){ + continue; + } + xmlNodes.sequentialCameraSettings.add(sequentialCameraSetting); + } + } + } + + + /** + * Adds widefield channel infos, if existing, to passed LMSMainXmlNodes + * @param xmlNodes has to contain main or sequential camera setting(s) + */ + public static void extractWidefieldChannelInfos(LMSMainXmlNodes xmlNodes){ + if (xmlNodes.sequentialCameraSettings.size() > 0){ + //sequential camera settings > widefield channel config > widefield channel info + for (int channelIndex = 0; channelIndex < xmlNodes.sequentialCameraSettings.size(); channelIndex++){ + Element sequentialCameraSetting = xmlNodes.sequentialCameraSettings.get(channelIndex); + Element widefieldChannelConfig = Extractor.getChildNodeWithNameAsElement(sequentialCameraSetting, "WideFieldChannelConfigurator"); + Element widefieldChannelInfo = Extractor.getChildNodeWithNameAsElement(widefieldChannelConfig, "WideFieldChannelInfo"); + xmlNodes.widefieldChannelInfos.add(widefieldChannelInfo); + } + } else { + //main camera setting > widefield channel config > widefield channel infos + xmlNodes.widefieldChannelConfig = Extractor.getChildNodeWithNameAsElement(xmlNodes.mainCameraSetting, "WideFieldChannelConfigurator"); + if (xmlNodes.widefieldChannelConfig == null) return; + + NodeList widefieldChannelInfos = Extractor.getDescendantNodesWithName(xmlNodes.widefieldChannelConfig, "WideFieldChannelInfo"); + for (int channelIndex = 0; channelIndex < widefieldChannelInfos.getLength(); channelIndex++){ + Element widefieldChannelInfo = (Element)widefieldChannelInfos.item(channelIndex); + xmlNodes.widefieldChannelInfos.add(widefieldChannelInfo); + } + } + } + + public static void extractWidefocalExperimentSettings(LMSMainXmlNodes xmlNodes){ + xmlNodes.widefocalExperimentSettings = (Element)Extractor.getNodeWithAttribute(xmlNodes.attachments, + "Name", "WidefocalExperimentSettings"); + } +} diff --git a/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/extract/ImageSettingsExtractor.java b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/extract/ImageSettingsExtractor.java new file mode 100644 index 00000000000..191df1d7b85 --- /dev/null +++ b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/extract/ImageSettingsExtractor.java @@ -0,0 +1,55 @@ +/* + * #%L + * OME Bio-Formats package for reading and converting biological file formats. + * %% + * Copyright (C) 2005 - 2017 Open Microscopy Environment: + * - Board of Regents of the University of Wisconsin-Madison + * - Glencoe Software, Inc. + * - University of Dundee + * %% + * 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, see + * . + * #L% + */ + +package loci.formats.in.LeicaMicrosystemsMetadata.extract; + +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import loci.formats.in.LeicaMicrosystemsMetadata.helpers.LMSMainXmlNodes; +import loci.formats.in.LeicaMicrosystemsMetadata.model.ImageDetails; + +/** + * Helper class for extracting image details from LMS XML + * + * @author Constanze Wendlandt constanze.wendlandt at leica-microsystems.com + */ +public class ImageSettingsExtractor { + + /** + * Extracts image details from LMS XML + */ + public static void extractImageDetails(LMSMainXmlNodes xmlNodes, ImageDetails imageDetails){ + NodeList attachmentNodes = Extractor.getDescendantNodesWithName(xmlNodes.imageNode, "User-Comment"); + if (attachmentNodes != null){ + for (int i = 0; i < attachmentNodes.getLength(); i++) { + Node attachment = attachmentNodes.item(i); + imageDetails.userComments.add(attachment.getTextContent()); + if (i == 0) + imageDetails.description = attachment.getTextContent(); + } + } + } +} diff --git a/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/extract/MicroscopeExtractor.java b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/extract/MicroscopeExtractor.java new file mode 100644 index 00000000000..c86eb75b7f9 --- /dev/null +++ b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/extract/MicroscopeExtractor.java @@ -0,0 +1,108 @@ +/* + * #%L + * OME Bio-Formats package for reading and converting biological file formats. + * %% + * Copyright (C) 2005 - 2017 Open Microscopy Environment: + * - Board of Regents of the University of Wisconsin-Madison + * - Glencoe Software, Inc. + * - University of Dundee + * %% + * 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, see + * . + * #L% + */ + +package loci.formats.in.LeicaMicrosystemsMetadata.extract; + +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; + +import loci.formats.in.LeicaMicrosystemsMetadata.helpers.LMSMainXmlNodes; +import loci.formats.in.LeicaMicrosystemsMetadata.helpers.LMSMainXmlNodes.HardwareSettingLayout; +import loci.formats.in.LeicaMicrosystemsMetadata.model.MicroscopeDetails; +import loci.formats.in.LeicaMicrosystemsMetadata.model.Objective; +import ome.xml.model.enums.MicroscopeType; + +/** + * MicroscopeExtractor is a helper class for extracting microscope hardware information from LMS XML files. + * + * @author Constanze Wendlandt constanze.wendlandt at leica-microsystems.com + */ +public class MicroscopeExtractor { + + public static MicroscopeDetails extractMicroscopeDetails(LMSMainXmlNodes xmlNodes){ + MicroscopeDetails micDetails = new MicroscopeDetails(); + + micDetails.microscopeModel = MicroscopeExtractor.extractMicroscopeModel(xmlNodes); + micDetails.microscopeType = MicroscopeExtractor.extractMicroscopeType(xmlNodes); + micDetails.serialNumber = MicroscopeExtractor.extractMicroscopeSerialNumber(xmlNodes); + micDetails.objective = MicroscopeExtractor.extractObjective(xmlNodes); + + return micDetails; + } + + public static String extractMicroscopeModel(LMSMainXmlNodes xmlNodes){ + if (xmlNodes.hardwareSettingLayout == HardwareSettingLayout.OLD){ + Element scannerSetting = Extractor.getChildNodeWithNameAsElement(xmlNodes.hardwareSetting, "ScannerSetting"); + NodeList scannerSettingRecords = Extractor.getDescendantNodesWithName(scannerSetting, "ScannerSettingRecord"); + Element systemTypeNode = (Element)Extractor.getNodeWithAttribute(scannerSettingRecords, "Identifier", "SystemType"); + return Extractor.getAttributeValue(systemTypeNode, "Variant"); + } else { + return Extractor.getAttributeValue(xmlNodes.hardwareSetting, "SystemTypeName"); + } + } + + public static MicroscopeType extractMicroscopeType(LMSMainXmlNodes xmlNodes){ + MicroscopeType micType = MicroscopeType.OTHER; + Element setting = xmlNodes.getAtlSetting(); + String isInverse = Extractor.getAttributeValue(setting, "IsInverseMicroscopeModel"); + if (isInverse != ""){ + micType = isInverse.equals("1") ? MicroscopeType.INVERTED : MicroscopeType.UPRIGHT; + } + + return micType; + } + + public static String extractMicroscopeSerialNumber(LMSMainXmlNodes xmlNodes) { + if (xmlNodes.masterConfocalSetting != null){ + return Extractor.getAttributeValue(xmlNodes.masterConfocalSetting, "SystemSerialNumber"); + } else if (xmlNodes.filterSettingRecords != null){ + Element systemNumberNode = (Element)Extractor.getNodeWithAttribute(xmlNodes.filterSettingRecords, "Description", "System Number"); + return Extractor.getAttributeValue(systemNumberNode, "Variant"); + } else { + return ""; + } + } + + public static Objective extractObjective(LMSMainXmlNodes xmlNodes){ + Element setting = xmlNodes.getAtlSetting(); + if (setting == null){ + return xmlNodes.confocalSettingRecords.objectiveRecord; + } else { + Objective objective = new Objective(); + // e.g. "HC PL APO 20x/0.70 DRY" or "HCX PL FLUOTAR 40x/0.75 DRY" + objective.model = setting.getAttribute("ObjectiveName"); + objective.setCorrectionFromObjectiveName(objective.model); + String naS = setting.getAttribute("NumericalAperture"); + objective.numericalAperture = Extractor.parseDouble(naS); + objective.objectiveNumber = setting.getAttribute("ObjectiveNumber"); + String magnificationS = setting.getAttribute("Magnification"); + objective.magnification = Extractor.parseDouble(magnificationS); + objective.immersion = setting.getAttribute("Immersion"); + String refractionIndexS = setting.getAttribute("RefractionIndex"); + objective.refractionIndex = Extractor.parseDouble(refractionIndexS); + return objective; + } + } +} diff --git a/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/extract/PositionExtractor.java b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/extract/PositionExtractor.java new file mode 100644 index 00000000000..31f4db64381 --- /dev/null +++ b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/extract/PositionExtractor.java @@ -0,0 +1,167 @@ +/* + * #%L + * OME Bio-Formats package for reading and converting biological file formats. + * %% + * Copyright (C) 2005 - 2017 Open Microscopy Environment: + * - Board of Regents of the University of Wisconsin-Madison + * - Glencoe Software, Inc. + * - University of Dundee + * %% + * 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, see + * . + * #L% + */ + +package loci.formats.in.LeicaMicrosystemsMetadata.extract; + +import java.util.List; + +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; + +import loci.formats.in.LeicaMicrosystemsMetadata.helpers.LMSMainXmlNodes; +import loci.formats.in.LeicaMicrosystemsMetadata.helpers.Tuple; +import loci.formats.in.LeicaMicrosystemsMetadata.model.Dimension; +import loci.formats.in.LeicaMicrosystemsMetadata.model.Dimension.DimensionKey; +import loci.formats.in.LeicaMicrosystemsMetadata.model.DimensionStore; +import loci.formats.in.LeicaMicrosystemsMetadata.model.DimensionStore.ZDriveMode; +import ome.units.UNITS; +import ome.units.quantity.Length; + +/** + * PositionExtractor is a helper class for extracting XYZ field position and setup information from LMS XML files. + * + * @author Constanze Wendlandt constanze.wendlandt at leica-microsystems.com + */ +public class PositionExtractor extends Extractor { + + /*** + * Extracts XYZ field positions and configuration from LMS XML and adds it to dimensionStore + * @param xmlNodes + * @param dimensionStore + */ + public static void extractFieldPositions(LMSMainXmlNodes xmlNodes, DimensionStore dimensionStore){ + Element mainSetting = xmlNodes.getAtlSetting(); + + NodeList attachments = Extractor.getDescendantNodesWithName(xmlNodes.imageNode, "Attachment"); + Element tilescanInfo = (Element)Extractor.getNodeWithAttribute(attachments, "Name", "TileScanInfo"); + + // flipX, flipY, swapXY + if (tilescanInfo != null){ + String flipXS = getAttributeValue(tilescanInfo, "FlipX"); + dimensionStore.flipX = flipXS.equals("1"); + + String flipYS = getAttributeValue(tilescanInfo, "FlipY"); + dimensionStore.flipY = flipYS.equals("1"); + + String swapXYS = getAttributeValue(tilescanInfo, "SwapXY"); + dimensionStore.swapXY = swapXYS.equals("1"); + } else { + String flipXS = getAttributeValue(mainSetting, "FlipX"); + dimensionStore.flipX = flipXS.equals("1"); + + String flipYS = getAttributeValue(mainSetting, "FlipY"); + dimensionStore.flipY = flipYS.equals("1"); + + String swapXYS = getAttributeValue(mainSetting, "SwapXY"); + dimensionStore.swapXY = swapXYS.equals("1"); + } + + // get XY(Z) field positions from tilescan info + if (tilescanInfo != null){ + NodeList tiles = tilescanInfo.getChildNodes(); + for (int i = 0; i < tiles.getLength(); i++){ + Element tile; + try { + tile = (Element)tiles.item(i); + } catch (Exception e){ + continue; + } + + String fieldPosXS = getAttributeValue(tile, "PosX"); + Length fieldPosX = parseLength(fieldPosXS, UNITS.METER); + String fieldPosYS = getAttributeValue(tile, "PosY"); + Length fieldPosY = parseLength(fieldPosYS, UNITS.METER); + String fieldPosZS = getAttributeValue(tile, "PosZ"); + Length fieldPosZ = null; + if (!fieldPosZS.isEmpty()){ + dimensionStore.tilescanInfoHasZ = true; + fieldPosZ = parseLength(fieldPosZS, UNITS.METER); + } + dimensionStore.fieldPositions.add(new Tuple(fieldPosX, fieldPosY, fieldPosZ)); + } + } else { + String fieldPosXS = getAttributeValue(mainSetting, "StagePosX"); + Length fieldPosX = parseLength(fieldPosXS, UNITS.METER); + String fieldPosYS = getAttributeValue(mainSetting, "StagePosY"); + Length fieldPosY = parseLength(fieldPosYS, UNITS.METER); + dimensionStore.fieldPositions.add(new Tuple(fieldPosX, fieldPosY, null)); + } + + // swapping and flipping + for (int planeIndex = 0; planeIndex < dimensionStore.fieldPositions.size(); planeIndex++){ + Tuple fieldPosition = dimensionStore.fieldPositions.get(planeIndex); + + if (dimensionStore.swapXY){ + Length temp = fieldPosition.first; + fieldPosition.first = fieldPosition.second; + fieldPosition.second = temp; + } + + if (dimensionStore.flipX) + fieldPosition.first = flip(fieldPosition.first); + + if (dimensionStore.flipY) + fieldPosition.second = flip(fieldPosition.second); + } + + // extract additional Z drive / position values + String beginS = getAttributeValue(mainSetting, "Begin"); + dimensionStore.zBegin = parseDouble(beginS); + String endS = getAttributeValue(mainSetting, "End"); + dimensionStore.zEnd = parseDouble(endS); + + String zUseMode = getAttributeValue(mainSetting, "ZUseMode"); + dimensionStore.zDriveMode = zUseMode.equals("1") ? ZDriveMode.ZGalvo : ZDriveMode.ZWide; + + Element zPositionList = getChildNodeWithNameAsElement(mainSetting, "AdditionalZPositionList"); + if (zPositionList != null){ + List zPositions = getChildNodesWithNameAsElement(zPositionList, "AdditionalZPosition"); + for (Element zPosition : zPositions){ + String positionS = getAttributeValue(zPosition, "ZPosition"); + double position = parseDouble(positionS); + String useMode = getAttributeValue(zPosition, "ZUseModeName"); + if (useMode.equals("z-galvo")){ + dimensionStore.zGalvoPosition = position; + } else if (useMode.equals("z-wide")){ + dimensionStore.zWidePosition = position; + } + } + } + + //SP5 images without ATL settings: main ATLConfocalSetting > begin / end do not exist, neither does AdditionalZPositionList + if (dimensionStore.zBegin == 0 && dimensionStore.zEnd == 0){ + Dimension dimZ = dimensionStore.getDimension(DimensionKey.Z); + dimensionStore.zBegin = dimZ.origin; + int sign = xmlNodes.confocalSettingRecords.reverseZ ? -1 : 1; + dimensionStore.zEnd = dimensionStore.zBegin + sign * dimZ.getPhysicalStepSize() * dimZ.size; + } + } + + private static Length flip(Length pos) { + return pos != null ? new Length(-pos.value().doubleValue(), pos.unit()) : null; + } +} + + diff --git a/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/extract/ROIExtractor.java b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/extract/ROIExtractor.java new file mode 100644 index 00000000000..e26a8a5d715 --- /dev/null +++ b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/extract/ROIExtractor.java @@ -0,0 +1,212 @@ +/* + * #%L + * OME Bio-Formats package for reading and converting biological file formats. + * %% + * Copyright (C) 2005 - 2017 Open Microscopy Environment: + * - Board of Regents of the University of Wisconsin-Madison + * - Glencoe Software, Inc. + * - University of Dundee + * %% + * 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, see + * . + * #L% + */ + +package loci.formats.in.LeicaMicrosystemsMetadata.extract; + +import java.util.ArrayList; +import java.util.List; + +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; + +import loci.common.DataTools; +import loci.formats.in.LeicaMicrosystemsMetadata.model.ROI; + +/** + * ROIExtractor is a helper class for extracting ROI information from LMS XML files. + * + * @author Melissa Linkert melissa at glencoesoftware.com + * @author Constanze Wendlandt constanze.wendlandt at leica-microsystems.com + */ +public class ROIExtractor extends Extractor { + + /** + * Returns a list of {@link ROI}s extracted from an image node + */ + public static List translateROIs(Element imageNode, double physicalSizeX, double physicalSizeY) { + List imageROIs = new ArrayList(); + + NodeList rois = getDescendantNodesWithName(imageNode, "Annotation"); + if (rois == null) + return imageROIs; + + 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 && !type.trim().isEmpty()) { + roi.type = Integer.parseInt(type.trim()); + } + String color = roiNode.getAttribute("color"); + if (color != null && !color.trim().isEmpty()) { + roi.color = Long.parseLong(color.trim()); + } + roi.name = roiNode.getAttribute("name"); + roi.fontName = roiNode.getAttribute("fontName"); + roi.fontSize = roiNode.getAttribute("fontSize"); + + Double transX = DataTools.parseDouble(roiNode.getAttribute("transTransX")); + if (transX != null) { + roi.transX = transX / physicalSizeX; + } + Double transY = DataTools.parseDouble(roiNode.getAttribute("transTransY")); + if (transY != null) { + roi.transY = transY / physicalSizeY; + } + transX = DataTools.parseDouble(roiNode.getAttribute("transScalingX")); + if (transX != null) { + roi.scaleX = transX / physicalSizeX; + } + transY = DataTools.parseDouble(roiNode.getAttribute("transScalingY")); + if (transY != null) { + roi.scaleY = transY / physicalSizeY; + } + Double rotation = DataTools.parseDouble(roiNode.getAttribute("transRotation")); + if (rotation != null) { + roi.rotation = rotation; + } + String linewidth = roiNode.getAttribute("linewidth"); + try { + if (linewidth != null && !linewidth.trim().isEmpty()) { + roi.linewidth = Integer.parseInt(linewidth.trim()); + } + } catch (NumberFormatException e) { + } + + roi.text = roiNode.getAttribute("text"); + + NodeList vertices = getDescendantNodesWithName(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 && !xx.trim().isEmpty()) { + roi.x.add(DataTools.parseDouble(xx.trim())); + } + if (yy != null && !yy.trim().isEmpty()) { + roi.y.add(DataTools.parseDouble(yy.trim())); + } + } + imageROIs.add(roi); + } + + return imageROIs; + } + + /** + * Returns a list of "single" {@link ROI}s extracted from an image node + */ + public static List translateSingleROIs(Element imageNode, double physicalSizeX, double physicalSizeY) { + List imageROIs = new ArrayList(); + + NodeList children = getDescendantNodesWithName(imageNode, "ROI"); + if (children == null) + return imageROIs; + children = getDescendantNodesWithName((Element) children.item(0), "Children"); + if (children == null) + return imageROIs; + children = getDescendantNodesWithName((Element) children.item(0), "Element"); + if (children == null) + return imageROIs; + + for (int r = 0; r < children.getLength(); r++) { + NodeList rois = getDescendantNodesWithName((Element) children.item(r), "ROISingle"); + + Element roiNode = (Element) rois.item(0); + ROI roi = new ROI(); + + String type = roiNode.getAttribute("RoiType"); + if (type != null && !type.trim().isEmpty()) { + roi.type = Integer.parseInt(type.trim()); + } + String color = roiNode.getAttribute("Color"); + if (color != null && !color.trim().isEmpty()) { + roi.color = Long.parseLong(color.trim()); + } + Element parent = (Element) roiNode.getParentNode(); + parent = (Element) parent.getParentNode(); + roi.name = parent.getAttribute("Name"); + + NodeList vertices = getDescendantNodesWithName(roiNode, "P"); + + 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 && !xx.trim().isEmpty()) { + Double x = DataTools.parseDouble(xx.trim()); + if (x != null) { + roi.x.add(x / physicalSizeX); + } + } + if (yy != null && !yy.trim().isEmpty()) { + Double y = DataTools.parseDouble(yy.trim()); + if (y != null) { + roi.y.add(y / physicalSizeY); + } + } + } + + Element transform = (Element) getDescendantNodesWithName(roiNode, "Transformation").item(0); + + Double rotation = DataTools.parseDouble(transform.getAttribute("Rotation")); + if (rotation != null) { + roi.rotation = rotation; + } + + Element scaling = (Element) getDescendantNodesWithName(transform, "Scaling").item(0); + Double scaleX = DataTools.parseDouble(scaling.getAttribute("XScale")); + Double scaleY = DataTools.parseDouble(scaling.getAttribute("YScale")); + if (scaleX != null) { + roi.scaleX = scaleX; + } + if (scaleY != null) { + roi.scaleY = scaleY; + } + + Element translation = (Element) getDescendantNodesWithName(transform, "Translation").item(0); + Double transX = DataTools.parseDouble(translation.getAttribute("X")); + Double transY = DataTools.parseDouble(translation.getAttribute("Y")); + if (transX != null) { + roi.transX = transX / physicalSizeX; + } + if (transY != null) { + roi.transY = transY / physicalSizeY; + } + + imageROIs.add(roi); + } + + return imageROIs; + } +} diff --git a/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/extract/TimestampExtractor.java b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/extract/TimestampExtractor.java new file mode 100644 index 00000000000..5e36643a6e2 --- /dev/null +++ b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/extract/TimestampExtractor.java @@ -0,0 +1,121 @@ +/* + * #%L + * OME Bio-Formats package for reading and converting biological file formats. + * %% + * Copyright (C) 2005 - 2017 Open Microscopy Environment: + * - Board of Regents of the University of Wisconsin-Madison + * - Glencoe Software, Inc. + * - University of Dundee + * %% + * 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, see + * . + * #L% + */ + +package loci.formats.in.LeicaMicrosystemsMetadata.extract; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; + +import loci.common.DateTools; + +/** + * TimestampExtractor is a helper class for extracting timestamp information from LMS XML files. + * + * @author Melissa Linkert melissa at glencoesoftware.com + * @author Constanze Wendlandt constanze.wendlandt at leica-microsystems.com + */ +public class TimestampExtractor extends Extractor { + /** + * Extracts and returns timestamps from image node + */ + public static List extractTimestamps(Element imageNode, int planeCount) { + List timestamps = new ArrayList(); + + NodeList timeStampLists = getDescendantNodesWithName(imageNode, "TimeStampList"); + if (timeStampLists == null) + return timestamps; + + Element timeStampList = (Element) timeStampLists.item(0); + + // probe if timestamps are saved in the format of LAS AF 3.1 or newer + String numberOfTimeStamps = timeStampList.getAttribute("NumberOfTimeStamps"); + if (numberOfTimeStamps != null && !numberOfTimeStamps.isEmpty()) { + // LAS AF 3.1 (or newer) timestamps are available + String timeStampsRaw = timeStampList.getTextContent(); + List timeStamps = Arrays.asList(timeStampsRaw.split(" ")); + for (int stamp = 0; stamp < timeStamps.size(); stamp++) { + if (stamp < planeCount) { + String timestamp = timeStamps.get(stamp); + timestamps.add(translateSingleTimestamp(timestamp)); + } + } + } else { + // probe if timestamps are saved in the format of LAS AF 3.0 or older + NodeList timestampNodes = getDescendantNodesWithName(imageNode, "TimeStamp"); + if (timestampNodes != null) { + // LAS AF 3.0 (or older) timestamps are available + for (int stamp = 0; stamp < timestampNodes.getLength(); stamp++) { + if (stamp < planeCount) { + Element timestamp = (Element) timestampNodes.item(stamp); + timestamps.add(translateSingleTimestamp(timestamp)); + } + } + } else { + return timestamps; + } + } + + return timestamps; + } + + /** + * Translates hex timestamp string to double value + * @return number of seconds + */ + private static double translateSingleTimestamp(String timestamp) { + timestamp = timestamp.trim(); + int stampLowStart = Math.max(0, timestamp.length() - 8); + int stampHighEnd = Math.max(0, stampLowStart); + String stampHigh = timestamp.substring(0, stampHighEnd); + String stampLow = timestamp.substring(stampLowStart, timestamp.length()); + long high = stampHigh == null || stampHigh.trim().isEmpty() ? 0 + : Long.parseLong(stampHigh.trim(), 16); + long low = stampLow == null || stampLow.trim().isEmpty() ? 0 + : Long.parseLong(stampLow.trim(), 16); + long milliseconds = DateTools.getMillisFromTicks(high, low); + double seconds = (double) milliseconds / 1000; + return seconds; + } + + /** + * Translates the content of a timestamp node to double value + */ + private static double translateSingleTimestamp(Element timestamp) { + String stampHigh = timestamp.getAttribute("HighInteger"); + String stampLow = timestamp.getAttribute("LowInteger"); + long high = stampHigh == null || stampHigh.trim().isEmpty() ? 0 + : Long.parseLong(stampHigh.trim()); + long low = stampLow == null || stampLow.trim().isEmpty() ? 0 + : Long.parseLong(stampLow.trim()); + + long milliseconds = DateTools.getMillisFromTicks(high, low); + double seconds = (double) milliseconds / 1000; + return seconds; + } +} diff --git a/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/extract/confocal/ConfocalSettingRecordsExtractor.java b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/extract/confocal/ConfocalSettingRecordsExtractor.java new file mode 100644 index 00000000000..3e649a53426 --- /dev/null +++ b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/extract/confocal/ConfocalSettingRecordsExtractor.java @@ -0,0 +1,309 @@ +/* + * #%L + * OME Bio-Formats package for reading and converting biological file formats. + * %% + * Copyright (C) 2005 - 2017 Open Microscopy Environment: + * - Board of Regents of the University of Wisconsin-Madison + * - Glencoe Software, Inc. + * - University of Dundee + * %% + * 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, see + * . + * #L% + */ + +package loci.formats.in.LeicaMicrosystemsMetadata.extract.confocal; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.w3c.dom.Element; +import org.w3c.dom.Node; + +import loci.formats.in.LeicaMicrosystemsMetadata.extract.Extractor; +import loci.formats.in.LeicaMicrosystemsMetadata.helpers.LMSMainXmlNodes; +import loci.formats.in.LeicaMicrosystemsMetadata.model.confocal.Aotf; +import loci.formats.in.LeicaMicrosystemsMetadata.model.confocal.ConfocalSettingRecords; +import loci.formats.in.LeicaMicrosystemsMetadata.model.confocal.Detector; +import loci.formats.in.LeicaMicrosystemsMetadata.model.confocal.Laser; +import loci.formats.in.LeicaMicrosystemsMetadata.model.confocal.LaserSetting; +import loci.formats.in.LeicaMicrosystemsMetadata.model.confocal.Multiband; + +/** + * Helper class for translating confocal scanner and filter setting records to more easily accessible data structures + * + * @author Constanze Wendlandt constanze.wendlandt at leica-microsystems.com + */ +public class ConfocalSettingRecordsExtractor extends Extractor { + public static void extractSettingRecords(LMSMainXmlNodes xmlNodes){ + + Element scannerSetting = Extractor.getChildNodeWithNameAsElement(xmlNodes.hardwareSetting, "ScannerSetting"); + xmlNodes.scannerSettingRecords = Extractor.getDescendantNodesWithName(scannerSetting, "ScannerSettingRecord"); + Element filterSetting = Extractor.getChildNodeWithNameAsElement(xmlNodes.hardwareSetting, "FilterSetting"); + xmlNodes.filterSettingRecords = Extractor.getDescendantNodesWithName(filterSetting, "FilterSettingRecord"); + + if (xmlNodes.scannerSettingRecords == null || xmlNodes.filterSettingRecords == null) return; + + xmlNodes.confocalSettingRecords = new ConfocalSettingRecords(); + ConfocalSettingRecords records = xmlNodes.confocalSettingRecords; + + // scanner setting records + for (int i = 0; i < xmlNodes.scannerSettingRecords.getLength(); i++){ + Node scannerRecord = xmlNodes.scannerSettingRecords.item(i); + + String identifier = getAttributeValue(scannerRecord, "Identifier"); + String variant = getAttributeValue(scannerRecord, "Variant"); + String description = getAttributeValue(scannerRecord, "Description"); + + if (identifier.equals("dblPinhole")){ + records.pinholeSize = Extractor.parseDouble(variant) * METER_MULTIPLY; + } else if (identifier.equals("dblZoom")){ + records.zoom = parseDouble(variant); + } else if (identifier.equals("eDirectional")){ + records.reverseX = variant.equals("1"); + } else if (identifier.equals("eDirectionalY")){ + records.reverseY = variant.equals("1"); + } else if (identifier.contains("Shutter") || description.contains("Shutter")){ + extractShutterRecord(records, identifier, variant); + } + } + + // filter setting records + for (int i = 0; i < xmlNodes.filterSettingRecords.getLength(); i++){ + Node filterRecord = xmlNodes.filterSettingRecords.item(i); + + String objectName = Extractor.getAttributeValue(filterRecord, "ObjectName"); + String className = Extractor.getAttributeValue(filterRecord, "ClassName"); + String attribute = Extractor.getAttributeValue(filterRecord, "Attribute"); + String data = Extractor.getAttributeValue(filterRecord, "Data"); + String variant = Extractor.getAttributeValue(filterRecord, "Variant"); + + if (className.equals("CScanCtrlUnit") && attribute.equals("Speed")){ + records.readOutRate = parseDouble(variant); + } else if (className.equals("CAotf")){ + extractAotfRecord(records, objectName, attribute, data, variant); + } else if (className.equals("CLaser")){ + extractLaserRecord(records, objectName, attribute, variant); + } else if (className.equals("CDetectionUnit")){ + extractDetectorRecord(records, objectName, attribute, data, variant); + } else if (className.equals("CTurret")){ + extractTurretRecord(records, objectName, attribute, variant); + } else if (className.equals("CSpectrophotometerUnit")){ + extractMultibandRecord(records, objectName, attribute, data, variant); + } else if (className.equals("CMicroscopeStand")){ + //z scan direction depends on the microscope stand + // upright stand (DM6000, DM5000): positive + // inverse stand (DMI6000): negative / reverse Z + if (objectName.equals("DMI6000")) + records.reverseZ = true; + } + } + } + + private static void extractShutterRecord(ConfocalSettingRecords records, String identifier, String variant){ + if (identifier.equals("bUseCARSLight")) + records.shutterInfo.cars = variant.equals("1"); + else if (identifier.equals("bUseChaserUVShutter")) + records.shutterInfo.chaserUv = variant.equals("1"); + else if (identifier.equals("bUseChaserVisibleShutter")) + records.shutterInfo.chaserVisible = variant.equals("1"); + else if (identifier.equals("bUseFSOPOLight")) + records.shutterInfo.fsopo = variant.equals("1"); + else if (identifier.equals("bUseMP2Shutter")) + records.shutterInfo.mp2 = variant.equals("1"); + else if (identifier.equals("bUseMPShutter")) + records.shutterInfo.mp = variant.equals("1"); + else if (identifier.equals("bUsePulsed635VisibleLight")) + records.shutterInfo.pulsed635 = variant.equals("1"); + else if (identifier.equals("bUsePumpLight")) + records.shutterInfo.pump = variant.equals("1"); + else if (identifier.equals("bUseSTED1Light")) + records.shutterInfo.sted1 = variant.equals("1"); + else if (identifier.equals("bUseSTED2Light")) + records.shutterInfo.sted2 = variant.equals("1"); + else if (identifier.equals("bUseSTED3Light")) + records.shutterInfo.sted3 = variant.equals("1"); + else if (identifier.equals("bUseSTED4Light")) + records.shutterInfo.sted4 = variant.equals("1"); + else if (identifier.equals("bUseStokesLight")) + records.shutterInfo.stokes = variant.equals("1"); + else if (identifier.equals("bUseSuperContVisibleShutter")) + records.shutterInfo.superContinuumVisible = variant.equals("1"); + else if (identifier.equals("bUseUV405Shutter")) + records.shutterInfo.uv405 = variant.equals("1"); + else if (identifier.equals("bUseUVShutter")) + records.shutterInfo.uv = variant.equals("1"); + else if (identifier.equals("bUseVisibleShutter")) + records.shutterInfo.visible = variant.equals("1"); + } + + private static void extractLaserRecord(ConfocalSettingRecords records, String objectName, String attribute, String variant){ + //e.g. "Laser (HeNe 543, visible)" + Pattern pattern = Pattern.compile("Laser \\(([a-zA-Z | \\d | \\w]+), ([a-zA-Z | \\d | \\w]+)\\)"); + Matcher matcher = pattern.matcher(objectName); + if (!matcher.find()) return; + + String name = matcher.group(1); + + Laser currentLaser = new Laser(); + boolean laserAlreadyExists = false; + + for (Laser laser : records.laserRecords){ + if (laser.name.equals(name)){ + laserAlreadyExists = true; + currentLaser = laser; + break; + } + } + + if(attribute.equals("Wavelength")) + currentLaser.wavelength = parseDouble(variant); + else if (attribute.equals("Power State")) + currentLaser.powerStateOn = variant.equals("On"); + + if (!laserAlreadyExists){ + currentLaser.name = name; + records.laserRecords.add(currentLaser); + } + } + + private static void extractDetectorRecord(ConfocalSettingRecords records, String objectName, String attribute, String data, String variant){ + Detector currentDetector = new Detector(); + boolean detectorAlreadyExists = false; + + for (Detector detector : records.detectorRecords){ + if (detector.model.equals(objectName)){ + detectorAlreadyExists = true; + currentDetector = detector; + break; + } + } + + if(attribute.equals("State")) + currentDetector.isActive = variant.equals("Active"); + else if (attribute.equals("VideoOffset")) + currentDetector.offset = parseDouble(variant); + else if (attribute.equals("HighVoltage")) + currentDetector.gain = parseDouble(variant); + + if (!detectorAlreadyExists){ + currentDetector.model = objectName; + currentDetector.channel = parseInt(data); + records.detectorRecords.add(currentDetector); + } + } + + private static void extractTurretRecord(ConfocalSettingRecords records, String objectName, String attribute, String variant){ + if (attribute.equals("Objective")){ + records.objectiveRecord.model = variant; + records.objectiveRecord.setCorrectionFromObjectiveName(variant); + records.objectiveRecord.setImmersionFromObjectiveName(variant); + } else if (attribute.equals("OrderNumber")){ + records.objectiveRecord.objectiveNumber = variant; + } else if (attribute.equals("NumericalAperture")){ + records.objectiveRecord.numericalAperture = parseDouble(variant); + } else if (attribute.equals("RefractionIndex")){ + records.objectiveRecord.refractionIndex = parseDouble(variant); + } + } + + private static void extractAotfRecord(ConfocalSettingRecords records, String objectName, String attribute, String data, String variant){ + if (objectName.endsWith("Low") || objectName.contains("AOBS")) return; + + Aotf currentAotf = new Aotf(); + boolean aotfAlreadyExists = false; + + for (Aotf aotf : records.aotfRecords){ + if (aotf.name.equals(objectName)){ + aotfAlreadyExists = true; + currentAotf = aotf; + break; + } + } + + if (attribute.equals("Intensity")){ + double intensity = parseDouble(variant); + + if (intensity > 0){ + double wavelength = parseDouble(data); + + LaserSetting currentLaserLineSetting = new LaserSetting(); + boolean laserLineAlreadyExists = false; + + for (LaserSetting laserLineSetting : currentAotf.laserLineSettings){ + if (laserLineSetting.wavelength == wavelength){ + laserLineAlreadyExists = true; + currentLaserLineSetting = laserLineSetting; + break; + } + } + + currentLaserLineSetting.intensity = intensity; + + if (!laserLineAlreadyExists){ + currentLaserLineSetting.wavelength = wavelength; + currentAotf.laserLineSettings.add(currentLaserLineSetting); + } + } + } + + if (!aotfAlreadyExists){ + currentAotf.name = objectName; + Pattern pattern = Pattern.compile("(\\S+) [AOTF|EOM].*"); + Matcher matcher = pattern.matcher(objectName); + if (matcher.find()){ + currentAotf.type = matcher.group(1); + } else { + System.out.println("aotf type could not be detected: " + objectName); + } + + records.aotfRecords.add(currentAotf); + } + } + + private static void extractMultibandRecord(ConfocalSettingRecords records, String objectName, String attribute, String data, String variant){ + Multiband currentMultiband = new Multiband(); + boolean multibandAlreadyExists = false; + + for (Multiband multiband : records.multibandRecords){ + if (multiband.name.equals(objectName)){ + multibandAlreadyExists = true; + currentMultiband = multiband; + break; + } + } + + if (attribute.equals("Wavelength")){ + if (data.equals("0")) + currentMultiband.leftWorld = parseDouble(variant); + else if (data.equals("1")) + currentMultiband.rightWorld = parseDouble(variant); + + } else if (attribute.equals("Stain")){ + currentMultiband.dyeName = variant; + } + + if (!multibandAlreadyExists){ + currentMultiband.name = objectName; + + Pattern pattern = Pattern.compile(".+Channel (\\d+)"); + Matcher matcher = pattern.matcher(objectName); + if (matcher.find()){ + currentMultiband.channel = parseInt(matcher.group(1)); + } + + records.multibandRecords.add(currentMultiband); + } + } +} diff --git a/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/extract/confocal/ConfocalSettingsExtractor.java b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/extract/confocal/ConfocalSettingsExtractor.java new file mode 100644 index 00000000000..665cee714c5 --- /dev/null +++ b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/extract/confocal/ConfocalSettingsExtractor.java @@ -0,0 +1,62 @@ +/* + * #%L + * OME Bio-Formats package for reading and converting biological file formats. + * %% + * Copyright (C) 2005 - 2017 Open Microscopy Environment: + * - Board of Regents of the University of Wisconsin-Madison + * - Glencoe Software, Inc. + * - University of Dundee + * %% + * 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, see + * . + * #L% + */ + +package loci.formats.in.LeicaMicrosystemsMetadata.extract.confocal; + +import org.w3c.dom.Element; + +import loci.formats.in.LeicaMicrosystemsMetadata.extract.Extractor; +import loci.formats.in.LeicaMicrosystemsMetadata.helpers.LMSMainXmlNodes; +import loci.formats.in.LeicaMicrosystemsMetadata.model.confocal.ConfocalAcquisitionSettings; + +/** + * This is a helper class for extracting confocal acquisition settings from LMS XML. + * + * @author Constanze Wendlandt constanze.wendlandt at leica-microsystems.com + */ +public class ConfocalSettingsExtractor extends Extractor { + + public static void extractInstrumentSettings(LMSMainXmlNodes xmlNodes, ConfocalAcquisitionSettings acquisitionSettings){ + Element atlConfocalSetting = xmlNodes.getAtlConfocalSetting(); + + if (atlConfocalSetting == null && xmlNodes.sequentialConfocalSettings.size() == 0 && xmlNodes.confocalSettingRecords != null){ + acquisitionSettings.lasers = xmlNodes.confocalSettingRecords.laserRecords; + acquisitionSettings.detectors = xmlNodes.confocalSettingRecords.detectorRecords; + } + else { + acquisitionSettings.lasers = LaserExtractor.getLasers(atlConfocalSetting, atlConfocalSetting, + xmlNodes.confocalSettingRecords); + acquisitionSettings.detectors = DetectorExtractor.getDetectors(atlConfocalSetting, xmlNodes.confocalSettingRecords); + } + } + + public static void extractChannelSettings(LMSMainXmlNodes xmlNodes, ConfocalAcquisitionSettings acquisitionSettings){ + Element atlConfocalSetting = xmlNodes.getAtlConfocalSetting(); + if (atlConfocalSetting == null && xmlNodes.sequentialConfocalSettings.size() == 0 && xmlNodes.confocalSettingRecords != null) + ConfocalSettingsFromSettingRecordsExtractor.extractChannelSettings(xmlNodes, acquisitionSettings); + else + ConfocalSettingsFromAtlSettingsExtractor.extractChannelSettings(xmlNodes, acquisitionSettings); + } +} diff --git a/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/extract/confocal/ConfocalSettingsFromAtlSettingsExtractor.java b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/extract/confocal/ConfocalSettingsFromAtlSettingsExtractor.java new file mode 100644 index 00000000000..df5f9547b54 --- /dev/null +++ b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/extract/confocal/ConfocalSettingsFromAtlSettingsExtractor.java @@ -0,0 +1,168 @@ +/* + * #%L + * OME Bio-Formats package for reading and converting biological file formats. + * %% + * Copyright (C) 2005 - 2017 Open Microscopy Environment: + * - Board of Regents of the University of Wisconsin-Madison + * - Glencoe Software, Inc. + * - University of Dundee + * %% + * 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, see + * . + * #L% + */ + +package loci.formats.in.LeicaMicrosystemsMetadata.extract.confocal; + +import java.util.ArrayList; +import java.util.List; + +import org.w3c.dom.Element; + +import loci.formats.in.LeicaMicrosystemsMetadata.extract.Extractor; +import loci.formats.in.LeicaMicrosystemsMetadata.helpers.LMSMainXmlNodes; +import loci.formats.in.LeicaMicrosystemsMetadata.model.confocal.ConfocalAcquisitionSettings; +import loci.formats.in.LeicaMicrosystemsMetadata.model.confocal.ConfocalChannelSetting; +import loci.formats.in.LeicaMicrosystemsMetadata.model.confocal.ConfocalSettingRecords; +import loci.formats.in.LeicaMicrosystemsMetadata.model.confocal.Detector; +import loci.formats.in.LeicaMicrosystemsMetadata.model.confocal.DetectorSetting; +import loci.formats.in.LeicaMicrosystemsMetadata.model.confocal.Laser; +import loci.formats.in.LeicaMicrosystemsMetadata.model.confocal.LaserSetting; +import loci.formats.in.LeicaMicrosystemsMetadata.model.confocal.Multiband; + +/** + * Helper class for extracting confocal acquisition settings from ATLConfocalSettings nodes + * + * @author Constanze Wendlandt constanze.wendlandt at leica-microsystems.com + */ +public class ConfocalSettingsFromAtlSettingsExtractor extends Extractor { + + public static void extractChannelSettings(LMSMainXmlNodes xmlNodes, ConfocalAcquisitionSettings acquisitionSettings){ + Element atlConfocalSetting = xmlNodes.getAtlConfocalSetting(); + + String zoomS = getAttributeValue(atlConfocalSetting, "Zoom"); + String readOutRateS = getAttributeValue(atlConfocalSetting, "ScanSpeed"); + String pinholeSizeS = Extractor.getAttributeValue(atlConfocalSetting, "Pinhole"); + + acquisitionSettings.zoom = parseDouble(zoomS); + acquisitionSettings.readOutRate = parseDouble(readOutRateS); + acquisitionSettings.pinholeSize = Extractor.parseDouble(pinholeSizeS) * METER_MULTIPLY; + + if (xmlNodes.sequentialConfocalSettings.size() > 0){ + for (Element setting : xmlNodes.sequentialConfocalSettings){ + acquisitionSettings.channelSettings.addAll(extractChannelSettings(setting, atlConfocalSetting, xmlNodes.confocalSettingRecords)); + } + } else { + acquisitionSettings.channelSettings.addAll(extractChannelSettings(atlConfocalSetting, atlConfocalSetting, xmlNodes.confocalSettingRecords)); + } + + mapInstrumentDetectorsToChannelDetectorSettings(acquisitionSettings); + mapInstrumentLasersToChannelLaserSettings(acquisitionSettings); + } + + + private static List extractChannelSettings(Element atlConfocalSetting, Element alternativeSetting, ConfocalSettingRecords records){ + // get + map detector and multiband info + List detectorSettings = DetectorExtractor.getActiveDetectorSettings(atlConfocalSetting); + List multibands = DetectorExtractor.getMultibands(atlConfocalSetting, alternativeSetting); + DetectorExtractor.addMultibandInfoToDetectorSettings(detectorSettings, multibands); + + // get + map laser and AOTF laser line setting info + List lasers = LaserExtractor.getLasers(atlConfocalSetting, alternativeSetting, records); + LaserExtractor.addShutterInfoToLasers(lasers, atlConfocalSetting, alternativeSetting); + List laserSettings = LaserExtractor.getLaserSettings(atlConfocalSetting); + LaserExtractor.mapLasersToLaserSettings(lasers, laserSettings); + + // map detector and laser settings + List channelSettings = createChannelSettings(detectorSettings, laserSettings); + + return channelSettings; + } + + + /** + * Creates confocal channel settings by mapping detector and laser settings extracted from one sequential confocal setting + * @param detectorSettings + * @param laserSettings + * @return + */ + private static List createChannelSettings(List detectorSettings, List laserSettings){ + List channelSettings = new ArrayList<>(); + + for (DetectorSetting detectorSetting : detectorSettings){ + ConfocalChannelSetting channelSetting = new ConfocalChannelSetting(); + channelSetting.detectorSetting = detectorSetting; + + // STELLARIS + if (detectorSetting.referenceLineWavelength > 0){ + for (LaserSetting laserSetting : laserSettings){ + if (detectorSetting.referenceLineWavelength == laserSetting.wavelength) + channelSetting.laserSetting = laserSetting; + } + // OLDER + } else { + if (detectorSetting.name.contains("NDD")){ + // map MP lasers to NDD detectors + for (LaserSetting laserSetting : laserSettings){ + if (laserSetting.laser.name.contains("MP")){ + channelSetting.laserSetting = laserSetting; + break; + } + } + } else { + LaserSetting selectedLaserSetting = null; + for (LaserSetting laserSetting : laserSettings) { + if (laserSetting.wavelength < detectorSetting.cutIn) { + if (selectedLaserSetting == null || selectedLaserSetting.laser.wavelength < laserSetting.laser.wavelength) + selectedLaserSetting = laserSetting; + } + } + channelSetting.laserSetting = selectedLaserSetting; + } + } + + channelSettings.add(channelSetting); + } + + return channelSettings; + } + + + private static void mapInstrumentDetectorsToChannelDetectorSettings(ConfocalAcquisitionSettings acquisitionSettings){ + for (ConfocalChannelSetting channelSetting : acquisitionSettings.channelSettings){ + for (Detector detector : acquisitionSettings.detectors){ + if (channelSetting.detectorSetting.name.equals(detector.model) || //SP8, STELLARIS + channelSetting.detectorSetting.channel == detector.channel || // SP5 + channelSetting.detectorSetting.detectorListIndex == detector.detectorListIndex){ + channelSetting.detectorSetting.detector = detector; + channelSetting.detectorSetting.name = detector.model; + break; + } + } + } + } + + + private static void mapInstrumentLasersToChannelLaserSettings(ConfocalAcquisitionSettings acquisitionSettings){ + for (ConfocalChannelSetting channelSetting : acquisitionSettings.channelSettings){ + for (Laser laser : acquisitionSettings.lasers){ + if (channelSetting.laserSetting != null && channelSetting.laserSetting.laser != null && + channelSetting.laserSetting.laser.name.equals(laser.name)){ + channelSetting.laserSetting.laser = laser; + break; + } + } + } + } +} diff --git a/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/extract/confocal/ConfocalSettingsFromSettingRecordsExtractor.java b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/extract/confocal/ConfocalSettingsFromSettingRecordsExtractor.java new file mode 100644 index 00000000000..6b471c86ffe --- /dev/null +++ b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/extract/confocal/ConfocalSettingsFromSettingRecordsExtractor.java @@ -0,0 +1,185 @@ +/* + * #%L + * OME Bio-Formats package for reading and converting biological file formats. + * %% + * Copyright (C) 2005 - 2017 Open Microscopy Environment: + * - Board of Regents of the University of Wisconsin-Madison + * - Glencoe Software, Inc. + * - University of Dundee + * %% + * 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, see + * . + * #L% + */ + +package loci.formats.in.LeicaMicrosystemsMetadata.extract.confocal; + +import java.util.ArrayList; +import java.util.List; + +import loci.formats.in.LeicaMicrosystemsMetadata.extract.Extractor; +import loci.formats.in.LeicaMicrosystemsMetadata.helpers.LMSMainXmlNodes; +import loci.formats.in.LeicaMicrosystemsMetadata.model.confocal.Aotf; +import loci.formats.in.LeicaMicrosystemsMetadata.model.confocal.ConfocalAcquisitionSettings; +import loci.formats.in.LeicaMicrosystemsMetadata.model.confocal.ConfocalChannelSetting; +import loci.formats.in.LeicaMicrosystemsMetadata.model.confocal.Detector; +import loci.formats.in.LeicaMicrosystemsMetadata.model.confocal.DetectorSetting; +import loci.formats.in.LeicaMicrosystemsMetadata.model.confocal.Laser; +import loci.formats.in.LeicaMicrosystemsMetadata.model.confocal.LaserSetting; +import loci.formats.in.LeicaMicrosystemsMetadata.model.confocal.Multiband; + +/** + * Helper class for extracting confocal acquisition settings from preprocessed filter and scanner setting records + * (see {@link ConfocalSettingRecords}) + * + * @author Constanze Wendlandt constanze.wendlandt at leica-microsystems.com + */ +public class ConfocalSettingsFromSettingRecordsExtractor extends Extractor { + + public static void extractChannelSettings(LMSMainXmlNodes xmlNodes, ConfocalAcquisitionSettings acquisitionSettings){ + acquisitionSettings.pinholeSize = xmlNodes.confocalSettingRecords.pinholeSize; + acquisitionSettings.zoom = xmlNodes.confocalSettingRecords.zoom; + acquisitionSettings.readOutRate = xmlNodes.confocalSettingRecords.readOutRate; + + List detectorSettings = getDetectorSettings(xmlNodes); + List laserSettings = getLaserSettings(xmlNodes); + acquisitionSettings.channelSettings = createChannelSettings(detectorSettings, laserSettings); + } + + private static List getDetectorSettings(LMSMainXmlNodes xmlNodes){ + List detectorSettings = new ArrayList<>(); + + for (Detector detector : xmlNodes.confocalSettingRecords.detectorRecords){ + if (!detector.isActive) continue; + + DetectorSetting detectorSetting = new DetectorSetting(); + detectorSetting.detector = detector; + detectorSetting.name = detector.model; + detectorSetting.gain = detector.gain; + detectorSetting.offset = detector.offset; + + // this will not match all detectors, since NDD and TLD detectors do not have a range + for (Multiband multiband : xmlNodes.confocalSettingRecords.multibandRecords){ + if (multiband.channel == detector.channel){ + detectorSetting.cutIn = multiband.leftWorld; + detectorSetting.cutOut = multiband.rightWorld; + detectorSetting.dyeName = multiband.dyeName; + detectorSetting.multiband = multiband; + break; + } + } + + detectorSettings.add(detectorSetting); + } + + return detectorSettings; + } + + private static List getLaserSettings(LMSMainXmlNodes xmlNodes){ + // getting AOTF shutter info + for (Aotf aotf : xmlNodes.confocalSettingRecords.aotfRecords){ + if (aotf.type == null) continue; + if (aotf.type.equals("Visible")) + aotf.shutterOpen = xmlNodes.confocalSettingRecords.shutterInfo.visible; + else if (aotf.type.equals("UV")) + aotf.shutterOpen = xmlNodes.confocalSettingRecords.shutterInfo.uv; + else if (aotf.type.equals("MP")) + aotf.shutterOpen = xmlNodes.confocalSettingRecords.shutterInfo.mp; + } + + // getting laser shutter info + for (Laser laser : xmlNodes.confocalSettingRecords.laserRecords){ + if (laser.wavelength == 405) + laser.shutterOpen = xmlNodes.confocalSettingRecords.shutterInfo.uv405; + } + + //map lasers to AOTFs' laser line settings + List laserSettings = new ArrayList<>(); + for (Aotf aotf : xmlNodes.confocalSettingRecords.aotfRecords){ + if (!aotf.shutterOpen) continue; + + for (LaserSetting laserLineSetting : aotf.laserLineSettings){ + for (Laser laser : xmlNodes.confocalSettingRecords.laserRecords){ + if (laser.powerStateOn && laser.shutterOpen && (laser.wavelength == laserLineSetting.wavelength || + laser.name.equals("Argon") && laser.argonWavelengths.contains(laserLineSetting.wavelength))){ + laserLineSetting.laser = laser; + laser.laserSetting = laserLineSetting; + } + } + + laserSettings.add(laserLineSetting); + } + } + + // after lasers with matching wavelengths were matched and there are still unmatched laser line settings, look for active WLL lasers + for (LaserSetting laserSetting : laserSettings){ + if (laserSetting.laser == null){ + for (Laser laser : xmlNodes.confocalSettingRecords.laserRecords){ + if (laser.powerStateOn && laser.shutterOpen && laser.name.equals("WLL")){ + laserSetting.laser = laser; + laser.laserSetting = laserSetting; + break; + } + } + } + } + + for (Laser laser : xmlNodes.confocalSettingRecords.laserRecords){ + if (laser.laserSetting == null && laser.powerStateOn && laser.shutterOpen){ + // laser is not connected to an AOTF, but it is switched on and its associated shutter is open. + // therefore, we assume here that it is connected to the light path and we create a laser setting for it. + LaserSetting laserSetting = new LaserSetting(); + laserSetting.intensity = 1; + laserSetting.wavelength = laser.wavelength; + laserSetting.laser = laser; + laserSettings.add(laserSetting); + } + } + + return laserSettings; + } + + private static List createChannelSettings(List detectorSettings, List laserSettings){ + List channelSettings = new ArrayList<>(); + + for (DetectorSetting detectorSetting : detectorSettings){ + ConfocalChannelSetting channelSetting = new ConfocalChannelSetting(); + channelSetting.detectorSetting = detectorSetting; + + if (detectorSetting.name.contains("NDD")){ + // map MP lasers to NDD detectors + for (LaserSetting laserSetting : laserSettings){ + if (laserSetting.laser.name.contains("MP")){ + channelSetting.laserSetting = laserSetting; + break; + } + } + } else { + // map detector settings to laser settings whose excitation wavelengths lie "left" of the detector range + LaserSetting selectedLaserSetting = null; + for (LaserSetting laserSetting : laserSettings) { + if (laserSetting.wavelength < detectorSetting.cutIn) { + if (selectedLaserSetting == null || (laserSetting.laser != null && selectedLaserSetting.laser != null && selectedLaserSetting.laser.wavelength < laserSetting.laser.wavelength)) + selectedLaserSetting = laserSetting; + } + } + channelSetting.laserSetting = selectedLaserSetting; + } + + channelSettings.add(channelSetting); + } + + return channelSettings; + } +} diff --git a/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/extract/confocal/DetectorExtractor.java b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/extract/confocal/DetectorExtractor.java new file mode 100644 index 00000000000..6116f0e8da9 --- /dev/null +++ b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/extract/confocal/DetectorExtractor.java @@ -0,0 +1,196 @@ +/* + * #%L + * OME Bio-Formats package for reading and converting biological file formats. + * %% + * Copyright (C) 2005 - 2017 Open Microscopy Environment: + * - Board of Regents of the University of Wisconsin-Madison + * - Glencoe Software, Inc. + * - University of Dundee + * %% + * 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, see + * . + * #L% + */ + +package loci.formats.in.LeicaMicrosystemsMetadata.extract.confocal; + +import java.util.ArrayList; +import java.util.List; + +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import loci.formats.in.LeicaMicrosystemsMetadata.extract.Extractor; +import loci.formats.in.LeicaMicrosystemsMetadata.model.confocal.ConfocalSettingRecords; +import loci.formats.in.LeicaMicrosystemsMetadata.model.confocal.Detector; +import loci.formats.in.LeicaMicrosystemsMetadata.model.confocal.DetectorSetting; +import loci.formats.in.LeicaMicrosystemsMetadata.model.confocal.Multiband; + +/** + * DetectorExtractor is a helper class for extracting detector information from LMS XML files. + * + * @author Constanze Wendlandt constanze.wendlandt at leica-microsystems.com + */ +public class DetectorExtractor extends Extractor { + + /** + * Creates Detectors from information extracted from ATL confocal settings + * @param setting atl confocal setting from which detector info is extracted + * @param records these are required to look up detector names for SP5 images + * @return + */ + public static List getDetectors(Element setting, ConfocalSettingRecords records){ + List detectors = new ArrayList<>(); + + String zoomS = getAttributeValue(setting, "Zoom"); + double zoom = parseDouble(zoomS); + + // main confocal settings > detector list: instrument detectors + Node detectorList = getChildNodeWithName(setting, "DetectorList"); + NodeList detectorNodes = detectorList.getChildNodes(); + + int detectorListIndex = 0; + for (int i = 0; i < detectorNodes.getLength(); i++) { + Element detectorNode; + try { + detectorNode = (Element) detectorNodes.item(i); + } catch (Exception e) { + continue; + } + + Detector detector = new Detector(); + + detector.model = detectorNode.getAttribute("Name"); + detector.type = detectorNode.getAttribute("Type"); + detector.channel = parseInt(detectorNode.getAttribute("Channel")); + detector.zoom = zoom; + detector.detectorListIndex = detectorListIndex; + + // SP5: detectors in ATL settings have no names, this information can be found in filter setting records + if (detector.model.isEmpty() && records != null){ + for (Detector detectorRecord : records.detectorRecords){ + if (detector.channel == detectorRecord.channel){ + detector.model = detectorRecord.model; + break; + } + } + } + + detectors.add(detector); + + detectorListIndex++; + } + + return detectors; + } + + public static List getActiveDetectorSettings(Element atlConfocalSetting){ + Node detectorList = getChildNodeWithName(atlConfocalSetting, "DetectorList"); + NodeList detectorNodes = detectorList.getChildNodes(); + List detectorSettings = new ArrayList(); + + int detectorListIndex = 0; // store order of appearance of detector, for later mapping to channels + for (int i = 0; i < detectorNodes.getLength(); i++) { + Element detectorNode; + try { + detectorNode = (Element) detectorNodes.item(i); + } catch (Exception e) { + continue; + } + + String isActive = detectorNode.getAttribute("IsActive"); + if (!isActive.equals("1")) continue; + + DetectorSetting detectorSetting = new DetectorSetting(); + + detectorSetting.name = detectorNode.getAttribute("Name"); + detectorSetting.type = detectorNode.getAttribute("Type"); + String channelS = detectorNode.getAttribute("Channel"); + detectorSetting.channel = parseInt(channelS); + String gainS = detectorNode.getAttribute("Gain"); + detectorSetting.gain = parseDouble(gainS); + String offsetS = detectorNode.getAttribute("Offset"); + detectorSetting.offset = parseDouble(offsetS); + detectorSetting.detectorListIndex = detectorListIndex; + detectorSetting.dyeName = detectorNode.getAttribute("DyeName"); // STELLARIS + + //only in STELLARIS + Element detectionReferenceLine; + try { + detectionReferenceLine = (Element)getChildNodeWithName(detectorNode, "DetectionReferenceLine"); + if (detectionReferenceLine != null){ + detectorSetting.referenceLineName = getAttributeValue(detectionReferenceLine, "LaserName"); + String referenceWavelengthS = getAttributeValue(detectionReferenceLine, "LaserWavelength"); + detectorSetting.referenceLineWavelength = parseInt(referenceWavelengthS); + } + } catch (Exception e) {} + + detectorSettings.add(detectorSetting); + + detectorListIndex++; + } + + return detectorSettings; + } + + + public static List getMultibands(Element atlConfocalSetting, Element alternativeSetting){ + Node spectro = getChildNodeWithName(atlConfocalSetting, "Spectro"); + // in STELLARIS, spectro nodes are not included in sequential settings + if (spectro == null) + spectro = getChildNodeWithName(alternativeSetting, "Spectro"); + + NodeList multibandNodes = spectro.getChildNodes(); + List multibands = new ArrayList(); + + for (int i = 0; i < multibandNodes.getLength(); i++) { + Element multibandNode; + try { + multibandNode = (Element) multibandNodes.item(i); + } catch (Exception e) { + continue; + } + + Multiband multiband = new Multiband(); + + String channelS = multibandNode.getAttribute("Channel"); + multiband.channel = parseInt(channelS); + String leftWorldS = multibandNode.getAttribute("LeftWorld"); + multiband.leftWorld = parseDouble(leftWorldS); + String rightWorldS = multibandNode.getAttribute("RightWorld"); + multiband.rightWorld = parseDouble(rightWorldS); + multiband.dyeName = multibandNode.getAttribute("DyeName"); + + multibands.add(multiband); + } + + return multibands; + } + + + public static void addMultibandInfoToDetectorSettings(List detectorSettings, List multibands){ + for (DetectorSetting detectorSetting : detectorSettings){ + for (Multiband multiband : multibands){ + if (multiband.channel == detectorSetting.channel){ + detectorSetting.cutIn = multiband.leftWorld; + detectorSetting.cutOut = multiband.rightWorld; + if (detectorSetting.dyeName == null || detectorSetting.dyeName.isEmpty()) + detectorSetting.dyeName = multiband.dyeName; + break; + } + } + } + } +} diff --git a/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/extract/confocal/LaserExtractor.java b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/extract/confocal/LaserExtractor.java new file mode 100644 index 00000000000..8fee1b28eab --- /dev/null +++ b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/extract/confocal/LaserExtractor.java @@ -0,0 +1,229 @@ +/* + * #%L + * OME Bio-Formats package for reading and converting biological file formats. + * %% + * Copyright (C) 2005 - 2017 Open Microscopy Environment: + * - Board of Regents of the University of Wisconsin-Madison + * - Glencoe Software, Inc. + * - University of Dundee + * %% + * 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, see + * . + * #L% + */ + +package loci.formats.in.LeicaMicrosystemsMetadata.extract.confocal; + +import java.util.ArrayList; +import java.util.List; + +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import loci.formats.in.LeicaMicrosystemsMetadata.extract.Extractor; +import loci.formats.in.LeicaMicrosystemsMetadata.model.confocal.ConfocalSettingRecords; +import loci.formats.in.LeicaMicrosystemsMetadata.model.confocal.Laser; +import loci.formats.in.LeicaMicrosystemsMetadata.model.confocal.LaserSetting; +import loci.formats.in.LeicaMicrosystemsMetadata.model.confocal.Laser.LmsLightSourceQualifier; +import loci.formats.in.LeicaMicrosystemsMetadata.model.confocal.Laser.LmsLightSourceType; + +/** + * LaserExtractor is a helper class for extracting laser information from LMS XML files. + * + * @author Constanze Wendlandt constanze.wendlandt at leica-microsystems.com + */ +public class LaserExtractor extends Extractor { + + /** + * Creates Lasers from information extracted from ATL confocal settings + * @param atlConfocalSetting the ATL confocal setting from which laser info shall be extracted + * @param alternativeSetting the alternative ATL confocal setting (main ATL confocal setting) to look up laser array info for STELLARIS + * @param confocalSettingRecords these are required to look up laser power states for SP5 images + * @return + */ + public static List getLasers(Element atlConfocalSetting, Element alternativeSetting, ConfocalSettingRecords confocalSettingRecords){ + List lasers = new ArrayList(); + + //SP5, SP8 + Node laserArray = getChildNodeWithName(atlConfocalSetting, "LaserArray"); + // in STELLARIS, laser array nodes are not included in sequential settings + if (laserArray == null) + laserArray = getChildNodeWithName(alternativeSetting, "LaserArray"); + + if (laserArray == null) return lasers; + + NodeList laserNodes = laserArray.getChildNodes(); + + for (int i = 0; i < laserNodes.getLength(); i++) { + Element laserNode; + try { + laserNode = (Element) laserNodes.item(i); + } catch (Exception e) { + continue; + } + + Laser laser = new Laser(); + laser.name = laserNode.getAttribute("LaserName"); + String wavelengthS = laserNode.getAttribute("Wavelength"); + laser.wavelength = parseDouble(wavelengthS); + + laser.powerState = laserNode.getAttribute("PowerState"); + if (!laser.powerState.isEmpty()){ + // SP8, STELLARIS + laser.powerStateOn = laser.powerState.equals("On"); + } + + String lightSourceQualifierS = laserNode.getAttribute("LightSourceQualifier"); + if (!lightSourceQualifierS.isEmpty()){ + // SP5 + int lightSourceQualifier = parseInt(lightSourceQualifierS); + laser.lightSourceQualifier = LmsLightSourceQualifier.getValue(lightSourceQualifier); + } else { + // SP8, STELLARIS + String lightSourceTypeS = laserNode.getAttribute("LightSourceType"); + int lightSourceType = parseInt(lightSourceTypeS); + laser.lightSourceType = LmsLightSourceType.getValue(lightSourceType); + } + + // in SP5 images, the power state attribute can not be found in ATL setting > laser array > lasers, + // in this case we need to look into the laser info extracted from filter setting records. + if (laser.powerState.isEmpty() && confocalSettingRecords != null){ + for (Laser laserRecord : confocalSettingRecords.laserRecords){ + if (laserRecord.name.equals(laser.name)){ + laser.powerStateOn = laserRecord.powerStateOn; + break; + } + } + } + + lasers.add(laser); + } + + return lasers; + } + + public static void addShutterInfoToLasers(List lasers, Element atlConfocalSetting, Element alternativeSetting){ + Node shutterList = getChildNodeWithName(atlConfocalSetting, "ShutterList"); + // in STELLARIS, shutterlist nodes are not included in sequential settings + if (shutterList == null) + shutterList = getChildNodeWithName(alternativeSetting, "ShutterList"); + // in some SP5 images, there is no ShutterList in ATL confocal settings + if (shutterList == null) return; + + NodeList shutterNodes = shutterList.getChildNodes(); + + for (int i = 0; i < shutterNodes.getLength(); i++) { + Element shutterNode; + try { + shutterNode = (Element) shutterNodes.item(i); + } catch (Exception e) { + continue; + } + + String isActiveS = shutterNode.getAttribute("IsActive"); + boolean isActive = isActiveS.equals("1"); + + String lightSourceQualifierS = shutterNode.getAttribute("LightSourceQualifier"); + if (!lightSourceQualifierS.isEmpty()){ + // SP5 + int lightSourceQualifierInt = parseInt(lightSourceQualifierS); + LmsLightSourceQualifier lightSourceQualifier = LmsLightSourceQualifier.getValue(lightSourceQualifierInt); + for (Laser laser : lasers){ + if (laser.lightSourceQualifier == lightSourceQualifier){ + laser.shutterOpen = isActive; + break; + } + } + } else { + // SP8, STELLARIS + String lightSourceTypeS = shutterNode.getAttribute("LightSourceType"); + int lightSourceTypeInt = parseInt(lightSourceTypeS); + LmsLightSourceType lightSourceType = LmsLightSourceType.getValue(lightSourceTypeInt); + for (Laser laser : lasers){ + if (laser.lightSourceType == lightSourceType){ + laser.shutterOpen = isActive; + } + } + } + } + } + + public static List getLaserSettings(Element atlConfocalSetting){ + Node aotfList = getChildNodeWithName(atlConfocalSetting, "AotfList"); + List aotfs = getChildNodesWithNameAsElement(aotfList, "Aotf"); + + List laserSettings = new ArrayList<>(); + + for (Element aotf : aotfs){ + List laserLineSettingNodes = getChildNodesWithNameAsElement(aotf, "LaserLineSetting"); + for (Element laserLineSettingNode : laserLineSettingNodes){ + String intensityDevS = laserLineSettingNode.getAttribute("IntensityDev"); + double intensityDev = parseDouble(intensityDevS); + if(intensityDev == 0) + continue; + + LaserSetting laserSetting = new LaserSetting(); + laserSetting.intensity = intensityDev; + String waveLengthS = laserLineSettingNode.getAttribute("LaserLine"); + laserSetting.wavelength = parseDouble(waveLengthS); + + String lightSourceQualifierS = aotf.getAttribute("LightSourceQualifier"); + if (!lightSourceQualifierS.isEmpty()){ + // SP5 + int lightSourceQualifier = parseInt(lightSourceQualifierS); + laserSetting.lightSourceQualifier = LmsLightSourceQualifier.getValue(lightSourceQualifier); + } else { + // SP8, STELLARIS + String lightSourceTypeS = aotf.getAttribute("LightSourceType"); + int lightSourceType = parseInt(lightSourceTypeS); + laserSetting.lightSourceType = LmsLightSourceType.getValue(lightSourceType); + } + + laserSettings.add(laserSetting); + } + } + + return laserSettings; + } + + public static void mapLasersToLaserSettings(List lasers, List laserSettings){ + for (LaserSetting laserSetting : laserSettings){ + for (Laser laser : lasers){ + if (laser.powerStateOn && laser.shutterOpen && laser.hasSameLightSourceType(laserSetting) && + (laser.wavelength == laserSetting.wavelength + || laser.lightSourceQualifier == LmsLightSourceQualifier.WLL || laser.lightSourceType == LmsLightSourceType.WLL + || laser.name.equals("Argon") && laser.argonWavelengths.contains(laserSetting.wavelength))){ + laserSetting.laser = laser; + laser.laserSetting = laserSetting; + break; + } + } + } + + for (Laser laser : lasers){ + if (laser.laserSetting == null && laser.powerStateOn && laser.shutterOpen){ + // laser is not connected to an AOTF, but it is switched on and its associated shutter is open. + // therefore, we assume here that it is connected to the light path and we create a laser setting for it. + LaserSetting laserSetting = new LaserSetting(); + laserSetting.intensity = 1; + laserSetting.wavelength = laser.wavelength; + laserSetting.laser = laser; + laserSetting.lightSourceQualifier = laser.lightSourceQualifier; + laserSetting.lightSourceType = laser.lightSourceType; + laserSettings.add(laserSetting); + } + } + } +} diff --git a/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/extract/widefield/WidefieldSettingsExtractor.java b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/extract/widefield/WidefieldSettingsExtractor.java new file mode 100644 index 00000000000..3db64aaaffb --- /dev/null +++ b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/extract/widefield/WidefieldSettingsExtractor.java @@ -0,0 +1,65 @@ +/* + * #%L + * OME Bio-Formats package for reading and converting biological file formats. + * %% + * Copyright (C) 2005 - 2017 Open Microscopy Environment: + * - Board of Regents of the University of Wisconsin-Madison + * - Glencoe Software, Inc. + * - University of Dundee + * %% + * 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, see + * . + * #L% + */ + +package loci.formats.in.LeicaMicrosystemsMetadata.extract.widefield; + +import java.util.ArrayList; +import java.util.List; + +import org.w3c.dom.Element; + +import loci.formats.in.LeicaMicrosystemsMetadata.extract.Extractor; +import loci.formats.in.LeicaMicrosystemsMetadata.helpers.LMSMainXmlNodes; +import loci.formats.in.LeicaMicrosystemsMetadata.model.Filter; + +/** + * WidefieldSettingsExtractor is a helper class for extracting widefield acquisition settings from LMS XML files. + * + * @author Constanze Wendlandt constanze.wendlandt at leica-microsystems.com + */ +public class WidefieldSettingsExtractor extends Extractor { + + /** + * Returns a list of {@link Filter}s created from widefield channel information + */ + public static List extractWidefieldFilters(LMSMainXmlNodes xmlNodes){ + List filters = new ArrayList(); + + for (Element widefieldChannelInfo : xmlNodes.widefieldChannelInfos){ + Filter filter = new Filter(); + + String wavelengthS = Extractor.getAttributeValue(widefieldChannelInfo, "EmissionWavelength"); + double waveLength = Extractor.parseDouble(wavelengthS); + filter.cutIn = waveLength; + filter.cutOut = waveLength; + filter.dye = Extractor.getAttributeValue(widefieldChannelInfo, "UserDefName"); + filter.name = Extractor.getAttributeValue(widefieldChannelInfo, "FFW_Emission1FilterName"); + + filters.add(filter); + } + + return filters; + } +} diff --git a/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/helpers/LMSMainXmlNodes.java b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/helpers/LMSMainXmlNodes.java new file mode 100644 index 00000000000..4756e453c8f --- /dev/null +++ b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/helpers/LMSMainXmlNodes.java @@ -0,0 +1,133 @@ +/* + * #%L + * OME Bio-Formats package for reading and converting biological file formats. + * %% + * Copyright (C) 2005 - 2017 Open Microscopy Environment: + * - Board of Regents of the University of Wisconsin-Madison + * - Glencoe Software, Inc. + * - University of Dundee + * %% + * 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, see + * . + * #L% + */ + +package loci.formats.in.LeicaMicrosystemsMetadata.helpers; + +import java.util.ArrayList; +import java.util.List; + +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; + +import loci.formats.in.LeicaMicrosystemsMetadata.model.confocal.ConfocalSettingRecords; + + +/** + * LMSMainXmlNodes is a storage class that holds the main XML nodes and XML layout information from LMS XML files. + * + * @author Constanze Wendlandt constanze.wendlandt at leica-microsystems.com + */ +public class LMSMainXmlNodes { + public Element imageNode; + public Element imageDescription; + public NodeList attachments; + + public Element hardwareSetting; + + public Element mainConfocalSetting; // ATLConfocalSettingDefinition under HardwareSetting attachment + public Element masterConfocalSetting; //ATLConfocalSettingDefinition under LDM_Block_Sequential_Master + public List sequentialConfocalSettings = new ArrayList(); //ATLConfocalSettingDefinitions under LDM_Block_Sequential_List + + public Element mainCameraSetting; // ATLCameraSettingDefinition under HardwareSetting attachment + public Element masterCameraSetting; //ATLCameraSettingDefinition under LDM_Block_Sequential_Master + public List sequentialCameraSettings = new ArrayList(); //ATLCameraSettingDefinitions under LDM_Block_Sequential_List + public Element widefieldChannelConfig; + public List widefieldChannelInfos = new ArrayList(); + + public Element widefocalExperimentSettings; // attachment, only for MICA + + public NodeList scannerSettingRecords; + public NodeList filterSettingRecords; + public ConfocalSettingRecords confocalSettingRecords = new ConfocalSettingRecords(); + + public boolean isMicaImage; + + public enum HardwareSettingLayout { + OLD, // e.g. SP5 --> + NEW, // e.g. SP8, STELLARIS, MICA, ... --> + NONE // e.g. some depth map or multifocus images don't have a hardware setting + } + public HardwareSettingLayout hardwareSettingLayout; + + public enum AtlSettingLayout { + CONFOCAL_OLD, + CONFOCAL_NEW, + WIDEFIELD, + MICA_CONFOCAL, + MICA_WIDEFIELD, + MICA_WIDEFOCAL, + UNKNOWN + } + + public AtlSettingLayout atlSettingLayout = AtlSettingLayout.UNKNOWN; + + public enum DataSourceType { + UNDEFINED, + CAMERA, // images acquired with widefield systems or widefield images acquired with MICA + CONFOCAL, // images acquired with confocal systems or confocal images acquired with MICA + SPIM, + WIDEFOCAL // e.g. confocal images acquired with MICA with additional TL channel + } + public DataSourceType dataSourceType; + + /** + * Depending on hardware setting layout and data source type, it returns the main ATL setting that + * shall be used for extracting further hardware settings information. + */ + public Element getAtlSetting(){ + switch (atlSettingLayout){ + case CONFOCAL_OLD: + return masterConfocalSetting; + case CONFOCAL_NEW: + case MICA_CONFOCAL: + return mainConfocalSetting; + case WIDEFIELD: + case MICA_WIDEFIELD: + case MICA_WIDEFOCAL: + return mainCameraSetting != null ? mainCameraSetting : masterCameraSetting; + default: + return null; + } + } + + /** + * Depending on hardware setting layout and data source type, it returns the main ATL confocal setting that + * shall be used for extracting further hardware settings information. + */ + public Element getAtlConfocalSetting(){ + switch (atlSettingLayout){ + case CONFOCAL_OLD: + case MICA_WIDEFIELD: + case MICA_WIDEFOCAL: + return masterConfocalSetting; + case CONFOCAL_NEW: + case MICA_CONFOCAL: + return mainConfocalSetting; + case WIDEFIELD: + default: + return null; + } + } +} diff --git a/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/helpers/Tuple.java b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/helpers/Tuple.java new file mode 100644 index 00000000000..65c5c01dff0 --- /dev/null +++ b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/helpers/Tuple.java @@ -0,0 +1,13 @@ +package loci.formats.in.LeicaMicrosystemsMetadata.helpers; + +public class Tuple { + public T1 first; + public T2 second; + public T3 third; + + public Tuple(T1 first, T2 second, T3 third){ + this.first = first; + this.second = second; + this.third = third; + } +} diff --git a/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/Channel.java b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/model/Channel.java similarity index 54% rename from components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/Channel.java rename to components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/model/Channel.java index 7fcecb7c9d4..2a9580cb782 100644 --- a/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/Channel.java +++ b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/model/Channel.java @@ -23,7 +23,11 @@ * #L% */ -package loci.formats.in.LeicaMicrosystemsMetadata; +package loci.formats.in.LeicaMicrosystemsMetadata.model; + +import loci.formats.in.LeicaMicrosystemsMetadata.model.confocal.DetectorSetting; +import loci.formats.in.LeicaMicrosystemsMetadata.model.confocal.LaserSetting; +import ome.xml.model.primitives.Color; /** * This class represents image channels extracted from LMS image xmls. @@ -33,12 +37,28 @@ public class Channel { // -- Fields -- public int channelTag; - public int resolution; + public int resolution; // bits per pixel public double min; public double max; public String unit; public String lutName; public long bytesInc; + public Color lutColor; + public int lutColorIndex; + + + public class ChannelProperties { + public int channelGroup; + public String channelType; + public String beamRoute; + public String detectorName; + public String dyeName; + public int sequentialSettingIndex; + public String digitalGatingMode; + public double tauScanLine; + } + + public ChannelProperties channelProperties = new ChannelProperties(); public boolean isLutInverted; public long bitInc; @@ -54,33 +74,44 @@ public enum ChannelType { // ARRIVAL_TIME } + public static final int RED = 0; + public static final int GREEN = 1; + public static final int BLUE = 2; + public static final int CYAN = 3; + public static final int MAGENTA = 4; + public static final int YELLOW = 5; + public static final int GREY = 6; + public ChannelType channelType; + public String dye; + public DetectorSetting detectorSetting; + public LaserSetting laserSetting; + public Filter filter; + public double pinholeSize; + public double exposureTime; + public String channelName; + // -- Constructor -- - public Channel(int channelTag, int resolution, double min, double max, String unit, - String lutName, long bytesInc) { - this.channelTag = channelTag; - this.resolution = resolution; - this.min = min; - this.max = max; - this.unit = unit; - this.lutName = lutName; - this.bytesInc = bytesInc; - setChannelType(); + public Channel() { } //-- Methods -- - private void setChannelType(){ - if (channelTag == 0) { - channelType = ChannelType.MONO; - } else { - if (lutName.equals("Red")){ + public void setChannelType(){ + switch(channelTag){ + case 1: channelType = ChannelType.RED; - } else if (lutName.equals("Green")){ + break; + case 2: channelType = ChannelType.GREEN; - } else if (lutName.equals("Blue")){ + break; + case 3: channelType = ChannelType.BLUE; - } + break; + case 0: + default: + channelType = ChannelType.MONO; + break; } } } diff --git a/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/Dimension.java b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/model/Dimension.java similarity index 71% rename from components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/Dimension.java rename to components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/model/Dimension.java index 98a7cd5217a..965db359317 100644 --- a/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/Dimension.java +++ b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/model/Dimension.java @@ -23,7 +23,7 @@ * #L% */ -package loci.formats.in.LeicaMicrosystemsMetadata; +package loci.formats.in.LeicaMicrosystemsMetadata.model; /** * This class represents image dimensions extracted from LMS image xmls. @@ -37,8 +37,9 @@ public class Dimension { public int size; public long bytesInc; public String unit = null; - private Double length = 0d; - private Double offByOneLength = 0d; + private Double lengthPerUnit = 0d; + private Double offByOneLengthPerPixel = 0d; + public Double origin; public boolean oldPhysicalSize = false; public int frameIndex = 0; @@ -72,13 +73,19 @@ public static DimensionKey with(int id) { // -- Constructors -- - public Dimension(DimensionKey key, int size, long bytesInc, String unit, Double length, boolean oldPhysicalSize) { + public Dimension(DimensionKey key, int size, long bytesInc, String unit, Double length, Double origin, boolean oldPhysicalSize) { this.key = key; this.size = size; this.bytesInc = bytesInc; this.unit = unit; this.oldPhysicalSize = oldPhysicalSize; - setLength(length); + + if (key == DimensionKey.X || key == DimensionKey.Y){ + setPixelLength(length); + } else { + this.lengthPerUnit = length / size; + } + this.origin = origin; } private Dimension() { @@ -93,30 +100,30 @@ public static Dimension createChannelDimension(int channelNumber, long bytesInc) } // -- Methods -- - public void setLength(Double length) { - this.length = length; - offByOneLength = 0d; + public void setPixelLength(Double length) { + lengthPerUnit = length; + if (size > 1) { - offByOneLength = this.length / size; - this.length /= (size - 1); // length per pixel + lengthPerUnit /= (size - 1); + offByOneLengthPerPixel = lengthPerUnit / size; } else { - this.length = 0d; - } - - if (unit.equals("Ks")) { - this.length /= 1000; - offByOneLength /= 1000; - } else if (unit.equals("m")) { - this.length *= METER_MULTIPLY; - offByOneLength *= METER_MULTIPLY; + lengthPerUnit = 0d; + offByOneLengthPerPixel = 0d; } + + this.lengthPerUnit *= METER_MULTIPLY; + offByOneLengthPerPixel *= METER_MULTIPLY; } - public Double getLength() { + /** + * Returns the physical distance between two neighboring steps in a dimension (e.g. pixels, slices, points in time, etc.) + * @return + */ + public Double getPhysicalStepSize() { if (key == DimensionKey.X || key == DimensionKey.Y) { - return oldPhysicalSize ? offByOneLength : length; + return oldPhysicalSize ? offByOneLengthPerPixel : lengthPerUnit; } else { - return length; + return lengthPerUnit; } } } diff --git a/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/model/DimensionStore.java b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/model/DimensionStore.java new file mode 100644 index 00000000000..4b146ebc1a3 --- /dev/null +++ b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/model/DimensionStore.java @@ -0,0 +1,243 @@ +/* + * #%L + * OME Bio-Formats package for reading and converting biological file formats. + * %% + * Copyright (C) 2005 - 2017 Open Microscopy Environment: + * - Board of Regents of the University of Wisconsin-Madison + * - Glencoe Software, Inc. + * - University of Dundee + * %% + * 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, see + * . + * #L% + */ + +package loci.formats.in.LeicaMicrosystemsMetadata.model; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import loci.formats.in.LeicaMicrosystemsMetadata.helpers.Tuple; +import loci.formats.in.LeicaMicrosystemsMetadata.model.Channel.ChannelType; +import loci.formats.in.LeicaMicrosystemsMetadata.model.Dimension.DimensionKey; +import ome.units.quantity.Length; + +/** + * This class bundles all image dimension information extracted from LMS image xmls. + * + * @author Constanze Wendlandt constanze.wendlandt at leica-microsystems.com + */ +public class DimensionStore { + public List dimensions = new ArrayList(); + public List channels = new ArrayList(); + public boolean rgb = false; + public boolean inverseRgb = false; + public double physicalSizeX; + public double physicalSizeY; + public double zBegin = 0; + public double zEnd = 0; + public double zWidePosition = 0; + public double zGalvoPosition = 0; + public List> fieldPositions = new ArrayList>(); + public boolean tilescanInfoHasZ = false; + public boolean flipX; + public boolean flipY; + public boolean swapXY; + public double zStep; + public double tStep; + public int tileCount = 1; + public long tileBytesInc; + public int extras = 1; + + public enum ZDriveMode { + ZGalvo, + ZWide + } + + public ZDriveMode zDriveMode; + + /** + * Inserts dimension and optionally adapts other dimension-dependent + * values + * + * @param imageIndex + * @param dimension + */ + public void addDimension(Dimension dimension) { + dimensions.add(dimension); + if (dimension.key == DimensionKey.X) { + physicalSizeX = dimension.getPhysicalStepSize(); + } else if (dimension.key == DimensionKey.Y) { + physicalSizeY = dimension.getPhysicalStepSize(); + } else if (dimension.key == DimensionKey.Z) { + if (dimension.getPhysicalStepSize() != null) { + zStep = Math.abs(dimension.getPhysicalStepSize()); + } + } else if (dimension.key == DimensionKey.T){ + tStep = dimension.getPhysicalStepSize(); + } + else if (dimension.key == DimensionKey.S) { + tileCount *= dimension.size; + tileBytesInc = dimension.bytesInc; + } + } + + /** + * Returns the dimension order as a string + * + * @param imageIndex + * @return dimension order string, as it is expected in + * CoreMetadata.dimensionOrder + */ + public String getDimensionOrder() { + sortDimensions(); + + String dimensionOrder = ""; + List standardDimensions = new ArrayList<>( + Arrays.asList(DimensionKey.X, DimensionKey.Y, DimensionKey.Z, + DimensionKey.C, DimensionKey.T)); + + for (Dimension dimension : dimensions) { + if (standardDimensions.contains(dimension.key)) { + dimensionOrder += dimension.key.token; + } + } + return dimensionOrder; + } + + /** + * Sorts list of existing dimensions by increasing bytesInc, beginning with X + * and Y, ending with stage position + * + * @param coreIndex + */ + private void sortDimensions() { + List dims = dimensions; + dims.sort((Dimension dim1, Dimension dim2) -> Long.compare(dim1.bytesInc, dim2.bytesInc)); + + // move X and Y to the start + Dimension dimX = getDimension(DimensionKey.X); + Dimension dimY = getDimension(DimensionKey.Y); + dimensions.remove(dimX); + dimensions.remove(dimY); + + // XY + if (dimX.bytesInc < dimY.bytesInc) { + dimensions.add(0, dimX); + dimensions.add(1, dimY); + } else { + // YX + dimensions.add(0, dimY); + dimensions.add(1, dimX); + } + + // move dimension S to the end to sort images by stage position, since tiles are + // accessed as separate series + Dimension dimS = getDimension(DimensionKey.S); + dimensions.remove(dimS); + dimensions.add(dimS); + } + + public List getDimensions() { + sortDimensions(); + return dimensions; + } + + /** + * Adds Z, T and S dimension if they haven't been added already + * + * @param imageIndex + */ + public void addMissingDimensions() { + dimensions.sort((dim1, dim2) -> Long.compare(dim1.bytesInc, dim2.bytesInc)); + Dimension lastDimension = dimensions.get(dimensions.size() - 1); + if (getDimension(DimensionKey.Z) == null) { + addDimension(new Dimension(DimensionKey.Z, 1, lastDimension.bytesInc, "m", 1.0, 0.0, lastDimension.oldPhysicalSize)); + } + if (getDimension(DimensionKey.T) == null) { + addDimension(new Dimension(DimensionKey.T, 1, lastDimension.bytesInc, "s", 1.0, 0.0, lastDimension.oldPhysicalSize)); + } + if (getDimension(DimensionKey.S) == null) { + addDimension(new Dimension(DimensionKey.S, 1, lastDimension.bytesInc, "", 1.0, 0.0, lastDimension.oldPhysicalSize)); + } + } + + /** + * Adds channel dimension + * + * @param coreIndex + * @param sizeC + * total number of channels + * @param bytesInc + */ + public void addChannelDimension() { + boolean rgb = (getDimension(DimensionKey.X).bytesInc % 3) == 0; + int sizeC = rgb ? channels.size() / 3 : channels.size(); + + long channelBytesInc = getChannelDimensionBytesInc(); + + addDimension(Dimension.createChannelDimension(sizeC, channelBytesInc)); + } + + private long getChannelDimensionBytesInc() { + boolean rgb = (getDimension(DimensionKey.X).bytesInc % 3) == 0; + long maxBytesInc = 0; + + if (rgb) { + for (int i = 0; i < channels.size(); i++) { + Channel channel = channels.get(i); + if (channel.channelTag == 3) { + maxBytesInc = channel.bytesInc > maxBytesInc ? channel.bytesInc : maxBytesInc; + } + } + } else { + for (Channel channel : channels) { + maxBytesInc = channel.bytesInc > maxBytesInc ? channel.bytesInc : maxBytesInc; + } + } + + if (maxBytesInc == 0) { + Dimension yDim = getDimension(DimensionKey.Y); + maxBytesInc = yDim.bytesInc * yDim.size; + } + return maxBytesInc; + } + + public Dimension getDimension(DimensionKey key) { + for (Dimension dimension : dimensions) { + if (dimension.key == key) + return dimension; + } + return null; + } + + /** + * Calculates the number of planes per tile (= field position), considering CZT + * @return + */ + public int getNumberOfPlanesPerTile(){ + int zSize = getDimension(DimensionKey.Z).size; + int tSize = getDimension(DimensionKey.T).size; + return zSize * tSize * channels.size(); + } + + public void setChannels(List channels){ + this.channels = channels; + this.rgb = this.channels.get(0).channelType != ChannelType.MONO; + + // RGB order is defined by ChannelTag order (1,2,3 = RGB, 3,2,1=BGR) + this.inverseRgb = this.rgb && this.channels.get(0).channelType == ChannelType.BLUE; + } +} diff --git a/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/model/Dye.java b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/model/Dye.java new file mode 100644 index 00000000000..c17803213a8 --- /dev/null +++ b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/model/Dye.java @@ -0,0 +1,41 @@ +/* + * #%L + * OME Bio-Formats package for reading and converting biological file formats. + * %% + * Copyright (C) 2005 - 2017 Open Microscopy Environment: + * - Board of Regents of the University of Wisconsin-Madison + * - Glencoe Software, Inc. + * - University of Dundee + * %% + * 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, see + * . + * #L% + */ + +package loci.formats.in.LeicaMicrosystemsMetadata.model; + + +/** + * Data structure for dye information extracted from LMS XML + * + * @author Constanze Wendlandt constanze.wendlandt at leica-microsystems.com + */ +public class Dye { + public String name; //e.g. Nuclei, ALEXA 488 + public String fluochromeName; //e.g. Hoechst 33342, ALEXA 488 + public String lutName; + public double excitationWavelength; + public double emissionWavelength; + public boolean isAutofluorescence; +} diff --git a/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/model/Filter.java b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/model/Filter.java new file mode 100644 index 00000000000..2cf73a69140 --- /dev/null +++ b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/model/Filter.java @@ -0,0 +1,42 @@ +/* + * #%L + * OME Bio-Formats package for reading and converting biological file formats. + * %% + * Copyright (C) 2005 - 2017 Open Microscopy Environment: + * - Board of Regents of the University of Wisconsin-Madison + * - Glencoe Software, Inc. + * - University of Dundee + * %% + * 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, see + * . + * #L% + */ + +package loci.formats.in.LeicaMicrosystemsMetadata.model; + +/** + * Data structure for filter information extracted from LMS XML + * + * @author Constanze Wendlandt constanze.wendlandt at leica-microsystems.com + */ +public class Filter { + public String id; + public String filterSetId; + public double cutIn; + public double cutOut; + public int sequenceIndex; + public int multibandIndex; + public String dye; + public String name; +} diff --git a/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/model/ImageDetails.java b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/model/ImageDetails.java new file mode 100644 index 00000000000..616555e44d5 --- /dev/null +++ b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/model/ImageDetails.java @@ -0,0 +1,42 @@ +/* + * #%L + * OME Bio-Formats package for reading and converting biological file formats. + * %% + * Copyright (C) 2005 - 2017 Open Microscopy Environment: + * - Board of Regents of the University of Wisconsin-Madison + * - Glencoe Software, Inc. + * - University of Dundee + * %% + * 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, see + * . + * #L% + */ + +package loci.formats.in.LeicaMicrosystemsMetadata.model; + +import java.util.ArrayList; +import java.util.List; + +/** + * Data structure for different image names and descriptions extracted from LMS XML + * + * @author Constanze Wendlandt constanze.wendlandt at leica-microsystems.com + */ +public class ImageDetails { + public String originalImageName; + public String targetImageName; // differs for tiles which are treated as separate series + public String description; + public String collectionPrefix = ""; + public List userComments = new ArrayList<>(); +} diff --git a/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/model/MicroscopeDetails.java b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/model/MicroscopeDetails.java new file mode 100644 index 00000000000..42686f5e4d2 --- /dev/null +++ b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/model/MicroscopeDetails.java @@ -0,0 +1,40 @@ +/* + * #%L + * OME Bio-Formats package for reading and converting biological file formats. + * %% + * Copyright (C) 2005 - 2017 Open Microscopy Environment: + * - Board of Regents of the University of Wisconsin-Madison + * - Glencoe Software, Inc. + * - University of Dundee + * %% + * 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, see + * . + * #L% + */ + +package loci.formats.in.LeicaMicrosystemsMetadata.model; + +import ome.xml.model.enums.MicroscopeType; + +/** + * Data structure for microscope stand and objective details extracted from LMS XML + * + * @author Constanze Wendlandt constanze.wendlandt at leica-microsystems.com + */ +public class MicroscopeDetails { + public String microscopeModel; + public MicroscopeType microscopeType; + public String serialNumber; + public Objective objective; +} diff --git a/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/model/Objective.java b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/model/Objective.java new file mode 100644 index 00000000000..05d22070770 --- /dev/null +++ b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/model/Objective.java @@ -0,0 +1,106 @@ +/* + * #%L + * OME Bio-Formats package for reading and converting biological file formats. + * %% + * Copyright (C) 2005 - 2017 Open Microscopy Environment: + * - Board of Regents of the University of Wisconsin-Madison + * - Glencoe Software, Inc. + * - University of Dundee + * %% + * 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, see + * . + * #L% + */ + +package loci.formats.in.LeicaMicrosystemsMetadata.model; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * Data structure for objective information extracted from LMS XML + * + * @author Constanze Wendlandt constanze.wendlandt at leica-microsystems.com + */ +public class Objective { + public String model; + public double numericalAperture; + public String objectiveNumber; + public double magnification; + public String immersion; + public double refractionIndex; + public String correction; + + /** + * Tries to find correction value in objective name string + * @param objectiveName e.g. "HC PL APO 20x/0.70 DRY" or "HCX PL FLUOTAR 40x/0.75 DRY" + * or "HCX PL APO lambda blue 63.0x1.40 OIL UV" or "HCX APO L 20.0x1.00 WATER " + */ + public void setCorrectionFromObjectiveName(String objectiveName){ + String[] objValues = objectiveName.split(" +"); + + List corrections = new ArrayList( + Arrays.asList("PlanApo", + "PlanFluor", + "SuperFluor", + "VioletCorrected", + "Achro", + "Achromat", + "Fluor", + "Fl", + "Fluar", + "Neofluar", + "Fluotar", + "Apo", + "PlanNeofluar", + "UV")); + + for (String value : objValues){ + for (String correction : corrections){ + if (correction.equalsIgnoreCase(value)){ + this.correction = value; + return; + } + } + } + } + + /** + * Tries to find immersion value in objective name string + * @param objectiveName e.g. "HC PL APO 20x/0.70 DRY" or "HCX PL FLUOTAR 40x/0.75 DRY" + * or "HCX PL APO lambda blue 63.0x1.40 OIL UV" or "HCX APO L 20.0x1.00 WATER " + */ + public void setImmersionFromObjectiveName(String objectiveName){ + String[] objValues = objectiveName.split(" +"); + + List immersions = new ArrayList( + Arrays.asList("Oil", + "Water", + "WaterDipping", + "Air", + "Dry", + "Multi", + "Glycerol")); + + for (String value : objValues){ + for (String immersion : immersions){ + if (immersion.equalsIgnoreCase(value)){ + this.immersion = value; + return; + } + } + } + } +} diff --git a/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/ROI.java b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/model/ROI.java similarity index 98% rename from components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/ROI.java rename to components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/model/ROI.java index 2e112bb50e4..570ade62a3e 100644 --- a/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/ROI.java +++ b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/model/ROI.java @@ -23,7 +23,7 @@ * #L% */ -package loci.formats.in.LeicaMicrosystemsMetadata; +package loci.formats.in.LeicaMicrosystemsMetadata.model; import java.util.ArrayList; import java.util.List; @@ -41,7 +41,7 @@ * @author Melissa Linkert melissa at glencoesoftware.com * @author Constanze Wendlandt constanze.wendlandt at leica-microsystems.com */ -class ROI { +public class ROI { // -- Constants -- public static final int TEXT = 512; diff --git a/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/model/confocal/Aotf.java b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/model/confocal/Aotf.java new file mode 100644 index 00000000000..b81f21a1d68 --- /dev/null +++ b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/model/confocal/Aotf.java @@ -0,0 +1,41 @@ +/* + * #%L + * OME Bio-Formats package for reading and converting biological file formats. + * %% + * Copyright (C) 2005 - 2017 Open Microscopy Environment: + * - Board of Regents of the University of Wisconsin-Madison + * - Glencoe Software, Inc. + * - University of Dundee + * %% + * 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, see + * . + * #L% + */ + +package loci.formats.in.LeicaMicrosystemsMetadata.model.confocal; + +import java.util.ArrayList; +import java.util.List; + +/** + * Data structure for AOTF information extracted from LMS XML + * + * @author Constanze Wendlandt constanze.wendlandt at leica-microsystems.com + */ +public class Aotf { + public String name; + public List laserLineSettings = new ArrayList<>(); + public String type; //e.g. visible, UV chaser, UV, MP EOM (treated as AOTF in LMS XML) + public boolean shutterOpen; +} diff --git a/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/model/confocal/ConfocalAcquisitionSettings.java b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/model/confocal/ConfocalAcquisitionSettings.java new file mode 100644 index 00000000000..1483cc1d70d --- /dev/null +++ b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/model/confocal/ConfocalAcquisitionSettings.java @@ -0,0 +1,46 @@ +/* + * #%L + * OME Bio-Formats package for reading and converting biological file formats. + * %% + * Copyright (C) 2005 - 2017 Open Microscopy Environment: + * - Board of Regents of the University of Wisconsin-Madison + * - Glencoe Software, Inc. + * - University of Dundee + * %% + * 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, see + * . + * #L% + */ + +package loci.formats.in.LeicaMicrosystemsMetadata.model.confocal; + +import java.util.ArrayList; +import java.util.List; + +import loci.formats.in.LeicaMicrosystemsMetadata.model.Filter; + +/** + * Data structure for confocal acquisition settings of an image extracted from LMS XML + * + * @author Constanze Wendlandt constanze.wendlandt at leica-microsystems.com + */ +public class ConfocalAcquisitionSettings { + public List detectors = new ArrayList<>(); + public List lasers = new ArrayList<>(); + public List filters = new ArrayList<>(); + public List channelSettings = new ArrayList<>(); + public double readOutRate; + public double zoom; + public double pinholeSize; +} diff --git a/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/model/confocal/ConfocalChannelSetting.java b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/model/confocal/ConfocalChannelSetting.java new file mode 100644 index 00000000000..86bc3c71c0c --- /dev/null +++ b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/model/confocal/ConfocalChannelSetting.java @@ -0,0 +1,37 @@ +/* + * #%L + * OME Bio-Formats package for reading and converting biological file formats. + * %% + * Copyright (C) 2005 - 2017 Open Microscopy Environment: + * - Board of Regents of the University of Wisconsin-Madison + * - Glencoe Software, Inc. + * - University of Dundee + * %% + * 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, see + * . + * #L% + */ + +package loci.formats.in.LeicaMicrosystemsMetadata.model.confocal; + +/** + * Data structure for confocal acquisition settings of one image channel extracted from LMS XML + * + * @author Constanze Wendlandt constanze.wendlandt at leica-microsystems.com + */ +public class ConfocalChannelSetting { + public int channelIndex; + public DetectorSetting detectorSetting; + public LaserSetting laserSetting; +} diff --git a/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/model/confocal/ConfocalSettingRecords.java b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/model/confocal/ConfocalSettingRecords.java new file mode 100644 index 00000000000..87cdfb29a41 --- /dev/null +++ b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/model/confocal/ConfocalSettingRecords.java @@ -0,0 +1,53 @@ +/* + * #%L + * OME Bio-Formats package for reading and converting biological file formats. + * %% + * Copyright (C) 2005 - 2017 Open Microscopy Environment: + * - Board of Regents of the University of Wisconsin-Madison + * - Glencoe Software, Inc. + * - University of Dundee + * %% + * 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, see + * . + * #L% + */ + +package loci.formats.in.LeicaMicrosystemsMetadata.model.confocal; + +import java.util.ArrayList; +import java.util.List; + +import loci.formats.in.LeicaMicrosystemsMetadata.model.Objective; + +/** + * Data structure for raw information extracted from confocal filter and scanner setting records + * + * @author Constanze Wendlandt constanze.wendlandt at leica-microsystems.com + */ +public class ConfocalSettingRecords { + // scanner setting records + public double pinholeSize; + public double zoom; + public double readOutRate; + public boolean reverseX; + public boolean reverseY; + public boolean reverseZ; + public ShutterInfo shutterInfo = new ShutterInfo(); + // filter setting records + public List laserRecords = new ArrayList<>(); + public List aotfRecords = new ArrayList<>(); + public List detectorRecords = new ArrayList<>(); + public List multibandRecords = new ArrayList<>(); + public Objective objectiveRecord = new Objective(); +} diff --git a/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/model/confocal/Detector.java b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/model/confocal/Detector.java new file mode 100644 index 00000000000..f49e6dc7981 --- /dev/null +++ b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/model/confocal/Detector.java @@ -0,0 +1,45 @@ +/* + * #%L + * OME Bio-Formats package for reading and converting biological file formats. + * %% + * Copyright (C) 2005 - 2017 Open Microscopy Environment: + * - Board of Regents of the University of Wisconsin-Madison + * - Glencoe Software, Inc. + * - University of Dundee + * %% + * 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, see + * . + * #L% + */ + +package loci.formats.in.LeicaMicrosystemsMetadata.model.confocal; + +/** + * Data structure for instrument associated detector information extracted from LMS XML + * + * @author Constanze Wendlandt constanze.wendlandt at leica-microsystems.com + */ +public class Detector { + public String detectorId; + public String model; + public String type; + public double zoom; + public int detectorListIndex; + + //filter setting record info + public boolean isActive; + public double offset; + public double gain; + public int channel; +} diff --git a/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/model/confocal/DetectorSetting.java b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/model/confocal/DetectorSetting.java new file mode 100644 index 00000000000..338f1db28d4 --- /dev/null +++ b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/model/confocal/DetectorSetting.java @@ -0,0 +1,55 @@ +/* + * #%L + * OME Bio-Formats package for reading and converting biological file formats. + * %% + * Copyright (C) 2005 - 2017 Open Microscopy Environment: + * - Board of Regents of the University of Wisconsin-Madison + * - Glencoe Software, Inc. + * - University of Dundee + * %% + * 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, see + * . + * #L% + */ + +package loci.formats.in.LeicaMicrosystemsMetadata.model.confocal; + +/** + * Data structure for channel associated detector settings extracted from LMS XML + * + * @author Constanze Wendlandt constanze.wendlandt at leica-microsystems.com + */ +public class DetectorSetting { + public Detector detector; + + public int channel; + public String name; //HyD S 4, Trans PMT, ... + public double gain; + public double offset; + public String type; //SiPM, PMT, ... + public String dyeName; + + public int sequenceIndex; + public int detectorListIndex; + public int channelIndex; + public String channelName; + public boolean transmittedLightMode; + public double readOutRate; + public double cutIn; + public double cutOut; + public Multiband multiband; + //only available in STELLARIS: + public int referenceLineWavelength; + public String referenceLineName; +} diff --git a/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/model/confocal/Laser.java b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/model/confocal/Laser.java new file mode 100644 index 00000000000..a587b2660db --- /dev/null +++ b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/model/confocal/Laser.java @@ -0,0 +1,133 @@ +/* + * #%L + * OME Bio-Formats package for reading and converting biological file formats. + * %% + * Copyright (C) 2005 - 2017 Open Microscopy Environment: + * - Board of Regents of the University of Wisconsin-Madison + * - Glencoe Software, Inc. + * - University of Dundee + * %% + * 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, see + * . + * #L% + */ + +package loci.formats.in.LeicaMicrosystemsMetadata.model.confocal; + +import java.util.Arrays; +import java.util.List; + + +/** + * Data structure for laser information extracted from LMS XML + * + * @author Constanze Wendlandt constanze.wendlandt at leica-microsystems.com + */ +public class Laser { + public String laserId; + public String name = ""; + public double wavelength = 0; + public String wavelengthUnit = ""; + // we have a dedicated power state string here to check if a power state attribute could be found in ATL confocal + //settings (string not empty) or if we need to look into confocal setting records (-> string = empty, true for SP5 images) + public String powerState = ""; + public boolean powerStateOn = false; + public boolean shutterOpen = true; + public boolean isFrap = false; + public LaserSetting laserSetting; + + //SP5 + public enum LmsLightSourceQualifier + { + Unknown(0), + Visible(10), + PulsedVis(11), + UV(20), + IR(30), + MP(40), + MP2(41), + ChaserVis(50), + ChaserUV(60), + WLL(70), + STED1(80), + STED2(81), + STED3(82), + STED4(83), + CARS(90), + Pump(91), + Stokes(92), + FSOPO(100); + + public final int value; + + private LmsLightSourceQualifier(int value){ + this.value = value; + } + + public boolean Compare(int i){return value == i;} + + public static LmsLightSourceQualifier getValue(int id){ + LmsLightSourceQualifier[] values = LmsLightSourceQualifier.values(); + for (int i = 0; i < values.length; i++){ + if (values[i].Compare(id)) + return values[i]; + } + return LmsLightSourceQualifier.Unknown; + } + } + + // SP8, STELLARIS + public enum LmsLightSourceType + { + Unknown(-1), + Visible(0), + UV(1), + PulsedSMD(2), + MP(3), + WLL(4), + Sted(5), + Cars(6), + ExternSpecial(7); + + public final int value; + + private LmsLightSourceType(int value){ + this.value = value; + } + + public boolean Compare(int i){return value == i;} + + public static LmsLightSourceType getValue(int id){ + LmsLightSourceType[] values = LmsLightSourceType.values(); + for (int i = 0; i < values.length; i++){ + if (values[i].Compare(id)) + return values[i]; + } + return LmsLightSourceType.Unknown; + } + } + + public LmsLightSourceQualifier lightSourceQualifier; + public LmsLightSourceType lightSourceType; + + public final List argonWavelengths = Arrays.asList(458.0, 476.0, 488.0, 496.0, 514.0); + + public boolean hasSameLightSourceType(LaserSetting laserSetting){ + return this.lightSourceQualifier != null && laserSetting.lightSourceQualifier != null ? + this.lightSourceQualifier == laserSetting.lightSourceQualifier : + this.lightSourceType != null && laserSetting.lightSourceType != null ? + this.lightSourceType == laserSetting.lightSourceType : + false; + } +} diff --git a/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/model/confocal/LaserSetting.java b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/model/confocal/LaserSetting.java new file mode 100644 index 00000000000..18ac58b4d10 --- /dev/null +++ b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/model/confocal/LaserSetting.java @@ -0,0 +1,43 @@ +/* + * #%L + * OME Bio-Formats package for reading and converting biological file formats. + * %% + * Copyright (C) 2005 - 2017 Open Microscopy Environment: + * - Board of Regents of the University of Wisconsin-Madison + * - Glencoe Software, Inc. + * - University of Dundee + * %% + * 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, see + * . + * #L% + */ + +package loci.formats.in.LeicaMicrosystemsMetadata.model.confocal; + +import loci.formats.in.LeicaMicrosystemsMetadata.model.confocal.Laser.LmsLightSourceQualifier; +import loci.formats.in.LeicaMicrosystemsMetadata.model.confocal.Laser.LmsLightSourceType; + +/** + * Data structure for laser settings information extracted from LMS XML + * + * @author Constanze Wendlandt constanze.wendlandt at leica-microsystems.com + */ +public class LaserSetting { + public Laser laser; + public double intensity; + public double wavelength; + + public LmsLightSourceQualifier lightSourceQualifier; + public LmsLightSourceType lightSourceType; +} diff --git a/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/model/confocal/Multiband.java b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/model/confocal/Multiband.java new file mode 100644 index 00000000000..420f8549aeb --- /dev/null +++ b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/model/confocal/Multiband.java @@ -0,0 +1,39 @@ +/* + * #%L + * OME Bio-Formats package for reading and converting biological file formats. + * %% + * Copyright (C) 2005 - 2017 Open Microscopy Environment: + * - Board of Regents of the University of Wisconsin-Madison + * - Glencoe Software, Inc. + * - University of Dundee + * %% + * 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, see + * . + * #L% + */ + +package loci.formats.in.LeicaMicrosystemsMetadata.model.confocal; + +/** + * Data structure for multiband (detector range) information extracted from LMS XML + * + * @author Constanze Wendlandt constanze.wendlandt at leica-microsystems.com + */ +public class Multiband { + public int channel; + public double leftWorld; // cut in + public double rightWorld; // cut out + public String dyeName; + public String name; +} diff --git a/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/model/confocal/ShutterInfo.java b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/model/confocal/ShutterInfo.java new file mode 100644 index 00000000000..5cbf4d09203 --- /dev/null +++ b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/model/confocal/ShutterInfo.java @@ -0,0 +1,21 @@ +package loci.formats.in.LeicaMicrosystemsMetadata.model.confocal; + +public class ShutterInfo { + public boolean cars = true; + public boolean chaserUv = true; + public boolean chaserVisible = true; + public boolean fsopo = true; + public boolean mp = true; + public boolean mp2 = true; + public boolean pulsed635 = true; + public boolean pump = true; + public boolean sted1 = true; + public boolean sted2 = true; + public boolean sted3 = true; + public boolean sted4 = true; + public boolean stokes = true; + public boolean superContinuumVisible = true; + public boolean uv = true; + public boolean uv405 = true; + public boolean visible = true; +} diff --git a/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/write/ConfocalSettingsWriter.java b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/write/ConfocalSettingsWriter.java new file mode 100644 index 00000000000..75bd31445a6 --- /dev/null +++ b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/write/ConfocalSettingsWriter.java @@ -0,0 +1,119 @@ +/* + * #%L + * OME Bio-Formats package for reading and converting biological file formats. + * %% + * Copyright (C) 2005 - 2017 Open Microscopy Environment: + * - Board of Regents of the University of Wisconsin-Madison + * - Glencoe Software, Inc. + * - University of Dundee + * %% + * 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, see + * . + * #L% + */ + +package loci.formats.in.LeicaMicrosystemsMetadata.write; + +import loci.formats.FormatTools; +import loci.formats.MetadataTools; +import loci.formats.in.LeicaMicrosystemsMetadata.model.confocal.ConfocalAcquisitionSettings; +import loci.formats.in.LeicaMicrosystemsMetadata.model.confocal.ConfocalChannelSetting; +import loci.formats.in.LeicaMicrosystemsMetadata.model.confocal.Detector; +import loci.formats.in.LeicaMicrosystemsMetadata.model.confocal.Laser; +import loci.formats.meta.MetadataStore; +import ome.units.UNITS; +import ome.units.quantity.Frequency; +import ome.units.quantity.Length; +import ome.xml.model.enums.DetectorType; +import ome.xml.model.enums.LaserMedium; +import ome.xml.model.enums.LaserType; +import ome.xml.model.primitives.PercentFraction; + +/** + * ConfocalSettingsWriter writes confocal instrument and channel settings to the MetadataStore + * + * @author Constanze Wendlandt constanze.wendlandt at leica-microsystems.com + */ +public class ConfocalSettingsWriter { + + public static void initConfocalInstrumentSettings(ConfocalAcquisitionSettings acquisitionSettings, int seriesIndex, MetadataStore store) { + for (int i = 0; i < acquisitionSettings.detectors.size(); i++) { + Detector detector = acquisitionSettings.detectors.get(i); + detector.detectorId = MetadataTools.createLSID("Detector", seriesIndex, i); + store.setDetectorID(detector.detectorId, seriesIndex, i); + store.setDetectorModel(detector.model, seriesIndex, i); + store.setDetectorZoom(acquisitionSettings.zoom, seriesIndex, i); + store.setDetectorType(detector.type != null && detector.type.equals("PMT") ? DetectorType.PMT : DetectorType.OTHER, seriesIndex, i); + } + + for (int i = 0; i < acquisitionSettings.lasers.size(); i++) { + Laser laser = acquisitionSettings.lasers.get(i); + laser.laserId = MetadataTools.createLSID("LightSource", seriesIndex, i); + store.setLaserID(laser.laserId, seriesIndex, i); + store.setLaserType(LaserType.OTHER, seriesIndex, i); + store.setLaserLaserMedium(LaserMedium.OTHER, seriesIndex, i); + store.setLaserModel(laser.name, seriesIndex, i); + Length wavelength = FormatTools.getWavelength(laser.wavelength); + store.setLaserWavelength(wavelength, seriesIndex, i); + } + } + + public static void initConfocalChannelSettings(ConfocalAcquisitionSettings acquisitionSettings, int seriesIndex, MetadataStore store) { + for (int i = 0; i < acquisitionSettings.channelSettings.size(); i++){ + ConfocalChannelSetting channelSetting = acquisitionSettings.channelSettings.get(i); + + // Detector settings + if (channelSetting.detectorSetting != null){ + if (channelSetting.detectorSetting.detector != null) + store.setDetectorSettingsID(channelSetting.detectorSetting.detector.detectorId, seriesIndex, i); + store.setDetectorSettingsOffset(channelSetting.detectorSetting.offset, seriesIndex, i); + store.setDetectorSettingsGain(channelSetting.detectorSetting.gain, seriesIndex, i); + store.setDetectorSettingsZoom(acquisitionSettings.zoom, seriesIndex, i); + Frequency frequency = FormatTools.createFrequency(acquisitionSettings.readOutRate, UNITS.HERTZ); + store.setDetectorSettingsReadOutRate(frequency, seriesIndex, i); + store.setChannelName(channelSetting.detectorSetting.dyeName, seriesIndex, i); + } + + // Filter settings + String filterId = MetadataTools.createLSID("Filter", seriesIndex, i); + store.setFilterID(filterId, seriesIndex, i); + if (channelSetting.detectorSetting.multiband != null && !channelSetting.detectorSetting.multiband.name.isEmpty()) + store.setFilterModel(channelSetting.detectorSetting.multiband.name, seriesIndex, i); + else + store.setFilterModel(channelSetting.detectorSetting.name, seriesIndex, i); + store.setTransmittanceRangeCutIn(FormatTools.getCutIn(channelSetting.detectorSetting.cutIn), seriesIndex, i); + store.setTransmittanceRangeCutOut(FormatTools.getCutOut(channelSetting.detectorSetting.cutOut), seriesIndex, i); + + String filterSetId = MetadataTools.createLSID("FilterSet", seriesIndex, i); + store.setFilterSetID(filterSetId, seriesIndex, i); + store.setFilterSetEmissionFilterRef(filterId, seriesIndex, i, i); + if (channelSetting.detectorSetting.multiband != null && !channelSetting.detectorSetting.multiband.name.isEmpty()) + store.setFilterSetModel(channelSetting.detectorSetting.multiband.name, seriesIndex, i); + else + store.setFilterSetModel(channelSetting.detectorSetting.name, seriesIndex, i); + store.setLightPathEmissionFilterRef(filterId, seriesIndex, i, 0); + + // Laser / light source settings + if (channelSetting.laserSetting != null){ + if (channelSetting.laserSetting.laser != null) + store.setChannelLightSourceSettingsID(channelSetting.laserSetting.laser.laserId, seriesIndex, i); + PercentFraction attenuation = new PercentFraction((float) channelSetting.laserSetting.intensity / 100f); + store.setChannelLightSourceSettingsAttenuation(attenuation, seriesIndex, i); + store.setChannelExcitationWavelength(FormatTools.getWavelength(channelSetting.laserSetting.wavelength), seriesIndex, i); + } + + store.setChannelPinholeSize(new Length(acquisitionSettings.pinholeSize, UNITS.MICROMETER), seriesIndex, i); + } + } +} diff --git a/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/write/DimensionWriter.java b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/write/DimensionWriter.java new file mode 100644 index 00000000000..d04f122306b --- /dev/null +++ b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/write/DimensionWriter.java @@ -0,0 +1,226 @@ +/* + * #%L + * OME Bio-Formats package for reading and converting biological file formats. + * %% + * Copyright (C) 2005 - 2017 Open Microscopy Environment: + * - Board of Regents of the University of Wisconsin-Madison + * - Glencoe Software, Inc. + * - University of Dundee + * %% + * 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, see + * . + * #L% + */ + +package loci.formats.in.LeicaMicrosystemsMetadata.write; + +import java.util.List; + +import loci.common.DateTools; +import loci.formats.CoreMetadata; +import loci.formats.FormatTools; +import loci.formats.MetadataTools; +import loci.formats.in.LeicaMicrosystemsMetadata.LMSFileReader; +import loci.formats.in.LeicaMicrosystemsMetadata.LMSFileReader.ImageFormat; +import loci.formats.in.LeicaMicrosystemsMetadata.helpers.Tuple; +import loci.formats.in.LeicaMicrosystemsMetadata.model.DimensionStore; +import loci.formats.in.LeicaMicrosystemsMetadata.model.DimensionStore.ZDriveMode; +import loci.formats.in.LeicaMicrosystemsMetadata.model.Dye; +import loci.formats.in.LeicaMicrosystemsMetadata.model.Channel; +import loci.formats.in.LeicaMicrosystemsMetadata.model.Dimension.DimensionKey; +import loci.formats.meta.MetadataStore; +import ome.units.UNITS; +import ome.units.quantity.Length; +import ome.units.quantity.Time; +import ome.xml.model.primitives.Timestamp; + +/** + * DimensionWriter sets up CoreMetadata dimension parameters and related image parameters + * + * @author Constanze Wendlandt constanze.wendlandt at leica-microsystems.com + */ +public class DimensionWriter { + + public static void writeDimensions(CoreMetadata core, DimensionStore dimensionStore){ + setCoreDimensionSizes(core, dimensionStore); + + core.imageCount = core.sizeZ * core.sizeT; + if (!core.rgb) + core.imageCount *= core.sizeC; + else { + core.imageCount *= (core.sizeC / 3); + } + + core.dimensionOrder = dimensionStore.getDimensionOrder(); + } + + /** + * Writes extracted dimension sizes to CoreMetadata + */ + private static void setCoreDimensionSizes(CoreMetadata core, DimensionStore dimensionStore) { + core.sizeX = dimensionStore.getDimension(DimensionKey.X).size; + core.sizeY = dimensionStore.getDimension(DimensionKey.Y).size; + core.sizeZ = dimensionStore.getDimension(DimensionKey.Z).size; + core.sizeT = dimensionStore.getDimension(DimensionKey.T).size; + + if (core.rgb) + dimensionStore.getDimension(DimensionKey.X).bytesInc /= 3; + + if (dimensionStore.extras > 1) { + if (core.sizeZ == 1) + core.sizeZ = dimensionStore.extras; + else { + if (core.sizeT == 0) + core.sizeT = dimensionStore.extras; + else + core.sizeT *= dimensionStore.extras; + } + } + + if (core.sizeX == 0) + core.sizeX = 1; + if (core.sizeY == 0) + core.sizeY = 1; + if (core.sizeC == 0) + core.sizeC = 1; + if (core.sizeZ == 0) + core.sizeZ = 1; + if (core.sizeT == 0) + core.sizeT = 1; + } + + public static void writeChannels(CoreMetadata core, MetadataStore store, DimensionStore dimensionStore, ImageFormat imageFormat, int seriesIndex){ + core.sizeC = dimensionStore.channels.size(); + core.rgb = dimensionStore.rgb; + //assuming that all channels of one image have the same resolution + core.bitsPerPixel = dimensionStore.channels.get(0).resolution; + + int bytesPerPixel = core.bitsPerPixel > 8 ? 2 : 1; + try { + core.pixelType = FormatTools.pixelTypeFromBytes(bytesPerPixel, false, true); + } catch (Exception e){ + System.out.println("Could not render pixel type from channel resolution (in bytes): " + bytesPerPixel); + e.printStackTrace(); + } + + for (int channelIndex = 0; channelIndex < dimensionStore.channels.size(); channelIndex++){ + Channel channel = dimensionStore.channels.get(channelIndex); + String channelId = MetadataTools.createLSID("Channel", channelIndex); + store.setChannelID(channelId, seriesIndex, channelIndex); + store.setChannelName(channel.channelName, seriesIndex, channelIndex); + if (!dimensionStore.rgb){ + store.setChannelColor(channel.lutColor, seriesIndex, channelIndex); + } + } + + // TIFF and JPEG files not interleaved + if (imageFormat == ImageFormat.TIF || imageFormat == ImageFormat.JPEG) { + core.interleaved = false; + } else { + core.interleaved = core.rgb; + } + + core.indexed = !core.rgb; + } + + public static void addDyeInfosToChannels(MetadataStore store, List dyes, DimensionStore dimensionStore, int seriesIndex){ + for (int channelIndex = 0; channelIndex < dimensionStore.channels.size(); channelIndex++){ + Channel channel = dimensionStore.channels.get(channelIndex); + for (Dye dye : dyes){ + if (dye.lutName.equals(channel.lutName)){ + Length emissionWavelength = FormatTools.getWavelength(dye.emissionWavelength); + store.setChannelEmissionWavelength(emissionWavelength, seriesIndex, channelIndex); + Length excitationWavelength = FormatTools.getWavelength(dye.excitationWavelength); + store.setChannelExcitationWavelength(excitationWavelength, seriesIndex, channelIndex); + store.setChannelFluor(dye.fluochromeName, seriesIndex, channelIndex); + break; + } + } + } + } + + public static void writeTimestamps(MetadataStore store, LMSFileReader reader, DimensionStore dimensionStore, List timestamps, + int seriesIndex){ + if (timestamps.size() == 0) return; + + double acquiredDate = timestamps.get(0); + + store.setImageAcquisitionDate(new Timestamp(DateTools.convertDate( + (long) (acquiredDate * 1000), DateTools.COBOL, + DateTools.ISO8601_FORMAT, false)), seriesIndex); + + int nPlanesPerTile = dimensionStore.getNumberOfPlanesPerTile(); + if (reader.isRGB()) nPlanesPerTile /= 3; + for (int planeIndex = 0; planeIndex < nPlanesPerTile; planeIndex++){ + if (planeIndex < timestamps.size()){ + double timestamp = timestamps.get(planeIndex); + if (timestamps.get(0) == acquiredDate) { + timestamp -= acquiredDate; + } else if (timestamp == acquiredDate && planeIndex > 0) { + timestamp = timestamps.get(0); + } + + store.setPlaneDeltaT(new Time(timestamp, UNITS.SECOND), seriesIndex, planeIndex); + } + } + } + + public static void writeExposureTimes(MetadataStore store, DimensionStore dimensionStore, LMSFileReader reader, int seriesIndex){ + for (int planeIndex = 0; planeIndex < reader.getImageCount(); planeIndex++){ + int channelIndex = reader.getZCTCoords(planeIndex)[1]; + store.setPlaneExposureTime(new Time(dimensionStore.channels.get(channelIndex).exposureTime, UNITS.SECOND), seriesIndex, planeIndex); + } + } + + /** + * Writes field positions to reader's {@link MetadataStore} + */ + public static void writeFieldPositions(MetadataStore store, DimensionStore dimensionStore, LMSFileReader reader, int seriesIndex, int tileIndex) { + //XY(Z) + reader.addSeriesMeta("Reverse X orientation", dimensionStore.flipX); + reader.addSeriesMeta("Reverse Y orientation", dimensionStore.flipY); + reader.addSeriesMeta("Swap XY orientation", dimensionStore.swapXY); + + Tuple fieldPosition = dimensionStore.fieldPositions.get(tileIndex); + int nPlanesPerTile = dimensionStore.getNumberOfPlanesPerTile(); + if (reader.isRGB()) nPlanesPerTile /= 3; + for (int planeIndexWithinTile = 0; planeIndexWithinTile < nPlanesPerTile; planeIndexWithinTile++){ + store.setPlanePositionX(fieldPosition.first, seriesIndex, planeIndexWithinTile); + store.setPlanePositionY(fieldPosition.second, seriesIndex, planeIndexWithinTile); + if (fieldPosition.third != null) + store.setPlanePositionZ(fieldPosition.third, seriesIndex, planeIndexWithinTile); + } + + //Z + if (!dimensionStore.tilescanInfoHasZ){ + for (int planeIndex = 0; planeIndex < reader.getImageCount(); planeIndex++){ + int sign = dimensionStore.zBegin <= dimensionStore.zEnd ? 1 : -1; + int zIndex = reader.getZCTCoords(planeIndex)[0]; + double otherZDrivePos = dimensionStore.zDriveMode == ZDriveMode.ZGalvo ? dimensionStore.zWidePosition : dimensionStore.zGalvoPosition; + Length zPos = FormatTools.createLength(otherZDrivePos + dimensionStore.zBegin + dimensionStore.zStep * sign * zIndex, UNITS.METER); + store.setPlanePositionZ(zPos, seriesIndex, planeIndex); + } + } + } + + /** + * Writes physical sizes to reader's {@link MetadataStore} + */ + public static void writePhysicalSizes(MetadataStore store, DimensionStore dimensionStore, int seriesIndex){ + store.setPixelsPhysicalSizeX(FormatTools.getPhysicalSizeX(dimensionStore.physicalSizeX), seriesIndex); + store.setPixelsPhysicalSizeY(FormatTools.getPhysicalSizeY(dimensionStore.physicalSizeY), seriesIndex); + store.setPixelsPhysicalSizeZ(FormatTools.getPhysicalSizeZ(dimensionStore.zStep), seriesIndex); + store.setPixelsTimeIncrement(new Time(dimensionStore.tStep, UNITS.SECOND), seriesIndex); + } +} diff --git a/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/write/ImageSettingsWriter.java b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/write/ImageSettingsWriter.java new file mode 100644 index 00000000000..8944fdc1f99 --- /dev/null +++ b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/write/ImageSettingsWriter.java @@ -0,0 +1,120 @@ +/* + * #%L + * OME Bio-Formats package for reading and converting biological file formats. + * %% + * Copyright (C) 2005 - 2017 Open Microscopy Environment: + * - Board of Regents of the University of Wisconsin-Madison + * - Glencoe Software, Inc. + * - University of Dundee + * %% + * 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, see + * . + * #L% + */ + +package loci.formats.in.LeicaMicrosystemsMetadata.write; + +import java.util.Deque; +import java.util.Iterator; + +import org.w3c.dom.Attr; +import org.w3c.dom.Element; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.NodeList; + +import loci.formats.CoreMetadata; +import loci.formats.in.LeicaMicrosystemsMetadata.LMSFileReader; +import loci.formats.in.LeicaMicrosystemsMetadata.model.ImageDetails; +import loci.formats.meta.MetadataStore; + +/** + * ImageSettingsWriter writes image names and xml details to the reader's {@link CoreMetadata} and {@MetadataStore} + * + * @author Constanze Wendlandt constanze.wendlandt at leica-microsystems.com + */ +public class ImageSettingsWriter { + + /** + * Writes image details to the reader's {@link CoreMetadata} + * @param store + * @param reader + * @param originalImageName + * @param description + * @param userComments + * @param seriesIndex + */ + public static void writeImageDetails(MetadataStore store, LMSFileReader reader, ImageDetails imageDetails, int seriesIndex){ + store.setImageName(imageDetails.targetImageName, seriesIndex); + store.setImageDescription(imageDetails.description, seriesIndex); + for (int i = 0; i < imageDetails.userComments.size(); i++){ + reader.addSeriesMeta("User-Comment[" + i + "]", imageDetails.userComments.get(i)); + } + } + + /** + * Creates key value pairs from attributes of the root's child nodes (tag | + * attribute name : attribute value) and adds them to reader's + * {@link CoreMetadata} + * + * @param root + * xml node + * @param nameStack + * list of node names to be prepended to key name + */ + public static void populateOriginalMetadata(Element root, Deque nameStack, LMSFileReader reader) { + 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"); + } + final StringBuilder key = new StringBuilder(); + final Iterator nameStackIterator = nameStack.descendingIterator(); + while (nameStackIterator.hasNext()) { + final String k = nameStackIterator.next(); + key.append(k); + key.append("|"); + } + if (suffix != null && value != null && suffix.length() > 0 && value.length() > 0 && !suffix.equals("HighInteger") + && !suffix.equals("LowInteger")) { + reader.addSeriesMetaList(key.toString() + suffix, 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")) { + reader.addSeriesMeta(key.toString() + attr.getName(), 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, reader); + } + } + + if (root.hasAttributes() && !name.equals("Element") && !name.equals("Attachment") + && !name.equals("LMSDataContainerHeader")) { + nameStack.pop(); + } + } +} diff --git a/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/write/InstrumentWriter.java b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/write/InstrumentWriter.java new file mode 100644 index 00000000000..b2d197dca66 --- /dev/null +++ b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/write/InstrumentWriter.java @@ -0,0 +1,78 @@ +/* + * #%L + * OME Bio-Formats package for reading and converting biological file formats. + * %% + * Copyright (C) 2005 - 2017 Open Microscopy Environment: + * - Board of Regents of the University of Wisconsin-Madison + * - Glencoe Software, Inc. + * - University of Dundee + * %% + * 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, see + * . + * #L% + */ + +package loci.formats.in.LeicaMicrosystemsMetadata.write; + +import loci.formats.MetadataTools; +import loci.formats.in.LeicaMicrosystemsMetadata.helpers.LMSMainXmlNodes; +import loci.formats.in.LeicaMicrosystemsMetadata.model.MicroscopeDetails; +import loci.formats.meta.MetadataStore; + +/** + * InstrumentWriter writes microscope stand and objective details to the reader's {@link CoreMetadata} and {@MetadataStore} + * + * @author Constanze Wendlandt constanze.wendlandt at leica-microsystems.com + */ +public class InstrumentWriter { + + /** + * Writes stand details to reader's [@link MetadataStore] + */ + public static void writeMicroscopeDetails(MetadataStore store, LMSMainXmlNodes xmlNodes, MicroscopeDetails micDetails, int seriesIndex){ + String instrumentID = MetadataTools.createLSID("Instrument", seriesIndex); + store.setInstrumentID(instrumentID, seriesIndex); + + store.setMicroscopeModel(micDetails.microscopeModel, seriesIndex); + store.setMicroscopeType(micDetails.microscopeType, seriesIndex); + store.setMicroscopeSerialNumber(micDetails.serialNumber, seriesIndex); + store.setImageInstrumentRef(instrumentID, seriesIndex); + + if (micDetails.objective == null) return; + + String objectiveID = MetadataTools.createLSID("Objective", seriesIndex, 0); + store.setObjectiveID(objectiveID, seriesIndex, 0); + store.setObjectiveModel(micDetails.objective.model, seriesIndex, 0); + store.setObjectiveLensNA(micDetails.objective.numericalAperture, seriesIndex, 0); + store.setObjectiveSerialNumber(micDetails.objective.objectiveNumber, seriesIndex, 0); + store.setObjectiveNominalMagnification(micDetails.objective.magnification, seriesIndex, 0); + + try { + store.setObjectiveImmersion(MetadataTools.getImmersion(micDetails.objective.immersion), seriesIndex, 0); + } catch (Exception e){ + System.out.println("Objective immersion could not be read."); + e.printStackTrace(); + } + + try { + store.setObjectiveCorrection(MetadataTools.getCorrection(micDetails.objective.correction), seriesIndex, 0); + } catch (Exception e){ + System.out.println("Objective correction could not be read."); + e.printStackTrace(); + } + + store.setObjectiveSettingsID(objectiveID, seriesIndex); + store.setObjectiveSettingsRefractiveIndex(micDetails.objective.refractionIndex, seriesIndex); + } +} diff --git a/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/write/WidefieldSettingsWriter.java b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/write/WidefieldSettingsWriter.java new file mode 100644 index 00000000000..0aa2a342eb1 --- /dev/null +++ b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/write/WidefieldSettingsWriter.java @@ -0,0 +1,67 @@ +/* + * #%L + * OME Bio-Formats package for reading and converting biological file formats. + * %% + * Copyright (C) 2005 - 2017 Open Microscopy Environment: + * - Board of Regents of the University of Wisconsin-Madison + * - Glencoe Software, Inc. + * - University of Dundee + * %% + * 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, see + * . + * #L% + */ + +package loci.formats.in.LeicaMicrosystemsMetadata.write; + +import java.util.List; + +import loci.formats.FormatTools; +import loci.formats.MetadataTools; +import loci.formats.in.LeicaMicrosystemsMetadata.model.Filter; +import loci.formats.meta.MetadataStore; + +/** + * WidefieldSettingsWriter writes widefield instrument and channel settings to the MetadataStore + * + * @author Constanze Wendlandt constanze.wendlandt at leica-microsystems.com + */ +public class WidefieldSettingsWriter { + + /** + * Adds filters and filter sets to OME metadata store + */ + public static void initFilters(List filters, int channelCount, int series, MetadataStore store) { + for (int filterIndex = 0; filterIndex < filters.size(); filterIndex++) { + Filter filter = filters.get(filterIndex); + + filter.id = MetadataTools.createLSID("Filter", series, filterIndex); + store.setFilterID(filter.id, series, filterIndex); + store.setFilterModel(filter.name, series, filterIndex); + store.setTransmittanceRangeCutIn(FormatTools.getCutIn(filter.cutIn), series, filterIndex); + store.setTransmittanceRangeCutOut(FormatTools.getCutOut(filter.cutOut), series, filterIndex); + + filter.filterSetId = MetadataTools.createLSID("FilterSet", series, filterIndex); + store.setFilterSetID(filter.filterSetId, series, filterIndex); + store.setFilterSetEmissionFilterRef(filter.id, series, filterIndex, filterIndex); + store.setFilterSetModel(filter.name, series, filterIndex); + + //map widefield channel info to image channels by assuming same indices + if (filterIndex < channelCount){ + store.setChannelName(filter.dye, series, filterIndex); + store.setLightPathEmissionFilterRef(filter.id, series, filterIndex, 0); + } + } + } +} diff --git a/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/LMSCollectionXmlDocument.java b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/xml/LMSCollectionXmlDocument.java similarity index 98% rename from components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/LMSCollectionXmlDocument.java rename to components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/xml/LMSCollectionXmlDocument.java index dd0aafe4636..9d010ce5593 100644 --- a/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/LMSCollectionXmlDocument.java +++ b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/xml/LMSCollectionXmlDocument.java @@ -23,7 +23,7 @@ * #L% */ -package loci.formats.in.LeicaMicrosystemsMetadata; +package loci.formats.in.LeicaMicrosystemsMetadata.xml; import java.util.ArrayList; import java.util.List; diff --git a/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/LMSImageXmlDocument.java b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/xml/LMSImageXmlDocument.java similarity index 86% rename from components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/LMSImageXmlDocument.java rename to components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/xml/LMSImageXmlDocument.java index 2dfe683502c..b9fd1818393 100644 --- a/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/LMSImageXmlDocument.java +++ b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/xml/LMSImageXmlDocument.java @@ -23,7 +23,7 @@ * #L% */ -package loci.formats.in.LeicaMicrosystemsMetadata; +package loci.formats.in.LeicaMicrosystemsMetadata.xml; import org.w3c.dom.Node; @@ -41,6 +41,9 @@ public LMSImageXmlDocument(String filepath, LMSCollectionXmlDocument parent){ super(filepath, parent); } + public LMSImageXmlDocument(Node root){ + super(root); + } /** * Returns the image node of the xml document which contains image metadata @@ -51,4 +54,9 @@ public LMSImageXmlDocument(String filepath, LMSCollectionXmlDocument parent){ * Returns the name of the image (it might be contained in the XML or otherwise e.g. in the file name) */ public abstract String getImageName(); + + /** + * Returns the path of collections the image is in (e.g. "Collection1/Collection2") + */ + public abstract String getCollectionPath(); } diff --git a/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/LMSXmlDocument.java b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/xml/LMSXmlDocument.java similarity index 87% rename from components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/LMSXmlDocument.java rename to components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/xml/LMSXmlDocument.java index 01cb161422f..75be35037c9 100644 --- a/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/LMSXmlDocument.java +++ b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/xml/LMSXmlDocument.java @@ -23,7 +23,7 @@ * #L% */ -package loci.formats.in.LeicaMicrosystemsMetadata; +package loci.formats.in.LeicaMicrosystemsMetadata.xml; import java.io.File; import java.io.FileInputStream; @@ -43,6 +43,7 @@ import org.xml.sax.SAXException; import loci.common.Location; +import loci.formats.in.LeicaMicrosystemsMetadata.LMSFileReader; import org.xml.sax.InputSource; @@ -69,12 +70,12 @@ public abstract class LMSXmlDocument { // -- Fields -- + private boolean isValid = false; protected Document doc; protected XPath xPath; protected String dir; protected String filepath; - protected static final Logger LOGGER = - LoggerFactory.getLogger(LMSXmlDocument.class); + protected static final Logger LOGGER = LoggerFactory.getLogger(LMSXmlDocument.class); public enum InitFrom { XML, @@ -92,6 +93,10 @@ public LMSXmlDocument(String filepath, LMSCollectionXmlDocument parent){ this.parent = parent; } + public LMSXmlDocument(Node root){ + initFromNode(root); + } + // -- Getters -- public Document getDoc() { return this.doc; @@ -108,12 +113,9 @@ private void initFromXmlString(String xml) { DocumentBuilder db = dbf.newDocumentBuilder(); Document doc = db.parse(new InputSource(new StringReader(xml))); doc.getDocumentElement().normalize(); - this.doc = doc; - this.xPath = XPathFactory.newInstance().newXPath(); - LMSFileReader.log.trace(this.toString()); + init(doc); } catch (ParserConfigurationException | SAXException | IOException e) { - LMSFileReader.log.error(e.getMessage()); - e.printStackTrace(); + LMSFileReader.log.error("LMSXmlDocument: XML document could not be parsed."); } } @@ -125,18 +127,36 @@ private void initFromFilepath(String filepath) { Document doc = db.parse(fi); fi.close(); doc.getDocumentElement().normalize(); - this.doc = doc; - this.xPath = XPathFactory.newInstance().newXPath(); - LMSFileReader.log.trace(this.toString()); + init(doc); } catch (ParserConfigurationException | SAXException | IOException e) { - LMSFileReader.log.error(e.getMessage()); - e.printStackTrace(); + LMSFileReader.log.error("LMSXmlDocument: XML document could not be read."); } this.dir = Paths.get(filepath).getParent().toString(); this.filepath = Paths.get(filepath).normalize().toString(); } + private void initFromNode(Node root){ + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + dbf.setNamespaceAware(true); + try { + DocumentBuilder db = dbf.newDocumentBuilder(); + Document doc = db.newDocument(); + Node importedNode = doc.importNode(root, true); + doc.appendChild(importedNode); + init(doc); + } catch (Exception e){ + LMSFileReader.log.error("LMSXmlDocument: XML document could not be created from node."); + } + } + + private void init(Document doc){ + this.doc = doc; + this.xPath = XPathFactory.newInstance().newXPath(); + isValid = true; + LMSFileReader.log.trace(this.toString()); + } + /** * Searches the XML document for expressions using xPath. * @@ -258,6 +278,10 @@ public List getParentFiles(){ } return parents; } + + public boolean isValid(){ + return isValid; + } /** * Checks if file at path exists and corrects paths to be case sensitive diff --git a/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/xml/LifImageXmlDocument.java b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/xml/LifImageXmlDocument.java new file mode 100644 index 00000000000..baf4316559b --- /dev/null +++ b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/xml/LifImageXmlDocument.java @@ -0,0 +1,75 @@ +/* + * #%L + * OME Bio-Formats package for reading and converting biological file formats. + * %% + * Copyright (C) 2005 - 2017 Open Microscopy Environment: + * - Board of Regents of the University of Wisconsin-Madison + * - Glencoe Software, Inc. + * - University of Dundee + * %% + * 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, see + * . + * #L% + */ + +package loci.formats.in.LeicaMicrosystemsMetadata.xml; + +import org.w3c.dom.Node; + +import loci.formats.in.LeicaMicrosystemsMetadata.extract.Extractor; + +/** + * This class loads and represents a Leica Microsystems XML document for one + * image that has + * been extracted from a LIF file + * + * @author Constanze Wendlandt constanze.wendlandt at leica-microsystems.com + */ +public class LifImageXmlDocument extends LMSImageXmlDocument { + private String collectionPath; + + public LifImageXmlDocument(Node root, String collectionPath) { + super(root); + this.collectionPath = collectionPath; + } + + @Override + public Node getImageNode() { + if (doc == null) + return null; + + Node data = GetChildWithName(doc.getDocumentElement(), "Data"); + if (data != null){ + Node image = GetChildWithName(data, "Image"); + return image; + } + + return null; + } + + @Override + public String getImageName() { + return getAttr(doc.getDocumentElement(), "Name"); + } + + @Override + public String getCollectionPath(){ + return collectionPath; + } + + public String getMemoryBlockId(){ + Node memory = GetChildWithName(doc.getDocumentElement(), "Memory"); + return Extractor.getAttributeValue(memory, "MemoryBlockID"); + } +} diff --git a/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/xml/LifXmlDocument.java b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/xml/LifXmlDocument.java new file mode 100644 index 00000000000..097a62eb6a0 --- /dev/null +++ b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/xml/LifXmlDocument.java @@ -0,0 +1,95 @@ +/* + * #%L + * OME Bio-Formats package for reading and converting biological file formats. + * %% + * Copyright (C) 2005 - 2017 Open Microscopy Environment: + * - Board of Regents of the University of Wisconsin-Madison + * - Glencoe Software, Inc. + * - University of Dundee + * %% + * 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, see + * . + * #L% + */ + +package loci.formats.in.LeicaMicrosystemsMetadata.xml; + +import java.util.ArrayList; +import java.util.List; + +import org.w3c.dom.Node; + +import loci.formats.in.LeicaMicrosystemsMetadata.extract.Extractor; + +/** + * This class loads and represents a Leica Microsystems XML document that has + * been extracted from a LIF file + * + * @author Constanze Wendlandt constanze.wendlandt at leica-microsystems.com + */ +public class LifXmlDocument extends LMSXmlDocument { + + public LifXmlDocument(String xml) { + super(xml); + } + + public List getImageXmlDocuments() { + List imageXmlDocs = new ArrayList(); + + if (doc == null) + return null; + + Node rootElement = GetChildWithName(doc.getDocumentElement(), "Element"); + imageXmlDocs.addAll(GetImageChildren(rootElement, "")); + + return imageXmlDocs; + } + + /** + * Creates LifImageXmlDocuments for the passed node (if it is an image) or for its descendants (if it is not) + * @param elementNode + * @return + */ + private List GetImageChildren(Node elementNode, String parentPath){ + List imageXmlDocs = new ArrayList(); + + Node data = GetChildWithName(elementNode, "Data"); + if (data != null){ + //Element is Experiment or Image Element + Node image = GetChildWithName(data, "Image"); + if (image != null){ + //Element is Image Element + imageXmlDocs.add(new LifImageXmlDocument(elementNode, parentPath)); + } + } + + Node children = GetChildWithName(elementNode, "Children"); + if (children != null){ + //Element is Experiment or Collection Element + //only attach name to path if element is a collection + Node experiment = GetChildWithName(data, "Experiment"); + if (experiment == null) + parentPath += Extractor.getAttributeValue(elementNode, "Name") + "/"; + + for (int i = 0; i < children.getChildNodes().getLength(); i++) { + Node child = children.getChildNodes().item(i); + if (child.getNodeName().equals("Element")) { + imageXmlDocs.addAll(GetImageChildren(child, parentPath)); + } + } + } + + return imageXmlDocs; + } +} diff --git a/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/LofXmlDocument.java b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/xml/LofXmlDocument.java similarity index 83% rename from components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/LofXmlDocument.java rename to components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/xml/LofXmlDocument.java index 18d1d13aff2..483f4a23a08 100644 --- a/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/LofXmlDocument.java +++ b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/xml/LofXmlDocument.java @@ -23,7 +23,7 @@ * #L% */ -package loci.formats.in.LeicaMicrosystemsMetadata; +package loci.formats.in.LeicaMicrosystemsMetadata.xml; import org.w3c.dom.Node; @@ -42,6 +42,9 @@ public LofXmlDocument(String xml, String name) { @Override public Node getImageNode() { + if (doc == null) + return null; + Node child = GetChildWithName(doc.getDocumentElement(), "Image"); if (child != null) return child; child = GetChildWithName(doc.getDocumentElement(), "Element"); @@ -61,4 +64,16 @@ public Node getImageNode() { public String getImageName(){ return name; } + + @Override + public String getCollectionPath(){ + String path = ""; + LMSXmlDocument parent = this.parent; + while (parent != null && parent instanceof XlcfDocument){ + path = ((XlcfDocument)parent).getName() + "/" + path; + parent = parent.parent; + } + + return path; + } } diff --git a/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/XlcfDocument.java b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/xml/XlcfDocument.java similarity index 81% rename from components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/XlcfDocument.java rename to components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/xml/XlcfDocument.java index f57a7454c35..8d7061330c2 100644 --- a/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/XlcfDocument.java +++ b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/xml/XlcfDocument.java @@ -23,7 +23,11 @@ * #L% */ -package loci.formats.in.LeicaMicrosystemsMetadata; +package loci.formats.in.LeicaMicrosystemsMetadata.xml; + +import org.w3c.dom.Node; + +import loci.formats.in.LeicaMicrosystemsMetadata.extract.Extractor; /** * This class loads and represents a Leica Microsystems XLCF xml document @@ -37,4 +41,9 @@ public XlcfDocument(String filepath, LMSCollectionXmlDocument parent) { super(filepath, parent); initChildren(); } + + public String getName(){ + Node element = GetChildWithName(doc.getDocumentElement(), "Element"); + return Extractor.getAttributeValue(element, "Name"); + } } diff --git a/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/XlefDocument.java b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/xml/XlefDocument.java similarity index 96% rename from components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/XlefDocument.java rename to components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/xml/XlefDocument.java index 7e5678e8b9d..95ad78ec1d0 100644 --- a/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/XlefDocument.java +++ b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/xml/XlefDocument.java @@ -23,10 +23,9 @@ * #L% */ -package loci.formats.in.LeicaMicrosystemsMetadata; +package loci.formats.in.LeicaMicrosystemsMetadata.xml; import java.util.List; -import org.w3c.dom.Node; /** * This class represents a Leica Microsystems XLEF xml document, diff --git a/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/XlifDocument.java b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/xml/XlifDocument.java similarity index 93% rename from components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/XlifDocument.java rename to components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/xml/XlifDocument.java index 477fc18a1ca..7a569230510 100644 --- a/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/XlifDocument.java +++ b/components/formats-gpl/src/loci/formats/in/LeicaMicrosystemsMetadata/xml/XlifDocument.java @@ -23,7 +23,7 @@ * #L% */ -package loci.formats.in.LeicaMicrosystemsMetadata; +package loci.formats.in.LeicaMicrosystemsMetadata.xml; import java.util.ArrayList; import java.util.List; @@ -149,4 +149,16 @@ public void printXlifInfo() { public boolean isValid(){ return imagePaths.size() > 0; } + + @Override + public String getCollectionPath(){ + String path = ""; + LMSXmlDocument parent = this.parent; + while (parent != null && parent instanceof XlcfDocument){ + path = ((XlcfDocument)parent).getName() + "/" + path; + parent = parent.parent; + } + + return path; + } } diff --git a/components/formats-gpl/src/loci/formats/in/XLEFReader.java b/components/formats-gpl/src/loci/formats/in/XLEFReader.java index 101de49bbdd..52ed730b283 100644 --- a/components/formats-gpl/src/loci/formats/in/XLEFReader.java +++ b/components/formats-gpl/src/loci/formats/in/XLEFReader.java @@ -37,6 +37,9 @@ import loci.formats.FormatException; import loci.formats.FormatTools; import loci.formats.in.LeicaMicrosystemsMetadata.*; +import loci.formats.in.LeicaMicrosystemsMetadata.xml.LMSImageXmlDocument; +import loci.formats.in.LeicaMicrosystemsMetadata.xml.XlefDocument; +import loci.formats.in.LeicaMicrosystemsMetadata.xml.XlifDocument; import loci.common.DataTools; import loci.common.RandomAccessInputStream; @@ -219,8 +222,8 @@ public ImageFormat getImageFormat() { */ private int getReaderIndex(int seriesIndex){ for (int readerIndex = 0; readerIndex < readers.size(); readerIndex++){ - int lastSeriesIndexLastReader = sum(metaTemp.tileCount, 0, readerIndex - 1) - 1; - int lastSeriesIndexThisReader = lastSeriesIndexLastReader + metaTemp.tileCount[readerIndex]; + int lastSeriesIndexLastReader = sumUpTilecounts(0, readerIndex - 1) - 1; + int lastSeriesIndexThisReader = lastSeriesIndexLastReader + metadataTranslators.get(readerIndex).dimensionStore.tileCount; if (seriesIndex >= readerIndex && seriesIndex > lastSeriesIndexLastReader && seriesIndex <= lastSeriesIndexThisReader){ @@ -237,7 +240,7 @@ private int getReaderIndex(int seriesIndex){ */ private int getSeriesPerReaderIndex(int series){ int readerIndex = getReaderIndex(series); - int sprIndex = series - sum(metaTemp.tileCount, 0, readerIndex-1); + int sprIndex = series - sumUpTilecounts(0, readerIndex-1); return sprIndex; } @@ -317,7 +320,7 @@ private void setMetadataOfMultipleImagesReaders(){ LMSFileReader reader = readers.get(getReaderIndex(i)); if (reader instanceof MultipleImagesReader){ ((MultipleImagesReader)reader).setCoreMetadata(core.get(i)); - ((MultipleImagesReader)reader).setMetadataTempBuffer(metaTemp); + ((MultipleImagesReader)reader).metadataTranslators = this.metadataTranslators; } } } @@ -378,18 +381,18 @@ private byte[] getRgbChannel(int channel, byte[] in) { } /** - * Sums up all values of an array, from start index to (including) end index + * Sums up tilecounts from all images, from start image index to (including) end image index * @param arr - * @param start first array position whose value shall be added - * @param end last array position whose value shall be added + * @param start index of first image whose tilecount shall be added + * @param end index of last image whose tilecount shall be added * @return */ - private int sum(int[] arr, int start, int end){ + private int sumUpTilecounts(int start, int end){ start = start < 0 ? 0 : start; if (end < start) return 0; int sum = 0; - for (int i = start; i <= end; i++){ - sum += arr[i]; + for (int i = 0; i <= end; i++){ + sum += metadataTranslators.get(i).dimensionStore.tileCount; } return sum; }