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}