001/*
002 * Copyright 2010-2015 Institut Pasteur.
003 * 
004 * This file is part of Icy.
005 * 
006 * Icy is free software: you can redistribute it and/or modify
007 * it under the terms of the GNU General Public License as published by
008 * the Free Software Foundation, either version 3 of the License, or
009 * (at your option) any later version.
010 * 
011 * Icy is distributed in the hope that it will be useful,
012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
014 * GNU General Public License for more details.
015 * 
016 * You should have received a copy of the GNU General Public License
017 * along with Icy. If not, see <http://www.gnu.org/licenses/>.
018 */
019package icy.sequence;
020
021import java.awt.Rectangle;
022import java.util.NoSuchElementException;
023
024import icy.image.IcyBufferedImage;
025import icy.image.ImageDataIterator;
026import icy.roi.ROI;
027import icy.type.DataIterator;
028import icy.type.DataType;
029import icy.type.rectangle.Rectangle5D;
030import icy.type.rectangle.Rectangle5D.Integer;
031
032/**
033 * Sequence data iterator.<br>
034 * This class permit to use simple iterator to read / write <code>Sequence</code> data<br>
035 * as double in XYCZT <i>([T[Z[C[Y[X}}]]])</i> dimension order.<br>
036 * Whatever is the internal {@link DataType} data is returned and set as double.<br>
037 * <b>If the sequence size or type is modified during iteration the iterator
038 * becomes invalid and can exception can happen.</b>
039 * 
040 * @author Stephane
041 */
042public class SequenceDataIterator implements DataIterator
043{
044    protected final Sequence sequence;
045    protected final ROI roi;
046
047    protected final Rectangle XYBounds;
048    protected final int startC, endC;
049    protected final int startZ, endZ;
050    protected final int startT, endT;
051    protected final boolean inclusive;
052
053    /**
054     * internals
055     */
056    protected int c, z, t;
057    protected boolean done;
058    protected ImageDataIterator imageIterator;
059
060    /**
061     * Create a new SequenceData iterator to iterate data through the specified 5D region
062     * (inclusive).
063     * 
064     * @param sequence
065     *        Sequence we want to iterate data from
066     * @param bounds5D
067     *        the 5D rectangular region we want to iterate
068     */
069    public SequenceDataIterator(Sequence sequence, Rectangle5D.Integer bounds5D)
070    {
071        super();
072
073        this.sequence = sequence;
074        roi = null;
075        imageIterator = null;
076        inclusive = true;
077
078        if (sequence != null)
079        {
080            final Rectangle5D.Integer bounds = (Integer) bounds5D.createIntersection(sequence.getBounds5D());
081
082            XYBounds = (Rectangle) bounds.toRectangle2D();
083
084            startZ = bounds.z;
085            endZ = (bounds.z + bounds.sizeZ) - 1;
086            startT = bounds.t;
087            endT = (bounds.t + bounds.sizeT) - 1;
088            startC = bounds.c;
089            endC = (bounds.c + bounds.sizeC) - 1;
090        }
091        else
092        {
093            XYBounds = null;
094            startZ = 0;
095            endZ = 0;
096            startT = 0;
097            endT = 0;
098            startC = 0;
099            endC = 0;
100        }
101
102        // start iterator
103        reset();
104    }
105
106    /**
107     * Create a new SequenceData iterator to iterate data through the specified dimensions
108     * (inclusive).
109     * 
110     * @param sequence
111     *        Sequence we want to iterate data from
112     * @param XYBounds
113     *        XY region to iterate
114     * @param z
115     *        Z position (stack) we want to iterate data
116     * @param t
117     *        T position (time) we want to iterate data
118     * @param c
119     *        C position (channel) we want to iterate data
120     */
121    public SequenceDataIterator(Sequence sequence, Rectangle XYBounds, int z, int t, int c)
122    {
123        this(sequence, new Rectangle5D.Integer(XYBounds.x, XYBounds.y, z, t, c, (XYBounds.x + XYBounds.width) - 1,
124                (XYBounds.y + XYBounds.height) - 1, 1, 1, 1));
125    }
126
127    /**
128     * @deprecated Use {@link #SequenceDataIterator(Sequence, Rectangle5D.Integer)} instead
129     */
130    @Deprecated
131    public SequenceDataIterator(Sequence sequence, int startX, int endX, int startY, int endY, int startC, int endC,
132            int startZ, int endZ, int startT, int endT)
133    {
134        this(sequence, new Rectangle5D.Integer(startX, startY, startZ, startT, startC, (endX - startX) + 1,
135                (endY - startY) + 1, (endZ - startZ) + 1, (endT - startT) + 1, (endC - startC) + 1));
136    }
137
138    /**
139     * @deprecated Use {@link #SequenceDataIterator(Sequence, Rectangle, int, int, int)} instead
140     */
141    @Deprecated
142    public SequenceDataIterator(Sequence sequence, int startX, int endX, int startY, int endY, int c, int z, int t)
143    {
144        this(sequence,
145                new Rectangle5D.Integer(startX, startY, z, t, c, (endX - startX) + 1, (endY - startY) + 1, 1, 1, 1));
146    }
147
148    /**
149     * Create a new SequenceData iterator to iterate data of specified channel.
150     * 
151     * @param sequence
152     *        Sequence we want to iterate data from
153     * @param z
154     *        Z position (stack) we want to iterate data
155     * @param t
156     *        T position (time) we want to iterate data
157     * @param c
158     *        C position (channel) we want to iterate data
159     */
160    public SequenceDataIterator(Sequence sequence, int z, int t, int c)
161    {
162        this(sequence, new Rectangle5D.Integer(0, 0, z, t, c, sequence.getSizeX(), sequence.getSizeY(), 1, 1, 1));
163    }
164
165    /**
166     * Create a new SequenceData iterator to iterate all data.
167     * 
168     * @param sequence
169     *        Sequence we want to iterate data from.
170     */
171    public SequenceDataIterator(Sequence sequence)
172    {
173        this(sequence, new Rectangle5D.Integer(0, 0, 0, 0, 0, sequence.getSizeX(), sequence.getSizeY(),
174                sequence.getSizeZ(), sequence.getSizeT(), sequence.getSizeC()));
175    }
176
177    /**
178     * Create a new SequenceData iterator to iterate data through the specified ROI.
179     * 
180     * @param sequence
181     *        Sequence we want to iterate data from.
182     * @param roi
183     *        ROI defining the region to iterate.
184     * @param inclusive
185     *        If true then all partially contained (intersected) pixels in the ROI are included.
186     * @param z
187     *        The specific Z position (slice) we want to iterate or <code>-1</code> to iterate over
188     *        the whole ROI Z dimension.
189     * @param t
190     *        The specific T position (frame) we want to iterate or <code>-1</code> to iterate over
191     *        the whole ROI T dimension.
192     * @param c
193     *        The specific C position (channel) we want to iterate or <code>-1</code> to iterate
194     *        over the whole ROI C dimension.
195     */
196    public SequenceDataIterator(Sequence sequence, ROI roi, boolean inclusive, int z, int t, int c)
197    {
198        super();
199
200        this.sequence = sequence;
201        this.roi = roi;
202        this.inclusive = inclusive;
203        XYBounds = null;
204
205        if ((sequence != null) && (roi != null))
206        {
207            final Rectangle5D bounds5D = roi.getBounds5D();
208
209            // force Z position
210            if (z != -1)
211            {
212                bounds5D.setZ(z);
213                bounds5D.setSizeZ(1d);
214            }
215            // force T position
216            if (t != -1)
217            {
218                bounds5D.setT(t);
219                bounds5D.setSizeT(1d);
220            }
221            // force C position
222            if (c != -1)
223            {
224                bounds5D.setC(c);
225                bounds5D.setSizeC(1d);
226            }
227
228            // get final bounds
229            final Rectangle5D.Integer bounds = (Integer) sequence.getBounds5D().createIntersection(bounds5D);
230
231            startZ = bounds.z;
232            endZ = (bounds.z + bounds.sizeZ) - 1;
233            startT = bounds.t;
234            endT = (bounds.t + bounds.sizeT) - 1;
235            startC = bounds.c;
236            endC = (bounds.c + bounds.sizeC) - 1;
237        }
238        else
239        {
240            startZ = 0;
241            endZ = 0;
242            startT = 0;
243            endT = 0;
244            startC = 0;
245            endC = 0;
246        }
247
248        // start iterator
249        reset();
250    }
251
252    /**
253     * Create a new SequenceData iterator to iterate data through the specified ROI.
254     * 
255     * @param sequence
256     *        Sequence we want to iterate data from.
257     * @param roi
258     *        ROI defining the region to iterate.
259     * @param inclusive
260     *        If true then all partially contained (intersected) pixels in the ROI are included.
261     */
262    public SequenceDataIterator(Sequence sequence, ROI roi, boolean inclusive)
263    {
264        this(sequence, roi, inclusive, -1, -1, -1);
265    }
266
267    /**
268     * Create a new SequenceData iterator to iterate data through the specified ROI.
269     * 
270     * @param sequence
271     *        Sequence we want to iterate data from.
272     * @param roi
273     *        ROI defining the region to iterate.
274     */
275    public SequenceDataIterator(Sequence sequence, ROI roi)
276    {
277        this(sequence, roi, false);
278    }
279
280    @Override
281    public void reset()
282    {
283        done = (sequence == null) || (startT > endT) || (startZ > endZ) || (startC > endC);
284
285        if (!done)
286        {
287            t = startT;
288            z = startZ;
289            c = startC;
290
291            // prepare XY data
292            prepareDataXY();
293            nextImageifNeeded();
294        }
295    }
296
297    protected void flushDataXY()
298    {
299        if (imageIterator != null)
300            imageIterator.flush();
301    }
302
303    /**
304     * Prepare data for XY iteration.
305     */
306    protected void prepareDataXY()
307    {
308        // flush previous dataXY
309        flushDataXY();
310
311        final IcyBufferedImage img = sequence.getImage(t, z);
312
313        // get the 2D mask for specified C
314        if (roi != null)
315        {
316            switch (roi.getDimension())
317            {
318                case 2:
319                    // ignore Z, T and C roi informations (wanted for fixed Z, T and C positions)
320                    imageIterator = new ImageDataIterator(img, roi.getBooleanMask2D(-1, -1, -1, inclusive), c);
321                    break;
322
323                case 3:
324                    // ignore T and C roi informations (wanted for fixed T and C positions)
325                    imageIterator = new ImageDataIterator(img, roi.getBooleanMask2D(z, -1, -1, inclusive), c);
326                    break;
327
328                case 4:
329                    // ignore C roi information (wanted for fixed C position)
330                    imageIterator = new ImageDataIterator(img, roi.getBooleanMask2D(z, t, -1, inclusive), c);
331                    break;
332
333                // assume 5D
334                default:
335                    imageIterator = new ImageDataIterator(img, roi.getBooleanMask2D(z, t, c, inclusive), c);
336            }
337        }
338        else
339            imageIterator = new ImageDataIterator(img, XYBounds, c);
340    }
341
342    @Override
343    public void next()
344    {
345        imageIterator.next();
346        nextImageifNeeded();
347    }
348
349    /**
350     * Advance one image position.
351     */
352    protected void nextImageifNeeded()
353    {
354        while (imageIterator.done() && !done)
355        {
356            if (++c > endC)
357            {
358                c = startC;
359
360                if (++z > endZ)
361                {
362                    z = startZ;
363
364                    if (++t > endT)
365                    {
366                        done = true;
367                        return;
368                    }
369                }
370            }
371
372            prepareDataXY();
373        }
374    }
375
376    @Override
377    public boolean done()
378    {
379        return done;
380    }
381
382    @Override
383    public double get()
384    {
385        if (done)
386            throw new NoSuchElementException(null);
387
388        return imageIterator.get();
389    }
390
391    @Override
392    public void set(double value)
393    {
394        if (done)
395            throw new NoSuchElementException(null);
396
397        imageIterator.set(value);
398    }
399
400    /**
401     * Return current X position.
402     */
403    public int getPositionX()
404    {
405        if (imageIterator != null)
406            return imageIterator.getPositionX();
407
408        return 0;
409    }
410
411    /**
412     * Return current Y position.
413     */
414    public int getPositionY()
415    {
416        if (imageIterator != null)
417            return imageIterator.getPositionY();
418
419        return 0;
420    }
421
422    /**
423     * Return current C position.
424     */
425    public int getPositionC()
426    {
427        return c;
428    }
429
430    /**
431     * Return current Z position.
432     */
433    public int getPositionZ()
434    {
435        return z;
436    }
437
438    /**
439     * Return current T position.
440     */
441    public int getPositionT()
442    {
443        return t;
444    }
445
446    /**
447     * Ensure changed data are correctly saved back to original data source (should be called at the end)
448     */
449    public void flush()
450    {
451        flushDataXY();
452    }
453}