/* * Copyright 2010-2013 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.image; import icy.roi.BooleanMask2D; import icy.roi.ROI; import icy.type.DataIterator; import icy.type.DataType; import icy.type.collection.array.Array1DUtil; import java.awt.Rectangle; import java.util.NoSuchElementException; /** * Image data iterator.<br> * This class permit to use simple iterator to read / write <code>IcyBufferedImage</code> data<br> * as double in XYC <i>([C[Y[X]]])</i> dimension order .<br> * Whatever is the internal {@link DataType} data is returned and set as double.<br> * <b>If the image size or type is modified during iteration the iterator * becomes invalid and can causes exception to happen.</b> * * @author Stephane */ public class ImageDataIterator implements DataIterator { protected final IcyBufferedImage image; protected final DataType dataType; protected int startX, endX; protected int startY, endY; protected final int fixedC, fixedZ, fixedT; protected final boolean inclusive; /** * internals */ protected BooleanMask2D maskXY; protected int x, y; protected boolean done; protected Object data; /** * Create a new ImageData iterator to iterate data through the specified XY region and channel. * * @param image * Image we want to iterate data from * @param XYBounds * XY region to iterate (inclusive). * @param channel * channel (C position) we want to iterate data */ public ImageDataIterator(IcyBufferedImage image, Rectangle XYBounds, int channel) { super(); this.image = image; maskXY = null; fixedZ = 0; fixedT = 0; inclusive = false; if (image != null) { dataType = image.getDataType_(); final Rectangle bounds = XYBounds.intersection(image.getBounds()); startX = bounds.x; endX = (bounds.x + bounds.width) - 1; startY = bounds.y; endY = (bounds.y + bounds.height) - 1; fixedC = channel; } else { dataType = DataType.UNDEFINED; fixedC = 0; } // start iterator reset(); } /** * @deprecated Use {@link #ImageDataIterator(IcyBufferedImage, Rectangle, int)} instead. */ @Deprecated public ImageDataIterator(IcyBufferedImage image, int startX, int endX, int startY, int endY, int startC, int endC) { this(image, new Rectangle(startX, startY, (endX - startX) + 1, (endY - startY) + 1), startC); } /** * @deprecated Use {@link #ImageDataIterator(IcyBufferedImage, Rectangle, int)} instead. */ @Deprecated public ImageDataIterator(IcyBufferedImage image, int startX, int endX, int startY, int endY, int c) { this(image, new Rectangle(startX, startY, (endX - startX) + 1, (endY - startY) + 1), c); } /** * Create a new ImageData iterator to iterate data of specified channel. * * @param image * Image we want to iterate data from * @param c * C position (channel) we want to iterate data */ public ImageDataIterator(IcyBufferedImage image, int c) { this(image, image.getBounds(), c); } /** * @deprecated Use {@link #ImageDataIterator(IcyBufferedImage, int)} instead.<br> * The <code>ImageDataIterator</code> iterate only on single channel data. */ @Deprecated public ImageDataIterator(IcyBufferedImage image) { this(image, image.getBounds(), 0); } /** * Create a new ImageData iterator to iterate data through the specified * <code>BooleanMask2D</code> and C dimension. * * @param image * Image we want to iterate data from * @param maskXY * BooleanMask2D defining the XY region to iterate * @param channel * channel (C position) we want to iterate data */ public ImageDataIterator(IcyBufferedImage image, BooleanMask2D maskXY, int channel) { super(); this.image = image; this.maskXY = maskXY; fixedZ = 0; fixedT = 0; inclusive = false; if (image != null) { dataType = image.getDataType_(); final Rectangle bounds = maskXY.bounds.intersection(image.getBounds()); startX = bounds.x; endX = (bounds.x + bounds.width) - 1; startY = bounds.y; endY = (bounds.y + bounds.height) - 1; fixedC = channel; } else { dataType = DataType.UNDEFINED; fixedC = 0; } // start iterator reset(); } /** * @deprecated Use {@link #ImageDataIterator(IcyBufferedImage, BooleanMask2D, int)} instead */ @Deprecated public ImageDataIterator(IcyBufferedImage image, BooleanMask2D maskXY) { this(image, maskXY, 0); } /** * @deprecated Use {@link #ImageDataIterator(IcyBufferedImage, BooleanMask2D, int)} instead. * You can use the {@link ROI#getBooleanMask2D(int, int, int, boolean)} method to * retrieve the boolean mask from the ROI. */ @Deprecated public ImageDataIterator(IcyBufferedImage image, ROI roi) { this(image, roi.getBooleanMask2D(0, 0, 0, false)); } @Override public void reset() { done = (image == null) || (fixedC < 0) || (fixedC >= image.getSizeC()) || (startY > endY) || (startX > endX); if (!done) { // get data data = image.getDataXY(fixedC); // and set start XY position y = startY; x = startX - 1; // allow to correctly set the XY position with boolean mask next(); } } protected boolean maskContains() { return maskXY.mask[(x - maskXY.bounds.x) + ((y - maskXY.bounds.y) * maskXY.bounds.width)]; } @Override public void next() { internalNext(); // advance while ROI do not contains current point if (maskXY != null) { while (!done && !maskContains()) internalNext(); } } /** * Advance one position. */ protected void internalNext() { if (++x > endX) { x = startX; if (++y > endY) done = true; } } @Override public boolean done() { return done; } @Override public double get() { if (done) throw new NoSuchElementException(null); return Array1DUtil.getValue(data, image.getOffset(x, y), dataType); } @Override public void set(double value) { if (done) throw new NoSuchElementException(null); Array1DUtil.setValue(data, image.getOffset(x, y), dataType, value); } /** * Returns current X position. */ public int getPositionX() { return x; } /** * Returns current Y position. */ public int getPositionY() { return y; } /** * Returns C position (fixed) */ public int getPositionC() { return fixedC; } }