/* * #%L * BSD implementations of Bio-Formats readers and writers * %% * 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; import java.io.IOException; import java.util.ArrayList; import java.util.List; import loci.formats.meta.MetadataStore; import loci.formats.CoreMetadata; /** * Handles swapping the dimension order of an image series. This class is * useful for both reassigning ZCT sizes (the input dimension order), and * shuffling around the resultant planar order (the output dimension order). */ public class DimensionSwapper extends ReaderWrapper { // -- Utility methods -- /** Converts the given reader into a DimensionSwapper, wrapping if needed. */ public static DimensionSwapper makeDimensionSwapper(IFormatReader r) { if (r instanceof DimensionSwapper) return (DimensionSwapper) r; return new DimensionSwapper(r); } // -- Fields -- /** Core metadata associated with this dimension swapper. */ private List<CoreMetadata> core; // -- Constructors -- /** Constructs a DimensionSwapper around a new image reader. */ public DimensionSwapper() { super(); } /** Constructs a DimensionSwapper with the given reader. */ public DimensionSwapper(IFormatReader r) { super(r); } // -- DimensionSwapper API methods -- /** * Sets the input dimension order according to the given string (e.g., * "XYZCT"). This string indicates the planar rasterization order from the * source, overriding the detected order. It may result in the dimensional * axis sizes changing. * * If the given order is identical to the file's native order, then * nothing happens. Note that this method will throw an exception if X and Y * do not appear in positions 0 and 1 (although X and Y can be reversed). */ public void swapDimensions(String order) { FormatTools.assertId(getCurrentFile(), true, 2); if (order == null) throw new IllegalArgumentException("order is null"); String oldOrder = getInputOrder(); if (order.equals(oldOrder)) return; if (order.length() != 5) { throw new IllegalArgumentException("order is unexpected length (" + order.length() + ")"); } int newX = order.indexOf("X"); int newY = order.indexOf("Y"); int newZ = order.indexOf("Z"); int newC = order.indexOf("C"); int newT = order.indexOf("T"); if (newX < 0) throw new IllegalArgumentException("X does not appear"); if (newY < 0) throw new IllegalArgumentException("Y does not appear"); if (newZ < 0) throw new IllegalArgumentException("Z does not appear"); if (newC < 0) throw new IllegalArgumentException("C does not appear"); if (newT < 0) throw new IllegalArgumentException("T does not appear"); if (newX > 1) { throw new IllegalArgumentException("X in unexpected position (" + newX + ")"); } if (newY > 1) { throw new IllegalArgumentException("Y in unexpected position (" + newY + ")"); } int[] dims = new int[5]; int oldX = oldOrder.indexOf("X"); int oldY = oldOrder.indexOf("Y"); int oldZ = oldOrder.indexOf("Z"); int oldC = oldOrder.indexOf("C"); int oldT = oldOrder.indexOf("T"); if (oldC != newC && reader.getRGBChannelCount() > 1) { throw new IllegalArgumentException( "Cannot swap C dimension when RGB channel count > 1"); } dims[oldX] = getSizeX(); dims[oldY] = getSizeY(); dims[oldZ] = getSizeZ(); dims[oldC] = getSizeC(); dims[oldT] = getSizeT(); Modulo[] moduli = new Modulo[3]; moduli[oldZ - 2] = getModuloZ(); moduli[oldC - 2] = getModuloC(); moduli[oldT - 2] = getModuloT(); SwappableMetadata ms = (SwappableMetadata) core.get(getCoreIndex()); ms.sizeX = dims[newX]; ms.sizeY = dims[newY]; ms.sizeZ = dims[newZ]; ms.sizeC = dims[newC]; ms.sizeT = dims[newT]; ms.moduloZ = moduli[newZ - 2]; ms.moduloC = moduli[newC - 2]; ms.moduloT = moduli[newT - 2]; ms.inputOrder = order; MetadataStore store = getMetadataStore(); MetadataTools.populatePixels(store, this); } /** * Sets the output dimension order according to the given string (e.g., * "XYZCT"). This string indicates the final planar rasterization * order—i.e., the mapping from 1D plane number to 3D (Z, C, T) tuple. * Changing it will not affect the Z, C or T sizes but will alter the order * in which planes are returned when iterating. * * This method is useful when your application requires a particular output * dimension order; e.g., ImageJ virtual stacks must be in XYCZT order. */ public void setOutputOrder(String outputOrder) { FormatTools.assertId(getCurrentFile(), true, 2); core.get(getCoreIndex()).dimensionOrder = outputOrder; } public String getInputOrder() { FormatTools.assertId(getCurrentFile(), true, 2); return ((SwappableMetadata) core.get(getCoreIndex())).inputOrder; } // -- IFormatReader API methods -- /* @see IFormatReader#getSizeX() */ @Override public int getSizeX() { FormatTools.assertId(getCurrentFile(), true, 2); return core.get(getCoreIndex()).sizeX; } /* @see IFormatReader#getSizeY() */ @Override public int getSizeY() { FormatTools.assertId(getCurrentFile(), true, 2); return core.get(getCoreIndex()).sizeY; } /* @see IFormatReader#getSizeZ() */ @Override public int getSizeZ() { FormatTools.assertId(getCurrentFile(), true, 2); return core.get(getCoreIndex()).sizeZ; } /* @see IFormatReader#getSizeC() */ @Override public int getSizeC() { FormatTools.assertId(getCurrentFile(), true, 2); return core.get(getCoreIndex()).sizeC; } /* @see IFormatReader#getSizeT() */ @Override public int getSizeT() { FormatTools.assertId(getCurrentFile(), true, 2); return core.get(getCoreIndex()).sizeT; } /* @see IFormatReader#getDimensionOrder() */ @Override public String getDimensionOrder() { FormatTools.assertId(getCurrentFile(), true, 2); return core.get(getCoreIndex()).dimensionOrder; } /* @see IFormatReader#openBytes(int) */ @Override public byte[] openBytes(int no) throws FormatException, IOException { return super.openBytes(reorder(no)); } /* @see IFormatReader#openBytes(int, int, int, int, int) */ @Override public byte[] openBytes(int no, int x, int y, int w, int h) throws FormatException, IOException { return super.openBytes(reorder(no), x, y, w, h); } /* @see IFormatReader#openBytes(int, byte[]) */ @Override public byte[] openBytes(int no, byte[] buf) throws FormatException, IOException { return super.openBytes(reorder(no), buf); } /* @see 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 { return super.openBytes(reorder(no), buf, x, y, w, h); } /* @see IFormatReader#openThumbImage(int) */ @Override public byte[] openThumbBytes(int no) throws FormatException, IOException { return super.openThumbBytes(reorder(no)); } /* @see IFormatReader#getZCTCoords(int) */ @Override public int[] getZCTCoords(int no) { return FormatTools.getZCTCoords(this, no); } @Override public int[] getZCTModuloCoords(int index) { return FormatTools.getZCTModuloCoords(this, index); } /* @see IFormatReader#getIndex(int, int, int) */ @Override public int getIndex(int z, int c, int t) { return FormatTools.getIndex(this, z, c, t); } @Override public int getIndex(int z, int c, int t, int moduloZ, int moduloC, int moduloT) { return FormatTools.getIndex(this, z, c, t, moduloZ, moduloC, moduloT); } /* @see IFormatReader#getCoreMetadataList() */ @Override public List<CoreMetadata> getCoreMetadataList() { FormatTools.assertId(getCurrentFile(), true, 2); return core; } // -- IFormatHandler API methods -- /* @see IFormatHandler#setId(String) */ @Override public void setId(String id) throws FormatException, IOException { String oldFile = getCurrentFile(); super.setId(id); if (!id.equals(oldFile) || core == null || core.size() != reader.getCoreMetadataList().size()) { // NB: Create our own copy of the CoreMetadata, // which we can manipulate safely. List<CoreMetadata> oldcore = reader.getCoreMetadataList(); core = new ArrayList<CoreMetadata>(); for (int s=0; s<oldcore.size(); s++) { core.add(new SwappableMetadata(reader, s)); } } } // -- Helper methods -- protected int reorder(int no) { if (getInputOrder() == null) return no; return FormatTools.getReorderedIndex(getInputOrder(), getDimensionOrder(), getSizeZ(), getEffectiveSizeC(), getSizeT(), getImageCount(), no); } }