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.image; 020 021import java.awt.Rectangle; 022import java.util.NoSuchElementException; 023 024import icy.roi.BooleanMask2D; 025import icy.roi.ROI; 026import icy.type.DataIterator; 027import icy.type.DataType; 028import icy.type.collection.array.Array1DUtil; 029 030/** 031 * Image data iterator.<br> 032 * This class permit to use simple iterator to read / write <code>IcyBufferedImage</code> data<br> 033 * as double in XYC <i>([C[Y[X]]])</i> dimension order .<br> 034 * Whatever is the internal {@link DataType} data is returned and set as double.<br> 035 * <b>If the image size or type is modified during iteration the iterator 036 * becomes invalid and can causes exception to happen.</b> 037 * 038 * @author Stephane 039 */ 040public class ImageDataIterator implements DataIterator 041{ 042 protected final IcyBufferedImage image; 043 protected final DataType dataType; 044 045 /** 046 * internals 047 */ 048 protected final BooleanMask2D mask; 049 protected final Rectangle regionBounds; 050 protected final Rectangle imageBounds; 051 protected final Rectangle finalBounds; 052 protected final int c; 053 protected final int w, h; 054 protected int x, y; 055 protected int imgOff; 056 protected int maskOff; 057 protected boolean changed; 058 protected boolean done; 059 protected Object data; 060 061 /** 062 * Create a new ImageData iterator to iterate data through the specified XY region and channel. 063 * 064 * @param image 065 * Image we want to iterate data from 066 * @param XYBounds 067 * XY region to iterate (inclusive). 068 * @param channel 069 * channel (C position) we want to iterate data 070 */ 071 protected ImageDataIterator(IcyBufferedImage image, Rectangle boundsXY, BooleanMask2D maskXY, int channel) 072 { 073 super(); 074 075 if (maskXY != null) 076 regionBounds = maskXY.bounds; 077 else 078 regionBounds = boundsXY; 079 080 this.image = image; 081 this.mask = maskXY; 082 083 if (image != null) 084 { 085 imageBounds = image.getBounds(); 086 dataType = image.getDataType_(); 087 c = channel; 088 // retain data while we are iterating over image data 089 image.lockRaster(); 090 } 091 else 092 { 093 imageBounds = new Rectangle(); 094 dataType = DataType.UBYTE; 095 c = 0; 096 } 097 098 finalBounds = regionBounds.intersection(imageBounds); 099 100 // cached 101 w = finalBounds.width; 102 h = finalBounds.height; 103 104 changed = false; 105 106 // start iterator 107 reset(); 108 } 109 110 /** 111 * Create a new ImageData iterator to iterate data through the specified XY region and channel. 112 * 113 * @param image 114 * Image we want to iterate data from 115 * @param boundsXY 116 * XY region to iterate (inclusive). 117 * @param channel 118 * channel (C position) we want to iterate data 119 */ 120 public ImageDataIterator(IcyBufferedImage image, Rectangle boundsXY, int channel) 121 { 122 this(image, boundsXY, null, channel); 123 } 124 125 /** 126 * @deprecated Use {@link #ImageDataIterator(IcyBufferedImage, Rectangle, int)} instead. 127 */ 128 @Deprecated 129 public ImageDataIterator(IcyBufferedImage image, int startX, int endX, int startY, int endY, int startC, int endC) 130 { 131 this(image, new Rectangle(startX, startY, (endX - startX) + 1, (endY - startY) + 1), startC); 132 } 133 134 /** 135 * @deprecated Use {@link #ImageDataIterator(IcyBufferedImage, Rectangle, int)} instead. 136 */ 137 @Deprecated 138 public ImageDataIterator(IcyBufferedImage image, int startX, int endX, int startY, int endY, int c) 139 { 140 this(image, new Rectangle(startX, startY, (endX - startX) + 1, (endY - startY) + 1), c); 141 } 142 143 /** 144 * Create a new ImageData iterator to iterate data of specified channel. 145 * 146 * @param image 147 * Image we want to iterate data from 148 * @param c 149 * C position (channel) we want to iterate data 150 */ 151 public ImageDataIterator(IcyBufferedImage image, int c) 152 { 153 this(image, image.getBounds(), c); 154 } 155 156 /** 157 * @deprecated Use {@link #ImageDataIterator(IcyBufferedImage, int)} instead.<br> 158 * The <code>ImageDataIterator</code> iterate only on single channel data. 159 */ 160 @Deprecated 161 public ImageDataIterator(IcyBufferedImage image) 162 { 163 this(image, image.getBounds(), 0); 164 } 165 166 /** 167 * Create a new ImageData iterator to iterate data through the specified <code>BooleanMask2D</code> and C dimension. 168 * 169 * @param image 170 * Image we want to iterate data from 171 * @param maskXY 172 * BooleanMask2D defining the XY region to iterate 173 * @param channel 174 * channel (C position) we want to iterate data 175 */ 176 public ImageDataIterator(IcyBufferedImage image, BooleanMask2D maskXY, int channel) 177 { 178 this(image, null, maskXY, channel); 179 } 180 181 /** 182 * @deprecated Use {@link #ImageDataIterator(IcyBufferedImage, BooleanMask2D, int)} instead 183 */ 184 @Deprecated 185 public ImageDataIterator(IcyBufferedImage image, BooleanMask2D maskXY) 186 { 187 this(image, maskXY, 0); 188 } 189 190 /** 191 * @deprecated Use {@link #ImageDataIterator(IcyBufferedImage, BooleanMask2D, int)} instead. 192 * You can use the {@link ROI#getBooleanMask2D(int, int, int, boolean)} method to 193 * retrieve the boolean mask from the ROI. 194 */ 195 @Deprecated 196 public ImageDataIterator(IcyBufferedImage image, ROI roi) 197 { 198 this(image, roi.getBooleanMask2D(0, 0, 0, false)); 199 } 200 201 @Override 202 protected void finalize() throws Throwable 203 { 204 flush(); 205 206 super.finalize(); 207 } 208 209 public int getMinX() 210 { 211 return finalBounds.x; 212 } 213 214 public int getMaxX() 215 { 216 return (finalBounds.x + w) - 1; 217 } 218 219 public int getMinY() 220 { 221 return finalBounds.y; 222 } 223 224 public int getMaxY() 225 { 226 return (finalBounds.y + h) - 1; 227 } 228 229 @Override 230 public void reset() 231 { 232 done = (image == null) || (c < 0) || (c >= image.getSizeC()) || finalBounds.isEmpty(); 233 234 if (!done) 235 { 236 // get data 237 data = image.getDataXY(c); 238 239 // reset position 240 y = 0; 241 x = -1; 242 imgOff = (finalBounds.x - imageBounds.x) + ((finalBounds.y - imageBounds.y) * imageBounds.width); 243 imgOff--; 244 if (mask != null) 245 { 246 maskOff = (finalBounds.x - regionBounds.x) + ((finalBounds.y - regionBounds.y) * regionBounds.width); 247 maskOff--; 248 } 249 // allow to correctly set initial position in boolean mask 250 next(); 251 } 252 } 253 254 @Override 255 public void next() 256 { 257 nextPosition(); 258 259 if (mask != null) 260 { 261 // advance while mask do not contains current point 262 while (!done && !mask.mask[maskOff]) 263 nextPosition(); 264 } 265 } 266 267 /** 268 * Advance one position 269 */ 270 protected void nextPosition() 271 { 272 if (mask != null) 273 { 274 imgOff++; 275 maskOff++; 276 if (++x >= w) 277 { 278 x = 0; 279 imgOff += imageBounds.width - finalBounds.width; 280 maskOff += regionBounds.width - finalBounds.width; 281 282 if (++y >= h) 283 done = true; 284 } 285 } 286 else 287 { 288 imgOff++; 289 if (++x >= w) 290 { 291 x = 0; 292 imgOff += imageBounds.width - finalBounds.width; 293 294 if (++y >= h) 295 done = true; 296 } 297 } 298 } 299 300 @Override 301 public boolean done() 302 { 303 return done; 304 } 305 306 @Override 307 public double get() 308 { 309 if (done) 310 throw new NoSuchElementException(null); 311 312 return Array1DUtil.getValue(data, imgOff, dataType); 313 } 314 315 @Override 316 public void set(double value) 317 { 318 if (done) 319 throw new NoSuchElementException(null); 320 321 Array1DUtil.setValue(data, imgOff, dataType, value); 322 changed = true; 323 } 324 325 /** 326 * Returns current X position. 327 */ 328 public int getX() 329 { 330 return finalBounds.x + x; 331 } 332 333 /** 334 * Returns current Y position. 335 */ 336 public int getY() 337 { 338 return finalBounds.y + y; 339 } 340 341 /** 342 * Returns C position (fixed) 343 */ 344 public int getC() 345 { 346 return c; 347 } 348 349 /** 350 * @deprecated Use {@link #getX()} instead 351 */ 352 @Deprecated 353 public int getPositionX() 354 { 355 return getX(); 356 } 357 358 /** 359 * @deprecated Use {@link #getY()} instead 360 */ 361 @Deprecated 362 public int getPositionY() 363 { 364 return getY(); 365 } 366 367 /** 368 * @deprecated Use {@link #getC()} instead 369 */ 370 @Deprecated 371 public int getPositionC() 372 { 373 return getC(); 374 } 375 376 public void flush() 377 { 378 // release image raster and save changes to cache 379 if (image != null) 380 image.releaseRaster(changed); 381 changed = false; 382 } 383}