/* * 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.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; /** * internals */ protected final BooleanMask2D mask; protected final Rectangle regionBounds; protected final Rectangle imageBounds; protected final Rectangle finalBounds; protected final int c; protected final int w, h; protected int x, y; protected int imgOff; protected int maskOff; 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 */ protected ImageDataIterator(IcyBufferedImage image, Rectangle boundsXY, BooleanMask2D maskXY, int channel) { super(); if (maskXY != null) regionBounds = maskXY.bounds; else regionBounds = boundsXY; this.image = image; this.mask = maskXY; if (image != null) { imageBounds = image.getBounds(); dataType = image.getDataType_(); c = channel; } else { imageBounds = new Rectangle(); dataType = DataType.UBYTE; c = 0; } finalBounds = regionBounds.intersection(imageBounds); // cached w = finalBounds.width; h = finalBounds.height; // start iterator reset(); } /** * 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 boundsXY * XY region to iterate (inclusive). * @param channel * channel (C position) we want to iterate data */ public ImageDataIterator(IcyBufferedImage image, Rectangle boundsXY, int channel) { this(image, boundsXY, null, channel); } /** * @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) { this(image, null, maskXY, channel); } /** * @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)); } public int getMinX() { return finalBounds.x; } public int getMaxX() { return (finalBounds.x + w) - 1; } public int getMinY() { return finalBounds.y; } public int getMaxY() { return (finalBounds.y + h) - 1; } @Override public void reset() { done = (image == null) || (c < 0) || (c >= image.getSizeC()) || finalBounds.isEmpty(); if (!done) { // get data data = image.getDataXY(c); // reset position y = 0; x = -1; imgOff = (finalBounds.x - imageBounds.x) + ((finalBounds.y - imageBounds.y) * imageBounds.width); imgOff--; if (mask != null) { maskOff = (finalBounds.x - regionBounds.x) + ((finalBounds.y - regionBounds.y) * regionBounds.width); maskOff--; } // allow to correctly set initial position in boolean mask next(); } } @Override public void next() { nextPosition(); if (mask != null) { // advance while mask do not contains current point while (!done && !mask.mask[maskOff]) nextPosition(); } } /** * Advance one position */ protected void nextPosition() { if (mask != null) { imgOff++; maskOff++; if (++x >= w) { x = 0; imgOff += imageBounds.width - finalBounds.width; maskOff += regionBounds.width - finalBounds.width; if (++y >= h) done = true; } } else { imgOff++; if (++x >= w) { x = 0; imgOff += imageBounds.width - finalBounds.width; if (++y >= h) done = true; } } } @Override public boolean done() { return done; } @Override public double get() { if (done) throw new NoSuchElementException(null); return Array1DUtil.getValue(data, imgOff, dataType); } @Override public void set(double value) { if (done) throw new NoSuchElementException(null); Array1DUtil.setValue(data, imgOff, dataType, value); } /** * Returns current X position. */ public int getX() { return finalBounds.x + x; } /** * Returns current Y position. */ public int getY() { return finalBounds.y + y; } /** * Returns C position (fixed) */ public int getC() { return c; } /** * @deprecated Use {@link #getX()} instead */ @Deprecated public int getPositionX() { return getX(); } /** * @deprecated Use {@link #getY()} instead */ @Deprecated public int getPositionY() { return getY(); } /** * @deprecated Use {@link #getC()} instead */ @Deprecated public int getPositionC() { return getC(); } }