/* * #%L * OME Bio-Formats package for reading and converting biological file formats. * %% * Copyright (C) 2005 - 2015 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 * <http://www.gnu.org/licenses/gpl-2.0.html>. * #L% */ package loci.formats.in; import java.io.BufferedReader; import java.io.IOException; import java.io.StringReader; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Deque; import java.util.HashMap; import loci.common.DateTools; import loci.common.IniList; import loci.common.IniParser; import loci.common.IniTable; import loci.common.RandomAccessInputStream; import loci.common.xml.BaseHandler; import loci.common.xml.XMLTools; import loci.formats.FormatException; import loci.formats.FormatTools; import loci.formats.MetadataTools; import loci.formats.meta.MetadataStore; import loci.formats.tiff.IFD; import loci.formats.tiff.TiffParser; import ome.xml.model.primitives.Timestamp; import ome.units.quantity.Length; import ome.units.quantity.Time; import ome.units.UNITS; import org.xml.sax.Attributes; /** * FEITiffReader is the file format reader for TIFF files produced by various * FEI software. */ public class FEITiffReader extends BaseTiffReader { // -- Constants -- public static final int SFEG_TAG = 34680; public static final int HELIOS_TAG = 34682; public static final int TITAN_TAG = 34683; private static final String DATE_FORMAT = "MM/dd/yyyy HH:mm:ss a"; private static final double MAG_MULTIPLIER = 0.0024388925; // -- Fields -- private String imageName; private String imageDescription; private String date; private String userName; private String microscopeModel; private Length stageX, stageY, stageZ; private Double sizeX, sizeY, timeIncrement; private ArrayList<String> detectors; private Double magnification; // -- Constructor -- public FEITiffReader() { super("FEI TIFF", new String[] {"tif", "tiff"}); suffixSufficient = false; domains = new String[] {FormatTools.SEM_DOMAIN}; } // -- IFormatReader API methods -- /* @see loci.formats.IFormatReader#isThisType(RandomAccessInputStream) */ @Override public boolean isThisType(RandomAccessInputStream stream) throws IOException { TiffParser tp = new TiffParser(stream); IFD ifd = tp.getFirstIFD(); if (ifd == null) return false; return ifd.containsKey(SFEG_TAG) || ifd.containsKey(HELIOS_TAG) || ifd.containsKey(TITAN_TAG); } /* @see loci.formats.IFormatReader#close(boolean) */ @Override public void close(boolean fileOnly) throws IOException { super.close(fileOnly); if (!fileOnly) { imageName = null; imageDescription = null; date = null; userName = null; microscopeModel = null; stageX = stageY = stageZ = null; sizeX = sizeY = timeIncrement = null; detectors = null; magnification = null; } } // -- Internal BaseTiffReader API methods -- /* @see BaseTiffReader#initStandardMetadata() */ @Override protected void initStandardMetadata() throws FormatException, IOException { super.initStandardMetadata(); boolean helios = ifds.get(0).containsKey(HELIOS_TAG); boolean titan = ifds.get(0).containsKey(TITAN_TAG); // Helios etc data might have a stray Titan tag if (titan && ifds.get(0).getIFDTextValue(TITAN_TAG).trim().isEmpty()) { titan = false; } // Titan data (always?) has an empty Helios tag as well, so the Titan tag is checked first String software = "S-FEG"; if (titan) { software = "Titan"; } else if (helios) { software = "Helios NanoLab"; } addGlobalMeta("Software", software); int tagKey = SFEG_TAG; if (titan) { tagKey = TITAN_TAG; } else if (helios) { tagKey = HELIOS_TAG; } String tag = ifds.get(0).getIFDTextValue(tagKey); if (tag == null) { return; } tag = tag.trim(); if (tag.isEmpty()) { return;//fall back to regular reader } // store metadata for later conversion to OME-XML if (tag.startsWith("<")) { XMLTools.parseXML(tag, new FEIHandler()); } else { IniParser parser = new IniParser(); IniList ini = parser.parseINI(new BufferedReader(new StringReader(tag))); detectors = new ArrayList<String>(); if (helios) { IniTable userTable = ini.getTable("User"); date = userTable.get("Date") + " " + userTable.get("Time"); if (getMetadataOptions().getMetadataLevel() != MetadataLevel.MINIMUM) { userName = userTable.get("User"); IniTable systemTable = ini.getTable("System"); if (systemTable == null) { systemTable = ini.getTable("SYSTEM"); } if (systemTable != null) { microscopeModel = systemTable.get("SystemType"); } IniTable beamTable = ini.getTable("Beam"); if (beamTable != null) { String beamTableName = beamTable.get("Beam"); if (beamTableName != null) { beamTable = ini.getTable(beamTableName); } } if (beamTable != null) { String beamX = beamTable.get("StageX"); String beamY = beamTable.get("StageY"); String beamZ = beamTable.get("StageZ"); IniTable stageTable = ini.getTable("Stage"); if (beamX != null) { final Double number = Double.valueOf(beamX); stageX = new Length(number, UNITS.REFERENCEFRAME); } else if (stageTable != null) { final Double number = Double.valueOf(stageTable.get("StageX")); stageX = new Length(number, UNITS.REFERENCEFRAME); } if (beamY != null) { final Double number = Double.valueOf(beamY); stageY = new Length(number, UNITS.REFERENCEFRAME); } else if (stageTable != null) { final Double number = Double.valueOf(stageTable.get("StageY")); stageY = new Length(number, UNITS.REFERENCEFRAME); } if (beamZ != null) { final Double number = Double.valueOf(beamZ); stageZ = new Length(number, UNITS.REFERENCEFRAME); } else if (stageTable != null) { final Double number = Double.valueOf(stageTable.get("StageZ")); stageZ = new Length(number, UNITS.REFERENCEFRAME); } } IniTable scanTable = ini.getTable("Scan"); // physical sizes are stored in meters sizeX = new Double(scanTable.get("PixelWidth")) * 1000000; sizeY = new Double(scanTable.get("PixelHeight")) * 1000000; timeIncrement = new Double(scanTable.get("FrameTime")); } } else { IniTable dataTable = ini.getTable("DatabarData"); imageName = dataTable.get("ImageName"); imageDescription = dataTable.get("szUserText"); String magnification = ini.getTable("Vector").get("Magnification"); sizeX = new Double(magnification) * MAG_MULTIPLIER; sizeY = new Double(magnification) * MAG_MULTIPLIER; IniTable scanTable = ini.getTable("Vector.Sysscan"); final Double posX = Double.valueOf(scanTable.get("PositionX")); final Double posY = Double.valueOf(scanTable.get("PositionY")); stageX = new Length(posX, UNITS.REFERENCEFRAME); stageY = new Length(posY, UNITS.REFERENCEFRAME); IniTable detectorTable = ini.getTable("Vector.Video.Detectors"); int detectorCount = Integer.parseInt(detectorTable.get("NrDetectorsConnected")); for (int i=0; i<detectorCount; i++) { detectors.add(detectorTable.get("Detector_" + i + "_Name")); } } // store everything else in the metadata hashtable if (getMetadataOptions().getMetadataLevel() != MetadataLevel.MINIMUM) { HashMap<String, String> iniMap = ini.flattenIntoHashMap(); metadata.putAll(iniMap); } } } /* @see BaseTiffReader#initMetadataStore() */ @Override protected void initMetadataStore() throws FormatException { super.initMetadataStore(); MetadataStore store = makeFilterMetadata(); MetadataTools.populatePixels(store, this); if (date != null) { date = DateTools.formatDate(date, DATE_FORMAT); if (date != null) { store.setImageAcquisitionDate(new Timestamp(date), 0); } } if (imageName != null) { store.setImageName(imageName, 0); } if (getMetadataOptions().getMetadataLevel() != MetadataLevel.MINIMUM) { if (imageDescription != null) { store.setImageDescription(imageDescription, 0); } if (userName != null) { store.setExperimenterID(MetadataTools.createLSID("Experimenter", 0), 0); store.setExperimenterLastName(userName, 0); } if (microscopeModel != null) { String instrument = MetadataTools.createLSID("Instrument", 0); store.setInstrumentID(instrument, 0); store.setImageInstrumentRef(instrument, 0); store.setMicroscopeModel(microscopeModel, 0); } if (detectors != null && detectors.size() > 0) { String instrument = MetadataTools.createLSID("Instrument", 0); store.setInstrumentID(instrument, 0); store.setImageInstrumentRef(instrument, 0); for (int i=0; i<detectors.size(); i++) { String detectorID = MetadataTools.createLSID("Detector", 0, i); store.setDetectorID(detectorID, 0, i); store.setDetectorModel(detectors.get(i), 0, i); store.setDetectorType(getDetectorType("Other"), 0, i); } } if (magnification != null) { store.setObjectiveID(MetadataTools.createLSID("Objective", 0, 0), 0, 0); store.setObjectiveNominalMagnification(magnification, 0, 0); store.setObjectiveCorrection(getCorrection("Other"), 0, 0); store.setObjectiveImmersion(getImmersion("Other"), 0, 0); } store.setStageLabelX(stageX, 0); store.setStageLabelY(stageY, 0); store.setStageLabelZ(stageZ, 0); store.setStageLabelName("", 0); Length physicalSizeX = FormatTools.getPhysicalSizeX(sizeX); Length physicalSizeY = FormatTools.getPhysicalSizeY(sizeY); if (physicalSizeX != null) { store.setPixelsPhysicalSizeX(physicalSizeX, 0); } if (physicalSizeY != null) { store.setPixelsPhysicalSizeY(physicalSizeY, 0); } if (timeIncrement != null) { store.setPixelsTimeIncrement(new Time(timeIncrement, UNITS.S), 0); } } } // -- Helper class -- class FEIHandler extends BaseHandler { private String key, value; private String qName; private Deque<String> parentNames = new ArrayDeque<String>(); // -- DefaultHandler API methods -- @Override public void characters(char[] data, int start, int len) { String d = new String(data, start, len).trim(); if (d.isEmpty()) { return; } String parent = parentNames.peek(); if (parent == null) { return; } if (parent.equals(qName)) { parentNames.pop(); parent = parentNames.peek(); } if (qName.equals("Label")) { key = d; value = null; } else if (qName.equals("Value")) { value = d; } else { key = parent + " " + qName; value = d; } if (key != null && value != null) { addGlobalMeta(key, value); if (key.equals("Stage X") || ("StagePosition".equals(parent) && key.equals("X"))) { final Double number = Double.valueOf(value); stageX = new Length(number, UNITS.REFERENCEFRAME); } else if (key.equals("Stage Y") || ("StagePosition".equals(parent) && key.equals("Y"))) { final Double number = Double.valueOf(value); stageY = new Length(number, UNITS.REFERENCEFRAME); } else if (key.equals("Stage Z") || ("StagePosition".equals(parent) && key.equals("Z"))) { final Double number = Double.valueOf(value); stageZ = new Length(number, UNITS.REFERENCEFRAME); } else if (key.equals("Microscope")) { microscopeModel = value; } else if (key.equals("User")) { userName = value; } else if (key.equals("Magnification")) { magnification = new Double(value); } // physical sizes stored in meters, but usually too small to be used without converting else if (key.endsWith("X") && "PixelSize".equals(parent)) { sizeX = new Double(value) * 1000000; } else if (key.endsWith("Y") && "PixelSize".equals(parent)) { sizeY = new Double(value) * 1000000; } } } @Override public void startElement(String uri, String localName, String qName, Attributes attributes) { this.qName = qName; parentNames.push(qName); } @Override public void endElement(String uri, String localName, String qName) { if (parentNames.size() > 0) { String name = parentNames.peek(); if (qName.equals(name)) { parentNames.pop(); } } } } }