/* * #%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.IOException; import java.util.ArrayList; import java.util.Arrays; import loci.common.DataTools; import loci.common.Location; import loci.common.RandomAccessInputStream; import loci.formats.CoreMetadata; import loci.formats.FormatException; import loci.formats.FormatTools; import loci.formats.MetadataTools; import loci.formats.meta.MetadataStore; import loci.formats.tiff.TiffParser; import ome.xml.model.primitives.PositiveFloat; import ome.units.quantity.Length; import ome.units.quantity.Time; import ome.units.UNITS; /** * ImprovisionTiffReader is the file format reader for * Improvision TIFF files. */ public class ImprovisionTiffReader extends BaseTiffReader { // -- Constants -- public static final String IMPROVISION_MAGIC_STRING = "Improvision"; // -- Fields -- private String[] cNames; private int pixelSizeT; private double pixelSizeX, pixelSizeY, pixelSizeZ; private String[] files; private MinimalTiffReader[] readers; private int lastFile = 0; // -- Constructor -- public ImprovisionTiffReader() { super("Improvision TIFF", new String[] {"tif", "tiff"}); suffixSufficient = false; domains = new String[] {FormatTools.UNKNOWN_DOMAIN}; } // -- IFormatReader API methods -- /* @see loci.formats.IFormatReader#isThisType(RandomAccessInputStream) */ @Override public boolean isThisType(RandomAccessInputStream stream) throws IOException { TiffParser tp = new TiffParser(stream); String comment = tp.getComment(); if (comment == null) return false; return comment.indexOf(IMPROVISION_MAGIC_STRING) >= 0; } /* @see loci.formats.IFormatReader#close(boolean) */ @Override public void close(boolean fileOnly) throws IOException { super.close(fileOnly); if (!fileOnly) { cNames = null; pixelSizeT = 1; pixelSizeX = pixelSizeY = pixelSizeZ = 0; if (readers != null) { for (MinimalTiffReader reader : readers) { if (reader != null) reader.close(); } } readers = null; files = null; lastFile = 0; } } /* @see loci.formats.IFormatReader#getSeriesUsedFiles(boolean) */ @Override public String[] getSeriesUsedFiles(boolean noPixels) { FormatTools.assertId(currentId, true, 1); return noPixels ? null : files; } /* @see loci.formats.IFormatReader#get8BitLookupTable() */ @Override public byte[][] get8BitLookupTable() throws FormatException, IOException { FormatTools.assertId(currentId, true, 1); if (readers == null || lastFile < 0 || lastFile >= readers.length || readers[lastFile] == null) { return super.get8BitLookupTable(); } return readers[lastFile].get8BitLookupTable(); } /* @see loci.formats.IFormatReader#get16BitLookupTable() */ @Override public short[][] get16BitLookupTable() throws FormatException, IOException { FormatTools.assertId(currentId, true, 1); if (readers == null || lastFile < 0 || lastFile >= readers.length || readers[lastFile] == null) { return super.get16BitLookupTable(); } return readers[lastFile].get16BitLookupTable(); } /** * @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); int[] zct = getZCTCoords(no); int file = FormatTools.getIndex("XYZCT", getSizeZ(), getEffectiveSizeC(), getSizeT(), getImageCount(), zct[0], zct[1], zct[2]) % files.length; int plane = no / files.length; lastFile = file; return readers[file].openBytes(plane, buf, x, y, w, h); } // -- Internal BaseTiffReader API methods -- /* @see BaseTiffReader#initStandardMetadata() */ @Override protected void initStandardMetadata() throws FormatException, IOException { super.initStandardMetadata(); put("Improvision", "yes"); // parse key/value pairs in the comment String comment = ifds.get(0).getComment(); String tz = null, tc = null, tt = null; if (comment != null) { String[] lines = comment.split("\n"); for (String line : lines) { int equals = line.indexOf("="); if (equals < 0) continue; String key = line.substring(0, equals); String value = line.substring(equals + 1); addGlobalMeta(key, value); if (key.equals("TotalZPlanes")) tz = value; else if (key.equals("TotalChannels")) tc = value; else if (key.equals("TotalTimepoints")) tt = value; else if (key.equals("XCalibrationMicrons")) { pixelSizeX = Double.parseDouble(DataTools.sanitizeDouble(value)); } else if (key.equals("YCalibrationMicrons")) { pixelSizeY = Double.parseDouble(DataTools.sanitizeDouble(value)); } else if (key.equals("ZCalibrationMicrons")) { pixelSizeZ = Double.parseDouble(DataTools.sanitizeDouble(value)); } } metadata.remove("Comment"); } CoreMetadata m = core.get(0); m.sizeT = 1; if (getSizeZ() == 0) m.sizeZ = 1; if (getSizeC() == 0) m.sizeC = 1; if (tz != null) m.sizeZ *= Integer.parseInt(tz); if (tc != null) m.sizeC *= Integer.parseInt(tc); if (tt != null) m.sizeT *= Integer.parseInt(tt); if (getSizeZ() * getSizeC() * getSizeT() < getImageCount()) { m.sizeC *= getImageCount(); } else m.imageCount = getSizeZ() * getSizeT() * Integer.parseInt(tc); // parse each of the comments to determine axis ordering long[] stamps = new long[ifds.size()]; int[][] coords = new int[ifds.size()][3]; cNames = new String[getSizeC()]; boolean multipleFiles = false; for (int i=0; i<ifds.size(); i++) { Arrays.fill(coords[i], -1); comment = ifds.get(i).getComment(); // TODO : can use loci.common.IniParser to parse the comments comment = comment.replaceAll("\r\n", "\n"); comment = comment.replaceAll("\r", "\n"); String channelName = null; String[] lines = comment.split("\n"); for (String line : lines) { int equals = line.indexOf("="); if (equals < 0) continue; String key = line.substring(0, equals); String value = line.substring(equals + 1); if (key.equals("TimeStampMicroSeconds")) { stamps[i] = Long.parseLong(value); } else if (key.equals("ZPlane")) coords[i][0] = Integer.parseInt(value); else if (key.equals("ChannelNo")) { coords[i][1] = Integer.parseInt(value); int ndx = Integer.parseInt(value) - 1; if (cNames[ndx] == null) cNames[ndx] = channelName; } else if (key.equals("TimepointName")) { coords[i][2] = Integer.parseInt(value); } else if (key.equals("ChannelName")) { channelName = value; } else if (key.equals("MultiFileTIFF")) { multipleFiles = value.equalsIgnoreCase("yes"); } if (getMetadataOptions().getMetadataLevel() == MetadataLevel.MINIMUM && coords[i][0] >= 0 && coords[i][1] >= 0 && coords[i][2] >= 0) { break; } } } if (multipleFiles) { // look for other TIFF files that belong to this dataset String currentUUID = getUUID(currentId); Location parent = new Location(currentId).getAbsoluteFile().getParentFile(); String[] list = parent.list(true); Arrays.sort(list); ArrayList<String> matchingFiles = new ArrayList<String>(); for (String f : list) { String path = new Location(parent, f).getAbsolutePath(); if (isThisType(path) && getUUID(path).equals(currentUUID)) { matchingFiles.add(path); } } files = matchingFiles.toArray(new String[matchingFiles.size()]); } else { files = new String[] {currentId}; } if (files.length * ifds.size() < getImageCount()) { files = new String[] {currentId}; m.imageCount = ifds.size(); m.sizeZ = ifds.size(); m.sizeT = 1; if (!isRGB()) { m.sizeC = 1; } } readers = new MinimalTiffReader[files.length]; for (int i=0; i<readers.length; i++) { readers[i] = new MinimalTiffReader(); readers[i].setId(files[i]); } if (getMetadataOptions().getMetadataLevel() != MetadataLevel.MINIMUM) { // determine average time per plane long sum = 0; for (int i=1; i<stamps.length; i++) { long diff = stamps[i] - stamps[i - 1]; if (diff > 0) sum += diff; } pixelSizeT = (int) (sum / getSizeT()); } // determine dimension order m.dimensionOrder = "XY"; if (isRGB()) m.dimensionOrder += "C"; for (int i=1; i<coords.length; i++) { int zDiff = coords[i][0] - coords[i - 1][0]; int cDiff = coords[i][1] - coords[i - 1][1]; int tDiff = coords[i][2] - coords[i - 1][2]; if (zDiff > 0 && getDimensionOrder().indexOf("Z") < 0) { m.dimensionOrder += "Z"; } if (cDiff > 0 && getDimensionOrder().indexOf("C") < 0) { m.dimensionOrder += "C"; } if (tDiff > 0 && getDimensionOrder().indexOf("T") < 0) { m.dimensionOrder += "T"; } if (m.dimensionOrder.length() == 5) break; } if (getDimensionOrder().indexOf("Z") < 0) m.dimensionOrder += "Z"; if (getDimensionOrder().indexOf("C") < 0) m.dimensionOrder += "C"; if (getDimensionOrder().indexOf("T") < 0) m.dimensionOrder += "T"; } /* @see BaseTiffReader#initMetadataStore() */ @Override protected void initMetadataStore() throws FormatException { super.initMetadataStore(); MetadataStore store = makeFilterMetadata(); MetadataTools.populatePixels(store, this); if (getMetadataOptions().getMetadataLevel() != MetadataLevel.MINIMUM) { Length sizeX = FormatTools.getPhysicalSizeX(pixelSizeX); Length sizeY = FormatTools.getPhysicalSizeY(pixelSizeY); Length sizeZ = FormatTools.getPhysicalSizeZ(pixelSizeZ); if (sizeX != null) { store.setPixelsPhysicalSizeX(sizeX, 0); } if (sizeY != null) { store.setPixelsPhysicalSizeY(sizeY, 0); } if (sizeZ != null) { store.setPixelsPhysicalSizeZ(sizeZ, 0); } store.setPixelsTimeIncrement(new Time(pixelSizeT / 1000000.0, UNITS.S), 0); for (int i=0; i<getEffectiveSizeC(); i++) { if (cNames != null && i < cNames.length) { store.setChannelName(cNames[i], 0, i); } } store.setImageDescription("", 0); } } // -- Helper methods -- private String getUUID(String path) throws FormatException, IOException { RandomAccessInputStream s = new RandomAccessInputStream(path, 16); TiffParser parser = new TiffParser(s); String comment = parser.getComment(); s.close(); comment = comment.replaceAll("\r\n", "\n"); comment = comment.replaceAll("\r", "\n"); String[] lines = comment.split("\n"); for (String line : lines) { line = line.trim(); if (line.startsWith("SampleUUID=")) { return line.substring(line.indexOf("=") + 1).trim(); } } return ""; } }