// // APLReader.java // /* OME Bio-Formats package for reading and converting biological file formats. Copyright (C) 2005-@year@ UW-Madison LOCI and Glencoe Software, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package loci.formats.in; import java.io.File; import java.io.IOException; import java.util.Vector; import loci.common.DataTools; import loci.common.Location; import loci.common.RandomAccessInputStream; import loci.common.services.DependencyException; import loci.common.services.ServiceFactory; import loci.formats.CoreMetadata; import loci.formats.FormatException; import loci.formats.FormatReader; import loci.formats.FormatTools; import loci.formats.MetadataTools; import loci.formats.meta.MetadataStore; import ome.xml.model.primitives.PositiveFloat; import loci.formats.services.MDBService; import loci.formats.tiff.IFD; import loci.formats.tiff.IFDList; import loci.formats.tiff.PhotoInterp; import loci.formats.tiff.TiffParser; /** * APLReader is the file format reader for Olympus APL files. * * <dl><dt><b>Source code:</b></dt> * <dd><a href="http://trac.openmicroscopy.org.uk/ome/browser/bioformats.git/components/bio-formats/src/loci/formats/in/APLReader.java">Trac</a>, * <a href="http://git.openmicroscopy.org/?p=bioformats.git;a=blob;f=components/bio-formats/src/loci/formats/in/APLReader.java;hb=HEAD">Gitweb</a></dd></dl> */ public class APLReader extends FormatReader { // -- Constants -- private static final String[] METADATA_SUFFIXES = new String[] {"apl", "tnb", "mtb" }; // -- Fields -- private String[] tiffFiles; private String[] xmlFiles; private TiffParser[] parser; private IFDList[] ifds; private Vector<String> used; // -- Constructor -- /** Constructs a new APL reader. */ public APLReader() { super("Olympus APL", new String[] {"apl", "tnb", "mtb", "tif"}); domains = new String[] {FormatTools.LM_DOMAIN}; hasCompanionFiles = true; suffixSufficient = false; datasetDescription = "One .apl file, one .mtb file, one .tnb file, and " + "a directory containing one or more .tif files"; } // -- IFormatReader API methods -- /* @see loci.formats.IFormatReader#isThisType(String, boolean) */ public boolean isThisType(String name, boolean open) { if (checkSuffix(name, METADATA_SUFFIXES)) return true; if (checkSuffix(name, "tif") && open) { Location file = new Location(name).getAbsoluteFile(); Location parent = file.getParentFile(); if (parent != null) { try { parent = parent.getParentFile(); parent = parent.getParentFile(); } catch (NullPointerException e) { return false; } Location aplFile = new Location(parent, parent.getName() + ".apl"); return aplFile.exists(); } } return false; } /* @see loci.formats.IFormatReader#isSingleFile(String) */ public boolean isSingleFile(String id) throws FormatException, IOException { return false; } /* @see loci.formats.IFormatReader#getSeriesUsedFiles(boolean) */ public String[] getSeriesUsedFiles(boolean noPixels) { FormatTools.assertId(currentId, true, 1); Vector<String> files = new Vector<String>(); files.addAll(used); if (getSeries() < xmlFiles.length && new Location(xmlFiles[getSeries()]).exists()) { files.add(xmlFiles[getSeries()]); } if (!noPixels && getSeries() < tiffFiles.length && new Location(tiffFiles[getSeries()]).exists()) { files.add(tiffFiles[getSeries()]); } return files.toArray(new String[files.size()]); } /** * @see loci.formats.IFormatReader#openBytes(int, byte[], int, int, int, int) */ public byte[] openBytes(int no, byte[] buf, int x, int y, int w, int h) throws FormatException, IOException { return parser[series].getSamples(ifds[series].get(no), buf, x, y, w, h); } /* @see loci.formats.IFormatReader#close(boolean) */ public void close(boolean fileOnly) throws IOException { super.close(fileOnly); if (!fileOnly) { tiffFiles = null; xmlFiles = null; used = null; ifds = null; if (parser != null) { for (TiffParser p : parser) { if (p != null) { p.getStream().close(); } } } } } /* @see loci.formats.IFormatReader#fileGroupOption(String) */ public int fileGroupOption(String id) throws FormatException, IOException { return FormatTools.MUST_GROUP; } /* @see loci.formats.IFormatReader#getOptimalTileWidth() */ public int getOptimalTileWidth() { FormatTools.assertId(currentId, true, 1); try { return (int) ifds[getSeries()].get(0).getTileWidth(); } catch (FormatException e) { LOGGER.debug("Could not retrieve tile width", e); } return super.getOptimalTileWidth(); } /* @see loci.formats.IFormatReader#getOptimalTileHeight() */ public int getOptimalTileHeight() { FormatTools.assertId(currentId, true, 1); try { return (int) ifds[getSeries()].get(0).getTileLength(); } catch (FormatException e) { LOGGER.debug("Could not retrieve tile height", e); } return super.getOptimalTileHeight(); } // -- Internal FormatReader API methods -- /* @see loci.formats.FormatReader#initFile(String) */ protected void initFile(String id) throws FormatException, IOException { super.initFile(id); LOGGER.debug("Initializing {}", id); // find the corresponding .mtb file if (!checkSuffix(id, "mtb")) { if (checkSuffix(id, METADATA_SUFFIXES)) { int separator = id.lastIndexOf(File.separator); if (separator < 0) separator = 0; int underscore = id.lastIndexOf("_"); if (underscore < separator) underscore = id.lastIndexOf("."); String mtbFile = id.substring(0, underscore) + "_d.mtb"; if (!new Location(mtbFile).exists()) { throw new FormatException(".mtb file not found"); } currentId = new Location(mtbFile).getAbsolutePath(); } else { Location parent = new Location(id).getAbsoluteFile().getParentFile(); parent = parent.getParentFile(); String[] list = parent.list(true); for (String f : list) { if (checkSuffix(f, "mtb")) { currentId = new Location(parent, f).getAbsolutePath(); break; } } if (!checkSuffix(currentId, "mtb")) { throw new FormatException(".mtb file not found"); } } } String mtb = new Location(currentId).getAbsolutePath(); LOGGER.debug("Reading .mtb file '{}'", mtb); MDBService mdb = null; try { ServiceFactory factory = new ServiceFactory(); mdb = factory.getInstance(MDBService.class); } catch (DependencyException de) { throw new FormatException("MDB Tools Java library not found", de); } mdb.initialize(mtb); Vector<String[]> rows = mdb.parseDatabase().get(0); String[] columnNames = rows.get(0); String[] tmpNames = columnNames; columnNames = new String[tmpNames.length - 1]; System.arraycopy(tmpNames, 1, columnNames, 0, columnNames.length); // add full table to metadata hashtable if (getMetadataOptions().getMetadataLevel() != MetadataLevel.MINIMUM) { for (int i=1; i<rows.size(); i++) { String[] row = rows.get(i); for (int q=0; q<row.length; q++) { addGlobalMeta(columnNames[q] + " " + i, row[q]); } } } used = new Vector<String>(); used.add(mtb); String tnb = mtb.substring(0, mtb.lastIndexOf(".")); if (tnb.lastIndexOf("_") > tnb.lastIndexOf(File.separator)) { tnb = tnb.substring(0, tnb.lastIndexOf("_")); } used.add(tnb + "_1.tnb"); used.add(tnb + ".apl"); String idPath = new Location(id).getAbsolutePath(); if (!used.contains(idPath) && checkSuffix(idPath, METADATA_SUFFIXES)) { used.add(idPath); } // calculate indexes to relevant metadata int calibrationUnit = DataTools.indexOf(columnNames, "Calibration Unit"); int colorChannels = DataTools.indexOf(columnNames, "Color Channels"); int frames = DataTools.indexOf(columnNames, "Frames"); int calibratedHeight = DataTools.indexOf(columnNames, "Height"); int calibratedWidth = DataTools.indexOf(columnNames, "Width"); int path = DataTools.indexOf(columnNames, "Image Path"); int filename = DataTools.indexOf(columnNames, "File Name"); int magnification = DataTools.indexOf(columnNames, "Magnification"); int width = DataTools.indexOf(columnNames, "X-Resolution"); int height = DataTools.indexOf(columnNames, "Y-Resolution"); int imageName = DataTools.indexOf(columnNames, "Image Name"); int zLayers = DataTools.indexOf(columnNames, "Z-Layers"); String parentDirectory = mtb.substring(0, mtb.lastIndexOf(File.separator)); // look for the directory that contains TIFF and XML files LOGGER.debug("Searching {} for a directory with TIFFs", parentDirectory); Location dir = new Location(parentDirectory); String[] list = dir.list(); String topDirectory = null; for (String f : list) { LOGGER.debug(" '{}'", f); Location file = new Location(dir, f); if (file.isDirectory() && f.indexOf("_DocumentFiles") > 0) { LOGGER.debug("Found {}", topDirectory); topDirectory = file.getAbsolutePath(); break; } } if (topDirectory == null) { throw new FormatException("Could not find a directory with TIFF files."); } Vector<Integer> seriesIndexes = new Vector<Integer>(); for (int i=1; i<rows.size(); i++) { String file = rows.get(i)[filename].trim(); if (file.equals("")) continue; file = topDirectory + File.separator + file; if (new Location(file).exists() && checkSuffix(file, "tif")) { seriesIndexes.add(i); } } int seriesCount = seriesIndexes.size(); core = new CoreMetadata[seriesCount]; for (int i=0; i<seriesCount; i++) { core[i] = new CoreMetadata(); } tiffFiles = new String[seriesCount]; xmlFiles = new String[seriesCount]; parser = new TiffParser[seriesCount]; ifds = new IFDList[seriesCount]; for (int i=0; i<seriesCount; i++) { int secondRow = seriesIndexes.get(i); int firstRow = secondRow - 1; String[] row2 = rows.get(firstRow); String[] row3 = rows.get(secondRow); core[i].sizeT = parseDimension(row3[frames]); core[i].sizeZ = parseDimension(row3[zLayers]); core[i].sizeC = parseDimension(row3[colorChannels]); core[i].dimensionOrder = "XYCZT"; if (core[i].sizeZ == 0) core[i].sizeZ = 1; if (core[i].sizeC == 0) core[i].sizeC = 1; if (core[i].sizeT == 0) core[i].sizeT = 1; xmlFiles[i] = topDirectory + File.separator + row2[filename]; tiffFiles[i] = topDirectory + File.separator + row3[filename]; parser[i] = new TiffParser(tiffFiles[i]); parser[i].setDoCaching(false); ifds[i] = parser[i].getIFDs(); for (IFD ifd : ifds[i]) { parser[i].fillInIFD(ifd); } // get core metadata from TIFF file IFD ifd = ifds[i].get(0); PhotoInterp photo = ifd.getPhotometricInterpretation(); int samples = ifd.getSamplesPerPixel(); core[i].sizeX = (int) ifd.getImageWidth(); core[i].sizeY = (int) ifd.getImageLength(); core[i].rgb = samples > 1 || photo == PhotoInterp.RGB; core[i].pixelType = ifd.getPixelType(); core[i].littleEndian = ifd.isLittleEndian(); core[i].indexed = photo == PhotoInterp.RGB_PALETTE && ifd.containsKey(IFD.COLOR_MAP); core[i].imageCount = ifds[i].size(); if (core[i].sizeZ * core[i].sizeT * (core[i].rgb ? 1 : core[i].sizeC) != core[i].imageCount) { core[i].sizeT = core[i].imageCount / (core[i].rgb ? 1 : core[i].sizeC); core[i].sizeZ = 1; } } MetadataStore store = makeFilterMetadata(); MetadataTools.populatePixels(store, this); for (int i=0; i<seriesCount; i++) { String[] row = rows.get(seriesIndexes.get(i)); // populate Image data MetadataTools.setDefaultCreationDate(store, mtb, i); store.setImageName(row[imageName], i); if (getMetadataOptions().getMetadataLevel() != MetadataLevel.MINIMUM) { // populate Dimensions data // calculate physical X and Y sizes double realWidth = Double.parseDouble(row[calibratedWidth]); double realHeight = Double.parseDouble(row[calibratedHeight]); String units = row[calibrationUnit]; double px = realWidth / core[i].sizeX; double py = realHeight / core[i].sizeY; if (units.equals("mm")) { px *= 1000; py *= 1000; } // TODO : add cases for other units store.setPixelsPhysicalSizeX(new PositiveFloat(px), i); store.setPixelsPhysicalSizeY(new PositiveFloat(py), i); } } } // -- Helper methods -- /** * Parse an integer from the given dimension String. * * @return the parsed integer, or 1 if parsing failed. */ private int parseDimension(String dim) { try { return Integer.parseInt(dim); } catch (NumberFormatException e) { return 1; } } }