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.roi; 020 021import icy.canvas.IcyCanvas; 022import icy.type.point.Point5D; 023import icy.type.rectangle.Rectangle3D; 024import icy.type.rectangle.Rectangle4D; 025import icy.type.rectangle.Rectangle5D; 026 027import java.util.ArrayList; 028import java.util.List; 029 030/** 031 * 5D ROI base class. 032 */ 033public abstract class ROI5D extends ROI 034{ 035 /** 036 * @deprecated Use {@link ROI5D#getROI5DList(List)} instead. 037 */ 038 @Deprecated 039 public static ArrayList<ROI5D> getROI5DList(ArrayList<ROI> rois) 040 { 041 final ArrayList<ROI5D> result = new ArrayList<ROI5D>(); 042 043 for (ROI roi : rois) 044 if (roi instanceof ROI5D) 045 result.add((ROI5D) roi); 046 047 return result; 048 } 049 050 /** 051 * Return all 5D ROI from the ROI list 052 */ 053 public static List<ROI5D> getROI5DList(List<ROI> rois) 054 { 055 final List<ROI5D> result = new ArrayList<ROI5D>(); 056 057 for (ROI roi : rois) 058 if (roi instanceof ROI5D) 059 result.add((ROI5D) roi); 060 061 return result; 062 } 063 064 public ROI5D() 065 { 066 super(); 067 } 068 069 @Override 070 public String getDefaultName() 071 { 072 return "ROI5D"; 073 } 074 075 @Override 076 final public int getDimension() 077 { 078 return 5; 079 } 080 081 @Override 082 public boolean isActiveFor(IcyCanvas canvas) 083 { 084 return true; 085 } 086 087 /** 088 * Returns an integer {@link Rectangle5D} that completely encloses the <code>ROI</code>. Note 089 * that there is no guarantee that the returned <code>Rectangle5D</code> is the smallest 090 * bounding box that encloses the <code>ROI</code>, only that the <code>ROI</code> lies entirely 091 * within the indicated <code>Rectangle5D</code>. The returned <code>Rectangle5D</code> might 092 * also fail to completely enclose the <code>ROI</code> if the <code>ROI</code> overflows the 093 * limited range of the integer data type. The <code>getBounds5D</code> method generally returns 094 * a tighter bounding box due to its greater flexibility in representation. 095 * 096 * @return an integer <code>Rectangle5D</code> that completely encloses the <code>ROI</code>. 097 */ 098 public Rectangle5D.Integer getBounds() 099 { 100 return getBounds5D().toInteger(); 101 } 102 103 /** 104 * Returns the integer ROI position which normally correspond to the <i>minimum</i> point of the 105 * ROI bounds. 106 * 107 * @see #getBounds() 108 */ 109 public Point5D.Integer getPosition() 110 { 111 final Rectangle5D.Integer bounds = getBounds(); 112 return new Point5D.Integer(bounds.x, bounds.y, bounds.z, bounds.t, bounds.c); 113 } 114 115 @Override 116 public boolean canSetBounds() 117 { 118 // default 119 return false; 120 } 121 122 @Override 123 public void setBounds5D(Rectangle5D bounds) 124 { 125 // do nothing by default (not supported) 126 } 127 128 @Override 129 public boolean canSetPosition() 130 { 131 // default implementation use translation if available 132 return canTranslate(); 133 } 134 135 @Override 136 public void setPosition5D(Point5D position) 137 { 138 // use translation operation by default if supported 139 if (canTranslate()) 140 { 141 final Point5D oldPos = getPosition5D(); 142 translate(position.getX() - oldPos.getX(), position.getY() - oldPos.getY(), 143 position.getZ() - oldPos.getZ(), position.getT() - oldPos.getT(), position.getC() - oldPos.getC()); 144 } 145 } 146 147 /** 148 * Returns <code>true</code> if the ROI support translate operation. 149 * 150 * @see #translate(double, double, double, double, double) 151 */ 152 public boolean canTranslate() 153 { 154 // by default 155 return false; 156 } 157 158 /** 159 * Translate the ROI position by the specified delta X/Y/Z/T.<br> 160 * Note that not all ROI support this operation so you should test it by calling {@link #canTranslate()} first. 161 * 162 * @param dx 163 * translation value to apply on X dimension 164 * @param dy 165 * translation value to apply on Y dimension 166 * @param dz 167 * translation value to apply on Z dimension 168 * @param dt 169 * translation value to apply on T dimension 170 * @param dc 171 * translation value to apply on C dimension 172 * @see #canTranslate() 173 * @see #setPosition5D(Point5D) 174 */ 175 public void translate(double dx, double dy, double dz, double dt, double dc) 176 { 177 178 } 179 180 /* 181 * Generic implementation using the BooleanMask which is not accurate and slow. 182 * Override this for specific ROI type. 183 */ 184 @Override 185 public boolean contains(ROI roi) 186 { 187 if (roi instanceof ROI5D) 188 { 189 final ROI5D roi5d = (ROI5D) roi; 190 191 // special case of ROI Point 192 if (roi5d.isEmpty()) 193 return contains(roi5d.getPosition5D()); 194 195 BooleanMask5D mask; 196 BooleanMask5D roiMask; 197 198 // take content first 199 mask = getBooleanMask(false); 200 roiMask = roi5d.getBooleanMask(false); 201 202 // test first only on content 203 if (!mask.contains(roiMask)) 204 return false; 205 206 // take content and edge 207 mask = getBooleanMask(true); 208 roiMask = roi5d.getBooleanMask(true); 209 210 // then test on content and edge 211 if (!mask.contains(roiMask)) 212 return false; 213 214 // contained 215 return true; 216 } 217 218 // use default implementation 219 return super.contains(roi); 220 } 221 222 /* 223 * Generic implementation using the BooleanMask which is not accurate and slow. 224 * Override this for specific ROI type. 225 */ 226 @Override 227 public boolean intersects(ROI roi) 228 { 229 if (roi instanceof ROI5D) 230 return getBooleanMask(true).intersects(((ROI5D) roi).getBooleanMask(true)); 231 232 // use default implementation 233 return super.intersects(roi); 234 } 235 236 @Override 237 public BooleanMask2D getBooleanMask2D(int z, int t, int c, boolean inclusive) 238 { 239 final BooleanMask2D result = super.getBooleanMask2D(z, t, c, inclusive); 240 241 // optimized bounds to optimize memory usage for this specific Z, T, C slice mask 242 result.optimizeBounds(); 243 244 return result; 245 } 246 247 /** 248 * Returns the {@link BooleanMask3D} object representing the XYZ volume content at specified Z, 249 * T, C position.<br> 250 * It contains the 3D rectangle mask bounds and the associated boolean array mask.<br> 251 * 252 * @param z 253 * Z position we want to retrieve the boolean mask or -1 to retrieve the whole Z 254 * dimension 255 * @param t 256 * T position we want to retrieve the boolean mask. 257 * @param c 258 * C position we want to retrieve the boolean mask. 259 * @param inclusive 260 * If true then all partially contained (intersected) pixels are included in the mask. 261 */ 262 public BooleanMask3D getBooleanMask3D(int z, int t, int c, boolean inclusive) 263 { 264 // whole Z dimension 265 if (z == -1) 266 return getBooleanMask3D(t, c, inclusive); 267 268 // define bounds 269 final Rectangle3D.Integer bounds = getBounds5D().toRectangle3D().toInteger(); 270 bounds.setZ(z); 271 bounds.setSizeZ(1); 272 273 return new BooleanMask3D(bounds, new BooleanMask2D[] {getBooleanMask2D(z, t, c, inclusive)}); 274 } 275 276 /** 277 * Returns the {@link BooleanMask3D} object representing the XYZ volume content at specified T, 278 * C position.<br> 279 * It contains the 3D rectangle mask bounds and the associated boolean array mask.<br> 280 * 281 * @param inclusive 282 * If true then all partially contained (intersected) pixels are included in the mask. 283 */ 284 public BooleanMask3D getBooleanMask3D(int t, int c, boolean inclusive) 285 { 286 final Rectangle3D.Integer bounds = getBounds5D().toRectangle3D().toInteger(); 287 final BooleanMask2D masks[] = new BooleanMask2D[bounds.sizeZ]; 288 289 for (int z = 0; z < masks.length; z++) 290 masks[z] = getBooleanMask2D(bounds.z + z, t, c, inclusive); 291 292 return new BooleanMask3D(bounds, masks); 293 } 294 295 /** 296 * Returns the {@link BooleanMask4D} object representing the XYZT space content at specified Z 297 * and C position. 298 * 299 * @param z 300 * Z position we want to retrieve the boolean mask or -1 to retrieve the whole Z 301 * dimension 302 * @param t 303 * T position we want to retrieve the boolean mask or -1 to retrieve the whole T 304 * dimension 305 * @param c 306 * C position we want to retrieve the boolean mask. 307 * @param inclusive 308 * If true then all partially contained (intersected) pixels are included in the mask. 309 */ 310 public BooleanMask4D getBooleanMask4D(int z, int t, int c, boolean inclusive) 311 { 312 // whole Z dimension 313 if (z == -1) 314 { 315 // whole Z and T dimension 316 if (t == -1) 317 return getBooleanMask4D(c, inclusive); 318 319 // define bounds 320 final Rectangle4D.Integer bounds = getBounds5D().toRectangle4D().toInteger(); 321 bounds.setT(t); 322 bounds.setSizeT(1); 323 324 // whole Z dimension but specific T 325 return new BooleanMask4D(bounds, new BooleanMask3D[] {getBooleanMask3D(t, c, inclusive)}); 326 } 327 328 final Rectangle4D.Integer bounds4d = getBounds5D().toRectangle4D().toInteger(); 329 330 // specific Z 331 bounds4d.setZ(z); 332 bounds4d.setSizeZ(1); 333 334 // specific T dimension ? 335 if (t != -1) 336 { 337 bounds4d.setT(t); 338 bounds4d.setSizeT(1); 339 } 340 341 final Rectangle3D.Integer bounds3d = (Rectangle3D.Integer) bounds4d.toRectangle3D(); 342 final BooleanMask3D masks[] = new BooleanMask3D[bounds4d.sizeT]; 343 344 for (int i = 0; i < bounds4d.sizeT; i++) 345 masks[i] = new BooleanMask3D((Rectangle3D.Integer) bounds3d.clone(), new BooleanMask2D[] {getBooleanMask2D( 346 z, bounds4d.t + i, c, inclusive)}); 347 348 return new BooleanMask4D(bounds4d, masks); 349 } 350 351 /** 352 * Get the {@link BooleanMask4D} object representing the roi for specified C position.<br> 353 * It contains the 4D rectangle mask bounds and the associated boolean array mask.<br> 354 * 355 * @param inclusive 356 * If true then all partially contained (intersected) pixels are included in the mask. 357 */ 358 public BooleanMask4D getBooleanMask4D(int c, boolean inclusive) 359 { 360 final Rectangle4D.Integer bounds = getBounds5D().toRectangle4D().toInteger(); 361 final BooleanMask3D masks[] = new BooleanMask3D[bounds.sizeT]; 362 363 for (int t = 0; t < masks.length; t++) 364 masks[t] = getBooleanMask3D(bounds.t + t, c, inclusive); 365 366 return new BooleanMask4D(bounds, masks); 367 } 368 369 /** 370 * Returns the {@link BooleanMask5D} object representing the XYZTC space content at specified Z, 371 * T, C position. 372 * 373 * @param z 374 * Z position we want to retrieve the boolean mask or -1 to retrieve the whole Z 375 * dimension 376 * @param t 377 * T position we want to retrieve the boolean mask or -1 to retrieve the whole T 378 * dimension 379 * @param c 380 * C position we want to retrieve the boolean mask or -1 to retrieve the whole C 381 * dimension 382 * @param inclusive 383 * If true then all partially contained (intersected) pixels are included in the mask. 384 */ 385 public BooleanMask5D getBooleanMask5D(int z, int t, int c, boolean inclusive) 386 { 387 // whole Z dimension 388 if (z == -1) 389 { 390 // whole Z and T dimension 391 if (t == -1) 392 { 393 // whole Z, T and C dimension 394 if (c == -1) 395 return getBooleanMask(inclusive); 396 397 // define bounds 398 final Rectangle5D.Integer bounds = getBounds5D().toInteger(); 399 bounds.setC(c); 400 bounds.setSizeC(1); 401 402 // whole Z and T dimension but specific C 403 return new BooleanMask5D(bounds, new BooleanMask4D[] {getBooleanMask4D(c, inclusive)}); 404 } 405 406 final Rectangle5D.Integer bounds5d = getBounds(); 407 408 // specific T 409 bounds5d.setT(t); 410 bounds5d.setSizeT(1); 411 // specific C dimension ? 412 if (c != -1) 413 { 414 bounds5d.setC(c); 415 bounds5d.setSizeC(1); 416 } 417 418 final Rectangle4D.Integer bounds4d = (Rectangle4D.Integer) bounds5d.toRectangle4D(); 419 final BooleanMask4D masks[] = new BooleanMask4D[bounds5d.sizeC]; 420 421 for (int i = 0; i < bounds5d.sizeC; i++) 422 masks[i] = new BooleanMask4D((Rectangle4D.Integer) bounds4d.clone(), 423 new BooleanMask3D[] {getBooleanMask3D(t, bounds5d.c + i, inclusive)}); 424 425 return new BooleanMask5D(bounds5d, masks); 426 } 427 428 final Rectangle5D.Integer bounds5d = getBounds(); 429 430 // specific Z 431 bounds5d.setZ(z); 432 bounds5d.setSizeZ(1); 433 // specific T dimension ? 434 if (t != -1) 435 { 436 bounds5d.setT(t); 437 bounds5d.setSizeT(1); 438 } 439 // specific C dimension ? 440 if (c != -1) 441 { 442 bounds5d.setC(c); 443 bounds5d.setSizeC(1); 444 } 445 446 final Rectangle4D.Integer bounds4d = (Rectangle4D.Integer) bounds5d.toRectangle4D(); 447 final Rectangle3D.Integer bounds3d = (Rectangle3D.Integer) bounds4d.toRectangle3D(); 448 final BooleanMask4D masks[] = new BooleanMask4D[bounds5d.sizeC]; 449 450 for (int i = 0; i < bounds5d.sizeC; i++) 451 { 452 final BooleanMask3D masks3d[] = new BooleanMask3D[bounds4d.sizeT]; 453 454 for (int j = 0; j < bounds5d.sizeT; j++) 455 masks3d[i] = new BooleanMask3D((Rectangle3D.Integer) bounds3d.clone(), 456 new BooleanMask2D[] {getBooleanMask2D(z, bounds5d.t + j, bounds5d.c + i, inclusive)}); 457 458 masks[i] = new BooleanMask4D((Rectangle4D.Integer) bounds4d.clone(), masks3d); 459 } 460 461 return new BooleanMask5D(bounds5d, masks); 462 } 463 464 /** 465 * Get the {@link BooleanMask5D} object representing the roi.<br> 466 * It contains the 5D rectangle mask bounds and the associated boolean array mask.<br> 467 * 468 * @param inclusive 469 * If true then all partially contained (intersected) pixels are included in the mask. 470 */ 471 public BooleanMask5D getBooleanMask(boolean inclusive) 472 { 473 final Rectangle5D.Integer bounds = getBounds(); 474 final BooleanMask4D masks[] = new BooleanMask4D[bounds.sizeC]; 475 476 for (int c = 0; c < masks.length; c++) 477 masks[c] = getBooleanMask4D(bounds.c + c, inclusive); 478 479 return new BooleanMask5D(bounds, masks); 480 } 481 482 /* 483 * Generic implementation for ROI5D using the BooleanMask object so 484 * the result is just an approximation. 485 * Override to optimize for specific ROI. 486 */ 487 @Override 488 public double computeNumberOfContourPoints() 489 { 490 // approximation by using number of point of the edge of boolean mask 491 return getBooleanMask(true).getContourPointsAsIntArray().length / getDimension(); 492 } 493 494 /* 495 * Generic implementation for ROI5D using the BooleanMask object so 496 * the result is just an approximation. 497 * Override to optimize for specific ROI. 498 */ 499 @Override 500 public double computeNumberOfPoints() 501 { 502 double numPoints = 0; 503 504 // approximation by using number of point of boolean mask with and without border 505 numPoints += getBooleanMask(true).getNumberOfPoints(); 506 numPoints += getBooleanMask(false).getNumberOfPoints(); 507 numPoints /= 2d; 508 509 return numPoints; 510 } 511 512}