// // DimensionSwapper.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; import java.io.IOException; import loci.formats.meta.MetadataStore; /** * 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). * * <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/DimensionSwapper.java">Trac</a>, * <a href="http://git.openmicroscopy.org/?p=bioformats.git;a=blob;f=components/bio-formats/src/loci/formats/DimensionSwapper.java;hb=HEAD">Gitweb</a></dd></dl> */ 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 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); } private String[] outputOrder; // -- 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 = getDimensionOrder(); 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(); int series = getSeries(); core[series].sizeX = dims[newX]; core[series].sizeY = dims[newY]; core[series].sizeZ = dims[newZ]; core[series].sizeC = dims[newC]; core[series].sizeT = dims[newT]; //core.currentOrder[series] = order; if (outputOrder[series] == null) { outputOrder[series] = core[series].dimensionOrder; } core[series].dimensionOrder = order; if (oldC != newC) { // C was overridden; clear the sub-C dimensional metadata core[series].cLengths = new int[] {getSizeC()}; core[series].cTypes = new String[] {FormatTools.CHANNEL}; } 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); this.outputOrder[getSeries()] = outputOrder; } public String getInputOrder() { FormatTools.assertId(getCurrentFile(), true, 2); return core[getSeries()].dimensionOrder; } // -- IFormatReader API methods -- /* @see IFormatReader#getSizeX() */ public int getSizeX() { FormatTools.assertId(getCurrentFile(), true, 2); return core[getSeries()].sizeX; } /* @see IFormatReader#getSizeY() */ public int getSizeY() { FormatTools.assertId(getCurrentFile(), true, 2); return core[getSeries()].sizeY; } /* @see IFormatReader#getSizeZ() */ public int getSizeZ() { FormatTools.assertId(getCurrentFile(), true, 2); return core[getSeries()].sizeZ; } /* @see IFormatReader#getSizeC() */ public int getSizeC() { FormatTools.assertId(getCurrentFile(), true, 2); return core[getSeries()].sizeC; } /* @see IFormatReader#getSizeT() */ public int getSizeT() { FormatTools.assertId(getCurrentFile(), true, 2); return core[getSeries()].sizeT; } /* @see IFormatReader#getChannelDimLengths() */ public int[] getChannelDimLengths() { FormatTools.assertId(getCurrentFile(), true, 2); int[] cLengths = core[getSeries()].cLengths; return cLengths == null ? super.getChannelDimLengths() : cLengths; } /* @see IFormatReader#getChannelDimTypes() */ public String[] getChannelDimTypes() { FormatTools.assertId(getCurrentFile(), true, 2); String[] cTypes = core[getSeries()].cTypes; return cTypes == null ? super.getChannelDimTypes() : cTypes; } /* @see IFormatReader#getDimensionOrder() */ public String getDimensionOrder() { FormatTools.assertId(getCurrentFile(), true, 2); String outOrder = outputOrder[getSeries()]; if (outOrder != null) return outOrder; return getInputOrder(); } /* @see IFormatReader#openBytes(int) */ public byte[] openBytes(int no) throws FormatException, IOException { return super.openBytes(reorder(no)); } /* @see IFormatReader#openBytes(int, int, int, int, int) */ 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[]) */ 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) */ 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) */ public byte[] openThumbBytes(int no) throws FormatException, IOException { return super.openThumbBytes(reorder(no)); } /* @see IFormatReader#getZCTCoords(int) */ public int[] getZCTCoords(int no) { return FormatTools.getZCTCoords(this, no); } /* @see IFormatReader#getIndex(int, int, int) */ public int getIndex(int z, int c, int t) { return FormatTools.getIndex(this, z, c, t); } /* @see IFormatReader#getCoreMetadata() */ @Override public CoreMetadata[] getCoreMetadata() { FormatTools.assertId(getCurrentFile(), true, 2); return core; } // -- IFormatHandler API methods -- /* @see IFormatHandler#setId(String) */ public void setId(String id) throws FormatException, IOException { String oldFile = getCurrentFile(); super.setId(id); if (!id.equals(oldFile) || outputOrder == null || outputOrder.length != getSeriesCount()) { outputOrder = new String[getSeriesCount()]; // NB: Create our own copy of the CoreMetadata, // which we can manipulate safely. core = copyCoreMetadata(reader); } } // -- Helper methods -- protected int reorder(int no) { if (getInputOrder() == null) return no; return FormatTools.getReorderedIndex(getInputOrder(), getDimensionOrder(), getSizeZ(), getEffectiveSizeC(), getSizeT(), getImageCount(), no); } }