/* * #%L * Bio-Formats command line tools for reading and converting files * %% * Copyright (C) 2005 - 2015 Open Microscopy Environment: * - Board of Regents of the University of Wisconsin-Madison * - Glencoe Software, Inc. * - University of Dundee * %% * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * #L% */ package loci.formats.tools; import java.awt.image.BufferedImage; import java.io.IOException; import java.io.File; import java.util.Hashtable; import java.util.StringTokenizer; import loci.common.ByteArrayHandle; import loci.common.DataTools; import loci.common.DebugTools; 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.ChannelFiller; import loci.formats.ChannelMerger; import loci.formats.ChannelSeparator; import loci.formats.DimensionSwapper; import loci.formats.FilePattern; import loci.formats.FileStitcher; import loci.formats.FormatException; import loci.formats.FormatTools; import loci.formats.IFormatReader; import loci.formats.ImageReader; import loci.formats.ImageTools; import loci.formats.Memoizer; import loci.formats.MetadataTools; import loci.formats.MinMaxCalculator; import loci.formats.MissingLibraryException; import loci.formats.Modulo; import loci.formats.UpgradeChecker; import loci.formats.gui.AWTImageTools; import loci.formats.gui.BufferedImageReader; import loci.formats.gui.ImageViewer; import loci.formats.in.DefaultMetadataOptions; import loci.formats.in.MetadataLevel; import loci.formats.in.MetadataOptions; import loci.formats.in.OMETiffReader; import loci.formats.meta.MetadataRetrieve; import loci.formats.meta.MetadataStore; import loci.formats.services.OMEXMLService; import loci.formats.services.OMEXMLServiceImpl; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.collect.ImmutableSet; /** * ImageInfo is a utility class for reading a file * and reporting information about it. */ public class ImageInfo { // -- Constants -- private static final Logger LOGGER = LoggerFactory.getLogger(ImageInfo.class); private static final String NEWLINE = System.getProperty("line.separator"); private static final String NO_UPGRADE_CHECK = "-no-upgrade"; private static final ImmutableSet<String> HELP_ARGUMENTS = ImmutableSet.of("-h", "-help", "--help"); // -- Fields -- private String id = null; private boolean printVersion = false; private boolean pixels = true; private boolean doCore = true; private boolean doMeta = true; private boolean filter = true; private boolean thumbs = false; private boolean minmax = false; private boolean merge = false; private boolean stitch = false; private boolean group = true; private boolean separate = false; private boolean expand = false; private boolean omexml = false; private boolean cache = false; private boolean originalMetadata = true; private boolean normalize = false; private boolean fastBlit = false; private boolean autoscale = false; private boolean preload = false; private boolean ascii = false; private boolean usedFiles = true; private boolean omexmlOnly = false; private boolean validate = true; private boolean flat = true; private String omexmlVersion = null; private int start = 0; private int end = Integer.MAX_VALUE; private int series = 0; private int resolution = 0; private int xCoordinate = 0, yCoordinate = 0, width = 0, height = 0; private String swapOrder = null, shuffleOrder = null; private String map = null; private String format = null; private String cachedir = null; private int xmlSpaces = 3; private IFormatReader reader; private IFormatReader baseReader; private MinMaxCalculator minMaxCalc; private DimensionSwapper dimSwapper; private BufferedImageReader biReader; private Double[] preGlobalMin = null, preGlobalMax = null; private Double[] preKnownMin = null, preKnownMax = null; private Double[] prePlaneMin = null, prePlaneMax = null; private boolean preIsMinMaxPop = false; // -- ImageInfo methods -- public boolean parseArgs(String[] args) { id = null; printVersion = false; pixels = true; doCore = true; doMeta = true; filter = true; thumbs = false; minmax = false; merge = false; stitch = false; group = true; separate = false; expand = false; omexml = false; cache = false; originalMetadata = true; normalize = false; fastBlit = false; autoscale = false; preload = false; usedFiles = true; omexmlOnly = false; validate = true; flat = true; omexmlVersion = null; xmlSpaces = 3; start = 0; end = Integer.MAX_VALUE; series = 0; resolution = 0; xCoordinate = 0; yCoordinate = 0; width = 0; height = 0; swapOrder = null; shuffleOrder = null; map = null; cachedir = null; if (args == null) return false; for (int i=0; i<args.length; i++) { if (args[i].startsWith("-")) { if (args[i].equals("-nopix")) pixels = false; else if (args[i].equals("-version")) printVersion = true; else if (args[i].equals("-nocore")) doCore = false; else if (args[i].equals("-nometa")) doMeta = false; else if (args[i].equals("-nofilter")) filter = false; else if (args[i].equals("-thumbs")) thumbs = true; else if (args[i].equals("-minmax")) minmax = true; else if (args[i].equals("-merge")) merge = true; else if (args[i].equals("-stitch")) stitch = true; else if (args[i].equals("-nogroup")) group = false; else if (args[i].equals("-separate")) separate = true; else if (args[i].equals("-expand")) expand = true; else if (args[i].equals("-cache")) cache = true; else if (args[i].equals("-omexml")) omexml = true; else if (args[i].equals("-no-sas")) originalMetadata = false; else if (args[i].equals("-normalize")) normalize = true; else if (args[i].equals("-fast")) fastBlit = true; else if (args[i].equals("-autoscale")) { fastBlit = true; autoscale = true; } else if (args[i].equals("-novalid")) validate = false; else if (args[i].equals("-noflat")) flat = false; else if (args[i].equals("-debug")) { DebugTools.enableLogging("DEBUG"); } else if (args[i].equals("-trace")) { DebugTools.enableLogging("TRACE"); } else if (args[i].equals("-omexml-only")) { omexmlOnly = true; omexml = true; DebugTools.enableLogging("OFF"); } else if (args[i].equals("-preload")) preload = true; else if (args[i].equals("-ascii")) ascii = true; else if (args[i].equals("-nousedfiles")) usedFiles = false; else if (args[i].equals("-xmlversion")) omexmlVersion = args[++i]; else if (args[i].equals("-xmlspaces")) { xmlSpaces = Integer.parseInt(args[++i]); } else if (args[i].equals("-crop")) { StringTokenizer st = new StringTokenizer(args[++i], ","); xCoordinate = Integer.parseInt(st.nextToken()); yCoordinate = Integer.parseInt(st.nextToken()); width = Integer.parseInt(st.nextToken()); height = Integer.parseInt(st.nextToken()); } else if (args[i].equals("-range")) { try { start = Integer.parseInt(args[++i]); end = Integer.parseInt(args[++i]); } catch (NumberFormatException exc) { } } else if (args[i].equals("-series")) { try { series = Integer.parseInt(args[++i]); } catch (NumberFormatException exc) { } } else if (args[i].equals("-resolution")) { try { resolution = Integer.parseInt(args[++i]); } catch (NumberFormatException exc) { } } else if (args[i].equals("-swap")) { swapOrder = args[++i].toUpperCase(); } else if (args[i].equals("-shuffle")) { shuffleOrder = args[++i].toUpperCase(); } else if (args[i].equals("-map")) map = args[++i]; else if (args[i].equals("-format")) format = args[++i]; else if (args[i].equals("-cache-dir")) { cache = true; cachedir = args[++i]; } else if (!args[i].equals(NO_UPGRADE_CHECK)) { LOGGER.error("Found unknown command flag: {}; exiting.", args[i]); return false; } } else { if (id == null) id = args[i]; else { LOGGER.error("Found unknown argument: {}; exiting.", args[i]); return false; } } } return true; } public void printUsage() { String fmt = reader instanceof ImageReader ? "any" : reader.getFormat(); String[] s = { "To test read a file in " + fmt + " format, run:", " showinf file [-nopix] [-nocore] [-nometa] [-thumbs] [-minmax] ", " [-merge] [-nogroup] [-stitch] [-separate] [-expand] [-omexml]", " [-normalize] [-fast] [-debug] [-range start end] [-series num]", " [-resolution num] [-swap inputOrder] [-shuffle outputOrder]", " [-map id] [-preload] [-crop x,y,w,h] [-autoscale] [-novalid]", " [-omexml-only] [-no-sas] [-no-upgrade] [-noflat] [-format Format]", " [-cache] [-cache-dir dir]", "", " -version: print the library version and exit", " file: the image file to read", " -nopix: read metadata only, not pixels", " -nocore: do not output core metadata", " -nometa: do not parse format-specific metadata table", " -nofilter: do not filter metadata fields", " -thumbs: read thumbnails instead of normal pixels", " -minmax: compute min/max statistics", " -merge: combine separate channels into RGB image", " -nogroup: force multi-file datasets to be read as individual files", " -stitch: stitch files with similar names", " -separate: split RGB image into separate channels", " -expand: expand indexed color to RGB", " -omexml: populate OME-XML metadata", " -normalize: normalize floating point images (*)", " -fast: paint RGB images as quickly as possible (*)", " -debug: turn on debugging output", " -range: specify range of planes to read (inclusive)", " -series: specify which image series to read", " -noflat: do not flatten subresolutions", " -resolution: used in combination with -noflat to specify which", " subresolution to read (for images with subresolutions)", " -swap: override the default input dimension order", " -shuffle: override the default output dimension order", " -map: specify file on disk to which name should be mapped", " -preload: pre-read entire file into a buffer; significantly", " reduces the time required to read the images, but", " requires more memory", " -crop: crop images before displaying; argument is 'x,y,w,h'", " -autoscale: automatically adjust brightness and contrast (*)", " -novalid: do not perform validation of OME-XML", "-omexml-only: only output the generated OME-XML", " -no-sas: do not output OME-XML StructuredAnnotation elements", " -no-upgrade: do not perform the upgrade check", " -format: read file with a particular reader (e.g., ZeissZVI)", " -cache: cache the initialized reader", " -cache-dir: use the specified directory to store the cached", " initialized reader. If unspecified, the cached reader", " will be stored under the same folder as the image file", "", "* = may result in loss of precision", "" }; for (int i=0; i<s.length; i++) LOGGER.info(s[i]); } public void setReader(IFormatReader reader) { this.reader = reader; } public void createReader() { if (reader != null) return; // reader was set programmatically if (format != null) { // create reader of a specific format type try { Class<?> c = Class.forName("loci.formats.in." + format + "Reader"); reader = (IFormatReader) c.newInstance(); } catch (ClassNotFoundException exc) { LOGGER.warn("Unknown reader: {}", format); LOGGER.debug("", exc); } catch (InstantiationException exc) { LOGGER.warn("Cannot instantiate reader: {}", format); LOGGER.debug("", exc); } catch (IllegalAccessException exc) { LOGGER.warn("Cannot access reader: {}", format); LOGGER.debug("", exc); } } if (reader == null) reader = new ImageReader(); baseReader = reader; } public void mapLocation() throws IOException { if (map != null) Location.mapId(id, map); else if (preload) { RandomAccessInputStream f = new RandomAccessInputStream(id); int len = (int) f.length(); LOGGER.info("Caching {} bytes:", len); byte[] b = new byte[len]; int blockSize = 8 * 1024 * 1024; // 8 MB int read = 0, left = len; while (left > 0) { int r = f.read(b, read, blockSize < left ? blockSize : left); read += r; left -= r; float ratio = (float) read / len; int p = (int) (100 * ratio); LOGGER.info("\tRead {} bytes ({}% complete)", read, p); } f.close(); ByteArrayHandle file = new ByteArrayHandle(b); Location.mapFile(id, file); } } public void configureReaderPreInit() throws FormatException, IOException { if (omexml) { reader.setOriginalMetadataPopulated(originalMetadata); try { ServiceFactory factory = new ServiceFactory(); OMEXMLService service = factory.getInstance(OMEXMLService.class); reader.setMetadataStore( service.createOMEXMLMetadata(null, omexmlVersion)); } catch (DependencyException de) { throw new MissingLibraryException(OMEXMLServiceImpl.NO_OME_XML_MSG, de); } catch (ServiceException se) { throw new FormatException(se); } } // check file format if (reader instanceof ImageReader) { // determine format ImageReader ir = (ImageReader) reader; if (new Location(id).exists()) { LOGGER.info("Checking file format [{}]", ir.getFormat(id)); } } else { // verify format LOGGER.info("Checking {} format [{}]", reader.getFormat(), reader.isThisType(id) ? "yes" : "no"); } LOGGER.info("Initializing reader"); if (stitch) { reader = new FileStitcher(reader, true); Location f = new Location(id); String pat = null; if (!f.exists()) { ((FileStitcher) reader).setUsingPatternIds(true); pat = id; } else { pat = FilePattern.findPattern(f); } if (pat != null) id = pat; } if (expand) reader = new ChannelFiller(reader); if (separate) reader = new ChannelSeparator(reader); if (merge) reader = new ChannelMerger(reader); if (cache) { if (cachedir != null) { reader = new Memoizer(reader, 0, new File(cachedir)); } else { reader = new Memoizer(reader, 0); } } minMaxCalc = null; if (minmax || autoscale) reader = minMaxCalc = new MinMaxCalculator(reader); dimSwapper = null; if (swapOrder != null || shuffleOrder != null) { reader = dimSwapper = new DimensionSwapper(reader); } reader = biReader = new BufferedImageReader(reader); reader.close(); reader.setNormalized(normalize); reader.setMetadataFiltered(filter); reader.setGroupFiles(group); MetadataOptions metaOptions = new DefaultMetadataOptions(doMeta ? MetadataLevel.ALL : MetadataLevel.MINIMUM); reader.setMetadataOptions(metaOptions); reader.setFlattenedResolutions(flat); } public void configureReaderPostInit() { if (swapOrder != null) dimSwapper.swapDimensions(swapOrder); if (shuffleOrder != null) dimSwapper.setOutputOrder(shuffleOrder); } public void checkWarnings() { if (!normalize && (reader.getPixelType() == FormatTools.FLOAT || reader.getPixelType() == FormatTools.DOUBLE)) { LOGGER.warn(""); LOGGER.warn("Java does not support " + "display of unnormalized floating point data."); LOGGER.warn("Please use the '-normalize' option " + "to avoid receiving a cryptic exception."); } if (reader.isRGB() && reader.getRGBChannelCount() > 4) { LOGGER.warn(""); LOGGER.warn("Java does not support merging more than 4 channels."); LOGGER.warn("Please use the '-separate' option " + "to avoid losing channels beyond the 4th."); } } public void readCoreMetadata() throws FormatException, IOException { if (!doCore) return; // skip core metadata printout // read basic metadata LOGGER.info(""); LOGGER.info("Reading core metadata"); LOGGER.info("{} = {}", stitch ? "File pattern" : "filename", stitch ? id : reader.getCurrentFile()); if (map != null) LOGGER.info("Mapped filename = {}", map); if (usedFiles) { String[] used = reader.getUsedFiles(); boolean usedValid = used != null && used.length > 0; if (usedValid) { for (int u=0; u<used.length; u++) { if (used[u] == null) { usedValid = false; break; } } } if (!usedValid) { LOGGER.warn("************ invalid used files list ************"); } if (used == null) { LOGGER.info("Used files = null"); } else if (used.length == 0) { LOGGER.info("Used files = []"); } else if (used.length > 1) { LOGGER.info("Used files:"); for (int u=0; u<used.length; u++) LOGGER.info("\t{}", used[u]); } else if (!id.equals(used[0])) { LOGGER.info("Used files = [{}]", used[0]); } } int seriesCount = reader.getSeriesCount(); LOGGER.info("Series count = {}", seriesCount); MetadataStore ms = reader.getMetadataStore(); MetadataRetrieve mr = ms instanceof MetadataRetrieve ? (MetadataRetrieve) ms : null; for (int j=0; j<seriesCount; j++) { reader.setSeries(j); // read basic metadata for series #i int imageCount = reader.getImageCount(); int resolutions = reader.getResolutionCount(); boolean rgb = reader.isRGB(); int sizeX = reader.getSizeX(); int sizeY = reader.getSizeY(); int sizeZ = reader.getSizeZ(); int sizeC = reader.getSizeC(); int sizeT = reader.getSizeT(); int pixelType = reader.getPixelType(); int validBits = reader.getBitsPerPixel(); int effSizeC = reader.getEffectiveSizeC(); int rgbChanCount = reader.getRGBChannelCount(); boolean indexed = reader.isIndexed(); boolean falseColor = reader.isFalseColor(); byte[][] table8 = reader.get8BitLookupTable(); short[][] table16 = reader.get16BitLookupTable(); Modulo moduloZ = reader.getModuloZ(); Modulo moduloC = reader.getModuloC(); Modulo moduloT = reader.getModuloT(); int thumbSizeX = reader.getThumbSizeX(); int thumbSizeY = reader.getThumbSizeY(); boolean little = reader.isLittleEndian(); String dimOrder = reader.getDimensionOrder(); boolean orderCertain = reader.isOrderCertain(); boolean thumbnail = reader.isThumbnailSeries(); boolean interleaved = reader.isInterleaved(); boolean metadataComplete = reader.isMetadataComplete(); // output basic metadata for series #i String seriesName = mr == null ? null : mr.getImageName(j); LOGGER.info("Series #{}{}{}:", new Object[] {j, seriesName == null ? " " : " -- ", seriesName == null ? "" : seriesName}); if (flat == false && resolutions > 1) { LOGGER.info("\tResolutions = {}", resolutions); for (int i = 0; i < resolutions; i++) { reader.setResolution(i); LOGGER.info("\t\tsizeX[{}] = {}", i, reader.getSizeX()); } reader.setResolution(0); } LOGGER.info("\tImage count = {}", imageCount); LOGGER.info("\tRGB = {} ({}) {}", new Object[] {rgb, rgbChanCount, merge ? "(merged)" : separate ? "(separated)" : ""}); if (rgb != (rgbChanCount != 1)) { LOGGER.warn("\t************ RGB mismatch ************"); } LOGGER.info("\tInterleaved = {}", interleaved); StringBuilder sb = new StringBuilder(); sb.append("\tIndexed = "); sb.append(indexed); sb.append(" ("); sb.append(!falseColor); sb.append(" color"); if (table8 != null) { sb.append(", 8-bit LUT: "); sb.append(table8.length); sb.append(" x "); sb.append(table8[0] == null ? "null" : "" + table8[0].length); } if (table16 != null) { sb.append(", 16-bit LUT: "); sb.append(table16.length); sb.append(" x "); sb.append(table16[0] == null ? "null" : "" + table16[0].length); } sb.append(")"); LOGGER.info(sb.toString()); if (table8 != null && table16 != null) { LOGGER.warn("\t************ multiple LUTs ************"); } LOGGER.info("\tWidth = {}", sizeX); LOGGER.info("\tHeight = {}", sizeY); printDimension("SizeZ", sizeZ, sizeZ, moduloZ); printDimension("SizeT", sizeT, sizeT, moduloT); printDimension("SizeC", sizeC, effSizeC, moduloC); if (imageCount != sizeZ * effSizeC * sizeT) { LOGGER.info("\t************ ZCT mismatch ************"); } LOGGER.info("\tThumbnail size = {} x {}", thumbSizeX, thumbSizeY); LOGGER.info("\tEndianness = {}", little ? "intel (little)" : "motorola (big)"); LOGGER.info("\tDimension order = {} ({})", dimOrder, orderCertain ? "certain" : "uncertain"); LOGGER.info("\tPixel type = {}", FormatTools.getPixelTypeString(pixelType)); LOGGER.info("\tValid bits per pixel = {}", validBits); LOGGER.info("\tMetadata complete = {}", metadataComplete); LOGGER.info("\tThumbnail series = {}", thumbnail); if (doMeta) { LOGGER.info("\t-----"); int[] indices; if (imageCount > 6) { int q = imageCount / 2; indices = new int[] { 0, q - 2, q - 1, q, q + 1, q + 2, imageCount - 1 }; } else if (imageCount > 2) { indices = new int[] {0, imageCount / 2, imageCount - 1}; } else if (imageCount > 1) indices = new int[] {0, 1}; else indices = new int[] {0}; int[][] zct = new int[indices.length][]; int[] indices2 = new int[indices.length]; sb.setLength(0); for (int i=0; i<indices.length; i++) { zct[i] = reader.getZCTCoords(indices[i]); indices2[i] = reader.getIndex(zct[i][0], zct[i][1], zct[i][2]); sb.append("\tPlane #"); sb.append(indices[i]); sb.append(" <=> Z "); sb.append(zct[i][0]); sb.append(", C "); sb.append(zct[i][1]); sb.append(", T "); sb.append(zct[i][2]); if (indices[i] != indices2[i]) { sb.append(" [mismatch: "); sb.append(indices2[i]); sb.append("]"); sb.append(NEWLINE); } else sb.append(NEWLINE); } LOGGER.info(sb.toString()); } } } public void initPreMinMaxValues() throws FormatException, IOException { // get a priori min/max values preGlobalMin = preGlobalMax = null; preKnownMin = preKnownMax = null; prePlaneMin = prePlaneMax = null; preIsMinMaxPop = false; if (minmax) { int sizeC = reader.getSizeC(); preGlobalMin = new Double[sizeC]; preGlobalMax = new Double[sizeC]; preKnownMin = new Double[sizeC]; preKnownMax = new Double[sizeC]; for (int c=0; c<sizeC; c++) { preGlobalMin[c] = minMaxCalc.getChannelGlobalMinimum(c); preGlobalMax[c] = minMaxCalc.getChannelGlobalMaximum(c); preKnownMin[c] = minMaxCalc.getChannelKnownMinimum(c); preKnownMax[c] = minMaxCalc.getChannelKnownMaximum(c); } prePlaneMin = minMaxCalc.getPlaneMinimum(0); prePlaneMax = minMaxCalc.getPlaneMaximum(0); preIsMinMaxPop = minMaxCalc.isMinMaxPopulated(); } } public void printMinMaxValues() throws FormatException, IOException { // get computed min/max values int sizeC = reader.getSizeC(); Double[] globalMin = new Double[sizeC]; Double[] globalMax = new Double[sizeC]; Double[] knownMin = new Double[sizeC]; Double[] knownMax = new Double[sizeC]; for (int c=0; c<sizeC; c++) { globalMin[c] = minMaxCalc.getChannelGlobalMinimum(c); globalMax[c] = minMaxCalc.getChannelGlobalMaximum(c); knownMin[c] = minMaxCalc.getChannelKnownMinimum(c); knownMax[c] = minMaxCalc.getChannelKnownMaximum(c); } Double[] planeMin = minMaxCalc.getPlaneMinimum(0); Double[] planeMax = minMaxCalc.getPlaneMaximum(0); boolean isMinMaxPop = minMaxCalc.isMinMaxPopulated(); // output min/max results LOGGER.info(""); LOGGER.info("Min/max values:"); for (int c=0; c<sizeC; c++) { LOGGER.info("\tChannel {}:", c); LOGGER.info("\t\tGlobal minimum = {} (initially {})", globalMin[c], preGlobalMin[c]); LOGGER.info("\t\tGlobal maximum = {} (initially {})", globalMax[c], preGlobalMax[c]); LOGGER.info("\t\tKnown minimum = {} (initially {})", knownMin[c], preKnownMin[c]); LOGGER.info("\t\tKnown maximum = {} (initially {})", knownMax[c], preKnownMax[c]); } StringBuilder sb = new StringBuilder(); sb.append("\tFirst plane minimum(s) ="); if (planeMin == null) sb.append(" none"); else { for (int subC=0; subC<planeMin.length; subC++) { sb.append(" "); sb.append(planeMin[subC]); } } sb.append(" (initially"); if (prePlaneMin == null) sb.append(" none"); else { for (int subC=0; subC<prePlaneMin.length; subC++) { sb.append(" "); sb.append(prePlaneMin[subC]); } } sb.append(")"); LOGGER.info(sb.toString()); sb.setLength(0); sb.append("\tFirst plane maximum(s) ="); if (planeMax == null) sb.append(" none"); else { for (int subC=0; subC<planeMax.length; subC++) { sb.append(" "); sb.append(planeMax[subC]); } } sb.append(" (initially"); if (prePlaneMax == null) sb.append(" none"); else { for (int subC=0; subC<prePlaneMax.length; subC++) { sb.append(" "); sb.append(prePlaneMax[subC]); } } sb.append(")"); LOGGER.info(sb.toString()); LOGGER.info("\tMin/max populated = {} (initially {})", isMinMaxPop, preIsMinMaxPop); } public void readPixels() throws FormatException, IOException { String seriesLabel = reader.getSeriesCount() > 1 ? (" series #" + series) : ""; LOGGER.info(""); int num = reader.getImageCount(); if (start < 0) start = 0; if (start >= num) start = num - 1; if (end < 0) end = 0; if (end >= num) end = num - 1; if (end < start) end = start; LOGGER.info("Reading{} pixel data ({}-{})", new Object[] {seriesLabel, start, end}); int sizeX = reader.getSizeX(); int sizeY = reader.getSizeY(); if (width == 0) width = sizeX; if (height == 0) height = sizeY; int pixelType = reader.getPixelType(); BufferedImage[] images = new BufferedImage[end - start + 1]; long s = System.currentTimeMillis(); long timeLastLogged = s; for (int i=start; i<=end; i++) { if (!fastBlit) { images[i - start] = thumbs ? biReader.openThumbImage(i) : biReader.openImage(i, xCoordinate, yCoordinate, width, height); } else { byte[] b = thumbs ? reader.openThumbBytes(i) : reader.openBytes(i, xCoordinate, yCoordinate, width, height); Object pix = DataTools.makeDataArray(b, FormatTools.getBytesPerPixel(pixelType), FormatTools.isFloatingPoint(pixelType), reader.isLittleEndian()); Double min = null, max = null; if (autoscale) { Double[] planeMin = minMaxCalc.getPlaneMinimum(i); Double[] planeMax = minMaxCalc.getPlaneMaximum(i); if (planeMin != null && planeMax != null) { min = planeMin[0]; max = planeMax[0]; for (int j=1; j<planeMin.length; j++) { if (planeMin[j].doubleValue() < min.doubleValue()) { min = planeMin[j]; } if (planeMax[j].doubleValue() > max.doubleValue()) { max = planeMax[j]; } } } } else if (normalize) { min = Double.valueOf(0); max = Double.valueOf(1); } if (normalize) { if (pix instanceof float[]) { pix = DataTools.normalizeFloats((float[]) pix); } else if (pix instanceof double[]) { pix = DataTools.normalizeDoubles((double[]) pix); } } images[i - start] = AWTImageTools.makeImage(ImageTools.make24Bits(pix, sizeX, sizeY, reader.isInterleaved(), false, min, max), sizeX, sizeY, FormatTools.isSigned(pixelType)); } if (images[i - start] == null) { LOGGER.warn("\t************ Failed to read plane #{} ************", i); } if (reader.isIndexed() && reader.get8BitLookupTable() == null && reader.get16BitLookupTable() == null) { LOGGER.warn("\t************ no LUT for plane #{} ************", i); } // check for pixel type mismatch int pixType = AWTImageTools.getPixelType(images[i - start]); if (pixType != pixelType && pixType != pixelType + 1 && !fastBlit) { LOGGER.info("\tPlane #{}: pixel type mismatch: {}/{}", new Object[] {i, FormatTools.getPixelTypeString(pixType), FormatTools.getPixelTypeString(pixelType)}); } else { // log number of planes read every second or so long t = System.currentTimeMillis(); if (i == end || (t - timeLastLogged) / 1000 > 0) { int current = i - start + 1; int total = end - start + 1; int percent = 100 * current / total; LOGGER.info("\tRead {}/{} planes ({}%)", new Object[] { current, total, percent }); timeLastLogged = t; } } } long e = System.currentTimeMillis(); LOGGER.info("[done]"); // output timing results float sec = (e - s) / 1000f; float avg = (float) (e - s) / images.length; LOGGER.info("{}s elapsed ({}ms per plane)", sec, avg); if (minmax) printMinMaxValues(); // display pixels in image viewer if (ascii) { for (int i=0; i<images.length; i++) { final BufferedImage img = images[i]; LOGGER.info(""); LOGGER.info("Image #{}:", i); LOGGER.info(new AsciiImage(img).toString()); } } else { LOGGER.info(""); LOGGER.info("Launching image viewer"); ImageViewer viewer = new ImageViewer(false); viewer.setImages(reader, images); viewer.setVisible(true); } } public void printGlobalMetadata() { LOGGER.info(""); LOGGER.info("Reading global metadata"); Hashtable<String, Object> meta = reader.getGlobalMetadata(); String[] keys = MetadataTools.keys(meta); for (String key : keys) { LOGGER.info("{}: {}", key, meta.get(key)); } } public void printOriginalMetadata() { String seriesLabel = reader.getSeriesCount() > 1 ? (" series #" + series) : ""; LOGGER.info(""); LOGGER.info("Reading{} metadata", seriesLabel); Hashtable<String, Object> meta = reader.getSeriesMetadata(); String[] keys = MetadataTools.keys(meta); for (int i=0; i<keys.length; i++) { LOGGER.info("{}: {}", keys[i], meta.get(keys[i])); } } public void printOMEXML() throws MissingLibraryException, ServiceException { LOGGER.info(""); MetadataStore ms = reader.getMetadataStore(); if (baseReader instanceof ImageReader) { baseReader = ((ImageReader) baseReader).getReader(); } if (baseReader instanceof OMETiffReader) { ms = ((OMETiffReader) baseReader).getMetadataStoreForDisplay(); } OMEXMLService service; try { ServiceFactory factory = new ServiceFactory(); service = factory.getInstance(OMEXMLService.class); } catch (DependencyException de) { throw new MissingLibraryException(OMEXMLServiceImpl.NO_OME_XML_MSG, de); } String version = service.getOMEXMLVersion(ms); if (version == null) LOGGER.info("Generating OME-XML"); else { LOGGER.info("Generating OME-XML (schema version {})", version); } if (ms instanceof MetadataRetrieve) { // adding MetadataOnly elements to an OME-TIFF's XML will cause // validation errors if (!(baseReader instanceof OMETiffReader)) { service.removeBinData(service.getOMEMetadata((MetadataRetrieve) ms)); for (int i=0; i<reader.getSeriesCount(); i++) { service.addMetadataOnly( service.getOMEMetadata((MetadataRetrieve) ms), i); } } if (omexmlOnly) { DebugTools.enableLogging("INFO"); } String xml = service.getOMEXML((MetadataRetrieve) ms); LOGGER.info("{}", XMLTools.indentXML(xml, xmlSpaces, true)); if (omexmlOnly) { DebugTools.enableLogging("OFF"); } if (validate) { service.validateOMEXML(xml); } } else { LOGGER.info("The metadata could not be converted to OME-XML."); if (omexmlVersion == null) { LOGGER.info("The OME-XML Java library is probably not available."); } else { LOGGER.info("{} is probably not a legal schema version.", omexmlVersion); } } } /** * A utility method for reading a file from the command line, * and displaying the results in a simple display. */ public boolean testRead(String[] args) throws FormatException, ServiceException, IOException { DebugTools.enableLogging("INFO"); for (final String arg : args) { if (HELP_ARGUMENTS.contains(arg)) { if (reader == null) { reader = new ImageReader(); } printUsage(); return false; } } boolean validArgs = parseArgs(args); if (!validArgs) return false; if (printVersion) { LOGGER.info("Version: {}", FormatTools.VERSION); LOGGER.info("VCS revision: {}", FormatTools.VCS_REVISION); LOGGER.info("Build date: {}", FormatTools.DATE); return true; } createReader(); if (id == null) { printUsage(); return false; } mapLocation(); configureReaderPreInit(); // initialize reader long s = System.currentTimeMillis(); reader.setId(id); long e = System.currentTimeMillis(); float sec = (e - s) / 1000f; LOGGER.info("Initialization took {}s", sec); configureReaderPostInit(); checkWarnings(); readCoreMetadata(); reader.setSeries(series); if (flat == false) reader.setResolution(resolution); initPreMinMaxValues(); // read pixels if (pixels) readPixels(); // read format-specific metadata table if (doMeta) { printGlobalMetadata(); printOriginalMetadata(); } // output and validate OME-XML if (omexml) printOMEXML(); if (!pixels) { reader.close(); } return true; } /** * Log the size of the given dimension, using log4j. * @param dim the name of the dimension to log * @param size the total size of the dimension * @param effectiveSize the effective size of the dimension (e.g. 1 for RGB channels) * @param modulo the {@link loci.formats.Modulo} object associated with this dimension */ private void printDimension(String dim, int size, int effectiveSize, Modulo modulo) { StringBuffer sb = new StringBuffer("\t"); sb.append(dim); sb.append(" = "); sb.append(size); if (size != effectiveSize) { sb.append(" (effectively "); sb.append(effectiveSize); sb.append(")"); } int product = 1; if (modulo.length() == 1) { product = size; } else { sb.append(" ("); sb.append(size / modulo.length()); sb.append(" "); sb.append(modulo.parentType); sb.append(" x "); sb.append(modulo.length()); sb.append(" "); sb.append(modulo.type); sb.append(")"); product = (size / modulo.length()) * modulo.length(); } LOGGER.info(sb.toString()); if (product != size) { LOGGER.warn("\t************ {} dimension mismatch ************", dim); } } // -- Main method -- public static void main(String[] args) throws Exception { if (DataTools.indexOf(args, NO_UPGRADE_CHECK) == -1) { UpgradeChecker checker = new UpgradeChecker(); boolean canUpgrade = checker.newVersionAvailable(UpgradeChecker.DEFAULT_CALLER); if (canUpgrade) { LOGGER.info("*** A new stable version is available. ***"); LOGGER.info("*** Install the new version using: ***"); LOGGER.info("*** 'upgradechecker -install' ***"); } } if (!new ImageInfo().testRead(args)) System.exit(1); } }