/* * Copyright 2010-2015 Institut Pasteur. * * This file is part of Icy. * * Icy 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 3 of the License, or * (at your option) any later version. * * Icy 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 Icy. If not, see <http://www.gnu.org/licenses/>. */ package icy.sequence; import java.awt.Dimension; import java.awt.Rectangle; import java.awt.image.BufferedImage; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map.Entry; import java.util.Set; import java.util.TreeMap; import javax.swing.undo.UndoManager; import org.w3c.dom.Node; import icy.common.CollapsibleEvent; import icy.common.UpdateEventHandler; import icy.common.exception.TooLargeArrayException; import icy.common.listener.ChangeListener; import icy.file.FileUtil; import icy.gui.viewer.Viewer; import icy.image.IcyBufferedImage; import icy.image.IcyBufferedImageEvent; import icy.image.IcyBufferedImageListener; import icy.image.IcyBufferedImageUtil; import icy.image.colormap.IcyColorMap; import icy.image.colormodel.IcyColorModel; import icy.image.colormodel.IcyColorModelEvent; import icy.image.colormodel.IcyColorModelListener; import icy.image.lut.LUT; import icy.main.Icy; import icy.math.MathUtil; import icy.math.Scaler; import icy.math.UnitUtil; import icy.math.UnitUtil.UnitPrefix; import icy.painter.Overlay; import icy.painter.OverlayEvent; import icy.painter.OverlayEvent.OverlayEventType; import icy.painter.OverlayListener; import icy.painter.OverlayWrapper; import icy.painter.Painter; import icy.preferences.GeneralPreferences; import icy.roi.ROI; import icy.roi.ROI2D; import icy.roi.ROI3D; import icy.roi.ROIEvent; import icy.roi.ROIListener; import icy.sequence.SequenceEvent.SequenceEventSourceType; import icy.sequence.SequenceEvent.SequenceEventType; import icy.sequence.edit.DataSequenceEdit; import icy.sequence.edit.DefaultSequenceEdit; import icy.sequence.edit.MetadataSequenceEdit; import icy.sequence.edit.ROIAddSequenceEdit; import icy.sequence.edit.ROIAddsSequenceEdit; import icy.sequence.edit.ROIRemoveSequenceEdit; import icy.sequence.edit.ROIRemovesSequenceEdit; import icy.system.IcyExceptionHandler; import icy.system.thread.ThreadUtil; import icy.type.DataType; import icy.type.TypeUtil; import icy.type.collection.CollectionUtil; import icy.type.collection.array.Array1DUtil; import icy.type.dimension.Dimension5D; import icy.type.rectangle.Rectangle5D; import icy.undo.IcyUndoManager; import icy.undo.IcyUndoableEdit; import icy.util.OMEUtil; import icy.util.StringUtil; import loci.formats.ome.OMEXMLMetadataImpl; /** * Image sequence object.<br> * A <code>Sequence</code> is basically a 5 dimension (XYCZT) image where :<br> * XY dimension = planar image<br> * C dimension = channel<br> * Z dimension = depth<br> * T dimension = time<br> * <br> * The XYC dimensions are bounded into the {@link IcyBufferedImage} object so <code>Sequence</code> define a list of * {@link IcyBufferedImage} where each image is associated to a Z and T * information. * * @author Fabrice de Chaumont & Stephane */ public class Sequence implements SequenceModel, IcyColorModelListener, IcyBufferedImageListener, ChangeListener, ROIListener, OverlayListener { private static final String DEFAULT_NAME = "no name"; /** * @deprecated */ @Deprecated public static final int TYPE_BYTE = TypeUtil.TYPE_BYTE; /** * @deprecated */ @Deprecated public static final int TYPE_DOUBLE = TypeUtil.TYPE_DOUBLE; /** * @deprecated */ @Deprecated public static final int TYPE_FLOAT = TypeUtil.TYPE_FLOAT; /** * @deprecated */ @Deprecated public static final int TYPE_INT = TypeUtil.TYPE_INT; /** * @deprecated */ @Deprecated public static final int TYPE_SHORT = TypeUtil.TYPE_SHORT; /** * @deprecated */ @Deprecated public static final int TYPE_UNDEFINED = TypeUtil.TYPE_UNDEFINED; public static final String ID_NAME = "name"; public static final String ID_POSITION_X = "positionX"; public static final String ID_POSITION_Y = "positionY"; public static final String ID_POSITION_Z = "positionZ"; public static final String ID_PIXEL_SIZE_X = "pixelSizeX"; public static final String ID_PIXEL_SIZE_Y = "pixelSizeY"; public static final String ID_PIXEL_SIZE_Z = "pixelSizeZ"; public static final String ID_TIME_INTERVAL = "timeInterval"; public static final String ID_CHANNEL_NAME = "channelName"; /** * id generator */ protected static int id_gen = 1; /** * volumetric images (4D [XYCZ]) */ protected final TreeMap<Integer, VolumetricImage> volumetricImages; /** * painters */ protected final Set<Overlay> overlays; /** * ROIs */ protected final Set<ROI> rois; /** * id of sequence (uniq during an Icy session) */ protected final int id; /** * colorModel of sequence */ protected IcyColorModel colorModel; /** * user lut for this sequence (saved in metadata) */ protected LUT userLut; /** * Origin filename (from/to which the sequence has been loaded/saved)<br> * null --> no file attachment<br> * directory or metadata file --> multiples files attachment<br> * image file --> single file attachment */ protected String filename; /** * Resolution level from the original image<br> * 0 --> full image resolution<br> * 1 --> resolution / 2<br> * 2 --> resolution / 4<br> * 3 --> ...<br> * Default value is 0 */ protected int originResolution; /** * Region (X,Y) from original image if this image is a crop of the original image.<br> * Default value is <code>null</code> (no crop) */ protected Rectangle originXYRegion; /** * Z range from original image if this image is a crop in Z of the original image.<br> * Default value is -1, -1 if we have the whole Z range. */ protected int originZRangeMin; protected int originZRangeMax; /** * T range from original image if this image is a crop in T of the original image.<br> * Default value is -1, -1 if we have the whole T range. */ protected int originTRangeMin; protected int originTRangeMax; /** * Channel position from original image if this image is a single channel extraction of the original image.<br> * Default value is -1 which mean that all channels were preserved. */ protected int originChannel; /** * Metadata */ protected OMEXMLMetadataImpl metaData; // /** // * X, Y, Z resolution (in mm) // */ // private double pixelSizeX; // private double pixelSizeY; // private double pixelSizeZ; // /** // * T resolution (in ms) // */ // private double timeInterval; // /** // * channels name // */ // private String channelsName[]; // /** // * automatic update of component absolute bounds // */ // private boolean componentAbsBoundsAutoUpdate; /** * automatic update of channel bounds */ protected boolean autoUpdateChannelBounds; /** * persistent object to load/save data (XML format) */ protected final SequencePersistent persistent; /** * undo manager */ protected final IcyUndoManager undoManager; /** * internal updater */ protected final UpdateEventHandler updater; /** * listeners */ protected final List<SequenceListener> listeners; protected final List<SequenceModelListener> modelListeners; /** * internals */ protected boolean channelBoundsInvalid; /** * Creates a new empty sequence with specified meta data object and name. */ public Sequence(OMEXMLMetadataImpl meta, String name) { super(); // set id synchronized (Sequence.class) { id = id_gen; id_gen++; } // set metadata object if (meta == null) metaData = MetaDataUtil.createDefaultMetadata(name); else metaData = meta; // set name if (!StringUtil.isEmpty(name)) MetaDataUtil.setName(metaData, 0, name); else { // default name if (StringUtil.isEmpty(MetaDataUtil.getName(metaData, 0))) MetaDataUtil.setName(metaData, 0, DEFAULT_NAME + StringUtil.toString(id, 3)); } filename = null; originResolution = 0; originXYRegion = null; originZRangeMin = -1; originZRangeMax = -1; originTRangeMin = -1; originTRangeMax = -1; originChannel = -1; // default pixel size and time interval if (MetaDataUtil.getPixelSizeX(metaData, 0, 1d) == 1d) MetaDataUtil.setPixelSizeX(metaData, 0, 1d); if (MetaDataUtil.getPixelSizeY(metaData, 0, 1d) == 1d) MetaDataUtil.setPixelSizeY(metaData, 0, 1d); if (MetaDataUtil.getPixelSizeZ(metaData, 0, 1d) == 1d) MetaDataUtil.setPixelSizeZ(metaData, 0, 1d); if (MetaDataUtil.getTimeInterval(metaData, 0, 0.0d) == 0.0d) MetaDataUtil.setTimeInterval(metaData, 0, 0.0d); volumetricImages = new TreeMap<Integer, VolumetricImage>(); overlays = new HashSet<Overlay>(); rois = new HashSet<ROI>(); persistent = new SequencePersistent(this); undoManager = new IcyUndoManager(this, GeneralPreferences.getHistorySize()); updater = new UpdateEventHandler(this, false); listeners = new ArrayList<SequenceListener>(); modelListeners = new ArrayList<SequenceModelListener>(); // no colorModel yet colorModel = null; userLut = null; channelBoundsInvalid = false; // automatic update of channel bounds autoUpdateChannelBounds = true; } /** * Creates a sequence with specified name and containing the specified image */ public Sequence(String name, IcyBufferedImage image) { this(name, (BufferedImage) image); } /** * Creates a sequence with specified name and containing the specified image */ public Sequence(String name, BufferedImage image) { this((OMEXMLMetadataImpl) null, name); addImage(image); } /** * Creates a new empty sequence with specified metadata. */ public Sequence(OMEXMLMetadataImpl meta) { this(meta, null); } /** * Creates a sequence containing the specified image. */ public Sequence(IcyBufferedImage image) { this((BufferedImage) image); } /** * Creates a sequence containing the specified image. */ public Sequence(BufferedImage image) { this((OMEXMLMetadataImpl) null, null); addImage(image); } /** * Creates an empty sequence with specified name. */ public Sequence(String name) { this(null, name); } /** * Creates an empty sequence. */ public Sequence() { this((OMEXMLMetadataImpl) null, null); } @Override protected void finalize() throws Throwable { super.finalize(); } /** * This method close all attached viewers */ public void close() { Icy.getMainInterface().closeSequence(this); } /** * Called when sequence has been closed (all viewers displaying it closed).<br> * <i>Used internally, you should not call it this method directly !</i> */ public void closed() { // do this in background as it can take sometime while (!ThreadUtil.bgRun(new Runnable() { @Override public void run() { // Sequence persistence enabled --> save XML if (GeneralPreferences.getSequencePersistence()) saveXMLData(); } })) { // wait until the process execute ThreadUtil.sleep(10L); } // notify close fireClosedEvent(); } /** * Copy data and metadata from the specified Sequence * * @param source * the source sequence to copy data from * @param copyName * if set to <code>true</code> it will also copy the name from the source sequence */ public void copyFrom(Sequence source, boolean copyName) { copyDataFrom(source); copyMetaDataFrom(source, copyName); } /** * Copy data from the specified Sequence */ public void copyDataFrom(Sequence source) { final int sizeT = source.getSizeT(); final int sizeZ = source.getSizeZ(); beginUpdate(); try { removeAllImages(); for (int t = 0; t < sizeT; t++) { for (int z = 0; z < sizeZ; z++) { final IcyBufferedImage img = source.getImage(t, z); if (img != null) setImage(t, z, IcyBufferedImageUtil.getCopy(img)); else source.setImage(t, z, null); } } } finally { endUpdate(); } } /** * Copy metadata from the specified Sequence * * @param source * the source sequence to copy metadata from * @param copyName * if set to <code>true</code> it will also copy the name from the source sequence */ public void copyMetaDataFrom(Sequence source, boolean copyName) { // copy all metadata from source metaData = OMEUtil.createOMEMetadata(source.getMetadata()); // restore name if needed if (copyName) setName(source.getName()); // notify metadata changed metaChanged(null); } /** * Create a complete restore point for this sequence. * * @param name * restore point name (visible in the History panel) * @return false if for some reason the operation failed (out of memory for instance) * @see #undo() */ public boolean createUndoPoint(String name) { try { undoManager.addEdit(new DefaultSequenceEdit(SequenceUtil.getCopy(this, false, false, false), this)); return true; } catch (Throwable t) { return false; } } /** * Create a restore point for sequence data. * * @param name * restore point name (visible in the History panel) * @return false if for some reason the operation failed (out of memory for instance) * @see #undo() */ public boolean createUndoDataPoint(String name) { try { undoManager.addEdit(new DataSequenceEdit(SequenceUtil.getCopy(this, false, false, false), this)); return true; } catch (Throwable t) { return false; } } /** * Create a restore point for sequence metadata. * * @param name * restore point name (visible in the History panel) * @return false if for some reason the operation failed (out of memory for instance) * @see #undo() */ public boolean createUndoMetadataPoint(String name) { try { undoManager.addEdit(new MetadataSequenceEdit(OMEUtil.createOMEMetadata(metaData), this)); return true; } catch (Throwable t) { return false; } } /** * Add an Undoable edit to the Sequence UndoManager * * @param edit * the undoable edit to add * @return <code>false</code> if the operation failed */ public boolean addUndoableEdit(IcyUndoableEdit edit) { if (edit != null) return undoManager.addEdit(edit); return false; } /** * Undo to the last <i>Undoable</i> change set in the Sequence {@link UndoManager} * * @return <code>true</code> if the operation succeed * @see #createUndoPoint(String) * @see UndoManager#undo() */ public boolean undo() { if (undoManager.canUndo()) { undoManager.undo(); return true; } return false; } /** * Redo the next <i>Undoable</i> change set in the Sequence {@link UndoManager} * * @return <code>true</code> if the operation succeed * @see #createUndoPoint(String) * @see UndoManager#redo() */ public boolean redo() { if (undoManager.canRedo()) { undoManager.redo(); return true; } return false; } /** * Clear all undo operations from the {@link UndoManager}.<br> * You should use this method after you modified the sequence without providing any <i>undo</i> * support. */ public void clearUndoManager() { getUndoManager().discardAllEdits(); } protected void setColorModel(IcyColorModel cm) { // remove listener if (colorModel != null) colorModel.removeListener(this); colorModel = cm; // add listener if (cm != null) cm.addListener(this); // sequence type changed typeChanged(); // sequence component bounds changed componentBoundsChanged(cm, -1); // sequence colormap changed colormapChanged(cm, -1); } /** * @deprecated Use {@link SequenceUtil#convertToType(Sequence, DataType, boolean)} instead. */ @Deprecated public Sequence convertToType(DataType dataType, boolean rescale) { return SequenceUtil.convertToType(this, dataType, rescale); } /** * @deprecated Use {@link SequenceUtil#convertType(Sequence, DataType, Scaler[])} instead. */ @Deprecated public Sequence convertToType(DataType dataType, Scaler scaler) { return SequenceUtil.convertToType(this, dataType, scaler); } /** * @deprecated Use {@link SequenceUtil#convertToType(Sequence, DataType, boolean)} instead */ @Deprecated public Sequence convertToType(int dataType, boolean signed, boolean rescale) { return convertToType(DataType.getDataType(dataType, signed), rescale); } /** * @deprecated Use {@link SequenceUtil#extractChannel(Sequence, int)} instead. */ @Deprecated public Sequence extractChannel(int channelNumber) { return SequenceUtil.extractChannel(this, channelNumber); } /** * @deprecated Use {@link SequenceUtil#extractChannels(Sequence, List)} instead. */ @Deprecated public Sequence extractChannels(List<Integer> channelNumbers) { return SequenceUtil.extractChannels(this, channelNumbers); } /** * @deprecated Use {@link SequenceUtil#extractChannel(Sequence, int)} instead */ @Deprecated public Sequence extractBand(int bandNumber) { return extractChannel(bandNumber); } /** * @deprecated Use {@link SequenceUtil#extractChannels(Sequence, List)} instead */ @Deprecated public Sequence extractBands(List<Integer> bandNumbers) { return extractChannels(bandNumbers); } /** * Returns all VolumetricImage as TreeMap (contains t position) */ public TreeMap<Integer, VolumetricImage> getVolumetricImages() { synchronized (volumetricImages) { return new TreeMap<Integer, VolumetricImage>(volumetricImages); } } /** * Returns all VolumetricImage */ public ArrayList<VolumetricImage> getAllVolumetricImage() { synchronized (volumetricImages) { return new ArrayList<VolumetricImage>(volumetricImages.values()); } } /** * Returns first viewer attached to this sequence */ public Viewer getFirstViewer() { return Icy.getMainInterface().getFirstViewer(this); } /** * Returns viewers attached to this sequence */ public ArrayList<Viewer> getViewers() { return Icy.getMainInterface().getViewers(this); } /** * get sequence id (this id is unique during an ICY session) */ public int getId() { return id; } /** * Sequence name */ public void setName(String value) { if (getName() != value) { MetaDataUtil.setName(metaData, 0, value); metaChanged(ID_NAME); } } public String getName() { return MetaDataUtil.getName(metaData, 0); } /** * Origin filename (from/to which the sequence has been loaded/saved).<br> * This filename information is also used to store the XML persistent data.<br/> * null --> no file attachment<br> * directory or metadata file --> multiples files attachment<br> * image file --> single file attachment * * @return the filename. */ public String getFilename() { return filename; } /** * @param filename * the filename to set */ public void setFilename(String filename) { if (this.filename != filename) { this.filename = filename; } } /** * Returns the output base filename.<br> * This function is supposed to be used internally only. * * @param folderExt * If the filename of this sequence refer a folder then we extend it with 'folderExt' to build the base name. * @see #getOutputExtension() */ public String getOutputBaseName(String folderExt) { String result = getFilename(); if (StringUtil.isEmpty(result)) return ""; // remove some problematic character for XML file result = FileUtil.cleanPath(result); // filename reference a directory --> use "<directory>/<folderExt>" if (FileUtil.isDirectory(result)) result += "/" + folderExt; // otherwise remove extension else result = FileUtil.setExtension(result, ""); return result; } /** * Returns the output filename extension (not the file extension, just extension from base name).<br> * The extension is based on some internals informations as serie index and resolution level.<br> * This function is supposed to be used internally only. * * @see #getOutputBaseName(String) */ public String getOutputExtension() { String result = ""; // retrieve the serie index final int serieNum = getSerieIndex(); // multi serie image --> add a specific extension if (serieNum != 0) result += "_S" + serieNum; // retrieve the resolution final int resolution = getOriginResolution(); // sub resolution --> add a specific extension if (resolution != 0) result += "_R" + resolution; // retrieve the XY region offset final Rectangle xyRegion = getOriginXYRegion(); // not null --> add a specific extension if (xyRegion != null) result += "_XY(" + xyRegion.x + "," + xyRegion.y + "-" + xyRegion.width + "," + xyRegion.height + ")"; // retrieve the Z range final int zMin = getOriginZRangeMin(); final int zMax = getOriginZRangeMax(); // sub Z range --> add a specific extension if ((zMin != -1) || (zMax != -1)) result += "_Z(" + zMin + "-" + zMax + ")"; // retrieve the T range final int tMin = getOriginTRangeMin(); final int tMax = getOriginTRangeMax(); // sub T range --> add a specific extension if ((tMin != -1) || (tMax != -1)) result += "_T(" + tMin + "-" + tMax + ")"; // retrieve the original channel final int channel = getOriginChannel(); // single channel extraction --> add a specific extension if (channel != -1) result += "_C" + channel; return result; } /** * Return the desired output filename for this Sequence (without file extension.<br> * It uses the origin filename and add a specific extension depending some internals properties. * * @param withExtension * Add the original file extension is set to <code>true</code> * @see #getFilename() * @see #getOutputBaseName(String) * @see #getOutputExtension() */ public String getOutputFilename(boolean withExtension) { String result = getFilename(); if (StringUtil.isEmpty(result)) return ""; final String ext = FileUtil.getFileExtension(result, true); result = getOutputBaseName(FileUtil.getFileName(result, false)) + getOutputExtension(); if (withExtension) result += ext; return result; } /** * Returns the resolution level from the origin image (defined by {@link #getFilename()}).<br> * By default it returns 0 if this sequence corresponds to the full resolution of the original image.<br> * A value of 1 mean original resolution / 2<br> * 2 --> original resolution / 4<br> * 3 --> original resolution / 8<br> * ... */ public int getOriginResolution() { return originResolution; } /** * Internal use only, you should not directly use this method. * * @see #getOriginResolution() */ public void setOriginResolution(int value) { originResolution = value; } /** * Returns the region (X,Y) from original image if this image is a crop of the original image.<br> * Default value is <code>null</code> (full size). */ public Rectangle getOriginXYRegion() { return originXYRegion; } /** * Internal use only, you should not directly use this method. * * @see #getOriginXYRegion() */ public void setOriginXYRegion(Rectangle value) { // better to use a copy originXYRegion = new Rectangle(value); } /** * Returns the Z range minimum from original image if this image is a crop in Z of the original image.<br> * Default value is -1 which mean we have the whole Z range. */ public int getOriginZRangeMin() { return originZRangeMin; } /** * Internal use only, you should not directly use this method. * * @see #getOriginZRangeMin() */ public void setOriginZRangeMin(int value) { originZRangeMin = value; } /** * Returns the Z range maximum from original image if this image is a crop in Z of the original image.<br> * Default value is -1 which mean we have the whole Z range. */ public int getOriginZRangeMax() { return originZRangeMax; } /** * Internal use only, you should not directly use this method. * * @see #getOriginZRangeMax() */ public void setOriginZRangeMax(int value) { originZRangeMax = value; } /** * Returns the T range minimum from original image if this image is a crop in T of the original image.<br> * Default value is -1 which mean we have the whole T range. */ public int getOriginTRangeMin() { return originTRangeMin; } /** * Internal use only, you should not directly use this method. * * @see #getOriginTRangeMin() */ public void setOriginTRangeMin(int value) { originTRangeMin = value; } /** * Returns the T range maximum from original image if this image is a crop in T of the original image.<br> * Default value is -1 which mean we have the whole T range. */ public int getOriginTRangeMax() { return originTRangeMax; } /** * Internal use only, you should not directly use this method. * * @see #getOriginTRangeMax() */ public void setOriginTRangeMax(int value) { originTRangeMax = value; } /** * Returns the channel position from original image if this image is a single channel extraction of the original * image.<br> * Default value is -1 which mean that all channels were preserved. */ public int getOriginChannel() { return originChannel; } /** * Internal use only, you should not directly use this method. * * @see #getOriginChannel() */ public void setOriginChannel(int value) { originChannel = value; } /** * Returns serie index if the Sequence comes from a multi serie image.<br> * By default it returns 0 if the sequence comes from a single serie image or if this is the * first serie image. */ public int getSerieIndex() { // retrieve the image ID (sequences are always single serie) final String id = MetaDataUtil.getImageID(getMetadata(), 0); if (id.startsWith("Image:")) { final String[] serieNums = id.substring(6).split(":"); if (serieNums.length > 0) return StringUtil.parseInt(serieNums[0], 0); } return 0; } /** * Returns meta data object */ public OMEXMLMetadataImpl getMetadata() { return metaData; } /** * Set the meta data object */ public void setMetaData(OMEXMLMetadataImpl metaData) { if (this.metaData != metaData) { this.metaData = metaData; // all meta data changed metaChanged(null); } } /** * Returns the X physical position / offset (in �m) of the image represented by this Sequence.<br> * This information can be used to represent the position of the image in the original sample (microscope * information) or the position of a sub image the original image (crop operation).<br> * Note that OME store this information at Plane level (each Z,T,C), here we always use value from Plane(0,0,0) */ public double getPositionX() { return MetaDataUtil.getPositionX(metaData, 0, 0, 0, 0, 0d); } /** * Returns the Y physical position / offset (in �m) of the image represented by this Sequence.<br> * This information can be used to represent the position of the image in the original sample (microscope * information) or the position of a sub image the original image (crop operation).<br> * Note that OME store this information at Plane level (each Z,T,C), here we always use value from Plane(0,0,0) */ public double getPositionY() { return MetaDataUtil.getPositionY(metaData, 0, 0, 0, 0, 0d); } /** * Returns the Z physical position / offset (in �m) of the image represented by this Sequence.<br> * This information can be used to represent the position of the image in the original sample (microscope * information) or the position of a sub image the original image (crop operation).<br> * Note that OME store this information at Plane level (each Z,T,C), here we always use value from Plane(0,0,0) */ public double getPositionZ() { return MetaDataUtil.getPositionZ(metaData, 0, 0, 0, 0, 0d); } /** * Sets the X physical position / offset (in �m) of the image represented by this Sequence.<br> * This information can be used to represent the position of the image in the original sample (microscope * information) or the position of a sub image the original image (crop operation).<br> * Note that OME store this information at Plane level (each Z,T,C), here we always use value from Plane(0,0,0) */ public void setPositionX(double value) { if (getPositionX() != value) { MetaDataUtil.setPositionX(metaData, 0, 0, 0, 0, value); metaChanged(ID_POSITION_X); } } /** * Sets the X physical position / offset (in �m) of the image represented by this Sequence.<br> * This information can be used to represent the position of the image in the original sample (microscope * information) or the position of a sub image the original image (crop operation).<br> * Note that OME store this information at Plane level (each Z,T,C), here we always use value from Plane(0,0,0) */ public void setPositionY(double value) { if (getPositionY() != value) { MetaDataUtil.setPositionY(metaData, 0, 0, 0, 0, value); metaChanged(ID_POSITION_Y); } } /** * Sets the X physical position / offset (in �m) of the image represented by this Sequence.<br> * This information can be used to represent the position of the image in the original sample (microscope * information) or the position of a sub image the original image (crop operation).<br> * Note that OME store this information at Plane level (each Z,T,C), here we always use value from Plane(0,0,0) */ public void setPositionZ(double value) { if (getPositionZ() != value) { MetaDataUtil.setPositionZ(metaData, 0, 0, 0, 0, value); metaChanged(ID_POSITION_Z); } } /** * Returns pixel size for [X,Y,Z] dimension (in �m to be OME compatible) */ public double[] getPixelSize() { return new double[] {getPixelSizeX(), getPixelSizeY(), getPixelSizeZ()}; } /** * Returns X pixel size (in �m to be OME compatible) */ public double getPixelSizeX() { return MetaDataUtil.getPixelSizeX(metaData, 0, 1d); } /** * Returns Y pixel size (in �m to be OME compatible) */ public double getPixelSizeY() { return MetaDataUtil.getPixelSizeY(metaData, 0, 1d); } /** * Returns Z pixel size (in �m to be OME compatible) */ public double getPixelSizeZ() { return MetaDataUtil.getPixelSizeZ(metaData, 0, 1d); } /** * Returns T time interval (in second for OME compatibility) */ public double getTimeInterval() { double result = MetaDataUtil.getTimeInterval(metaData, 0, 0d); // not yet defined ? if (result == 0d) { result = MetaDataUtil.getTimeIntervalFromTimePositions(metaData, 0); // we got something --> set it as the time interval if (result != 0d) MetaDataUtil.setTimeInterval(metaData, 0, result); } return result; } /** * Set X pixel size (in �m to be OME compatible) */ public void setPixelSizeX(double value) { if (getPixelSizeX() != value) { MetaDataUtil.setPixelSizeX(metaData, 0, value); metaChanged(ID_PIXEL_SIZE_X); } } /** * Set Y pixel size (in �m to be OME compatible) */ public void setPixelSizeY(double value) { if (getPixelSizeY() != value) { MetaDataUtil.setPixelSizeY(metaData, 0, value); metaChanged(ID_PIXEL_SIZE_Y); } } /** * Set Z pixel size (in �m to be OME compatible) */ public void setPixelSizeZ(double value) { if (getPixelSizeZ() != value) { MetaDataUtil.setPixelSizeZ(metaData, 0, value); metaChanged(ID_PIXEL_SIZE_Z); } } /** * Set T time resolution (in second to be OME compatible) */ public void setTimeInterval(double value) { if (MetaDataUtil.getTimeInterval(metaData, 0, 0d) != value) { MetaDataUtil.setTimeInterval(metaData, 0, value); metaChanged(ID_TIME_INTERVAL); } } /** * Returns the pixel size scaling factor to convert a number of pixel/voxel unit into <code>�m</code><br/> * <br> * For instance to get the scale ration for 2D distance:<br> * <code>valueMicroMeter = pixelNum * getPixelSizeScaling(2, 1)</code><br> * For a 2D surface:<br> * <code>valueMicroMeter2 = pixelNum * getPixelSizeScaling(2, 2)</code><br> * For a 3D volume:<br> * <code>valueMicroMeter3 = pixelNum * getPixelSizeScaling(3, 3)</code><br> * * @param dimCompute * dimension order for size calculation<br> * <li>1 --> pixel size X used for conversion</li><br> * <li>2 --> pixel size X and Y used for conversion</li><br> * <li>3 or above --> pixel size X, Y and Z used for conversion</li><br> * @param dimResult * dimension order for the result (unit)<br> * <li>1 --> distance</li><br> * <li>2 --> area</li><br> * <li>3 or above --> volume</li><br> */ public double getPixelSizeScaling(int dimCompute, int dimResult) { double result; switch (dimCompute) { case 0: // incorrect return 0d; case 1: result = getPixelSizeX(); break; case 2: result = getPixelSizeX() * getPixelSizeY(); break; default: result = getPixelSizeX() * getPixelSizeY() * getPixelSizeZ(); break; } result = Math.pow(result, (double) dimResult / (double) dimCompute); return result; } /** * Returns the best pixel size unit for the specified dimension order given the sequence's pixel * size informations.<br/> * <li>Compute a 2D distance:</li> * * <pre> * dimCompute = 2; * dimUnit = 1; * valueMicroMeter = pixelNum * getPixelSizeScaling(dimCompute); * bestUnit = getBestPixelSizeUnit(dimCompute, dimUnit); * finalValue = UnitUtil.getValueInUnit(valueMicroMeter, UnitPrefix.MICRO, bestUnit); * valueString = Double.toString(finalValue) + " " + bestUnit.toString() + "m"; * </pre> * * <li>Compute a 2D surface:</li> * * <pre> * dimCompute = 2; * dimUnit = 2; * valueMicroMeter = pixelNum * getPixelSizeScaling(dimCompute); * bestUnit = getBestPixelSizeUnit(dimCompute, dimUnit); * finalValue = UnitUtil.getValueInUnit(valueMicroMeter, UnitPrefix.MICRO, bestUnit); * valueString = Double.toString(finalValue) + " " + bestUnit.toString() + "m2"; * </pre> * * <li>Compute a 3D volume:</li> * * <pre> * dimCompute = 3; * dimUnit = 3; * valueMicroMeter = pixelNum * getPixelSizeScaling(dimCompute); * bestUnit = getBestPixelSizeUnit(dimCompute, dimUnit); * finalValue = UnitUtil.getValueInUnit(valueMicroMeter, UnitPrefix.MICRO, bestUnit); * valueString = Double.toString(finalValue) + " " + bestUnit.toString() + "m3"; * </pre> * * @param dimCompute * dimension order for size calculation<br> * <li>1 --> pixel size X used for conversion</li><br> * <li>2 --> pixel size X and Y used for conversion</li><br> * <li>3 or above --> pixel size X, Y and Z used for conversion</li><br> * @param dimResult * dimension order for the result (unit)<br> * <li>1 --> distance</li><br> * <li>2 --> area</li><br> * <li>3 or above --> volume</li><br> * @see #calculateSizeBestUnit(double, int, int) */ public UnitPrefix getBestPixelSizeUnit(int dimCompute, int dimResult) { switch (dimResult) { case 0: // keep original return UnitPrefix.MICRO; case 1: return UnitUtil.getBestUnit((getPixelSizeScaling(dimCompute, dimResult) * 10), UnitPrefix.MICRO, dimResult); case 2: return UnitUtil.getBestUnit((getPixelSizeScaling(dimCompute, dimResult) * 100), UnitPrefix.MICRO, dimResult); default: return UnitUtil.getBestUnit((getPixelSizeScaling(dimCompute, dimResult) * 1000), UnitPrefix.MICRO, dimResult); } } /** * Returns the size in �m for the specified amount of sample/pixel value in the specified * dimension order.<br> * <br> * For the perimeter in �m:<br> * <code>perimeter = calculateSize(contourInPixel, 2, 1)</code><br> * For a 2D surface in �m2:<br> * <code>surface = calculateSize(interiorInPixel, 2, 2)</code><br> * For a 2D surface area in �m2:<br> * <code>volume = calculateSize(contourInPixel, 3, 2)</code><br> * For a 3D volume in �m3:<br> * <code>volume = calculateSize(interiorInPixel, 3, 3)</code><br> * * @param pixelNumber * number of pixel * @param dimCompute * dimension order for size calculation<br> * <li>1 --> pixel size X used for conversion</li><br> * <li>2 --> pixel size X and Y used for conversion</li><br> * <li>3 or above --> pixel size X, Y and Z used for conversion</li><br> * @param dimResult * dimension order for the result (unit)<br> * <li>1 --> distance</li><br> * <li>2 --> area</li><br> * <li>3 or above --> volume</li><br> * @see #calculateSizeBestUnit(double, int, int) */ public double calculateSize(double pixelNumber, int dimCompute, int dimResult) { return pixelNumber * getPixelSizeScaling(dimCompute, dimResult); } /** * Returns the size converted in the best unit (see {@link #getBestPixelSizeUnit(int, int)} for * the specified amount of sample/pixel value in the specified dimension order.<br/> * <li>Compute a 2D distance:</li> * * <pre> * dimCompute = 2; * dimUnit = 1; * valueBestUnit = calculateSizeBestUnit(pixelNum, dimCompute, dimUnit); * bestUnit = getBestPixelSizeUnit(dimCompute, dimUnit); * valueString = Double.toString(valueBestUnit) + " " + bestUnit.toString() + "m"; * </pre> * * <li>Compute a 2D surface:</li> * * <pre> * dimCompute = 2; * dimUnit = 2; * valueBestUnit = calculateSizeBestUnit(pixelNum, dimCompute, dimUnit); * bestUnit = getBestPixelSizeUnit(dimCompute, dimUnit); * valueString = Double.toString(valueBestUnit) + " " + bestUnit.toString() + "m2"; * </pre> * * <li>Compute a 3D volume:</li> * * <pre> * dimCompute = 3; * dimUnit = 3; * valueBestUnit = calculateSizeBestUnit(pixelNum, dimCompute, dimUnit); * bestUnit = getBestPixelSizeUnit(dimCompute, dimUnit); * valueString = Double.toString(valueBestUnit) + " " + bestUnit.toString() + "m3"; * </pre> * * @param pixelNumber * number of pixel * @param dimCompute * dimension order for size calculation<br> * <li>1 --> pixel size X used for conversion</li><br> * <li>2 --> pixel size X and Y used for conversion</li><br> * <li>3 or above --> pixel size X, Y and Z used for conversion</li><br> * @param dimResult * dimension order for the result (unit)<br> * <li>1 --> distance</li><br> * <li>2 --> area</li><br> * <li>3 or above --> volume</li><br> * @see #calculateSize(double, int, int) * @see #getBestPixelSizeUnit(int, int) */ public double calculateSizeBestUnit(double pixelNumber, int dimCompute, int dimResult) { final double value = calculateSize(pixelNumber, dimCompute, dimResult); final UnitPrefix unit = getBestPixelSizeUnit(dimCompute, dimResult); return UnitUtil.getValueInUnit(value, UnitPrefix.MICRO, unit, dimResult); } /** * Returns the size and appropriate unit in form of String for specified amount of sample/pixel * value in the specified dimension order.<br> * <br> * For instance if you want to retrieve the 2D distance:<br> * <code>distanceStr = calculateSize(distanceInPixel, 2, 1, 5)</code><br> * For a 2D surface:<br> * <code>surfaceStr = calculateSize(surfaceInPixel, 2, 2, 5)</code><br> * For a 3D volume:<br> * <code>volumeStr = calculateSize(volumeInPixel, 3, 3, 5)</code><br> * * @param pixelNumber * number of pixel * @param dimCompute * dimension order for the calculation * @param dimResult * dimension order for the result (unit) * @param significantDigit * wanted significant digit for the result (0 for all) * @see #calculateSize(double, int, int) */ public String calculateSize(double pixelNumber, int dimCompute, int dimResult, int significantDigit) { double value = calculateSize(pixelNumber, dimCompute, dimResult); final String postFix = (dimResult > 1) ? StringUtil.toString(dimResult) : ""; final UnitPrefix unit = UnitUtil.getBestUnit(value, UnitPrefix.MICRO, dimResult); // final UnitPrefix unit = getBestPixelSizeUnit(dimCompute, dimResult); value = UnitUtil.getValueInUnit(value, UnitPrefix.MICRO, unit, dimResult); if (significantDigit != 0) value = MathUtil.roundSignificant(value, significantDigit); return StringUtil.toString(value) + " " + unit.toString() + "m" + postFix; } /** * Get default name for specified channel */ public String getDefaultChannelName(int index) { return MetaDataUtil.getDefaultChannelName(index); } /** * Get name for specified channel */ public String getChannelName(int index) { return MetaDataUtil.getChannelName(metaData, 0, index); } /** * Set name for specified channel */ public void setChannelName(int index, String value) { if (!StringUtil.equals(getChannelName(index), value)) { MetaDataUtil.setChannelName(metaData, 0, index, value); metaChanged(ID_CHANNEL_NAME, index); } } /** * @deprecated Use {@link #getAutoUpdateChannelBounds()} instead. */ @Deprecated public boolean isComponentAbsBoundsAutoUpdate() { return getAutoUpdateChannelBounds(); } /** * @deprecated Use {@link #setAutoUpdateChannelBounds(boolean)} instead. */ @Deprecated public void setComponentAbsBoundsAutoUpdate(boolean value) { // nothing here } /** * @return true is channel bounds are automatically updated when sequence data is modified. * @see #setAutoUpdateChannelBounds(boolean) */ public boolean getAutoUpdateChannelBounds() { return autoUpdateChannelBounds; } /** * If set to <code>true</code> (default) then channel bounds will be automatically recalculated * when sequence data is modified.<br> * This can consume a lot of time if you make many updates on large sequence.<br> * In this case you should do your updates in a {@link #beginUpdate()} ... {@link #endUpdate()} block to avoid * severals recalculation. */ public void setAutoUpdateChannelBounds(boolean value) { if (autoUpdateChannelBounds != value) { if (value) updateChannelsBounds(false); autoUpdateChannelBounds = value; } } /** * @deprecated Use {@link #getAutoUpdateChannelBounds()} instead. */ @Deprecated public boolean isComponentUserBoundsAutoUpdate() { return getAutoUpdateChannelBounds(); } /** * @deprecated Use {@link #setAutoUpdateChannelBounds(boolean)} instead. */ @Deprecated public void setComponentUserBoundsAutoUpdate(boolean value) { setAutoUpdateChannelBounds(value); } /** * @return the AWT dispatching property * @deprecated Don't use it, events should stay on current thread */ @Deprecated public boolean isAWTDispatching() { return updater.isAwtDispatch(); } /** * All events are dispatched on AWT when true else they are dispatched on current thread * * @deprecated Don't use it, events should stay on current thread */ @Deprecated public void setAWTDispatching(boolean value) { updater.setAwtDispatch(value); } /** * Add the specified listener to listeners list */ public void addListener(SequenceListener listener) { listeners.add(listener); } /** * Remove the specified listener from listeners list */ public void removeListener(SequenceListener listener) { listeners.remove(listener); } /** * Get listeners list */ public SequenceListener[] getListeners() { return listeners.toArray(new SequenceListener[0]); } /** * Add the specified {@link icy.sequence.SequenceModel.SequenceModelListener} to listeners list */ @Override public void addSequenceModelListener(SequenceModelListener listener) { modelListeners.add(listener); } /** * Remove the specified {@link icy.sequence.SequenceModel.SequenceModelListener} from listeners * list */ @Override public void removeSequenceModelListener(SequenceModelListener listener) { modelListeners.remove(listener); } /** * Get the Undo manager of this sequence */ public IcyUndoManager getUndoManager() { return undoManager; } /** * @deprecated Use {@link #contains(Overlay)} instead. */ @Deprecated public boolean contains(Painter painter) { return getOverlay(painter) != null; } /** * Returns true if the sequence contains the specified overlay */ public boolean contains(Overlay overlay) { if (overlay == null) return false; synchronized (overlays) { return overlays.contains(overlay); } } /** * Returns true if the sequence contains the specified ROI */ public boolean contains(ROI roi) { if (roi == null) return false; synchronized (rois) { return rois.contains(roi); } } /** * @deprecated Use {@link #hasOverlay()} instead. */ @Deprecated public boolean hasPainter() { return hasOverlay(); } /** * @deprecated Use {@link #getOverlays()} instead. */ @Deprecated public ArrayList<Painter> getPainters() { final ArrayList<Painter> result = new ArrayList<Painter>(overlays.size()); synchronized (overlays) { for (Overlay overlay : overlays) { if (overlay instanceof OverlayWrapper) result.add(((OverlayWrapper) overlay).getPainter()); else result.add(overlay); } } return result; } /** * @deprecated Use {@link #getOverlaySet()} instead. */ @Deprecated public HashSet<Painter> getPainterSet() { final HashSet<Painter> result = new HashSet<Painter>(overlays.size()); synchronized (overlays) { for (Overlay overlay : overlays) { if (overlay instanceof OverlayWrapper) result.add(((OverlayWrapper) overlay).getPainter()); else result.add(overlay); } } return result; } /** * @deprecated Use {@link #getOverlays(Class)} instead. */ @Deprecated public List<Painter> getPainters(Class<? extends Painter> painterClass) { final ArrayList<Painter> result = new ArrayList<Painter>(overlays.size()); synchronized (overlays) { for (Overlay overlay : overlays) { if (overlay instanceof OverlayWrapper) { if (painterClass.isInstance(((OverlayWrapper) overlay).getPainter())) result.add(overlay); } else { if (painterClass.isInstance(overlay)) result.add(overlay); } } } return result; } /** * Returns true if the sequence contains at least one Overlay. */ public boolean hasOverlay() { return overlays.size() > 0; } /** * Returns all overlays attached to this sequence */ public List<Overlay> getOverlays() { synchronized (overlays) { return new ArrayList<Overlay>(overlays); } } /** * Returns all overlays attached to this sequence (HashSet form) */ public Set<Overlay> getOverlaySet() { synchronized (overlays) { return new HashSet<Overlay>(overlays); } } /** * Returns true if the sequence contains Overlay of specified Overlay class. */ public boolean hasOverlay(Class<? extends Overlay> overlayClass) { synchronized (overlays) { for (Overlay overlay : overlays) if (overlayClass.isInstance(overlay)) return true; } return false; } /** * Returns overlays of specified class attached to this sequence */ public List<Overlay> getOverlays(Class<? extends Overlay> overlayClass) { final List<Overlay> result = new ArrayList<Overlay>(overlays.size()); synchronized (overlays) { for (Overlay overlay : overlays) if (overlayClass.isInstance(overlay)) result.add(overlay); } return result; } /** * Returns true if the sequence contains at least one ROI. */ public boolean hasROI() { return rois.size() > 0; } /** * Returns all ROIs attached to this sequence. * * @param sorted * If true the returned list is ordered by the ROI id (creation order). */ public List<ROI> getROIs(boolean sorted) { final List<ROI> result; synchronized (rois) { result = new ArrayList<ROI>(rois); } // sort it if required if (sorted) Collections.sort(result, ROI.idComparator); return result; } /** * Returns all ROIs attached to this sequence. */ public ArrayList<ROI> getROIs() { return (ArrayList<ROI>) getROIs(false); } /** * Returns all ROIs attached to this sequence (HashSet form) */ public HashSet<ROI> getROISet() { synchronized (rois) { return new HashSet<ROI>(rois); } } /** * Returns all 2D ROIs attached to this sequence. * * @param sorted * If true the returned list is ordered by the ROI id (creation order). */ public List<ROI2D> getROI2Ds(boolean sorted) { final List<ROI2D> result = new ArrayList<ROI2D>(rois.size()); synchronized (rois) { for (ROI roi : rois) if (roi instanceof ROI2D) result.add((ROI2D) roi); } // sort it if required if (sorted) Collections.sort(result, ROI.idComparator); return result; } /** * Returns all 2D ROIs attached to this sequence. */ public ArrayList<ROI2D> getROI2Ds() { return (ArrayList<ROI2D>) getROI2Ds(false); } /** * Returns all 3D ROIs attached to this sequence. * * @param sorted * If true the returned list is ordered by the ROI id (creation order). */ public List<ROI3D> getROI3Ds(boolean sorted) { final List<ROI3D> result = new ArrayList<ROI3D>(rois.size()); synchronized (rois) { for (ROI roi : rois) if (roi instanceof ROI3D) result.add((ROI3D) roi); } // sort it if required if (sorted) Collections.sort(result, ROI.idComparator); return result; } /** * Returns all 3D ROIs attached to this sequence. */ public ArrayList<ROI3D> getROI3Ds() { return (ArrayList<ROI3D>) getROI3Ds(false); } /** * Returns true if the sequence contains ROI of specified ROI class. */ public boolean hasROI(Class<? extends ROI> roiClass) { synchronized (rois) { for (ROI roi : rois) if (roiClass.isInstance(roi)) return true; } return false; } /** * Returns ROIs of specified class attached to this sequence */ public List<ROI> getROIs(Class<? extends ROI> roiClass) { final ArrayList<ROI> result = new ArrayList<ROI>(rois.size()); synchronized (rois) { for (ROI roi : rois) if (roiClass.isInstance(roi)) result.add(roi); } return result; } /** * Returns the number of ROI of specified ROI class attached to the sequence. */ public int getROICount(Class<? extends ROI> roiClass) { int result = 0; synchronized (rois) { for (ROI roi : rois) if (roiClass.isInstance(roi)) result++; } return result; } /** * Returns true if the sequence contains at least one selected ROI. */ public boolean hasSelectedROI() { return getSelectedROI() != null; } /** * Returns the first selected ROI found (null if no ROI selected) */ public ROI getSelectedROI() { synchronized (rois) { for (ROI roi : rois) if (roi.isSelected()) return roi; } return null; } /** * Returns the first selected 2D ROI found (null if no 2D ROI selected) */ public ROI2D getSelectedROI2D() { synchronized (rois) { for (ROI roi : rois) if ((roi instanceof ROI2D) && roi.isSelected()) return (ROI2D) roi; } return null; } /** * Returns the first selected 3D ROI found (null if no 3D ROI selected) */ public ROI3D getSelectedROI3D() { synchronized (rois) { for (ROI roi : rois) if ((roi instanceof ROI3D) && roi.isSelected()) return (ROI3D) roi; } return null; } /** * Returns all selected ROI of given class (Set format). * * @param roiClass * ROI class restriction * @param wantReadOnly * also return ROI with read only state */ public Set<ROI> getSelectedROISet(Class<? extends ROI> roiClass, boolean wantReadOnly) { final Set<ROI> result = new HashSet<ROI>(rois.size()); synchronized (rois) { for (ROI roi : rois) if (roi.isSelected() && roiClass.isInstance(roi)) if (wantReadOnly || !roi.isReadOnly()) result.add(roi); } return result; } /** * Returns all selected ROI (Set format). */ public Set<ROI> getSelectedROISet() { final Set<ROI> result = new HashSet<ROI>(rois.size()); synchronized (rois) { for (ROI roi : rois) if (roi.isSelected()) result.add(roi); } return result; } /** * Returns all selected ROI of given class. * * @param roiClass * ROI class restriction * @param wantReadOnly * also return ROI with read only state */ public List<ROI> getSelectedROIs(Class<? extends ROI> roiClass, boolean wantReadOnly) { final List<ROI> result = new ArrayList<ROI>(rois.size()); synchronized (rois) { for (ROI roi : rois) if (roi.isSelected() && roiClass.isInstance(roi)) if (wantReadOnly || !roi.isReadOnly()) result.add(roi); } return result; } /** * Returns all selected ROI */ public ArrayList<ROI> getSelectedROIs() { final ArrayList<ROI> result = new ArrayList<ROI>(rois.size()); synchronized (rois) { for (ROI roi : rois) if (roi.isSelected()) result.add(roi); } return result; } /** * Returns all selected 2D ROI */ public ArrayList<ROI2D> getSelectedROI2Ds() { final ArrayList<ROI2D> result = new ArrayList<ROI2D>(rois.size()); synchronized (rois) { for (ROI roi : rois) if ((roi instanceof ROI2D) && roi.isSelected()) result.add((ROI2D) roi); } return result; } /** * Returns all selected 3D ROI */ public ArrayList<ROI3D> getSelectedROI3Ds() { final ArrayList<ROI3D> result = new ArrayList<ROI3D>(rois.size()); synchronized (rois) { for (ROI roi : rois) if ((roi instanceof ROI3D) && roi.isSelected()) result.add((ROI3D) roi); } return result; } /** * Returns the current focused ROI (null if no ROI focused) */ public ROI getFocusedROI() { synchronized (rois) { for (ROI roi : rois) if (roi.isFocused()) return roi; } return null; } /** * Set the selected ROI (exclusive selection).<br> * Specifying a <code>null</code> ROI here will actually clear all ROI selection.<br> * Note that you can use {@link #setSelectedROIs(List)} or {@link ROI#setSelected(boolean)} for * multiple ROI selection. * * @param roi * the ROI to select. * @return <code>false</code> is the specified ROI is not attached to the sequence. */ public boolean setSelectedROI(ROI roi) { beginUpdate(); try { synchronized (rois) { for (ROI currentRoi : rois) if (currentRoi != roi) currentRoi.setSelected(false); } if (contains(roi)) { roi.setSelected(true); return true; } } finally { endUpdate(); } return false; } /** * @deprecated Use {@link #setSelectedROI(ROI)} instead. */ @Deprecated public boolean setSelectedROI(ROI roi, boolean exclusive) { if (exclusive) return setSelectedROI(roi); if (contains(roi)) { roi.setSelected(true); return true; } return false; } /** * @deprecated Use {@link #setSelectedROIs(List)} instead. */ @Deprecated public void setSelectedROIs(ArrayList<ROI> selected) { setSelectedROIs((List<ROI>) selected); } /** * Set selected ROI (unselected all others) */ public void setSelectedROIs(List<? extends ROI> selected) { final List<ROI> oldSelected = getSelectedROIs(); final int newSelectedSize = (selected == null) ? 0 : selected.size(); final int oldSelectedSize = oldSelected.size(); // easy optimization if ((newSelectedSize == 0) && (oldSelectedSize == 0)) return; final HashSet<ROI> newSelected; // use HashSet for fast .contains() ! if (selected != null) newSelected = new HashSet<ROI>(selected); else newSelected = new HashSet<ROI>(); // selection changed ? if (!CollectionUtil.equals(oldSelected, newSelected)) { beginUpdate(); try { if (newSelectedSize > 0) { for (ROI roi : getROIs()) roi.setSelected(newSelected.contains(roi)); } else { // unselected all ROIs for (ROI roi : getROIs()) roi.setSelected(false); } } finally { endUpdate(); } } } /** * Set the focused ROI */ public boolean setFocusedROI(ROI roi) { // faster .contain() final Set<ROI> listRoi = getROISet(); beginUpdate(); try { for (ROI currentRoi : listRoi) if (currentRoi != roi) currentRoi.internalUnfocus(); if (listRoi.contains(roi)) { roi.internalFocus(); return true; } } finally { endUpdate(); } return false; } /** * Add the specified collection of ROI to the sequence. * * @param rois * the collection of ROI to attach to the sequence * @param canUndo * If true the action can be canceled by the undo manager. * @return <code>true</code> if the operation succeed or <code>false</code> if some ROIs could * not be added (already present) */ public boolean addROIs(Collection<ROI> rois, boolean canUndo) { if (!rois.isEmpty()) { final List<ROI> addedRois = new ArrayList<ROI>(); for (ROI roi : rois) { if (addROI(roi, false)) addedRois.add(roi); } if (canUndo && !addedRois.isEmpty()) addUndoableEdit(new ROIAddsSequenceEdit(this, addedRois)); return addedRois.size() == rois.size(); } return true; } /** * Add the specified ROI to the sequence. * * @param roi * ROI to attach to the sequence */ public boolean addROI(ROI roi) { return addROI(roi, false); } /** * Add the specified ROI to the sequence. * * @param roi * ROI to attach to the sequence * @param canUndo * If true the action can be canceled by the undo manager. * @return <code>true</code> if the operation succeed or <code>false</code> otherwise (already * present) */ public boolean addROI(ROI roi, boolean canUndo) { if ((roi == null) || contains(roi)) return false; synchronized (rois) { rois.add(roi); } // add listener to ROI roi.addListener(this); // notify roi added roiChanged(roi, SequenceEventType.ADDED); // then add ROI overlay to sequence addOverlay(roi.getOverlay()); if (canUndo) addUndoableEdit(new ROIAddSequenceEdit(this, roi)); return true; } /** * Remove the specified ROI from the sequence. * * @param roi * ROI to detach from the sequence */ public boolean removeROI(ROI roi) { return removeROI(roi, false); } /** * Remove the specified ROI from the sequence. * * @param roi * ROI to detach from the sequence * @param canUndo * If true the action can be canceled by the undo manager. * @return <code>false</code> if the ROI was not found in the sequence.<br/> * Returns <code>true</code> otherwise. */ public boolean removeROI(ROI roi, boolean canUndo) { if (contains(roi)) { // remove ROI overlay first removeOverlay(roi.getOverlay()); // remove ROI synchronized (rois) { rois.remove(roi); } // remove listener roi.removeListener(this); // notify roi removed roiChanged(roi, SequenceEventType.REMOVED); if (canUndo) addUndoableEdit(new ROIRemoveSequenceEdit(this, roi)); return true; } return false; } /** * Remove the specified collection of ROI from the sequence. * * @param rois * the collection of ROI to remove from the sequence * @param canUndo * If true the action can be canceled by the undo manager. * @return <code>true</code> if all ROI from the collection has been correctly removed. */ public boolean removeROIs(Collection<ROI> rois, boolean canUndo) { if (!rois.isEmpty()) { final List<ROI> removedRois = new ArrayList<ROI>(); for (ROI roi : rois) { if (removeROI(roi, false)) removedRois.add(roi); } if (canUndo && !removedRois.isEmpty()) addUndoableEdit(new ROIRemovesSequenceEdit(this, removedRois)); return removedRois.size() == rois.size(); } return true; } /** * Remove all selected ROI from the sequence. * * @param removeReadOnly * Specify if we should also remove <i>read only</i> ROI (see {@link ROI#isReadOnly()}) * @return <code>true</code> if at least one ROI was removed.<br/> * Returns <code>false</code> otherwise */ public boolean removeSelectedROIs(boolean removeReadOnly) { return removeSelectedROIs(removeReadOnly, false); } /** * Remove all selected ROI from the sequence. * * @param removeReadOnly * Specify if we should also remove <i>read only</i> ROI (see {@link ROI#isReadOnly()}) * @param canUndo * If true the action can be canceled by the undo manager. * @return <code>true</code> if at least one ROI was removed.<br/> * Returns <code>false</code> otherwise */ public boolean removeSelectedROIs(boolean removeReadOnly, boolean canUndo) { final List<ROI> undoList = new ArrayList<ROI>(); beginUpdate(); try { synchronized (rois) { for (ROI roi : getROIs()) { if (roi.isSelected() && (removeReadOnly || !roi.isReadOnly())) { // remove ROI overlay first removeOverlay(roi.getOverlay()); rois.remove(roi); // remove listener roi.removeListener(this); // notify roi removed roiChanged(roi, SequenceEventType.REMOVED); // save deleted ROI undoList.add(roi); } } } if (canUndo) undoManager.addEdit(new ROIRemovesSequenceEdit(this, undoList)); } finally { endUpdate(); } return !undoList.isEmpty(); } /** * Remove all ROI from the sequence. */ public void removeAllROI() { removeAllROI(false); } /** * Remove all ROI from the sequence. * * @param canUndo * If true the action can be canceled by the undo manager. */ public void removeAllROI(boolean canUndo) { if (!rois.isEmpty()) { final List<ROI> allROIs = getROIs(); // remove all ROI for (ROI roi : allROIs) removeROI(roi, false); if (canUndo) addUndoableEdit(new ROIRemovesSequenceEdit(this, allROIs)); } } /** * Return the overlay associated to the specified painter.<br> * Used only for backward compatibility with {@link Painter} interface. */ @SuppressWarnings("deprecation") protected Overlay getOverlay(Painter painter) { if (painter instanceof Overlay) return (Overlay) painter; synchronized (overlays) { for (Overlay overlay : overlays) if (overlay instanceof OverlayWrapper) if (((OverlayWrapper) overlay).getPainter() == painter) return overlay; } return null; } /** * @deprecated Use {@link #addOverlay(Overlay)} instead. */ @Deprecated public boolean addPainter(Painter painter) { if (painter instanceof Overlay) return addOverlay((Overlay) painter); if ((painter == null) || contains(painter)) return false; addOverlay(new OverlayWrapper(painter, "Overlay wrapper")); return true; } /** * @deprecated Use {@link #removeOverlay(Overlay)} instead. */ @Deprecated public boolean removePainter(Painter painter) { if (painter instanceof Overlay) return removeOverlay((Overlay) painter); return removeOverlay(getOverlay(painter)); } /** * Add an overlay to the sequence. */ public boolean addOverlay(Overlay overlay) { if ((overlay == null) || contains(overlay)) return false; synchronized (overlays) { overlays.add(overlay); } // add listener overlay.addOverlayListener(this); // notify overlay added overlayChanged(overlay, SequenceEventType.ADDED); return true; } /** * Remove an overlay from the sequence. */ public boolean removeOverlay(Overlay overlay) { boolean result; synchronized (overlays) { result = overlays.remove(overlay); } if (result) { // remove listener overlay.removeOverlayListener(this); // notify overlay removed overlayChanged(overlay, SequenceEventType.REMOVED); } return result; } /** * Returns the VolumetricImage at position t */ public VolumetricImage getVolumetricImage(int t) { synchronized (volumetricImages) { return volumetricImages.get(Integer.valueOf(t)); } } /** * Returns the first VolumetricImage */ protected VolumetricImage getFirstVolumetricImage() { final Entry<Integer, VolumetricImage> entry; synchronized (volumetricImages) { entry = volumetricImages.firstEntry(); } if (entry != null) return entry.getValue(); return null; } /** * Returns the last VolumetricImage */ protected VolumetricImage getLastVolumetricImage() { final Entry<Integer, VolumetricImage> entry; synchronized (volumetricImages) { entry = volumetricImages.lastEntry(); } if (entry != null) return entry.getValue(); return null; } /** * Add an empty volumetricImage at last index + 1 */ public VolumetricImage addVolumetricImage() { return setVolumetricImage(getSizeT()); } /** * Add an empty volumetricImage at t position */ protected VolumetricImage setVolumetricImage(int t) { // remove old volumetric image if any removeAllImages(t); final VolumetricImage volImg = new VolumetricImage(this); synchronized (volumetricImages) { volumetricImages.put(Integer.valueOf(t), volImg); } return volImg; } /** * Add a volumetricImage at t position<br> * It actually create a new volumetricImage and add it to the sequence<br> * The new created volumetricImage is returned */ public VolumetricImage addVolumetricImage(int t, VolumetricImage volImg) { if (volImg != null) { final VolumetricImage result; beginUpdate(); try { // get new volumetric image (remove old one if any) result = setVolumetricImage(t); for (Entry<Integer, IcyBufferedImage> entry : volImg.getImages().entrySet()) setImage(t, entry.getKey().intValue(), entry.getValue()); } finally { endUpdate(); } return result; } return null; } /** * @deprecated Use {@link #removeAllImages(int)} instead. */ @Deprecated public boolean removeVolumetricImage(int t) { return removeAllImages(t); } /** * Returns the last image of VolumetricImage[t] */ public IcyBufferedImage getLastImage(int t) { final VolumetricImage volImg = getVolumetricImage(t); if (volImg != null) return volImg.getLastImage(); return null; } /** * Returns the first image of first VolumetricImage */ public IcyBufferedImage getFirstImage() { final VolumetricImage volImg = getFirstVolumetricImage(); if (volImg != null) return volImg.getFirstImage(); return null; } /** * Returns the first non null image if exist */ public IcyBufferedImage getFirstNonNullImage() { synchronized (volumetricImages) { for (VolumetricImage volImg : volumetricImages.values()) { final IcyBufferedImage img = volImg.getFirstNonNullImage(); if (img != null) return img; } } return null; } /** * Returns the last image of last VolumetricImage */ public IcyBufferedImage getLastImage() { final VolumetricImage volImg = getLastVolumetricImage(); if (volImg != null) return volImg.getLastImage(); return null; } /** * Returns a single component image corresponding to the component c of the image * at time t and depth z.<br> * This actually create a new image which share its data with internal image * so any modifications to one affect the other.<br> * if <code>(c == -1)</code> then this method is equivalent to {@link #getImage(int, int)}<br> * if <code>((c == 0) || (sizeC == 1))</code> then this method is equivalent to {@link #getImage(int, int)}<br> * if <code>((c < 0) || (c >= sizeC))</code> then it returns <code>null</code> * * @see IcyBufferedImageUtil#extractChannel(IcyBufferedImage, int) * @since version 1.0.3.3b */ @Override public IcyBufferedImage getImage(int t, int z, int c) { final IcyBufferedImage src = getImage(t, z); if ((src == null) || (c == -1)) return src; return src.getImage(c); } /** * Returns image at time t and depth z */ @Override public IcyBufferedImage getImage(int t, int z) { final VolumetricImage volImg = getVolumetricImage(t); if (volImg != null) return volImg.getImage(z); return null; } /** * Returns all images at specified t position */ public ArrayList<IcyBufferedImage> getImages(int t) { final VolumetricImage volImg = getVolumetricImage(t); if (volImg != null) return volImg.getAllImage(); return new ArrayList<IcyBufferedImage>(); } /** * Returns all images of sequence in [ZT] order:<br> * * <pre> * T=0 Z=0 * T=0 Z=1 * T=0 Z=2 * ... * T=1 Z=0 * ... * </pre> */ public ArrayList<IcyBufferedImage> getAllImage() { final ArrayList<IcyBufferedImage> result = new ArrayList<IcyBufferedImage>(); synchronized (volumetricImages) { for (VolumetricImage volImg : volumetricImages.values()) result.addAll(volImg.getAllImage()); } return result; } /** * Add an image to the specified VolumetricImage at the specified z location */ protected void setImage(VolumetricImage volImg, int z, BufferedImage image) throws IllegalArgumentException { if (volImg != null) { // not the same image ? if (volImg.getImage(z) != image) { // this is different from removeImage as we don't remove empty VolumetricImage if (image == null) volImg.removeImage(z); else { IcyBufferedImage icyImg; // convert to icyImage if needed if (image instanceof IcyBufferedImage) icyImg = (IcyBufferedImage) image; else icyImg = IcyBufferedImage.createFrom(image); // possible type change ? final boolean typeChange = (colorModel == null) || isEmpty() || ((getNumImage() == 1) && (volImg.getImage(z) != null)); // not changing type and not compatible if (!typeChange && !isCompatible(icyImg)) throw new IllegalArgumentException("Sequence.setImage : image is not compatible !"); // we want to share the same color space for all the sequence: // colormap eats a lot of memory so it's better to keep one global and we never // use colormap for single image anyway. But it's important to preserve the colormodel for each // image though as it store the channel bounds informations. if (colorModel != null) icyImg.getIcyColorModel().setColorSpace(colorModel.getIcyColorSpace()); // apply this parameter from sequence parameter icyImg.setAutoUpdateChannelBounds(getAutoUpdateChannelBounds()); // set image volImg.setImage(z, icyImg); } } } } /** * Set an image at the specified position.<br/> * Note that the image will be transformed in IcyBufferedImage internally if needed * * @param t * T position * @param z * Z position * @param image * the image to set */ public void setImage(int t, int z, BufferedImage image) throws IllegalArgumentException { final boolean volImgCreated; if (image == null) return; VolumetricImage volImg = getVolumetricImage(t); if (volImg == null) { volImg = setVolumetricImage(t); volImgCreated = true; } else volImgCreated = false; try { // set image setImage(volImg, z, image); } catch (IllegalArgumentException e) { // image set failed ? remove empty image list if needed if (volImgCreated) removeAllImages(t); // throw exception throw e; } } /** * Add an image (image is added in Z dimension).<br> * This method is equivalent to <code>setImage(max(getSizeT() - 1, 0), getSizeZ(t), image)</code> */ public void addImage(BufferedImage image) throws IllegalArgumentException { final int t = Math.max(getSizeT() - 1, 0); setImage(t, getSizeZ(t), image); } /** * Add an image at specified T position.<br> * This method is equivalent to <code>setImage(t, getSizeZ(t), image)</code> */ public void addImage(int t, BufferedImage image) throws IllegalArgumentException { setImage(t, getSizeZ(t), image); } /** * Remove the image at the specified position. */ public boolean removeImage(int t, int z) { final VolumetricImage volImg = getVolumetricImage(t); if (volImg != null) { final boolean result; beginUpdate(); try { result = volImg.removeImage(z); // empty ? if (volImg.isEmpty()) // remove it removeAllImages(t); } finally { endUpdate(); } return result; } return false; } /** * Remove all images at position <code>t</code> */ public boolean removeAllImages(int t) { final VolumetricImage volImg; synchronized (volumetricImages) { volImg = volumetricImages.remove(Integer.valueOf(t)); } // we do manual clear to dispatch events correctly if (volImg != null) volImg.clear(); return volImg != null; } /** * Remove all images */ public void removeAllImages() { beginUpdate(); try { synchronized (volumetricImages) { while (!volumetricImages.isEmpty()) { final VolumetricImage volImg = volumetricImages.pollFirstEntry().getValue(); // we do manual clear to dispatch events correctly if (volImg != null) volImg.clear(); } } } finally { endUpdate(); } } /** * @deprecated Use {@link #removeAllImages(int)} instead. */ @Deprecated public boolean removeAllImage(int t) { return removeAllImages(t); } /** * @deprecated Use {@link #removeAllImages()} instead. */ @Deprecated public void removeAllImage() { removeAllImages(); } /** * Remove empty element of image list */ public void packImageList() { beginUpdate(); try { synchronized (volumetricImages) { for (Entry<Integer, VolumetricImage> entry : volumetricImages.entrySet()) { final VolumetricImage volImg = entry.getValue(); final int t = entry.getKey().intValue(); if (volImg == null) { removeAllImages(t); } else { // pack the list volImg.pack(); // empty ? --> remove it if (volImg.isEmpty()) removeAllImages(t); } } } } finally { endUpdate(); } } /** * return the number of loaded image */ public int getNumImage() { int result = 0; synchronized (volumetricImages) { for (VolumetricImage volImg : volumetricImages.values()) if (volImg != null) result += volImg.getNumImage(); } return result; } /** * return true if no image in sequence */ public boolean isEmpty() { synchronized (volumetricImages) { for (VolumetricImage volImg : volumetricImages.values()) if ((volImg != null) && (!volImg.isEmpty())) return false; } return true; } /** * Returns true if the sequence uses default attributed name */ public boolean isDefaultName() { return getName().startsWith(DEFAULT_NAME); } /** * Returns true is the specified channel uses default attributed name */ public boolean isDefaultChannelName(int index) { return StringUtil.equals(getChannelName(index), getDefaultChannelName(index)); } /** * Returns the number of volumetricImage in the sequence<br> * Use getSizeT instead * * @see #getSizeT * @deprecated */ @Deprecated public int getLength() { return getSizeT(); } /** * return the number of volumetricImage in the sequence */ @Override public int getSizeT() { synchronized (volumetricImages) { if (volumetricImages.isEmpty()) return 0; return volumetricImages.lastKey().intValue() + 1; } } /** * Returns the global number of z stack in the sequence. * Use getSizeZ instead * * @see #getSizeZ * @deprecated */ @Deprecated public int getDepth() { return getSizeZ(); } /** * Returns the global number of z stack in the sequence. */ @Override public int getSizeZ() { final int sizeT = getSizeT(); int maxZ = 0; for (int i = 0; i < sizeT; i++) maxZ = Math.max(maxZ, getSizeZ(i)); return maxZ; } /** * Returns the number of z stack of the volumetricImage[t]. */ public int getSizeZ(int t) { // t = -1 means global Z size if (t == -1) return getSizeZ(); final VolumetricImage volImg = getVolumetricImage(t); if (volImg != null) return volImg.getSize(); return 0; } /** * Returns the number of component/channel/band per image.<br> * Use getSizeC instead * * @see #getSizeC * @deprecated */ @Deprecated public int getNumComponents() { return getSizeC(); } /** * Returns the number of component/channel/band per image */ @Override public int getSizeC() { if (colorModel != null) return colorModel.getNumComponents(); return 0; } /** * Same as {@link #getSizeY()} */ public int getHeight() { return getSizeY(); } /** * Returns the height of the sequence (0 if the sequence contains no image). */ @Override public int getSizeY() { final IcyBufferedImage img = getFirstNonNullImage(); if (img != null) return img.getHeight(); return 0; } /** * Same as {@link #getSizeX()} */ public int getWidth() { return getSizeX(); } /** * Returns the width of the sequence (0 if the sequence contains no image). */ @Override public int getSizeX() { final IcyBufferedImage img = getFirstNonNullImage(); if (img != null) return img.getWidth(); return 0; } /** * Returns the size of the specified dimension */ public int getSize(DimensionId dim) { switch (dim) { case X: return getSizeX(); case Y: return getSizeY(); case C: return getSizeC(); case Z: return getSizeZ(); case T: return getSizeT(); default: case NULL: return 0; } } /** * Returns 2D dimension of sequence {sizeX, sizeY} */ public Dimension getDimension2D() { return new Dimension(getSizeX(), getSizeY()); } /** * Returns 5D dimension of sequence {sizeX, sizeY, sizeZ, sizeT, sizeC} */ public Dimension5D.Integer getDimension5D() { return new Dimension5D.Integer(getSizeX(), getSizeY(), getSizeZ(), getSizeT(), getSizeC()); } /** * @deprecated Use {@link #getDimension2D()} instead. */ @Deprecated public Dimension getDimension() { return getDimension2D(); } /** * Returns 2D bounds of sequence {0, 0, sizeX, sizeY} * * @see #getDimension2D() */ public Rectangle getBounds2D() { return new Rectangle(getSizeX(), getSizeY()); } /** * Returns 5D bounds of sequence {0, 0, 0, 0, 0, sizeX, sizeY, sizeZ, sizeT, sizeC} * * @see #getDimension5D() */ public Rectangle5D.Integer getBounds5D() { return new Rectangle5D.Integer(0, 0, 0, 0, 0, getSizeX(), getSizeY(), getSizeZ(), getSizeT(), getSizeC()); } /** * @deprecated Use {@link #getBounds2D()} instead */ @Deprecated public Rectangle getBounds() { return getBounds2D(); } /** * Returns the number of sample.<br> * This is equivalent to<br> * <code>getSizeX() * getSizeY() * getSizeC() * getSizeZ() * getSizeT()</code> */ public int getNumSample() { return getSizeX() * getSizeY() * getSizeC() * getSizeZ() * getSizeT(); } /** * Test if the specified image is compatible with current loaded images in sequence */ public boolean isCompatible(IcyBufferedImage image) { if ((colorModel == null) || isEmpty()) return true; return (image.getWidth() == getWidth()) && (image.getHeight() == getHeight()) && isCompatible(image.getIcyColorModel()); } /** * Test if the specified colorModel is compatible with sequence colorModel */ public boolean isCompatible(IcyColorModel cm) { // test that colorModel are compatible if (colorModel == null) return true; return colorModel.isCompatible(cm); } /** * Returns true if specified LUT is compatible with sequence LUT */ public boolean isLutCompatible(LUT lut) { IcyColorModel cm = colorModel; // not yet defined ? use default one if (cm == null) cm = IcyColorModel.createInstance(); return lut.isCompatible(cm); } /** * Returns the colorModel */ public IcyColorModel getColorModel() { return colorModel; } /** * Same as {@link #createCompatibleLUT()} */ public LUT getDefaultLUT() { return createCompatibleLUT(); } /** * Returns <code>true</code> if a user LUT has be defined for this sequence. */ public boolean hasUserLUT() { return (userLut != null); } /** * Returns the users LUT.<br> * If user LUT is not defined then a new default LUT is returned. * * @see #getDefaultLUT() */ public LUT getUserLUT() { // color model not anymore compatible with user LUT --> reset user LUT if ((userLut != null) && (colorModel != null) && !userLut.isCompatible(colorModel)) userLut = null; if (userLut == null) return getDefaultLUT(); return userLut; } /** * Sets the user LUT (saved in XML persistent metadata). */ public void setUserLUT(LUT lut) { if ((colorModel == null) || lut.isCompatible(colorModel)) userLut = lut; } /** * Creates and returns the default LUT for this sequence.<br> * If the sequence is empty it returns a default ARGB LUT. */ public LUT createCompatibleLUT() { final IcyColorModel result; // not yet defined ? use default one if (colorModel == null) result = IcyColorModel.createInstance(); else result = IcyColorModel.createInstance(colorModel, true, true); return new LUT(result); } /** * Get the default colormap for the specified channel * * @param channel * channel we want to set the colormap * @see #getColorMap(int) */ public IcyColorMap getDefaultColorMap(int channel) { if (colorModel != null) return colorModel.getColorMap(channel); return getDefaultLUT().getLutChannel(channel).getColorMap(); } /** * Set the default colormap for the specified channel * * @param channel * channel we want to set the colormap * @param map * source colormap to copy * @param setAlpha * also copy the alpha information * @see #getDefaultColorMap(int) */ public void setDefaultColormap(int channel, IcyColorMap map, boolean setAlpha) { if (colorModel != null) colorModel.setColorMap(channel, map, setAlpha); } /** * Set the default colormap for the specified channel * * @param channel * channel we want to set the colormap * @param map * source colormap to copy * @see #getDefaultColorMap(int) */ public void setDefaultColormap(int channel, IcyColorMap map) { setDefaultColormap(channel, map, map.isAlpha()); } /** * Get the user colormap for the specified channel.<br> * User colormap is saved in the XML persistent data and reloaded when opening the Sequence. * * @param channel * channel we want to set the colormap * @see #getDefaultColorMap(int) */ public IcyColorMap getColorMap(int channel) { final LUT lut = getUserLUT(); if (channel < lut.getNumChannel()) return lut.getLutChannel(channel).getColorMap(); return null; } /** * Set the user colormap for the specified channel.<br> * User colormap is saved in the XML persistent data and reloaded when opening the Sequence. * * @param channel * channel we want to set the colormap * @param map * source colormap to copy * @param setAlpha * also copy the alpha information * @see #getColorMap(int) */ public void setColormap(int channel, IcyColorMap map, boolean setAlpha) { final LUT lut = getUserLUT(); // we want to preserve the custom colormap if (userLut == null) userLut = lut; if (channel < lut.getNumChannel()) lut.getLutChannel(channel).setColorMap(map, setAlpha); } /** * Set the user colormap for the specified channel.<br> * User colormap is saved in the XML persistent data and reloaded when opening the Sequence. * * @param channel * channel we want to set the colormap * @param map * source colormap to copy * @see #getColorMap(int) */ public void setColormap(int channel, IcyColorMap map) { setColormap(channel, map, map.isAlpha()); } /** * Returns the data type of sequence */ public DataType getDataType_() { // assume unsigned byte by default if (colorModel == null) // preserve UNDEFINED here for backward compatibility (Math Operation for instance) return DataType.UNDEFINED; return colorModel.getDataType_(); } /** * Returns the data type of sequence * * @deprecated use {@link #getDataType_()} instead */ @Deprecated public int getDataType() { if (colorModel == null) return TypeUtil.TYPE_UNDEFINED; return colorModel.getDataType(); } /** * Returns true if this is a float data type sequence */ public boolean isFloatDataType() { return getDataType_().isFloat(); } /** * Returns true if this is a signed data type sequence */ public boolean isSignedDataType() { return getDataType_().isSigned(); } /** * Internal use only. */ private static double[][] adjustBounds(double[][] curBounds, double[][] bounds) { if (bounds == null) return curBounds; for (int comp = 0; comp < bounds.length; comp++) { final double[] compBounds = bounds[comp]; final double[] curCompBounds = curBounds[comp]; if (curCompBounds[0] < compBounds[0]) compBounds[0] = curCompBounds[0]; if (curCompBounds[1] > compBounds[1]) compBounds[1] = curCompBounds[1]; } return bounds; } /** * Recalculate all image channels bounds (min and max values).<br> * Internal use only. */ protected void recalculateAllImageChannelsBounds() { // nothing to do... if ((colorModel == null) || isEmpty()) return; final List<VolumetricImage> volumes = getAllVolumetricImage(); beginUpdate(); try { // recalculate images bounds (automatically update sequence bounds with event) for (VolumetricImage volImg : volumes) for (IcyBufferedImage img : volImg.getAllImage()) img.updateChannelsBounds(); } finally { endUpdate(); } } /** * Update channels bounds (min and max values)<br> * At this point we assume images has correct channels bounds information.<br> * Internal use only. */ protected void internalUpdateChannelsBounds() { // nothing to do... if ((colorModel == null) || isEmpty()) return; double[][] bounds; bounds = null; // recalculate bounds from all images synchronized (volumetricImages) { for (VolumetricImage volImg : volumetricImages.values()) { for (IcyBufferedImage img : volImg.getAllImage()) { if (img != null) bounds = adjustBounds(img.getChannelsTypeBounds(), bounds); } } } // set new computed bounds colorModel.setComponentsAbsBounds(bounds); bounds = null; // recalculate user bounds from all images synchronized (volumetricImages) { for (VolumetricImage volImg : volumetricImages.values()) { for (IcyBufferedImage img : volImg.getAllImage()) { if (img != null) bounds = adjustBounds(img.getChannelsBounds(), bounds); } } } // set new computed bounds colorModel.setComponentsUserBounds(bounds); } /** * Update channels bounds (min and max values).<br> * * @param forceRecalculation * If true we force all images channels bounds recalculation (this can take sometime). <br> * You can left this flag to false if sequence images have their bounds updated (which * should be the case by default). */ public void updateChannelsBounds(boolean forceRecalculation) { // force calculation of all images bounds if (forceRecalculation) recalculateAllImageChannelsBounds(); // then update sequence bounds internalUpdateChannelsBounds(); } /** * Update channels bounds (min and max values).<br> * All images channels bounds are recalculated (this can take sometime). */ public void updateChannelsBounds() { // force recalculation updateChannelsBounds(true); } /** * @deprecated Use {@link #updateChannelsBounds(boolean)} instead. */ @Deprecated public void updateComponentsBounds(boolean forceRecalculation, boolean adjustByteToo) { updateChannelsBounds(forceRecalculation); } /** * @deprecated Use {@link #updateChannelsBounds(boolean)} instead. */ @Deprecated public void updateComponentsBounds(boolean forceRecalculation) { updateChannelsBounds(forceRecalculation); } /** * @deprecated Use {@link #updateChannelsBounds(boolean)} instead. */ @Deprecated public void updateComponentsBounds() { // force recalculation updateChannelsBounds(true); } /** * Get the data type minimum value. */ public double getDataTypeMin() { return getDataType_().getMinValue(); } /** * Get the data type maximum value. */ public double getDataTypeMax() { return getDataType_().getMaxValue(); } /** * Get data type bounds (min and max values). */ public double[] getDataTypeBounds() { return new double[] {getDataTypeMin(), getDataTypeMax()}; } /** * Get the preferred data type minimum value in the whole sequence for the specified channel. */ public double getChannelTypeMin(int channel) { if (colorModel == null) return 0d; return colorModel.getComponentAbsMinValue(channel); } /** * Get the preferred data type maximum value in the whole sequence for the specified channel. */ public double getChannelTypeMax(int channel) { if (colorModel == null) return 0d; return colorModel.getComponentAbsMaxValue(channel); } /** * Get the preferred data type bounds (min and max values) in the whole sequence for the * specified channel. */ public double[] getChannelTypeBounds(int channel) { if (colorModel == null) return new double[] {0d, 0d}; return colorModel.getComponentAbsBounds(channel); } /** * Get the preferred data type bounds (min and max values) in the whole sequence for all * channels. */ public double[][] getChannelsTypeBounds() { final int sizeC = getSizeC(); final double[][] result = new double[sizeC][]; for (int c = 0; c < sizeC; c++) result[c] = getChannelTypeBounds(c); return result; } /** * Get the global preferred data type bounds (min and max values) for all channels. */ public double[] getChannelsGlobalTypeBounds() { final int sizeC = getSizeC(); final double[] result = getChannelTypeBounds(0); for (int c = 1; c < sizeC; c++) { final double[] bounds = getChannelTypeBounds(c); result[0] = Math.min(bounds[0], result[0]); result[1] = Math.max(bounds[1], result[1]); } return result; } /** * @deprecated Use {@link #getChannelsGlobalTypeBounds()} instead */ @Deprecated public double[] getChannelTypeGlobalBounds() { return getChannelsGlobalTypeBounds(); } /** * @deprecated Use {@link #getChannelTypeGlobalBounds()} instead. */ @Deprecated public double[] getGlobalChannelTypeBounds() { return getChannelTypeGlobalBounds(); } /** * @deprecated Use {@link #getChannelTypeMin(int)} instead. */ @Deprecated public double getComponentAbsMinValue(int component) { return getChannelTypeMin(component); } /** * @deprecated Use {@link #getChannelTypeMax(int)} instead. */ @Deprecated public double getComponentAbsMaxValue(int component) { return getChannelTypeMax(component); } /** * @deprecated Use {@link #getChannelTypeBounds(int)} instead. */ @Deprecated public double[] getComponentAbsBounds(int component) { return getChannelTypeBounds(component); } /** * @deprecated Use {@link #getChannelsTypeBounds()} instead. */ @Deprecated public double[][] getComponentsAbsBounds() { return getChannelsTypeBounds(); } /** * @deprecated Use {@link #getChannelsGlobalTypeBounds()} instead. */ @Deprecated public double[] getGlobalComponentAbsBounds() { return getChannelsGlobalTypeBounds(); } /** * Get the minimum value in the whole sequence for the specified channel. */ public double getChannelMin(int channel) { if (colorModel == null) return 0d; return colorModel.getComponentUserMinValue(channel); } /** * Get maximum value in the whole sequence for the specified channel. */ public double getChannelMax(int channel) { if (colorModel == null) return 0d; return colorModel.getComponentUserMaxValue(channel); } /** * Get bounds (min and max values) in the whole sequence for the specified channel. */ public double[] getChannelBounds(int channel) { if (colorModel == null) return new double[] {0d, 0d}; return colorModel.getComponentUserBounds(channel); } /** * Get bounds (min and max values) in the whole sequence for all channels. */ public double[][] getChannelsBounds() { final int sizeC = getSizeC(); final double[][] result = new double[sizeC][]; for (int c = 0; c < sizeC; c++) result[c] = getChannelBounds(c); return result; } /** * Get global bounds (min and max values) in the whole sequence for all channels. */ public double[] getChannelsGlobalBounds() { final int sizeC = getSizeC(); final double[] result = new double[2]; result[0] = Double.MAX_VALUE; result[1] = -Double.MAX_VALUE; for (int c = 0; c < sizeC; c++) { final double[] bounds = getChannelBounds(c); if (bounds[0] < result[0]) result[0] = bounds[0]; if (bounds[1] > result[1]) result[1] = bounds[1]; } return result; } /** * @deprecated Use {@link #getChannelMin(int)} instead. */ @Deprecated public double getComponentUserMinValue(int component) { return getChannelMin(component); } /** * @deprecated Use {@link #getChannelMax(int)} instead. */ @Deprecated public double getComponentUserMaxValue(int component) { return getChannelMax(component); } /** * @deprecated Use {@link #getChannelBounds(int)} instead. */ @Deprecated public double[] getComponentUserBounds(int component) { return getChannelBounds(component); } /** * @deprecated Use {@link #getChannelsBounds()} instead. */ @Deprecated public double[][] getComponentsUserBounds() { return getChannelsBounds(); } /** * Returns a direct reference to 4D byte array data [T][Z][C][XY] */ public Object getDataXYCZT() { switch (getDataType_().getJavaType()) { case BYTE: return getDataXYCZTAsByte(); case SHORT: return getDataXYCZTAsShort(); case INT: return getDataXYCZTAsInt(); case FLOAT: return getDataXYCZTAsFloat(); case DOUBLE: return getDataXYCZTAsDouble(); default: return null; } } /** * Returns a direct reference to 3D byte array data [Z][C][XY] for specified t */ public Object getDataXYCZ(int t) { switch (getDataType_().getJavaType()) { case BYTE: return getDataXYCZAsByte(t); case SHORT: return getDataXYCZAsShort(t); case INT: return getDataXYCZAsInt(t); case FLOAT: return getDataXYCZAsFloat(t); case DOUBLE: return getDataXYCZAsDouble(t); default: return null; } } /** * Returns a direct reference to 2D byte array data [C][XY] for specified t, z */ public Object getDataXYC(int t, int z) { final IcyBufferedImage img = getImage(t, z); if (img != null) return img.getDataXYC(); return null; } /** * Returns a direct reference to 1D byte array data [XY] for specified t, z, c */ public Object getDataXY(int t, int z, int c) { final IcyBufferedImage img = getImage(t, z); if (img != null) return img.getDataXY(c); return null; } /** * Returns the data value located at position (t, z, c, y, x) as double.<br> * It returns 0 if value is not found. */ public double getData(int t, int z, int c, int y, int x) { final IcyBufferedImage img = getImage(t, z); if (img != null) return img.getData(x, y, c); return 0d; } /** * Returns a direct reference to 3D byte array data [T][Z][XY] for specified c */ public Object getDataXYZT(int c) { switch (getDataType_().getJavaType()) { case BYTE: return getDataXYZTAsByte(c); case SHORT: return getDataXYZTAsShort(c); case INT: return getDataXYZTAsInt(c); case FLOAT: return getDataXYZTAsFloat(c); case DOUBLE: return getDataXYZTAsDouble(c); default: return null; } } /** * Returns a direct reference to 2D byte array data [Z][XY] for specified t, c */ public Object getDataXYZ(int t, int c) { switch (getDataType_().getJavaType()) { case BYTE: return getDataXYZAsByte(t, c); case SHORT: return getDataXYZAsShort(t, c); case INT: return getDataXYZAsInt(t, c); case FLOAT: return getDataXYZAsFloat(t, c); case DOUBLE: return getDataXYZAsDouble(t, c); default: return null; } } /** * Returns a 1D array data copy [XYCZT] of internal 4D array data [T][Z][C][XY] */ public Object getDataCopyXYCZT() { return getDataCopyXYCZT(null, 0); } /** * Returns a 1D array data copy [XYCZT] of internal 4D array data [T][Z][C][XY]<br> * If (out != null) then it's used to store result at the specified offset */ public Object getDataCopyXYCZT(Object out, int off) { switch (getDataType_().getJavaType()) { case BYTE: return getDataCopyXYCZTAsByte((byte[]) out, off); case SHORT: return getDataCopyXYCZTAsShort((short[]) out, off); case INT: return getDataCopyXYCZTAsInt((int[]) out, off); case FLOAT: return getDataCopyXYCZTAsFloat((float[]) out, off); case DOUBLE: return getDataCopyXYCZTAsDouble((double[]) out, off); default: return null; } } /** * Returns a 1D array data copy [XYCZ] of internal 3D array data [Z][C][XY] for specified t */ public Object getDataCopyXYCZ(int t) { return getDataCopyXYCZ(t, null, 0); } /** * Returns a 1D array data copy [XYCZ] of internal 3D array data [Z][C][XY] for specified t<br> * If (out != null) then it's used to store result at the specified offset */ public Object getDataCopyXYCZ(int t, Object out, int off) { switch (getDataType_().getJavaType()) { case BYTE: return getDataCopyXYCZAsByte(t, (byte[]) out, off); case SHORT: return getDataCopyXYCZAsShort(t, (short[]) out, off); case INT: return getDataCopyXYCZAsInt(t, (int[]) out, off); case FLOAT: return getDataCopyXYCZAsFloat(t, (float[]) out, off); case DOUBLE: return getDataCopyXYCZAsDouble(t, (double[]) out, off); default: return null; } } /** * Returns a 1D array data copy [XYC] of internal 2D array data [C][XY] for specified t, z */ public Object getDataCopyXYC(int t, int z) { return getDataCopyXYC(t, z, null, 0); } /** * Returns a 1D array data copy [XYC] of internal 2D array data [C][XY] for specified t, z<br> * If (out != null) then it's used to store result at the specified offset */ public Object getDataCopyXYC(int t, int z, Object out, int off) { final IcyBufferedImage img = getImage(t, z); if (img != null) return img.getDataCopyXYC(out, off); return out; } /** * Returns a 1D array data copy [XY] of internal 1D array data [XY] for specified t, z, c */ public Object getDataCopyXY(int t, int z, int c) { return getDataCopyXY(t, z, c, null, 0); } /** * Returns a 1D array data copy [XY] of internal 1D array data [XY] for specified t, z, c<br> * If (out != null) then it's used to store result at the specified offset */ public Object getDataCopyXY(int t, int z, int c, Object out, int off) { final IcyBufferedImage img = getImage(t, z); if (img != null) return img.getDataCopyXY(c, out, off); return out; } /** * Returns a 1D array data copy [CXYZT] of internal 4D array data [T][Z][C][XY] */ public Object getDataCopyCXYZT() { return getDataCopyCXYZT(null, 0); } /** * Returns a 1D array data copy [CXYZT] of internal 4D array data [T][Z][C][XY]<br> * If (out != null) then it's used to store result at the specified offset */ public Object getDataCopyCXYZT(Object out, int off) { switch (getDataType_().getJavaType()) { case BYTE: return getDataCopyCXYZTAsByte((byte[]) out, off); case SHORT: return getDataCopyCXYZTAsShort((short[]) out, off); case INT: return getDataCopyCXYZTAsInt((int[]) out, off); case FLOAT: return getDataCopyCXYZTAsFloat((float[]) out, off); case DOUBLE: return getDataCopyCXYZTAsDouble((double[]) out, off); default: return null; } } /** * Returns a 1D array data copy [CXYZ] of internal 3D array data [Z][C][XY] for specified t */ public Object getDataCopyCXYZ(int t) { return getDataCopyCXYZ(t, null, 0); } /** * Returns a 1D array data copy [CXYZ] of internal 3D array data [Z][C][XY] for specified t<br> * If (out != null) then it's used to store result at the specified offset */ public Object getDataCopyCXYZ(int t, Object out, int off) { switch (getDataType_().getJavaType()) { case BYTE: return getDataCopyCXYZAsByte(t, (byte[]) out, off); case SHORT: return getDataCopyCXYZAsShort(t, (short[]) out, off); case INT: return getDataCopyCXYZAsInt(t, (int[]) out, off); case FLOAT: return getDataCopyCXYZAsFloat(t, (float[]) out, off); case DOUBLE: return getDataCopyCXYZAsDouble(t, (double[]) out, off); default: return null; } } /** * Returns a 1D array data copy [CXY] of internal 2D array data [C][XY] for specified t, z */ public Object getDataCopyCXY(int t, int z) { return getDataCopyCXY(t, z, null, 0); } /** * Returns a 1D array data copy [CXY] of internal 2D array data [C][XY] for specified t, z<br> * If (out != null) then it's used to store result at the specified offset */ public Object getDataCopyCXY(int t, int z, Object out, int off) { final IcyBufferedImage img = getImage(t, z); if (img != null) return img.getDataCopyCXY(out, off); return out; } /** * Returns a 1D array data copy [C] of specified t, z, x, y */ public Object getDataCopyC(int t, int z, int x, int y) { return getDataCopyC(t, z, x, y, null, 0); } /** * Returns a 1D array data copy [C] of specified t, z, x, y<br> * If (out != null) then it's used to store result at the specified offset */ public Object getDataCopyC(int t, int z, int x, int y, Object out, int off) { final IcyBufferedImage img = getImage(t, z); if (img != null) return img.getDataCopyC(x, y, out, off); return out; } /** * Returns a 1D array data copy [XYZT] of internal 3D array data [T][Z][XY] for specified c */ public Object getDataCopyXYZT(int c) { return getDataCopyXYZT(c, null, 0); } /** * Returns a 1D array data copy [XYZT] of internal 3D array data [T][Z][XY] for specified c<br> * If (out != null) then it's used to store result at the specified offset */ public Object getDataCopyXYZT(int c, Object out, int off) { switch (getDataType_().getJavaType()) { case BYTE: return getDataCopyXYZTAsByte(c, (byte[]) out, off); case SHORT: return getDataCopyXYZTAsShort(c, (short[]) out, off); case INT: return getDataCopyXYZTAsInt(c, (int[]) out, off); case FLOAT: return getDataCopyXYZTAsFloat(c, (float[]) out, off); case DOUBLE: return getDataCopyXYZTAsDouble(c, (double[]) out, off); default: return null; } } /** * Returns a 1D array data copy [XYZ] of internal 2D array data [Z][XY] for specified t, c */ public Object getDataCopyXYZ(int t, int c) { return getDataCopyXYZ(t, c, null, 0); } /** * Returns a 1D array data copy [XYZ] of internal 2D array data [Z][XY] for specified t, c<br> * If (out != null) then it's used to store result at the specified offset */ public Object getDataCopyXYZ(int t, int c, Object out, int off) { switch (getDataType_().getJavaType()) { case BYTE: return getDataCopyXYZAsByte(t, c, (byte[]) out, off); case SHORT: return getDataCopyXYZAsShort(t, c, (short[]) out, off); case INT: return getDataCopyXYZAsInt(t, c, (int[]) out, off); case FLOAT: return getDataCopyXYZAsFloat(t, c, (float[]) out, off); case DOUBLE: return getDataCopyXYZAsDouble(t, c, (double[]) out, off); default: return null; } } /** * Returns a direct reference to 4D byte array data [T][Z][C][XY] */ public byte[][][][] getDataXYCZTAsByte() { final int sizeT = getSizeT(); final byte[][][][] result = new byte[sizeT][][][]; for (int t = 0; t < sizeT; t++) result[t] = getDataXYCZAsByte(t); return result; } /** * Returns a direct reference to 4D byte array data [T][Z][C][XY] */ public short[][][][] getDataXYCZTAsShort() { final int sizeT = getSizeT(); final short[][][][] result = new short[sizeT][][][]; for (int t = 0; t < sizeT; t++) result[t] = getDataXYCZAsShort(t); return result; } /** * Returns a direct reference to 4D byte array data [T][Z][C][XY] */ public int[][][][] getDataXYCZTAsInt() { final int sizeT = getSizeT(); final int[][][][] result = new int[sizeT][][][]; for (int t = 0; t < sizeT; t++) result[t] = getDataXYCZAsInt(t); return result; } /** * Returns a direct reference to 4D byte array data [T][Z][C][XY] */ public float[][][][] getDataXYCZTAsFloat() { final int sizeT = getSizeT(); final float[][][][] result = new float[sizeT][][][]; for (int t = 0; t < sizeT; t++) result[t] = getDataXYCZAsFloat(t); return result; } /** * Returns a direct reference to 4D byte array data [T][Z][C][XY] */ public double[][][][] getDataXYCZTAsDouble() { final int sizeT = getSizeT(); final double[][][][] result = new double[sizeT][][][]; for (int t = 0; t < sizeT; t++) result[t] = getDataXYCZAsDouble(t); return result; } /** * Returns a direct reference to 3D byte array data [Z][C][XY] for specified t */ public byte[][][] getDataXYCZAsByte(int t) { final int sizeZ = getSizeZ(t); final byte[][][] result = new byte[sizeZ][][]; for (int z = 0; z < sizeZ; z++) result[z] = getDataXYCAsByte(t, z); return result; } /** * Returns a direct reference to 3D byte array data [Z][C][XY] for specified t */ public short[][][] getDataXYCZAsShort(int t) { final int sizeZ = getSizeZ(t); final short[][][] result = new short[sizeZ][][]; for (int z = 0; z < sizeZ; z++) result[z] = getDataXYCAsShort(t, z); return result; } /** * Returns a direct reference to 3D byte array data [Z][C][XY] for specified t */ public int[][][] getDataXYCZAsInt(int t) { final int sizeZ = getSizeZ(t); final int[][][] result = new int[sizeZ][][]; for (int z = 0; z < sizeZ; z++) result[z] = getDataXYCAsInt(t, z); return result; } /** * Returns a direct reference to 3D byte array data [Z][C][XY] for specified t */ public float[][][] getDataXYCZAsFloat(int t) { final int sizeZ = getSizeZ(t); final float[][][] result = new float[sizeZ][][]; for (int z = 0; z < sizeZ; z++) result[z] = getDataXYCAsFloat(t, z); return result; } /** * Returns a direct reference to 3D byte array data [Z][C][XY] for specified t */ public double[][][] getDataXYCZAsDouble(int t) { final int sizeZ = getSizeZ(t); final double[][][] result = new double[sizeZ][][]; for (int z = 0; z < sizeZ; z++) result[z] = getDataXYCAsDouble(t, z); return result; } /** * Returns a direct reference to 2D byte array data [C][XY] for specified t, z */ public byte[][] getDataXYCAsByte(int t, int z) { final IcyBufferedImage img = getImage(t, z); if (img != null) return img.getDataXYCAsByte(); return null; } /** * Returns a direct reference to 2D byte array data [C][XY] for specified t, z */ public short[][] getDataXYCAsShort(int t, int z) { final IcyBufferedImage img = getImage(t, z); if (img != null) return img.getDataXYCAsShort(); return null; } /** * Returns a direct reference to 2D byte array data [C][XY] for specified t, z */ public int[][] getDataXYCAsInt(int t, int z) { final IcyBufferedImage img = getImage(t, z); if (img != null) return img.getDataXYCAsInt(); return null; } /** * Returns a direct reference to 2D byte array data [C][XY] for specified t, z */ public float[][] getDataXYCAsFloat(int t, int z) { final IcyBufferedImage img = getImage(t, z); if (img != null) return img.getDataXYCAsFloat(); return null; } /** * Returns a direct reference to 2D byte array data [C][XY] for specified t, z */ public double[][] getDataXYCAsDouble(int t, int z) { final IcyBufferedImage img = getImage(t, z); if (img != null) return img.getDataXYCAsDouble(); return null; } /** * Returns a direct reference to 1D byte array data [XY] for specified t, z, c */ public byte[] getDataXYAsByte(int t, int z, int c) { final IcyBufferedImage img = getImage(t, z); if (img != null) return img.getDataXYAsByte(c); return null; } /** * Returns a direct reference to 1D byte array data [XY] for specified t, z, c */ public short[] getDataXYAsShort(int t, int z, int c) { final IcyBufferedImage img = getImage(t, z); if (img != null) return img.getDataXYAsShort(c); return null; } /** * Returns a direct reference to 1D byte array data [XY] for specified t, z, c */ public int[] getDataXYAsInt(int t, int z, int c) { final IcyBufferedImage img = getImage(t, z); if (img != null) return img.getDataXYAsInt(c); return null; } /** * Returns a direct reference to 1D byte array data [XY] for specified t, z, c */ public float[] getDataXYAsFloat(int t, int z, int c) { final IcyBufferedImage img = getImage(t, z); if (img != null) return img.getDataXYAsFloat(c); return null; } /** * Returns a direct reference to 1D byte array data [XY] for specified t, z, c */ public double[] getDataXYAsDouble(int t, int z, int c) { final IcyBufferedImage img = getImage(t, z); if (img != null) return img.getDataXYAsDouble(c); return null; } /** * Returns a direct reference to 3D byte array data [T][Z][XY] for specified c */ public byte[][][] getDataXYZTAsByte(int c) { final int sizeT = getSizeT(); final byte[][][] result = new byte[sizeT][][]; for (int t = 0; t < sizeT; t++) result[t] = getDataXYZAsByte(t, c); return result; } /** * Returns a direct reference to 3D byte array data [T][Z][XY] for specified c */ public short[][][] getDataXYZTAsShort(int c) { final int sizeT = getSizeT(); final short[][][] result = new short[sizeT][][]; for (int t = 0; t < sizeT; t++) result[t] = getDataXYZAsShort(t, c); return result; } /** * Returns a direct reference to 3D byte array data [T][Z][XY] for specified c */ public int[][][] getDataXYZTAsInt(int c) { final int sizeT = getSizeT(); final int[][][] result = new int[sizeT][][]; for (int t = 0; t < sizeT; t++) result[t] = getDataXYZAsInt(t, c); return result; } /** * Returns a direct reference to 3D byte array data [T][Z][XY] for specified c */ public float[][][] getDataXYZTAsFloat(int c) { final int sizeT = getSizeT(); final float[][][] result = new float[sizeT][][]; for (int t = 0; t < sizeT; t++) result[t] = getDataXYZAsFloat(t, c); return result; } /** * Returns a direct reference to 3D byte array data [T][Z][XY] for specified c */ public double[][][] getDataXYZTAsDouble(int c) { final int sizeT = getSizeT(); final double[][][] result = new double[sizeT][][]; for (int t = 0; t < sizeT; t++) result[t] = getDataXYZAsDouble(t, c); return result; } /** * Returns a direct reference to 2D byte array data [Z][XY] for specified t, c */ public byte[][] getDataXYZAsByte(int t, int c) { final int sizeZ = getSizeZ(t); final byte[][] result = new byte[sizeZ][]; for (int z = 0; z < sizeZ; z++) result[z] = getDataXYAsByte(t, z, c); return result; } /** * Returns a direct reference to 2D byte array data [Z][XY] for specified t, c */ public short[][] getDataXYZAsShort(int t, int c) { final int sizeZ = getSizeZ(t); final short[][] result = new short[sizeZ][]; for (int z = 0; z < sizeZ; z++) result[z] = getDataXYAsShort(t, z, c); return result; } /** * Returns a direct reference to 2D byte array data [Z][XY] for specified t, c */ public int[][] getDataXYZAsInt(int t, int c) { final int sizeZ = getSizeZ(t); final int[][] result = new int[sizeZ][]; for (int z = 0; z < sizeZ; z++) result[z] = getDataXYAsInt(t, z, c); return result; } /** * Returns a direct reference to 2D byte array data [Z][XY] for specified t, c */ public float[][] getDataXYZAsFloat(int t, int c) { final int sizeZ = getSizeZ(t); final float[][] result = new float[sizeZ][]; for (int z = 0; z < sizeZ; z++) result[z] = getDataXYAsFloat(t, z, c); return result; } /** * Returns a direct reference to 2D byte array data [Z][XY] for specified t, c */ public double[][] getDataXYZAsDouble(int t, int c) { final int sizeZ = getSizeZ(t); final double[][] result = new double[sizeZ][]; for (int z = 0; z < sizeZ; z++) result[z] = getDataXYAsDouble(t, z, c); return result; } /** * Returns a 1D array data copy [XYCZT] of internal 4D array data [T][Z][C][XY] */ public byte[] getDataCopyXYCZTAsByte() { return getDataCopyXYCZTAsByte(null, 0); } /** * Returns a 1D array data copy [XYCZT] of internal 4D array data [T][Z][C][XY]<br> * If (out != null) then it's used to store result at the specified offset */ public byte[] getDataCopyXYCZTAsByte(byte[] out, int off) { final long sizeT = getSizeT(); final long len = (long) getSizeX() * (long) getSizeY() * (long) getSizeC() * (long) getSizeZ(); if ((len * sizeT) >= Integer.MAX_VALUE) throw new TooLargeArrayException(); final byte[] result = Array1DUtil.allocIfNull(out, (int) (len * sizeT)); int offset = off; for (int t = 0; t < sizeT; t++) { getDataCopyXYCZAsByte(t, result, offset); offset += len; } return result; } /** * Returns a 1D array data copy [XYCZT] of internal 4D array data [T][Z][C][XY] */ public short[] getDataCopyXYCZTAsShort() { return getDataCopyXYCZTAsShort(null, 0); } /** * Returns a 1D array data copy [XYCZT] of internal 4D array data [T][Z][C][XY]<br> * If (out != null) then it's used to store result at the specified offset */ public short[] getDataCopyXYCZTAsShort(short[] out, int off) { final long sizeT = getSizeT(); final long len = (long) getSizeX() * (long) getSizeY() * (long) getSizeC() * (long) getSizeZ(); if ((len * sizeT) >= Integer.MAX_VALUE) throw new TooLargeArrayException(); final short[] result = Array1DUtil.allocIfNull(out, (int) (len * sizeT)); int offset = off; for (int t = 0; t < sizeT; t++) { getDataCopyXYCZAsShort(t, result, offset); offset += len; } return result; } /** * Returns a 1D array data copy [XYCZT] of internal 4D array data [T][Z][C][XY] */ public int[] getDataCopyXYCZTAsInt() { return getDataCopyXYCZTAsInt(null, 0); } /** * Returns a 1D array data copy [XYCZT] of internal 4D array data [T][Z][C][XY]<br> * If (out != null) then it's used to store result at the specified offset */ public int[] getDataCopyXYCZTAsInt(int[] out, int off) { final long sizeT = getSizeT(); final long len = (long) getSizeX() * (long) getSizeY() * (long) getSizeC() * (long) getSizeZ(); if ((len * sizeT) >= Integer.MAX_VALUE) throw new TooLargeArrayException(); final int[] result = Array1DUtil.allocIfNull(out, (int) (len * sizeT)); int offset = off; for (int t = 0; t < sizeT; t++) { getDataCopyXYCZAsInt(t, result, offset); offset += len; } return result; } /** * Returns a 1D array data copy [XYCZT] of internal 4D array data [T][Z][C][XY] */ public float[] getDataCopyXYCZTAsFloat() { return getDataCopyXYCZTAsFloat(null, 0); } /** * Returns a 1D array data copy [XYCZT] of internal 4D array data [T][Z][C][XY]<br> * If (out != null) then it's used to store result at the specified offset */ public float[] getDataCopyXYCZTAsFloat(float[] out, int off) { final long sizeT = getSizeT(); final long len = (long) getSizeX() * (long) getSizeY() * (long) getSizeC() * (long) getSizeZ(); if ((len * sizeT) >= Integer.MAX_VALUE) throw new TooLargeArrayException(); final float[] result = Array1DUtil.allocIfNull(out, (int) (len * sizeT)); int offset = off; for (int t = 0; t < sizeT; t++) { getDataCopyXYCZAsFloat(t, result, offset); offset += len; } return result; } /** * Returns a 1D array data copy [XYCZT] of internal 4D array data [T][Z][C][XY] */ public double[] getDataCopyXYCZTAsDouble() { return getDataCopyXYCZTAsDouble(null, 0); } /** * Returns a 1D array data copy [XYCZT] of internal 4D array data [T][Z][C][XY]<br> * If (out != null) then it's used to store result at the specified offset */ public double[] getDataCopyXYCZTAsDouble(double[] out, int off) { final long sizeT = getSizeT(); final long len = (long) getSizeX() * (long) getSizeY() * (long) getSizeC() * (long) getSizeZ(); if ((len * sizeT) >= Integer.MAX_VALUE) throw new TooLargeArrayException(); final double[] result = Array1DUtil.allocIfNull(out, (int) (len * sizeT)); int offset = off; for (int t = 0; t < sizeT; t++) { getDataCopyXYCZAsDouble(t, result, offset); offset += len; } return result; } /** * Returns a 1D array data copy [XYCZ] of internal 3D array data [Z][C][XY] for specified t */ public byte[] getDataCopyXYCZAsByte(int t) { return getDataCopyXYCZAsByte(t, null, 0); } /** * Returns a 1D array data copy [XYCZ] of internal 3D array data [Z][C][XY] for specified t<br> * If (out != null) then it's used to store result at the specified offset */ public byte[] getDataCopyXYCZAsByte(int t, byte[] out, int off) { final long sizeZ = getSizeZ(); final long len = (long) getSizeX() * (long) getSizeY() * (long) getSizeC(); if ((len * sizeZ) >= Integer.MAX_VALUE) throw new TooLargeArrayException(); final byte[] result = Array1DUtil.allocIfNull(out, (int) (len * sizeZ)); int offset = off; for (int z = 0; z < sizeZ; z++) { getDataCopyXYCAsByte(t, z, result, offset); offset += len; } return result; } /** * Returns a 1D array data copy [XYCZ] of internal 3D array data [Z][C][XY] for specified t */ public short[] getDataCopyXYCZAsShort(int t) { return getDataCopyXYCZAsShort(t, null, 0); } /** * Returns a 1D array data copy [XYCZ] of internal 3D array data [Z][C][XY] for specified t<br> * If (out != null) then it's used to store result at the specified offset */ public short[] getDataCopyXYCZAsShort(int t, short[] out, int off) { final long sizeZ = getSizeZ(); final long len = (long) getSizeX() * (long) getSizeY() * (long) getSizeC(); if ((len * sizeZ) >= Integer.MAX_VALUE) throw new TooLargeArrayException(); final short[] result = Array1DUtil.allocIfNull(out, (int) (len * sizeZ)); int offset = off; for (int z = 0; z < sizeZ; z++) { getDataCopyXYCAsShort(t, z, result, offset); offset += len; } return result; } /** * Returns a 1D array data copy [XYCZ] of internal 3D array data [Z][C][XY] for specified t */ public int[] getDataCopyXYCZAsInt(int t) { return getDataCopyXYCZAsInt(t, null, 0); } /** * Returns a 1D array data copy [XYCZ] of internal 3D array data [Z][C][XY] for specified t<br> * If (out != null) then it's used to store result at the specified offset */ public int[] getDataCopyXYCZAsInt(int t, int[] out, int off) { final long sizeZ = getSizeZ(); final long len = (long) getSizeX() * (long) getSizeY() * (long) getSizeC(); if ((len * sizeZ) >= Integer.MAX_VALUE) throw new TooLargeArrayException(); final int[] result = Array1DUtil.allocIfNull(out, (int) (len * sizeZ)); int offset = off; for (int z = 0; z < sizeZ; z++) { getDataCopyXYCAsInt(t, z, result, offset); offset += len; } return result; } /** * Returns a 1D array data copy [XYCZ] of internal 3D array data [Z][C][XY] for specified t */ public float[] getDataCopyXYCZAsFloat(int t) { return getDataCopyXYCZAsFloat(t, null, 0); } /** * Returns a 1D array data copy [XYCZ] of internal 3D array data [Z][C][XY] for specified t<br> * If (out != null) then it's used to store result at the specified offset */ public float[] getDataCopyXYCZAsFloat(int t, float[] out, int off) { final long sizeZ = getSizeZ(); final long len = (long) getSizeX() * (long) getSizeY() * (long) getSizeC(); if ((len * sizeZ) >= Integer.MAX_VALUE) throw new TooLargeArrayException(); final float[] result = Array1DUtil.allocIfNull(out, (int) (len * sizeZ)); int offset = off; for (int z = 0; z < sizeZ; z++) { getDataCopyXYCAsFloat(t, z, result, offset); offset += len; } return result; } /** * Returns a 1D array data copy [XYCZ] of internal 3D array data [Z][C][XY] for specified t */ public double[] getDataCopyXYCZAsDouble(int t) { return getDataCopyXYCZAsDouble(t, null, 0); } /** * Returns a 1D array data copy [XYCZ] of internal 3D array data [Z][C][XY] for specified t<br> * If (out != null) then it's used to store result at the specified offset */ public double[] getDataCopyXYCZAsDouble(int t, double[] out, int off) { final long sizeZ = getSizeZ(); final long len = (long) getSizeX() * (long) getSizeY() * (long) getSizeC(); if ((len * sizeZ) >= Integer.MAX_VALUE) throw new TooLargeArrayException(); final double[] result = Array1DUtil.allocIfNull(out, (int) (len * sizeZ)); int offset = off; for (int z = 0; z < sizeZ; z++) { getDataCopyXYCAsDouble(t, z, result, offset); offset += len; } return result; } /** * Returns a 1D array data copy [XYC] of internal 2D array data [C][XY] for specified t, z */ public byte[] getDataCopyXYCAsByte(int t, int z) { return getDataCopyXYCAsByte(t, z, null, 0); } /** * Returns a 1D array data copy [XYC] of internal 2D array data [C][XY] for specified t, z<br> * If (out != null) then it's used to store result at the specified offset */ public byte[] getDataCopyXYCAsByte(int t, int z, byte[] out, int off) { final IcyBufferedImage img = getImage(t, z); if (img != null) return img.getDataCopyXYCAsByte(out, off); return out; } /** * Returns a 1D array data copy [XYC] of internal 2D array data [C][XY] for specified t, z */ public short[] getDataCopyXYCAsShort(int t, int z) { return getDataCopyXYCAsShort(t, z, null, 0); } /** * Returns a 1D array data copy [XYC] of internal 2D array data [C][XY] for specified t, z<br> * If (out != null) then it's used to store result at the specified offset */ public short[] getDataCopyXYCAsShort(int t, int z, short[] out, int off) { final IcyBufferedImage img = getImage(t, z); if (img != null) return img.getDataCopyXYCAsShort(out, off); return out; } /** * Returns a 1D array data copy [XYC] of internal 2D array data [C][XY] for specified t, z */ public int[] getDataCopyXYCAsInt(int t, int z) { return getDataCopyXYCAsInt(t, z, null, 0); } /** * Returns a 1D array data copy [XYC] of internal 2D array data [C][XY] for specified t, z<br> * If (out != null) then it's used to store result at the specified offset */ public int[] getDataCopyXYCAsInt(int t, int z, int[] out, int off) { final IcyBufferedImage img = getImage(t, z); if (img != null) return img.getDataCopyXYCAsInt(out, off); return out; } /** * Returns a 1D array data copy [XYC] of internal 2D array data [C][XY] for specified t, z */ public float[] getDataCopyXYCAsFloat(int t, int z) { return getDataCopyXYCAsFloat(t, z, null, 0); } /** * Returns a 1D array data copy [XYC] of internal 2D array data [C][XY] for specified t, z<br> * If (out != null) then it's used to store result at the specified offset */ public float[] getDataCopyXYCAsFloat(int t, int z, float[] out, int off) { final IcyBufferedImage img = getImage(t, z); if (img != null) return img.getDataCopyXYCAsFloat(out, off); return out; } /** * Returns a 1D array data copy [XYC] of internal 2D array data [C][XY] for specified t, z */ public double[] getDataCopyXYCAsDouble(int t, int z) { return getDataCopyXYCAsDouble(t, z, null, 0); } /** * Returns a 1D array data copy [XYC] of internal 2D array data [C][XY] for specified t, z<br> * If (out != null) then it's used to store result at the specified offset */ public double[] getDataCopyXYCAsDouble(int t, int z, double[] out, int off) { final IcyBufferedImage img = getImage(t, z); if (img != null) return img.getDataCopyXYCAsDouble(out, off); return out; } /** * Returns a 1D array data copy [XY] of internal 1D array data [XY] for specified t, z, c */ public byte[] getDataCopyXYAsByte(int t, int z, int c) { return getDataCopyXYAsByte(t, z, c, null, 0); } /** * Returns a 1D array data copy [XY] of internal 1D array data [XY] for specified t, z, c<br> * If (out != null) then it's used to store result at the specified offset */ public byte[] getDataCopyXYAsByte(int t, int z, int c, byte[] out, int off) { final IcyBufferedImage img = getImage(t, z); if (img != null) return img.getDataCopyXYAsByte(c, out, off); return out; } /** * Returns a 1D array data copy [XY] of internal 1D array data [XY] for specified t, z, c */ public short[] getDataCopyXYAsShort(int t, int z, int c) { return getDataCopyXYAsShort(t, z, c, null, 0); } /** * Returns a 1D array data copy [XY] of internal 1D array data [XY] for specified t, z, c<br> * If (out != null) then it's used to store result at the specified offset */ public short[] getDataCopyXYAsShort(int t, int z, int c, short[] out, int off) { final IcyBufferedImage img = getImage(t, z); if (img != null) return img.getDataCopyXYAsShort(c, out, off); return out; } /** * Returns a 1D array data copy [XY] of internal 1D array data [XY] for specified t, z, c */ public int[] getDataCopyXYAsInt(int t, int z, int c) { return getDataCopyXYAsInt(t, z, c, null, 0); } /** * Returns a 1D array data copy [XY] of internal 1D array data [XY] for specified t, z, c<br> * If (out != null) then it's used to store result at the specified offset */ public int[] getDataCopyXYAsInt(int t, int z, int c, int[] out, int off) { final IcyBufferedImage img = getImage(t, z); if (img != null) return img.getDataCopyXYAsInt(c, out, off); return out; } /** * Returns a 1D array data copy [XY] of internal 1D array data [XY] for specified t, z, c */ public float[] getDataCopyXYAsFloat(int t, int z, int c) { return getDataCopyXYAsFloat(t, z, c, null, 0); } /** * Returns a 1D array data copy [XY] of internal 1D array data [XY] for specified t, z, c<br> * If (out != null) then it's used to store result at the specified offset */ public float[] getDataCopyXYAsFloat(int t, int z, int c, float[] out, int off) { final IcyBufferedImage img = getImage(t, z); if (img != null) return img.getDataCopyXYAsFloat(c, out, off); return out; } /** * Returns a 1D array data copy [XY] of internal 1D array data [XY] for specified t, z, c */ public double[] getDataCopyXYAsDouble(int t, int z, int c) { return getDataCopyXYAsDouble(t, z, c, null, 0); } /** * Returns a 1D array data copy [XY] of internal 1D array data [XY] for specified t, z, c<br> * If (out != null) then it's used to store result at the specified offset */ public double[] getDataCopyXYAsDouble(int t, int z, int c, double[] out, int off) { final IcyBufferedImage img = getImage(t, z); if (img != null) return img.getDataCopyXYAsDouble(c, out, off); return out; } /** * Returns a 1D array data copy [CXYZT] of internal 4D array data [T][Z][C][XY] */ public byte[] getDataCopyCXYZTAsByte() { return getDataCopyCXYZTAsByte(null, 0); } /** * Returns a 1D array data copy [CXYZT] of internal 4D array data [T][Z][C][XY]<br> * If (out != null) then it's used to store result at the specified offset */ public byte[] getDataCopyCXYZTAsByte(byte[] out, int off) { final long sizeT = getSizeT(); final long len = (long) getSizeX() * (long) getSizeY() * (long) getSizeC() * (long) getSizeZ(); if ((len * sizeT) >= Integer.MAX_VALUE) throw new TooLargeArrayException(); final byte[] result = Array1DUtil.allocIfNull(out, (int) (len * sizeT)); int offset = off; for (int t = 0; t < sizeT; t++) { getDataCopyCXYZAsByte(t, result, offset); offset += len; } return result; } /** * Returns a 1D array data copy [CXYZT] of internal 4D array data [T][Z][C][XY] */ public short[] getDataCopyCXYZTAsShort() { return getDataCopyCXYZTAsShort(null, 0); } /** * Returns a 1D array data copy [CXYZT] of internal 4D array data [T][Z][C][XY]<br> * If (out != null) then it's used to store result at the specified offset */ public short[] getDataCopyCXYZTAsShort(short[] out, int off) { final long sizeT = getSizeT(); final long len = (long) getSizeX() * (long) getSizeY() * (long) getSizeC() * (long) getSizeZ(); if ((len * sizeT) >= Integer.MAX_VALUE) throw new TooLargeArrayException(); final short[] result = Array1DUtil.allocIfNull(out, (int) (len * sizeT)); int offset = off; for (int t = 0; t < sizeT; t++) { getDataCopyCXYZAsShort(t, result, offset); offset += len; } return result; } /** * Returns a 1D array data copy [CXYZT] of internal 4D array data [T][Z][C][XY] */ public int[] getDataCopyCXYZTAsInt() { return getDataCopyCXYZTAsInt(null, 0); } /** * Returns a 1D array data copy [CXYZT] of internal 4D array data [T][Z][C][XY]<br> * If (out != null) then it's used to store result at the specified offset */ public int[] getDataCopyCXYZTAsInt(int[] out, int off) { final long sizeT = getSizeT(); final long len = (long) getSizeX() * (long) getSizeY() * (long) getSizeC() * (long) getSizeZ(); if ((len * sizeT) >= Integer.MAX_VALUE) throw new TooLargeArrayException(); final int[] result = Array1DUtil.allocIfNull(out, (int) (len * sizeT)); int offset = off; for (int t = 0; t < sizeT; t++) { getDataCopyCXYZAsInt(t, result, offset); offset += len; } return result; } /** * Returns a 1D array data copy [CXYZT] of internal 4D array data [T][Z][C][XY] */ public float[] getDataCopyCXYZTAsFloat() { return getDataCopyCXYZTAsFloat(null, 0); } /** * Returns a 1D array data copy [CXYZT] of internal 4D array data [T][Z][C][XY]<br> * If (out != null) then it's used to store result at the specified offset */ public float[] getDataCopyCXYZTAsFloat(float[] out, int off) { final long sizeT = getSizeT(); final long len = (long) getSizeX() * (long) getSizeY() * (long) getSizeC() * (long) getSizeZ(); if ((len * sizeT) >= Integer.MAX_VALUE) throw new TooLargeArrayException(); final float[] result = Array1DUtil.allocIfNull(out, (int) (len * sizeT)); int offset = off; for (int t = 0; t < sizeT; t++) { getDataCopyCXYZAsFloat(t, result, offset); offset += len; } return result; } /** * Returns a 1D array data copy [CXYZT] of internal 4D array data [T][Z][C][XY] */ public double[] getDataCopyCXYZTAsDouble() { return getDataCopyCXYZTAsDouble(null, 0); } /** * Returns a 1D array data copy [CXYZT] of internal 4D array data [T][Z][C][XY]<br> * If (out != null) then it's used to store result at the specified offset */ public double[] getDataCopyCXYZTAsDouble(double[] out, int off) { final long sizeT = getSizeT(); final long len = (long) getSizeX() * (long) getSizeY() * (long) getSizeC() * (long) getSizeZ(); if ((len * sizeT) >= Integer.MAX_VALUE) throw new TooLargeArrayException(); final double[] result = Array1DUtil.allocIfNull(out, (int) (len * sizeT)); int offset = off; for (int t = 0; t < sizeT; t++) { getDataCopyCXYZAsDouble(t, result, offset); offset += len; } return result; } /** * Returns a 1D array data copy [CXYZ] of internal 3D array data [Z][C][XY] for specified t */ public byte[] getDataCopyCXYZAsByte(int t) { return getDataCopyCXYZAsByte(t, null, 0); } /** * Returns a 1D array data copy [CXYZ] of internal 3D array data [Z][C][XY] for specified t<br> * If (out != null) then it's used to store result at the specified offset */ public byte[] getDataCopyCXYZAsByte(int t, byte[] out, int off) { final long sizeZ = getSizeZ(); final long len = (long) getSizeX() * (long) getSizeY() * (long) getSizeC(); if ((len * sizeZ) >= Integer.MAX_VALUE) throw new TooLargeArrayException(); final byte[] result = Array1DUtil.allocIfNull(out, (int) (len * sizeZ)); int offset = off; for (int z = 0; z < sizeZ; z++) { getDataCopyCXYAsByte(t, z, result, offset); offset += len; } return result; } /** * Returns a 1D array data copy [CXYZ] of internal 3D array data [Z][C][XY] for specified t */ public short[] getDataCopyCXYZAsShort(int t) { return getDataCopyCXYZAsShort(t, null, 0); } /** * Returns a 1D array data copy [CXYZ] of internal 3D array data [Z][C][XY] for specified t<br> * If (out != null) then it's used to store result at the specified offset */ public short[] getDataCopyCXYZAsShort(int t, short[] out, int off) { final long sizeZ = getSizeZ(); final long len = (long) getSizeX() * (long) getSizeY() * (long) getSizeC(); if ((len * sizeZ) >= Integer.MAX_VALUE) throw new TooLargeArrayException(); final short[] result = Array1DUtil.allocIfNull(out, (int) (len * sizeZ)); int offset = off; for (int z = 0; z < sizeZ; z++) { getDataCopyCXYAsShort(t, z, result, offset); offset += len; } return result; } /** * Returns a 1D array data copy [CXYZ] of internal 3D array data [Z][C][XY] for specified t */ public int[] getDataCopyCXYZAsInt(int t) { return getDataCopyCXYZAsInt(t, null, 0); } /** * Returns a 1D array data copy [CXYZ] of internal 3D array data [Z][C][XY] for specified t<br> * If (out != null) then it's used to store result at the specified offset */ public int[] getDataCopyCXYZAsInt(int t, int[] out, int off) { final long sizeZ = getSizeZ(); final long len = (long) getSizeX() * (long) getSizeY() * (long) getSizeC(); if ((len * sizeZ) >= Integer.MAX_VALUE) throw new TooLargeArrayException(); final int[] result = Array1DUtil.allocIfNull(out, (int) (len * sizeZ)); int offset = off; for (int z = 0; z < sizeZ; z++) { getDataCopyCXYAsInt(t, z, result, offset); offset += len; } return result; } /** * Returns a 1D array data copy [CXYZ] of internal 3D array data [Z][C][XY] for specified t */ public float[] getDataCopyCXYZAsFloat(int t) { return getDataCopyCXYZAsFloat(t, null, 0); } /** * Returns a 1D array data copy [CXYZ] of internal 3D array data [Z][C][XY] for specified t<br> * If (out != null) then it's used to store result at the specified offset */ public float[] getDataCopyCXYZAsFloat(int t, float[] out, int off) { final long sizeZ = getSizeZ(); final long len = (long) getSizeX() * (long) getSizeY() * (long) getSizeC(); if ((len * sizeZ) >= Integer.MAX_VALUE) throw new TooLargeArrayException(); final float[] result = Array1DUtil.allocIfNull(out, (int) (len * sizeZ)); int offset = off; for (int z = 0; z < sizeZ; z++) { getDataCopyCXYAsFloat(t, z, result, offset); offset += len; } return result; } /** * Returns a 1D array data copy [CXYZ] of internal 3D array data [Z][C][XY] for specified t */ public double[] getDataCopyCXYZAsDouble(int t) { return getDataCopyCXYZAsDouble(t, null, 0); } /** * Returns a 1D array data copy [CXYZ] of internal 3D array data [Z][C][XY] for specified t<br> * If (out != null) then it's used to store result at the specified offset */ public double[] getDataCopyCXYZAsDouble(int t, double[] out, int off) { final long sizeZ = getSizeZ(); final long len = (long) getSizeX() * (long) getSizeY() * (long) getSizeC(); if ((len * sizeZ) >= Integer.MAX_VALUE) throw new TooLargeArrayException(); final double[] result = Array1DUtil.allocIfNull(out, (int) (len * sizeZ)); int offset = off; for (int z = 0; z < sizeZ; z++) { getDataCopyCXYAsDouble(t, z, result, offset); offset += len; } return result; } /** * Returns a 1D array data copy [CXY] of internal 2D array data [C][XY] for specified t, z */ public byte[] getDataCopyCXYAsByte(int t, int z) { return getDataCopyCXYAsByte(t, z, null, 0); } /** * Returns a 1D array data copy [CXY] of internal 2D array data [C][XY] for specified t, z<br> * If (out != null) then it's used to store result at the specified offset */ public byte[] getDataCopyCXYAsByte(int t, int z, byte[] out, int off) { final IcyBufferedImage img = getImage(t, z); if (img != null) return img.getDataCopyCXYAsByte(out, off); return out; } /** * Returns a 1D array data copy [CXY] of internal 2D array data [C][XY] for specified t, z */ public short[] getDataCopyCXYAsShort(int t, int z) { return getDataCopyCXYAsShort(t, z, null, 0); } /** * Returns a 1D array data copy [CXY] of internal 2D array data [C][XY] for specified t, z<br> * If (out != null) then it's used to store result at the specified offset */ public short[] getDataCopyCXYAsShort(int t, int z, short[] out, int off) { final IcyBufferedImage img = getImage(t, z); if (img != null) return img.getDataCopyCXYAsShort(out, off); return out; } /** * Returns a 1D array data copy [CXY] of internal 2D array data [C][XY] for specified t, z */ public int[] getDataCopyCXYAsInt(int t, int z) { return getDataCopyCXYAsInt(t, z, null, 0); } /** * Returns a 1D array data copy [CXY] of internal 2D array data [C][XY] for specified t, z<br> * If (out != null) then it's used to store result at the specified offset */ public int[] getDataCopyCXYAsInt(int t, int z, int[] out, int off) { final IcyBufferedImage img = getImage(t, z); if (img != null) return img.getDataCopyCXYAsInt(out, off); return out; } /** * Returns a 1D array data copy [CXY] of internal 2D array data [C][XY] for specified t, z */ public float[] getDataCopyCXYAsFloat(int t, int z) { return getDataCopyCXYAsFloat(t, z, null, 0); } /** * Returns a 1D array data copy [CXY] of internal 2D array data [C][XY] for specified t, z<br> * If (out != null) then it's used to store result at the specified offset */ public float[] getDataCopyCXYAsFloat(int t, int z, float[] out, int off) { final IcyBufferedImage img = getImage(t, z); if (img != null) return img.getDataCopyCXYAsFloat(out, off); return out; } /** * Returns a 1D array data copy [CXY] of internal 2D array data [C][XY] for specified t, z */ public double[] getDataCopyCXYAsDouble(int t, int z) { return getDataCopyCXYAsDouble(t, z, null, 0); } /** * Returns a 1D array data copy [CXY] of internal 2D array data [C][XY] for specified t, z<br> * If (out != null) then it's used to store result at the specified offset */ public double[] getDataCopyCXYAsDouble(int t, int z, double[] out, int off) { final IcyBufferedImage img = getImage(t, z); if (img != null) return img.getDataCopyCXYAsDouble(out, off); return out; } /** * Returns a 1D array data copy [C] of specified t, z, x, y */ public byte[] getDataCopyCAsByte(int t, int z, int x, int y) { return getDataCopyCAsByte(t, z, x, y, null, 0); } /** * Returns a 1D array data copy [C] of specified t, z, x, y<br> * If (out != null) then it's used to store result at the specified offset */ public byte[] getDataCopyCAsByte(int t, int z, int x, int y, byte[] out, int off) { final IcyBufferedImage img = getImage(t, z); if (img != null) return img.getDataCopyCAsByte(x, y, out, off); return out; } /** * Returns a 1D array data copy [C] of specified t, z, x, y */ public short[] getDataCopyCAsShort(int t, int z, int x, int y) { return getDataCopyCAsShort(t, z, x, y, null, 0); } /** * Returns a 1D array data copy [C] of specified t, z, x, y<br> * If (out != null) then it's used to store result at the specified offset */ public short[] getDataCopyCAsShort(int t, int z, int x, int y, short[] out, int off) { final IcyBufferedImage img = getImage(t, z); if (img != null) return img.getDataCopyCAsShort(x, y, out, off); return out; } /** * Returns a 1D array data copy [C] of specified t, z, x, y */ public int[] getDataCopyCAsInt(int t, int z, int x, int y) { return getDataCopyCAsInt(t, z, x, y, null, 0); } /** * Returns a 1D array data copy [C] of specified t, z, x, y<br> * If (out != null) then it's used to store result at the specified offset */ public int[] getDataCopyCAsInt(int t, int z, int x, int y, int[] out, int off) { final IcyBufferedImage img = getImage(t, z); if (img != null) return img.getDataCopyCAsInt(x, y, out, off); return out; } /** * Returns a 1D array data copy [C] of specified t, z, x, y */ public float[] getDataCopyCAsFloat(int t, int z, int x, int y) { return getDataCopyCAsFloat(t, z, x, y, null, 0); } /** * Returns a 1D array data copy [C] of specified t, z, x, y<br> * If (out != null) then it's used to store result at the specified offset */ public float[] getDataCopyCAsFloat(int t, int z, int x, int y, float[] out, int off) { final IcyBufferedImage img = getImage(t, z); if (img != null) return img.getDataCopyCAsFloat(x, y, out, off); return out; } /** * Returns a 1D array data copy [C] of specified t, z, x, y */ public double[] getDataCopyCAsDouble(int t, int z, int x, int y) { return getDataCopyCAsDouble(t, z, x, y, null, 0); } /** * Returns a 1D array data copy [C] of specified t, z, x, y<br> * If (out != null) then it's used to store result at the specified offset */ public double[] getDataCopyCAsDouble(int t, int z, int x, int y, double[] out, int off) { final IcyBufferedImage img = getImage(t, z); if (img != null) return img.getDataCopyCAsDouble(x, y, out, off); return out; } /** * Returns a 1D array data copy [XYZT] of internal 3D array data [T][Z][XY] for specified c */ public byte[] getDataCopyXYZTAsByte(int c) { return getDataCopyXYZTAsByte(c, null, 0); } /** * Returns a 1D array data copy [XYZT] of internal 3D array data [T][Z][XY] for specified c<br> * If (out != null) then it's used to store result at the specified offset */ public byte[] getDataCopyXYZTAsByte(int c, byte[] out, int off) { final long sizeT = getSizeT(); final long len = (long) getSizeX() * (long) getSizeY() * (long) getSizeZ(); if ((len * sizeT) >= Integer.MAX_VALUE) throw new TooLargeArrayException(); final byte[] result = Array1DUtil.allocIfNull(out, (int) (len * sizeT)); int offset = off; for (int t = 0; t < sizeT; t++) { getDataCopyXYZAsByte(t, c, result, offset); offset += len; } return result; } /** * Returns a 1D array data copy [XYZT] of internal 3D array data [T][Z][XY] for specified c */ public short[] getDataCopyXYZTAsShort(int c) { return getDataCopyXYZTAsShort(c, null, 0); } /** * Returns a 1D array data copy [XYZT] of internal 3D array data [T][Z][XY] for specified c<br> * If (out != null) then it's used to store result at the specified offset */ public short[] getDataCopyXYZTAsShort(int c, short[] out, int off) { final long sizeT = getSizeT(); final long len = (long) getSizeX() * (long) getSizeY() * (long) getSizeZ(); if ((len * sizeT) >= Integer.MAX_VALUE) throw new TooLargeArrayException(); final short[] result = Array1DUtil.allocIfNull(out, (int) (len * sizeT)); int offset = off; for (int t = 0; t < sizeT; t++) { getDataCopyXYZAsShort(t, c, result, offset); offset += len; } return result; } /** * Returns a 1D array data copy [XYZT] of internal 3D array data [T][Z][XY] for specified c */ public int[] getDataCopyXYZTAsInt(int c) { return getDataCopyXYZTAsInt(c, null, 0); } /** * Returns a 1D array data copy [XYZT] of internal 3D array data [T][Z][XY] for specified c<br> * If (out != null) then it's used to store result at the specified offset */ public int[] getDataCopyXYZTAsInt(int c, int[] out, int off) { final long sizeT = getSizeT(); final long len = (long) getSizeX() * (long) getSizeY() * (long) getSizeZ(); if ((len * sizeT) >= Integer.MAX_VALUE) throw new TooLargeArrayException(); final int[] result = Array1DUtil.allocIfNull(out, (int) (len * sizeT)); int offset = off; for (int t = 0; t < sizeT; t++) { getDataCopyXYZAsInt(t, c, result, offset); offset += len; } return result; } /** * Returns a 1D array data copy [XYZT] of internal 3D array data [T][Z][XY] for specified c */ public float[] getDataCopyXYZTAsFloat(int c) { return getDataCopyXYZTAsFloat(c, null, 0); } /** * Returns a 1D array data copy [XYZT] of internal 3D array data [T][Z][XY] for specified c<br> * If (out != null) then it's used to store result at the specified offset */ public float[] getDataCopyXYZTAsFloat(int c, float[] out, int off) { final long sizeT = getSizeT(); final long len = (long) getSizeX() * (long) getSizeY() * (long) getSizeZ(); if ((len * sizeT) >= Integer.MAX_VALUE) throw new TooLargeArrayException(); final float[] result = Array1DUtil.allocIfNull(out, (int) (len * sizeT)); int offset = off; for (int t = 0; t < sizeT; t++) { getDataCopyXYZAsFloat(t, c, result, offset); offset += len; } return result; } /** * Returns a 1D array data copy [XYZT] of internal 3D array data [T][Z][XY] for specified c */ public double[] getDataCopyXYZTAsDouble(int c) { return getDataCopyXYZTAsDouble(c, null, 0); } /** * Returns a 1D array data copy [XYZT] of internal 3D array data [T][Z][XY] for specified c<br> * If (out != null) then it's used to store result at the specified offset */ public double[] getDataCopyXYZTAsDouble(int c, double[] out, int off) { final long sizeT = getSizeT(); final long len = (long) getSizeX() * (long) getSizeY() * (long) getSizeZ(); if ((len * sizeT) >= Integer.MAX_VALUE) throw new TooLargeArrayException(); final double[] result = Array1DUtil.allocIfNull(out, (int) (len * sizeT)); int offset = off; for (int t = 0; t < sizeT; t++) { getDataCopyXYZAsDouble(t, c, result, offset); offset += len; } return result; } /** * Returns a 1D array data copy [XYZ] of internal 2D array data [Z][XY] for specified t, c */ public byte[] getDataCopyXYZAsByte(int t, int c) { return getDataCopyXYZAsByte(t, c, null, 0); } /** * Returns a 1D array data copy [XYZ] of internal 2D array data [Z][XY] for specified t, c<br> * If (out != null) then it's used to store result at the specified offset */ public byte[] getDataCopyXYZAsByte(int t, int c, byte[] out, int off) { final long sizeZ = getSizeZ(); final long len = (long) getSizeX() * (long) getSizeY(); if ((len * sizeZ) >= Integer.MAX_VALUE) throw new TooLargeArrayException(); final byte[] result = Array1DUtil.allocIfNull(out, (int) (len * sizeZ)); int offset = off; for (int z = 0; z < sizeZ; z++) { getDataCopyXYAsByte(t, z, c, result, offset); offset += len; } return result; } /** * Returns a 1D array data copy [XYZ] of internal 2D array data [Z][XY] for specified t, c */ public short[] getDataCopyXYZAsShort(int t, int c) { return getDataCopyXYZAsShort(t, c, null, 0); } /** * Returns a 1D array data copy [XYZ] of internal 2D array data [Z][XY] for specified t, c<br> * If (out != null) then it's used to store result at the specified offset */ public short[] getDataCopyXYZAsShort(int t, int c, short[] out, int off) { final long sizeZ = getSizeZ(); final long len = (long) getSizeX() * (long) getSizeY(); if ((len * sizeZ) >= Integer.MAX_VALUE) throw new TooLargeArrayException(); final short[] result = Array1DUtil.allocIfNull(out, (int) (len * sizeZ)); int offset = off; for (int z = 0; z < sizeZ; z++) { getDataCopyXYAsShort(t, z, c, result, offset); offset += len; } return result; } /** * Returns a 1D array data copy [XYZ] of internal 2D array data [Z][XY] for specified t, c */ public int[] getDataCopyXYZAsInt(int t, int c) { return getDataCopyXYZAsInt(t, c, null, 0); } /** * Returns a 1D array data copy [XYZ] of internal 2D array data [Z][XY] for specified t, c<br> * If (out != null) then it's used to store result at the specified offset */ public int[] getDataCopyXYZAsInt(int t, int c, int[] out, int off) { final long sizeZ = getSizeZ(); final long len = (long) getSizeX() * (long) getSizeY(); if ((len * sizeZ) >= Integer.MAX_VALUE) throw new TooLargeArrayException(); final int[] result = Array1DUtil.allocIfNull(out, (int) (len * sizeZ)); int offset = off; for (int z = 0; z < sizeZ; z++) { getDataCopyXYAsInt(t, z, c, result, offset); offset += len; } return result; } /** * Returns a 1D array data copy [XYZ] of internal 2D array data [Z][XY] for specified t, c */ public float[] getDataCopyXYZAsFloat(int t, int c) { return getDataCopyXYZAsFloat(t, c, null, 0); } /** * Returns a 1D array data copy [XYZ] of internal 2D array data [Z][XY] for specified t, c<br> * If (out != null) then it's used to store result at the specified offset */ public float[] getDataCopyXYZAsFloat(int t, int c, float[] out, int off) { final long sizeZ = getSizeZ(); final long len = (long) getSizeX() * (long) getSizeY(); if ((len * sizeZ) >= Integer.MAX_VALUE) throw new TooLargeArrayException(); final float[] result = Array1DUtil.allocIfNull(out, (int) (len * sizeZ)); int offset = off; for (int z = 0; z < sizeZ; z++) { getDataCopyXYAsFloat(t, z, c, result, offset); offset += len; } return result; } /** * Returns a 1D array data copy [XYZ] of internal 2D array data [Z][XY] for specified t, c */ public double[] getDataCopyXYZAsDouble(int t, int c) { return getDataCopyXYZAsDouble(t, c, null, 0); } /** * Returns a 1D array data copy [XYZ] of internal 2D array data [Z][XY] for specified t, c<br> * If (out != null) then it's used to store result at the specified offset */ public double[] getDataCopyXYZAsDouble(int t, int c, double[] out, int off) { final long sizeZ = getSizeZ(); final long len = (long) getSizeX() * (long) getSizeY(); if ((len * sizeZ) >= Integer.MAX_VALUE) throw new TooLargeArrayException(); final double[] result = Array1DUtil.allocIfNull(out, (int) (len * sizeZ)); int offset = off; for (int z = 0; z < sizeZ; z++) { getDataCopyXYAsDouble(t, z, c, result, offset); offset += len; } return result; } /** * @deprecated Uses {@link SequenceUtil#getSubSequence(Sequence, int, int, int, int, int, int, int, int)} instead. */ @Deprecated public Sequence getSubSequence(int startX, int startY, int startZ, int startT, int sizeX, int sizeY, int sizeZ, int sizeT) { return SequenceUtil.getSubSequence(this, startX, startY, startZ, startT, sizeX, sizeY, sizeZ, sizeT); } /** * @deprecated Use {@link SequenceUtil#getCopy(Sequence)} instead. */ @Deprecated public Sequence getCopy() { return SequenceUtil.getCopy(this); } /** * Set all viewer containing this sequence to time t. * * @deprecated Use this piece of code instead :<br> * <code>for(Viewer v: Icy.getMainInterface().getViewers(sequence))</code></br> * <code> v.setT(...)</code> */ @Deprecated public void setT(int t) { for (Viewer viewer : Icy.getMainInterface().getViewers()) if (viewer.getSequence() == this) viewer.setT(t); } /** * Load XML persistent data from file.<br> * This method should only be called once when the sequence has just be loaded from file.<br> * Note that it uses {@link #getFilename()} to define the XML filename so be sure that it is correctly filled before * calling this method. * * @return <code>true</code> if XML data has been correctly loaded, <code>false</code> otherwise. */ public boolean loadXMLData() { return persistent.loadXMLData(); } /** * Synchronize XML data with sequence data :<br> * This function refresh all the meta data and ROIs of the sequence and put it in the current * XML document. */ public void refreshXMLData() { persistent.refreshXMLData(); } /** * Save attached XML data. */ public boolean saveXMLData() { Exception exc = null; int retry = 0; // definitely ugly but the XML parser may throw some exception in multi thread environnement // and we really don't want to lost the sequence metadata ! while (retry < 5) { try { return persistent.saveXMLData(); } catch (Exception e) { exc = e; } retry++; } System.err.println("Error while saving Sequence XML persistent data :"); IcyExceptionHandler.showErrorMessage(exc, true); return false; } /** * Returns true if the specified XML data node exist * * @param name * name of node * @see #getNode(String) */ public Node isNodeExisting(String name) { return persistent.getNode(name); } /** * Get XML data node identified by specified name.<br> * The node is created if needed.</br> * Note that the following node names are reserved: <i>image, name, meta, rois, lut</i></br> * * @param name * name of wanted node * @see #isNodeExisting(String) */ public Node getNode(String name) { final Node result = persistent.getNode(name); if (result == null) return persistent.setNode(name); return result; } /** * @deprecated Use {@link #getNode(String)} instead. */ @Deprecated public Node setNode(String name) { return persistent.setNode(name); } @Override public String toString() { return getName(); } /** * Do common job on "image add" here * * @param image */ public void onImageAdded(IcyBufferedImage image) { // colorModel not yet defined ? if (colorModel == null) // define it from the image colorModel setColorModel(IcyColorModel.createInstance(image.getIcyColorModel(), true, true)); // add listener to image image.addListener(this); // notify changed dataChanged(image, SequenceEventType.ADDED); } /** * Do common job on "image replaced" here */ public void onImageReplaced(IcyBufferedImage oldImage, IcyBufferedImage newImage) { // we replaced the only present image final boolean typeChange = getNumImage() == 1; beginUpdate(); try { if (typeChange) { // colorModel not compatible ? if (!colorModel.isCompatible(newImage.getIcyColorModel())) // define it from the new image colorModel setColorModel(IcyColorModel.createInstance(newImage.getIcyColorModel(), true, true)); // only inform about a type change if sequence sizeX and sizeY changed else if ((oldImage.getSizeX() != newImage.getSizeX()) || (oldImage.getSizeY() != newImage.getSizeY())) typeChanged(); } // TODO: improve cleaning here // need that to avoid memory leak as we manually patch the image colorspace if (colorModel != null) colorModel.getIcyColorSpace().removeListener(oldImage.getIcyColorModel()); // remove listener from old image oldImage.removeListener(this); // notify about old image remove dataChanged(oldImage, SequenceEventType.REMOVED); // add listener to new image newImage.addListener(this); // notify about new image added dataChanged(newImage, SequenceEventType.ADDED); } finally { endUpdate(); } } /** * Do common job on "image remove" here * * @param image */ public void onImageRemoved(IcyBufferedImage image) { // no more image ? --> releasethe global colorModel if (isEmpty()) setColorModel(null); // TODO: improve cleaning here // need that to avoid memory leak as we manually patch the image colorspace if (colorModel != null) colorModel.getIcyColorSpace().removeListener(image.getIcyColorModel()); // remove listener from image image.removeListener(this); // notify changed dataChanged(image, SequenceEventType.REMOVED); } /** * fire change event */ @SuppressWarnings("deprecation") protected void fireChangedEvent(SequenceEvent e) { final List<SequenceListener> cachedListeners = new ArrayList<SequenceListener>(listeners); for (SequenceListener listener : cachedListeners) listener.sequenceChanged(e); // provide backward compatibility for painter if (e.getSourceType() == SequenceEventSourceType.SEQUENCE_OVERLAY) { final Painter painter; if (e.getSource() instanceof OverlayWrapper) painter = ((OverlayWrapper) e.getSource()).getPainter(); else painter = (Painter) e.getSource(); final SequenceEvent event = new SequenceEvent(this, SequenceEventSourceType.SEQUENCE_PAINTER, painter, e.getType(), e.getParam()); for (SequenceListener listener : cachedListeners) listener.sequenceChanged(event); } } /** * fire close event */ protected void fireClosedEvent() { for (SequenceListener listener : new ArrayList<SequenceListener>(listeners)) listener.sequenceClosed(this); } /** * fire model image changed event */ @Override public void fireModelImageChangedEvent() { for (SequenceModelListener listener : new ArrayList<SequenceModelListener>(modelListeners)) listener.imageChanged(); } /** * fire model dimension changed event */ @Override public void fireModelDimensionChangedEvent() { for (SequenceModelListener listener : new ArrayList<SequenceModelListener>(modelListeners)) listener.dimensionChanged(); } public void beginUpdate() { updater.beginUpdate(); } public void endUpdate() { updater.endUpdate(); // update end ? if (!updater.isUpdating()) { // do pending tasks if (channelBoundsInvalid) { channelBoundsInvalid = false; // images channels bounds are valid at this point internalUpdateChannelsBounds(); } } } public boolean isUpdating() { return updater.isUpdating(); } /** * sequence meta has changed */ public void metaChanged(String metaName) { updater.changed(new SequenceEvent(this, SequenceEventSourceType.SEQUENCE_META, metaName)); } /** * sequence meta has changed */ public void metaChanged(String metaName, int param) { updater.changed(new SequenceEvent(this, SequenceEventSourceType.SEQUENCE_META, metaName, null, param)); } /** * sequence type (colorModel, size) changed */ protected void typeChanged() { updater.changed(new SequenceEvent(this, SequenceEventSourceType.SEQUENCE_TYPE)); } /** * sequence colorMap changed */ protected void colormapChanged(IcyColorModel colorModel, int component) { updater.changed(new SequenceEvent(this, SequenceEventSourceType.SEQUENCE_COLORMAP, colorModel, component)); } /** * sequence component bounds changed */ protected void componentBoundsChanged(IcyColorModel colorModel, int component) { updater.changed( new SequenceEvent(this, SequenceEventSourceType.SEQUENCE_COMPONENTBOUNDS, colorModel, component)); } // /** // * @deprecated Use {@link #overlayChanged(Overlay, SequenceEventType)} instead. // */ // @Deprecated // private void painterChanged(Painter painter, SequenceEventType type) // { // updater.changed(new SequenceEvent(this, SequenceEventSourceType.SEQUENCE_PAINTER, painter, // type)); // } /** * @deprecated Use {@link #overlayChanged(Overlay)} instead. */ @Deprecated public void painterChanged(Painter painter) { updater.changed(new SequenceEvent(this, SequenceEventSourceType.SEQUENCE_OVERLAY, getOverlay(painter), SequenceEventType.CHANGED)); // painterChanged(painter, SequenceEventType.CHANGED); } /** * overlay painter has changed */ protected void overlayChanged(Overlay overlay, SequenceEventType type) { updater.changed(new SequenceEvent(this, SequenceEventSourceType.SEQUENCE_OVERLAY, overlay, type)); } /** * Notify specified painter of overlay has changed (the sequence should contains the specified * Overlay) */ public void overlayChanged(Overlay overlay) { if (contains(overlay)) overlayChanged(overlay, SequenceEventType.CHANGED); } /** * Called when an overlay has changed (internal method).<br> * Use {@link #overlayChanged(Overlay)} instead. */ @Override public void overlayChanged(OverlayEvent event) { // only take care about overlay painter change here (need redraw) if (event.getType() == OverlayEventType.PAINTER_CHANGED) overlayChanged(event.getSource(), SequenceEventType.CHANGED); } /** * @deprecated Use {@link #roiChanged(ROI)} method instead. */ @Deprecated public void roiChanged() { final Iterator<ROI> it = rois.iterator(); // send a event for all ROI while (it.hasNext()) roiChanged(it.next(), SequenceEventType.CHANGED); } /** * Notify specified roi has changed (the sequence should contains the specified ROI) */ public void roiChanged(ROI roi) { if (contains(roi)) roiChanged(roi, SequenceEventType.CHANGED); } /** * Notify specified roi has changed */ protected void roiChanged(ROI roi, SequenceEventType type) { updater.changed(new SequenceEvent(this, SequenceEventSourceType.SEQUENCE_ROI, roi, type)); } /** * Data has changed (global change)<br> * Be careful, this implies all component bounds are recalculated, can be heavy ! */ public void dataChanged() { updater.changed(new SequenceEvent(this, SequenceEventSourceType.SEQUENCE_DATA, null)); } /** * data has changed */ protected void dataChanged(IcyBufferedImage image, SequenceEventType type) { updater.changed(new SequenceEvent(this, SequenceEventSourceType.SEQUENCE_DATA, image, type, 0)); } @Override public void colorModelChanged(IcyColorModelEvent e) { switch (e.getType()) { case COLORMAP_CHANGED: colormapChanged(e.getColorModel(), e.getComponent()); break; case SCALER_CHANGED: componentBoundsChanged(e.getColorModel(), e.getComponent()); break; } } @Override public void imageChanged(IcyBufferedImageEvent e) { final IcyBufferedImage image = e.getImage(); switch (e.getType()) { case BOUNDS_CHANGED: // update sequence channel bounds if (autoUpdateChannelBounds) { // updating sequence ? delay update if (isUpdating()) channelBoundsInvalid = true; else // refresh sequence channel bounds from images bounds internalUpdateChannelsBounds(); } break; case COLORMAP_CHANGED: // ignore that, we don't care about image colormap break; case DATA_CHANGED: // image data changed dataChanged(image, SequenceEventType.CHANGED); break; } } @Override public void roiChanged(ROIEvent event) { // notify the ROI has changed roiChanged(event.getSource(), SequenceEventType.CHANGED); } /** * process on sequence change */ @Override public void onChanged(CollapsibleEvent e) { final SequenceEvent event = (SequenceEvent) e; switch (event.getSourceType()) { // do here global process on sequence data change case SEQUENCE_DATA: // automatic channel bounds update enabled if (autoUpdateChannelBounds) { // generic CHANGED event if (event.getSource() == null) // recalculate all images bounds and update sequence bounds updateChannelsBounds(true); else // refresh sequence channel bounds from images bounds internalUpdateChannelsBounds(); } // fire SequenceModel event fireModelImageChangedEvent(); break; // do here global process on sequence type change case SEQUENCE_TYPE: // fire SequenceModel event fireModelDimensionChangedEvent(); break; // do here global process on sequence colormap change case SEQUENCE_COLORMAP: break; // do here global process on sequence component bounds change case SEQUENCE_COMPONENTBOUNDS: break; // do here global process on sequence overlay change case SEQUENCE_OVERLAY: break; // do here global process on sequence ROI change case SEQUENCE_ROI: break; } // notify listener we have changed fireChangedEvent(event); } }