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.Point4D; 023import icy.type.point.Point5D; 024import icy.type.rectangle.Rectangle3D; 025import icy.type.rectangle.Rectangle4D; 026import icy.type.rectangle.Rectangle5D; 027import icy.util.XMLUtil; 028 029import java.awt.Rectangle; 030import java.util.ArrayList; 031import java.util.List; 032 033import org.w3c.dom.Node; 034 035/** 036 * 4D ROI base class. 037 */ 038public abstract class ROI4D extends ROI 039{ 040 /** 041 * @deprecated Use {@link ROI4D#getROI4DList(List)} instead. 042 */ 043 @Deprecated 044 public static ArrayList<ROI4D> getROI4DList(ArrayList<ROI> rois) 045 { 046 final ArrayList<ROI4D> result = new ArrayList<ROI4D>(); 047 048 for (ROI roi : rois) 049 if (roi instanceof ROI4D) 050 result.add((ROI4D) roi); 051 052 return result; 053 } 054 055 /** 056 * Return all 4D ROI from the ROI list 057 */ 058 public static List<ROI4D> getROI4DList(List<ROI> rois) 059 { 060 final List<ROI4D> result = new ArrayList<ROI4D>(); 061 062 for (ROI roi : rois) 063 if (roi instanceof ROI4D) 064 result.add((ROI4D) roi); 065 066 return result; 067 } 068 069 public static final String ID_C = "c"; 070 071 /** 072 * c coordinate attachment 073 */ 074 protected int c; 075 076 public ROI4D() 077 { 078 super(); 079 080 // by default we consider no specific C attachment 081 c = -1; 082 } 083 084 @Override 085 public String getDefaultName() 086 { 087 return "ROI4D"; 088 } 089 090 @Override 091 final public int getDimension() 092 { 093 return 4; 094 } 095 096 /** 097 * Returns true if specified ROI is on the same [C] position than current ROI. 098 * 099 * @param shouldContain 100 * if <code>true</code> then current ROI should "contains" specified ROI position [C] 101 */ 102 protected boolean onSamePos(ROI4D roi, boolean shouldContain) 103 { 104 final int c = getC(); 105 final int roiC = roi.getC(); 106 107 // same position ? 108 if (shouldContain) 109 { 110 if ((c != -1) && (c != roiC)) 111 return false; 112 } 113 else 114 { 115 if ((c != -1) && (roiC != -1) && (c != roiC)) 116 return false; 117 } 118 119 return true; 120 } 121 122 /** 123 * Tests if a specified {@link Point4D} is inside the ROI. 124 * 125 * @param p 126 * the specified <code>Point4D</code> to be tested 127 * @return <code>true</code> if the specified <code>Point3D</code> is inside the boundary of the <code>ROI</code>; 128 * <code>false</code> otherwise. 129 */ 130 public boolean contains(Point4D p) 131 { 132 return contains(p.getX(), p.getY(), p.getZ(), p.getT()); 133 } 134 135 /** 136 * Tests if the interior of the <code>ROI</code> entirely contains the specified <code>Rectangle4D</code>. The 137 * {@code ROI.contains()} method allows a implementation to 138 * conservatively return {@code false} when: 139 * <ul> 140 * <li>the <code>intersect</code> method returns <code>true</code> and 141 * <li>the calculations to determine whether or not the <code>ROI</code> entirely contains the 142 * <code>Rectangle3D</code> are prohibitively expensive. 143 * </ul> 144 * This means that for some ROIs this method might return {@code false} even though the {@code ROI} contains the 145 * {@code Rectangle4D}. 146 * 147 * @param r 148 * The specified <code>Rectangle4D</code> 149 * @return <code>true</code> if the interior of the <code>ROI</code> entirely contains the <code>Rectangle4D</code>; 150 * <code>false</code> otherwise or, if the <code>ROI</code> contains the <code>Rectangle4D</code> and the 151 * <code>intersects</code> method returns <code>true</code> and the containment calculations would be too 152 * expensive to perform. 153 * @see #contains(double, double, double, double, double, double, double, double) 154 */ 155 public boolean contains(Rectangle4D r) 156 { 157 return contains(r.getX(), r.getY(), r.getZ(), r.getT(), r.getSizeX(), r.getSizeY(), r.getSizeZ(), r.getSizeT()); 158 } 159 160 /** 161 * Tests if the specified coordinates are inside the <code>ROI</code>. 162 * 163 * @param x 164 * the specified X coordinate to be tested 165 * @param y 166 * the specified Y coordinate to be tested 167 * @param z 168 * the specified Z coordinate to be tested 169 * @param t 170 * the specified T coordinate to be tested 171 * @return <code>true</code> if the specified 4D coordinates are inside the <code>ROI</code> boundary; 172 * <code>false</code> otherwise. 173 */ 174 public abstract boolean contains(double x, double y, double z, double t); 175 176 /** 177 * Tests if the <code>ROI</code> entirely contains the specified 4D rectangular area. All 178 * coordinates that lie inside the rectangular area must lie within the <code>ROI</code> for the 179 * entire rectangular area to be considered contained within the <code>ROI</code>. 180 * <p> 181 * The {@code ROI.contains()} method allows a {@code ROI} implementation to conservatively return {@code false} 182 * when: 183 * <ul> 184 * <li>the <code>intersect</code> method returns <code>true</code> and 185 * <li>the calculations to determine whether or not the <code>ROI</code> entirely contains the rectangular area are 186 * prohibitively expensive. 187 * </ul> 188 * This means that for some {@code ROIs} this method might return {@code false} even though the {@code ROI} contains 189 * the rectangular area. 190 * 191 * @param x 192 * the X coordinate of the minimum corner position of the specified rectangular area 193 * @param y 194 * the Y coordinate of the minimum corner position of the specified rectangular area 195 * @param z 196 * the Z coordinate of the minimum corner position of the specified rectangular area 197 * @param t 198 * the T coordinate of the minimum corner position of the specified rectangular area 199 * @param sizeX 200 * size for X dimension of the specified rectangular area 201 * @param sizeY 202 * size for Y dimension of the specified rectangular area 203 * @param sizeZ 204 * size for Z dimension of the specified rectangular area 205 * @param sizeT 206 * size for T dimension of the specified rectangular area 207 * @return <code>true</code> if the interior of the <code>ROI</code> entirely contains the 208 * specified 4D rectangular area; <code>false</code> otherwise or, if the <code>ROI</code> contains the 4D 209 * rectangular area and the <code>intersects</code> method returns <code>true</code> and the containment 210 * calculations would be too 211 * expensive to perform. 212 */ 213 public abstract boolean contains(double x, double y, double z, double t, double sizeX, double sizeY, double sizeZ, 214 double sizeT); 215 216 @Override 217 public boolean contains(double x, double y, double z, double t, double c) 218 { 219 final boolean cok; 220 221 if (getC() == -1) 222 cok = true; 223 else 224 cok = (c >= getC()) && (c < (getC() + 1d)); 225 226 return contains(x, y, z, t) && cok; 227 } 228 229 @Override 230 public boolean contains(double x, double y, double z, double t, double c, double sizeX, double sizeY, double sizeT, 231 double sizeZ, double sizeC) 232 { 233 final boolean cok; 234 235 if (getC() == -1) 236 cok = true; 237 else 238 cok = (c >= getC()) && ((c + sizeC) <= (getC() + 1d)); 239 240 return contains(x, y, z, t, sizeX, sizeY, sizeZ, sizeT) && cok; 241 } 242 243 /* 244 * Generic implementation using the BooleanMask which is not accurate and slow. 245 * Override this for specific ROI type. 246 */ 247 @Override 248 public boolean contains(ROI roi) 249 { 250 if (roi instanceof ROI4D) 251 { 252 final ROI4D roi4d = (ROI4D) roi; 253 254 if (onSamePos(roi4d, true)) 255 { 256 // special case of ROI Point 257 if (roi4d.isEmpty()) 258 return contains(roi4d.getPosition4D()); 259 260 BooleanMask4D mask; 261 BooleanMask4D roiMask; 262 263 // take content first 264 mask = getBooleanMask(false); 265 roiMask = roi4d.getBooleanMask(false); 266 267 // test first only on content 268 if (!mask.contains(roiMask)) 269 return false; 270 271 // take content and edge 272 mask = getBooleanMask(true); 273 roiMask = roi4d.getBooleanMask(true); 274 275 // then test on content and edge 276 if (!mask.contains(roiMask)) 277 return false; 278 279 // contained 280 return true; 281 } 282 283 return false; 284 } 285 286 // use default implementation 287 return super.contains(roi); 288 } 289 290 /** 291 * Tests if the interior of the <code>ROI</code> intersects the interior of a specified <code>Rectangle4D</code>. 292 * The {@code ROI.intersects()} method allows a {@code ROI} implementation to conservatively return {@code true} 293 * when: 294 * <ul> 295 * <li>there is a high probability that the <code>Rectangle4D</code> and the <code>ROI</code> intersect, but 296 * <li>the calculations to accurately determine this intersection are prohibitively expensive. 297 * </ul> 298 * This means that for some {@code ROIs} this method might return {@code true} even though the {@code Rectangle4D} 299 * does not intersect the {@code ROI}. 300 * 301 * @param r 302 * the specified <code>Rectangle4D</code> 303 * @return <code>true</code> if the interior of the <code>ROI</code> and the interior of the 304 * specified <code>Rectangle4D</code> intersect, or are both highly likely to intersect 305 * and intersection calculations would be too expensive to perform; <code>false</code> otherwise. 306 * @see #intersects(double, double, double,double, double, double, double, double) 307 */ 308 public boolean intersects(Rectangle4D r) 309 { 310 return intersects(r.getX(), r.getY(), r.getZ(), r.getT(), r.getSizeX(), r.getSizeY(), r.getSizeZ(), 311 r.getSizeT()); 312 } 313 314 /** 315 * Tests if the interior of the <code>ROI</code> intersects the interior of a specified 316 * 4D rectangular area. The 4D rectangular area is considered to intersect the <code>ROI</code> if any point is 317 * contained in both the interior of the <code>ROI</code> and the specified 318 * rectangular area. 319 * <p> 320 * The {@code ROI.intersects()} method allows a {@code ROI} implementation to conservatively return {@code true} 321 * when: 322 * <ul> 323 * <li>there is a high probability that the 4D rectangular area and the <code>ROI</code> intersect, but 324 * <li>the calculations to accurately determine this intersection are prohibitively expensive. 325 * </ul> 326 * This means that for some {@code ROIs} this method might return {@code true} even though the 4D rectangular area 327 * does not intersect the {@code ROI}. 328 * 329 * @param x 330 * the X coordinate of the minimum corner position of the specified rectangular area 331 * @param y 332 * the Y coordinate of the minimum corner position of the specified rectangular area 333 * @param z 334 * the Z coordinate of the minimum corner position of the specified rectangular area 335 * @param t 336 * the T coordinate of the minimum corner position of the specified rectangular area 337 * @param sizeX 338 * size for X dimension of the specified rectangular area 339 * @param sizeY 340 * size for Y dimension of the specified rectangular area 341 * @param sizeZ 342 * size for Z dimension of the specified rectangular area 343 * @param sizeT 344 * size for T dimension of the specified rectangular area 345 * @return <code>true</code> if the interior of the <code>ROI</code> and the interior of the 346 * rectangular area intersect, or are both highly likely to intersect and intersection 347 * calculations would be too expensive to perform; <code>false</code> otherwise. 348 */ 349 public abstract boolean intersects(double x, double y, double z, double t, double sizeX, double sizeY, 350 double sizeZ, double sizeT); 351 352 @Override 353 public boolean intersects(double x, double y, double z, double t, double c, double sizeX, double sizeY, 354 double sizeZ, double sizeT, double sizeC) 355 { 356 // easy discard 357 if ((sizeX == 0d) || (sizeY == 0d) || (sizeZ == 0d) || (sizeT == 0d) || (sizeC == 0d)) 358 return false; 359 360 final boolean cok; 361 362 if (getC() == -1) 363 cok = true; 364 else 365 cok = ((c + sizeC) > getC()) && (c < (getC() + 1d)); 366 367 return cok && intersects(x, y, z, t, sizeX, sizeY, sizeZ, sizeT); 368 } 369 370 /* 371 * Generic implementation using the BooleanMask which is not accurate and slow. 372 * Override this for specific ROI type. 373 */ 374 @Override 375 public boolean intersects(ROI roi) 376 { 377 if (roi instanceof ROI4D) 378 { 379 final ROI4D roi4d = (ROI4D) roi; 380 381 if (onSamePos(roi4d, false)) 382 return getBooleanMask(true).intersects(roi4d.getBooleanMask(true)); 383 } 384 385 // use default implementation 386 return super.intersects(roi); 387 } 388 389 /** 390 * Calculate and returns the 4D bounding box of the <code>ROI</code>.<br> 391 * This method is used by {@link #getBounds4D()} which should try to cache the result as the 392 * bounding box calculation can take some computation time for complex ROI. 393 */ 394 public abstract Rectangle4D computeBounds4D(); 395 396 @Override 397 public Rectangle5D computeBounds5D() 398 { 399 final Rectangle4D bounds4D = computeBounds4D(); 400 if (bounds4D == null) 401 return new Rectangle5D.Double(); 402 403 final Rectangle5D.Double result = new Rectangle5D.Double(bounds4D.getX(), bounds4D.getY(), bounds4D.getZ(), 404 bounds4D.getT(), 0d, bounds4D.getSizeX(), bounds4D.getSizeY(), bounds4D.getSizeZ(), 405 bounds4D.getSizeT(), 0d); 406 407 if (getC() == -1) 408 { 409 result.c = Double.NEGATIVE_INFINITY; 410 result.sizeC = Double.POSITIVE_INFINITY; 411 } 412 else 413 { 414 result.c = getC(); 415 result.sizeC = 1d; 416 } 417 418 return result; 419 } 420 421 /** 422 * Returns an integer {@link Rectangle4D} that completely encloses the <code>ROI</code>. Note 423 * that there is no guarantee that the returned <code>Rectangle4D</code> is the smallest 424 * bounding box that encloses the <code>ROI</code>, only that the <code>ROI</code> lies entirely 425 * within the indicated <code>Rectangle4D</code>. The returned <code>Rectangle4D</code> might 426 * also fail to completely enclose the <code>ROI</code> if the <code>ROI</code> overflows the 427 * limited range of the integer data type. The <code>getBounds4D</code> method generally returns 428 * a tighter bounding box due to its greater flexibility in representation. 429 * 430 * @return an integer <code>Rectangle4D</code> that completely encloses the <code>ROI</code>. 431 */ 432 public Rectangle4D.Integer getBounds() 433 { 434 return getBounds4D().toInteger(); 435 } 436 437 /** 438 * Returns the bounding box of the <code>ROI</code>. Note that there is no guarantee that the 439 * returned {@link Rectangle4D} is the smallest bounding box that encloses the <code>ROI</code>, 440 * only that the <code>ROI</code> lies entirely within the indicated <code>Rectangle4D</code>. 441 * 442 * @return an instance of <code>Rectangle4D</code> that is a bounding box of the <code>ROI</code>. 443 */ 444 public Rectangle4D getBounds4D() 445 { 446 return getBounds5D().toRectangle4D(); 447 } 448 449 /** 450 * Returns the integer ROI position which normally correspond to the <i>minimum</i> point of the 451 * ROI bounds. 452 * 453 * @see #getBounds() 454 */ 455 public Point4D.Integer getPosition() 456 { 457 final Rectangle4D.Integer bounds = getBounds(); 458 return new Point4D.Integer(bounds.x, bounds.y, bounds.z, bounds.t); 459 } 460 461 /** 462 * Returns the ROI position which normally correspond to the <i>minimum</i> point of the ROI 463 * bounds. 464 * 465 * @see #getBounds4D() 466 */ 467 public Point4D getPosition4D() 468 { 469 return getBounds4D().getPosition(); 470 } 471 472 @Override 473 public boolean canSetBounds() 474 { 475 // default 476 return false; 477 } 478 479 /** 480 * Set the <code>ROI</code> 4D bounds.<br> 481 * Note that not all ROI supports bounds modification and you should call {@link #canSetBounds()} first to test if 482 * the operation is supported.<br> 483 * 484 * @param bounds 485 * new ROI 4D bounds 486 */ 487 public void setBounds4D(Rectangle4D bounds) 488 { 489 // do nothing by default (not supported) 490 } 491 492 @Override 493 public void setBounds5D(Rectangle5D bounds) 494 { 495 beginUpdate(); 496 try 497 { 498 // infinite C dim ? 499 if (bounds.getSizeC() == Double.POSITIVE_INFINITY) 500 setC(-1); 501 else 502 setC((int) bounds.getC()); 503 504 setBounds4D(bounds.toRectangle4D()); 505 } 506 finally 507 { 508 endUpdate(); 509 } 510 } 511 512 @Override 513 public boolean canSetPosition() 514 { 515 // default implementation use translation if available 516 return canTranslate(); 517 } 518 519 /** 520 * Set the <code>ROI</code> 4D position.<br> 521 * Note that not all ROI supports position modification and you should call {@link #canSetPosition()} first to test 522 * if the operation is supported.<br> 523 * 524 * @param position 525 * new ROI 4D position 526 */ 527 public void setPosition4D(Point4D position) 528 { 529 // use translation operation by default if supported 530 if (canTranslate()) 531 { 532 final Point4D oldPos = getPosition4D(); 533 translate(position.getX() - oldPos.getX(), position.getY() - oldPos.getY(), 534 position.getZ() - oldPos.getZ(), position.getT() - oldPos.getT()); 535 } 536 } 537 538 @Override 539 public void setPosition5D(Point5D position) 540 { 541 beginUpdate(); 542 try 543 { 544 setC((int) position.getC()); 545 setPosition4D(position.toPoint4D()); 546 } 547 finally 548 { 549 endUpdate(); 550 } 551 } 552 553 /** 554 * Returns <code>true</code> if the ROI support translate operation. 555 * 556 * @see #translate(double, double, double, double) 557 */ 558 public boolean canTranslate() 559 { 560 // by default 561 return false; 562 } 563 564 /** 565 * Translate the ROI position by the specified delta X/Y/Z/T.<br> 566 * Note that not all ROI support this operation so you should test it by calling {@link #canTranslate()} first. 567 * 568 * @param dx 569 * translation value to apply on X dimension 570 * @param dy 571 * translation value to apply on Y dimension 572 * @param dz 573 * translation value to apply on Z dimension 574 * @param dt 575 * translation value to apply on T dimension 576 * @see #canTranslate() 577 * @see #setPosition4D(Point4D) 578 */ 579 public void translate(double dx, double dy, double dz, double dt) 580 { 581 582 } 583 584 @Override 585 public boolean[] getBooleanMask2D(int x, int y, int width, int height, int z, int t, int c, boolean inclusive) 586 { 587 // not on the correct C position --> return empty mask 588 if (!isActiveFor(c)) 589 return new boolean[Math.max(0, width) * Math.max(0, height)]; 590 591 return getBooleanMask2D(x, y, width, height, z, t, inclusive); 592 } 593 594 /** 595 * Get the boolean bitmap mask for the specified rectangular area of the roi and for the 596 * specified Z,T position.<br> 597 * if the pixel (x,y) is contained in the roi Z,T position then result[(y * width) + x] = true<br> 598 * if the pixel (x,y) is not contained in the roi Z,T position then result[(y * width) + x] = 599 * 600 * @param x 601 * the X coordinate of the upper-left corner of the specified rectangular area 602 * @param y 603 * the Y coordinate of the upper-left corner of the specified rectangular area 604 * @param width 605 * the width of the specified rectangular area 606 * @param height 607 * the height of the specified rectangular area 608 * @param z 609 * Z position we want to retrieve the boolean mask 610 * @param t 611 * T position we want to retrieve the boolean mask 612 * @param inclusive 613 * If true then all partially contained (intersected) pixels are included in the mask. 614 * @return the boolean bitmap mask 615 */ 616 public boolean[] getBooleanMask2D(int x, int y, int width, int height, int z, int t, boolean inclusive) 617 { 618 final boolean[] result = new boolean[Math.max(0, width) * Math.max(0, height)]; 619 620 // simple and basic implementation, override it to have better performance 621 int offset = 0; 622 for (int j = 0; j < height; j++) 623 { 624 for (int i = 0; i < width; i++) 625 { 626 if (inclusive) 627 result[offset] = intersects(x + i, y + j, z, t, 1d, 1d, 1d, 1d); 628 else 629 result[offset] = contains(x + i, y + j, z, t, 1d, 1d, 1d, 1d); 630 offset++; 631 } 632 } 633 634 return result; 635 } 636 637 /** 638 * Get the boolean bitmap mask for the specified rectangular area of the roi and for the 639 * specified Z,T position.<br> 640 * if the pixel (x,y) is contained in the roi Z,T position then result[(y * width) + x] = true<br> 641 * if the pixel (x,y) is not contained in the roi Z,T position then result[(y * width) + x] = 642 * false 643 * 644 * @param rect 645 * 2D rectangular area we want to retrieve the boolean mask 646 * @param z 647 * Z position we want to retrieve the boolean mask 648 * @param t 649 * T position we want to retrieve the boolean mask 650 * @param inclusive 651 * If true then all partially contained (intersected) pixels are included in the mask. 652 */ 653 public boolean[] getBooleanMask2D(Rectangle rect, int z, int t, boolean inclusive) 654 { 655 return getBooleanMask2D(rect.x, rect.y, rect.width, rect.height, z, t, inclusive); 656 } 657 658 @Override 659 public BooleanMask2D getBooleanMask2D(int z, int t, int c, boolean inclusive) 660 { 661 // not on the correct C position --> return empty mask 662 if (!isActiveFor(c)) 663 return new BooleanMask2D(new Rectangle(), new boolean[0]); 664 665 return getBooleanMask2D(z, t, inclusive); 666 } 667 668 /** 669 * Get the {@link BooleanMask2D} object representing the roi for the specified Z,T position.<br> 670 * It contains the rectangle mask bounds and the associated boolean array mask.<br> 671 * if the pixel (x,y) is contained in the roi Z,T position then result.mask[(y * w) + x] = true<br> 672 * if the pixel (x,y) is not contained in the roi Z,T position then result.mask[(y * w) + x] = 673 * false 674 * 675 * @param z 676 * Z position we want to retrieve the boolean mask 677 * @param t 678 * T position we want to retrieve the boolean mask 679 * @param inclusive 680 * If true then all partially contained (intersected) pixels are included in the mask. 681 */ 682 public BooleanMask2D getBooleanMask2D(int z, int t, boolean inclusive) 683 { 684 final Rectangle bounds = getBounds4D().toRectangle2D().getBounds(); 685 686 // empty ROI --> return empty mask 687 if (bounds.isEmpty()) 688 return new BooleanMask2D(new Rectangle(), new boolean[0]); 689 690 final BooleanMask2D result = new BooleanMask2D(bounds, getBooleanMask2D(bounds, z, t, inclusive)); 691 692 // optimized bounds to optimize memory usage for this specific Z, T slice mask 693 result.optimizeBounds(); 694 695 return result; 696 } 697 698 /** 699 * Returns the {@link BooleanMask3D} object representing the XYZ volume content at specified Z, 700 * T, C position.<br> 701 * It contains the 3D rectangle mask bounds and the associated boolean array mask. 702 * 703 * @param z 704 * Z position we want to retrieve the boolean mask or -1 to retrieve the whole Z 705 * dimension 706 * @param t 707 * T position we want to retrieve the boolean mask. 708 * @param c 709 * C position we want to retrieve the boolean mask.<br> 710 * Set it to -1 to retrieve the mask whatever is the C position of this ROI4D. 711 * @param inclusive 712 * If true then all partially contained (intersected) pixels are included in the mask. 713 */ 714 public BooleanMask3D getBooleanMask3D(int z, int t, int c, boolean inclusive) 715 { 716 // not on the correct C position --> return empty mask 717 if (!isActiveFor(c)) 718 return new BooleanMask3D(); 719 720 // whole Z dimension 721 if (z == -1) 722 return getBooleanMask3D(t, inclusive); 723 724 // define bounds 725 final Rectangle3D.Integer bounds = getBounds4D().toRectangle3D().toInteger(); 726 bounds.setZ(z); 727 bounds.setSizeZ(1); 728 729 return new BooleanMask3D(bounds, new BooleanMask2D[] {getBooleanMask2D(z, t, inclusive)}); 730 } 731 732 /** 733 * Get the {@link BooleanMask3D} object representing the roi for specified T position.<br> 734 * It contains the 3D rectangle mask bounds and the associated boolean array mask. 735 * 736 * @param inclusive 737 * If true then all partially contained (intersected) pixels are included in the mask. 738 */ 739 public BooleanMask3D getBooleanMask3D(int t, boolean inclusive) 740 { 741 final Rectangle3D.Integer bounds = getBounds4D().toRectangle3D().toInteger(); 742 final BooleanMask2D masks[] = new BooleanMask2D[bounds.sizeZ]; 743 744 for (int z = 0; z < masks.length; z++) 745 masks[z] = getBooleanMask2D(bounds.z + z, t, inclusive); 746 747 return new BooleanMask3D(bounds, masks); 748 } 749 750 /** 751 * Returns the {@link BooleanMask4D} object representing the XYZT space content at specified Z, 752 * T, C position. 753 * 754 * @param z 755 * Z position we want to retrieve the boolean mask or -1 to retrieve the whole Z 756 * dimension 757 * @param t 758 * T position we want to retrieve the boolean mask or -1 to retrieve the whole T 759 * dimension 760 * @param c 761 * C position we want to retrieve the boolean mask.<br> 762 * Set it to -1 to retrieve the mask whatever is the C position of this ROI4D. 763 * @param inclusive 764 * If true then all partially contained (intersected) pixels are included in the mask. 765 */ 766 public BooleanMask4D getBooleanMask4D(int z, int t, int c, boolean inclusive) 767 { 768 // not on the correct C position --> return empty mask 769 if (!isActiveFor(c)) 770 return new BooleanMask4D(); 771 772 // whole Z dimension 773 if (z == -1) 774 { 775 // whole Z and T dimension 776 if (t == -1) 777 return getBooleanMask(inclusive); 778 779 // define bounds 780 final Rectangle4D.Integer bounds = getBounds4D().toInteger(); 781 bounds.setT(t); 782 bounds.setSizeT(1); 783 784 // whole Z dimension but specific T 785 return new BooleanMask4D(bounds, new BooleanMask3D[] {getBooleanMask3D(t, inclusive)}); 786 } 787 788 final Rectangle4D.Integer bounds4d = getBounds4D().toInteger(); 789 790 // specific Z 791 bounds4d.setZ(z); 792 bounds4d.setSizeZ(1); 793 // specific T dimension ? 794 if (t != -1) 795 { 796 bounds4d.setT(t); 797 bounds4d.setSizeT(1); 798 } 799 800 final Rectangle3D.Integer bounds3d = (Rectangle3D.Integer) bounds4d.toRectangle3D(); 801 final BooleanMask3D masks[] = new BooleanMask3D[bounds4d.sizeT]; 802 803 for (int i = 0; i < bounds4d.sizeT; i++) 804 masks[i] = new BooleanMask3D((Rectangle3D.Integer) bounds3d.clone(), new BooleanMask2D[] {getBooleanMask2D( 805 z, bounds4d.t + i, inclusive)}); 806 807 return new BooleanMask4D(bounds4d, masks); 808 } 809 810 /** 811 * Get the {@link BooleanMask4D} object representing the roi.<br> 812 * It contains the 4D rectangle mask bounds and the associated boolean array mask.<br> 813 * 814 * @param inclusive 815 * If true then all partially contained (intersected) pixels are included in the mask. 816 */ 817 public BooleanMask4D getBooleanMask(boolean inclusive) 818 { 819 final Rectangle4D.Integer bounds = getBounds(); 820 final BooleanMask3D masks[] = new BooleanMask3D[bounds.sizeT]; 821 822 for (int t = 0; t < masks.length; t++) 823 masks[t] = getBooleanMask3D(bounds.t + t, inclusive); 824 825 return new BooleanMask4D(bounds, masks); 826 } 827 828 /* 829 * Generic implementation for ROI4D using the BooleanMask object so 830 * the result is just an approximation. 831 * Override to optimize for specific ROI. 832 */ 833 @Override 834 public double computeNumberOfContourPoints() 835 { 836 // approximation by using number of point of the edge of boolean mask 837 return getBooleanMask(true).getContourPointsAsIntArray().length / getDimension(); 838 } 839 840 /* 841 * Generic implementation for ROI4D using the BooleanMask object so 842 * the result is just an approximation. 843 * Override to optimize for specific ROI. 844 */ 845 @Override 846 public double computeNumberOfPoints() 847 { 848 double numPoints = 0; 849 850 // approximation by using number of point of boolean mask with and without border 851 numPoints += getBooleanMask(true).getNumberOfPoints(); 852 numPoints += getBooleanMask(false).getNumberOfPoints(); 853 numPoints /= 2d; 854 855 return numPoints; 856 } 857 858 /** 859 * Returns the C position.<br> 860 * <code>-1</code> is a special value meaning the ROI is set on all C channels (infinite C 861 * dimension). 862 */ 863 public int getC() 864 { 865 return c; 866 } 867 868 /** 869 * Sets C position of this 4D ROI.<br> 870 * You cannot set the ROI on a negative C position as <code>-1</code> is a special value meaning 871 * the ROI is set on all C channels (infinite C dimension). 872 */ 873 public void setC(int value) 874 { 875 final int v; 876 877 // special value for infinite dimension --> change to -1 878 if (value == Integer.MIN_VALUE) 879 v = -1; 880 else 881 v = value; 882 883 if (c != v) 884 { 885 c = v; 886 roiChanged(false); 887 } 888 } 889 890 @Override 891 public boolean isActiveFor(IcyCanvas canvas) 892 { 893 return isActiveFor(canvas.getPositionC()); 894 } 895 896 /** 897 * Return true if the ROI is active for the specified C coordinate 898 */ 899 public boolean isActiveFor(int c) 900 { 901 return (getC() == -1) || (c == -1) || (getC() == c); 902 } 903 904 @Override 905 public boolean loadFromXML(Node node) 906 { 907 beginUpdate(); 908 try 909 { 910 if (!super.loadFromXML(node)) 911 return false; 912 913 setC(XMLUtil.getElementIntValue(node, ID_C, -1)); 914 } 915 finally 916 { 917 endUpdate(); 918 } 919 920 return true; 921 } 922 923 @Override 924 public boolean saveToXML(Node node) 925 { 926 if (!super.saveToXML(node)) 927 return false; 928 929 XMLUtil.setElementIntValue(node, ID_C, getC()); 930 931 return true; 932 } 933}