/*
* 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();
}
}