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 java.awt.Point; 022import java.awt.Rectangle; 023import java.awt.geom.Line2D; 024import java.awt.geom.Point2D; 025import java.awt.geom.Rectangle2D; 026import java.util.ArrayList; 027import java.util.Collection; 028import java.util.List; 029import java.util.Map; 030import java.util.Map.Entry; 031import java.util.Set; 032 033import icy.image.IcyBufferedImage; 034import icy.image.IntensityInfo; 035import icy.math.DataIteratorMath; 036import icy.math.MathUtil; 037import icy.painter.Anchor2D; 038import icy.painter.Anchor3D; 039import icy.plugin.interface_.PluginROIDescriptor; 040import icy.sequence.Sequence; 041import icy.sequence.SequenceDataIterator; 042import icy.sequence.SequenceUtil; 043import icy.type.DataIteratorUtil; 044import icy.type.DataType; 045import icy.type.collection.CollectionUtil; 046import icy.type.dimension.Dimension5D; 047import icy.type.geom.Line2DUtil; 048import icy.type.geom.Polygon2D; 049import icy.type.point.Point3D; 050import icy.type.point.Point4D; 051import icy.type.point.Point5D; 052import icy.type.rectangle.Rectangle2DUtil; 053import icy.type.rectangle.Rectangle3D; 054import icy.type.rectangle.Rectangle4D; 055import icy.type.rectangle.Rectangle5D; 056import icy.util.ShapeUtil.BooleanOperator; 057import plugins.kernel.roi.descriptor.intensity.ROIIntensityDescriptorsPlugin; 058import plugins.kernel.roi.descriptor.intensity.ROIMaxIntensityDescriptor; 059import plugins.kernel.roi.descriptor.intensity.ROIMeanIntensityDescriptor; 060import plugins.kernel.roi.descriptor.intensity.ROIMinIntensityDescriptor; 061import plugins.kernel.roi.descriptor.intensity.ROIStandardDeviationDescriptor; 062import plugins.kernel.roi.descriptor.intensity.ROISumIntensityDescriptor; 063import plugins.kernel.roi.descriptor.measure.ROIAreaDescriptor; 064import plugins.kernel.roi.descriptor.measure.ROIBasicMeasureDescriptorsPlugin; 065import plugins.kernel.roi.descriptor.measure.ROIContourDescriptor; 066import plugins.kernel.roi.descriptor.measure.ROIInteriorDescriptor; 067import plugins.kernel.roi.descriptor.measure.ROIMassCenterDescriptorsPlugin; 068import plugins.kernel.roi.descriptor.measure.ROIPerimeterDescriptor; 069import plugins.kernel.roi.descriptor.measure.ROISurfaceAreaDescriptor; 070import plugins.kernel.roi.descriptor.measure.ROIVolumeDescriptor; 071import plugins.kernel.roi.roi2d.ROI2DArea; 072import plugins.kernel.roi.roi2d.ROI2DEllipse; 073import plugins.kernel.roi.roi2d.ROI2DPoint; 074import plugins.kernel.roi.roi2d.ROI2DPolygon; 075import plugins.kernel.roi.roi2d.ROI2DRectShape; 076import plugins.kernel.roi.roi2d.ROI2DRectangle; 077import plugins.kernel.roi.roi2d.ROI2DShape; 078import plugins.kernel.roi.roi3d.ROI3DArea; 079import plugins.kernel.roi.roi3d.ROI3DPoint; 080import plugins.kernel.roi.roi3d.ROI3DShape; 081import plugins.kernel.roi.roi3d.ROI3DStackEllipse; 082import plugins.kernel.roi.roi3d.ROI3DStackPolygon; 083import plugins.kernel.roi.roi3d.ROI3DStackRectangle; 084import plugins.kernel.roi.roi4d.ROI4DArea; 085import plugins.kernel.roi.roi5d.ROI5DArea; 086 087/** 088 * ROI utilities class. 089 * 090 * @author Stephane 091 */ 092public class ROIUtil 093{ 094 final public static String STACK_SUFFIX = " stack"; 095 final public static String MASK_SUFFIX = " mask"; 096 final public static String SHAPE_SUFFIX = " shape"; 097 final public static String OBJECT_SUFFIX = " object"; 098 final public static String PART_SUFFIX = " part"; 099 100 /** 101 * Returns all available ROI descriptors (see {@link ROIDescriptor}) and their attached plugin 102 * (see {@link PluginROIDescriptor}).<br/> 103 * This list can be extended by installing new plugin(s) implementing the {@link PluginROIDescriptor} 104 * interface.<br/> 105 * This method is an alias of {@link ROIDescriptor#getDescriptors()} 106 * 107 * @see ROIDescriptor#compute(ROI, Sequence) 108 * @see PluginROIDescriptor#compute(ROI, Sequence) 109 */ 110 public static Map<ROIDescriptor, PluginROIDescriptor> getROIDescriptors() 111 { 112 return ROIDescriptor.getDescriptors(); 113 } 114 115 /** 116 * Computes the specified descriptor from the input {@link ROIDescriptor} set on given ROI 117 * and returns the result (or <code>null</code> if the descriptor is not found).<br/> 118 * This method is an alias of {@link ROIDescriptor#computeDescriptor(Collection, String, ROI, Sequence)} 119 * 120 * @param roiDescriptors 121 * the input {@link ROIDescriptor} set (see {@link #getROIDescriptors()} method) 122 * @param descriptorId 123 * the id of the descriptor we want to compute ({@link ROIBasicMeasureDescriptorsPlugin#ID_VOLUME} for 124 * instance) 125 * @param roi 126 * the ROI on which the descriptor(s) should be computed 127 * @param sequence 128 * an optional sequence where the pixel size can be retrieved 129 * @return the computed descriptor or <code>null</code> if the descriptor if not found in the 130 * specified set 131 * @throws UnsupportedOperationException 132 * if the type of the given ROI is not supported by this descriptor, or if <code>sequence</code> is 133 * <code>null</code> while the calculation requires it, or if 134 * the specified Z, T or C position are not supported by the descriptor 135 */ 136 public static Object computeDescriptor(Collection<ROIDescriptor> roiDescriptors, String descriptorId, ROI roi, 137 Sequence sequence) 138 { 139 return ROIDescriptor.computeDescriptor(roiDescriptors, descriptorId, roi, sequence); 140 } 141 142 /** 143 * @deprecated Use {@link ROIDescriptor#computeDescriptor(Collection, String, ROI, Sequence)} instead 144 */ 145 @Deprecated 146 public static Object computeDescriptor(Set<ROIDescriptor> roiDescriptors, String descriptorId, ROI roi, 147 Sequence sequence) 148 { 149 return ROIDescriptor.computeDescriptor(roiDescriptors, descriptorId, roi, sequence); 150 } 151 152 /** 153 * Computes the specified descriptor on given ROI and returns the result (or <code>null</code> if the descriptor is 154 * not found).<br/> 155 * This method is an alias of {@link ROIDescriptor#computeDescriptor(String, ROI, Sequence)} 156 * 157 * @param descriptorId 158 * the id of the descriptor we want to compute ({@link ROIBasicMeasureDescriptorsPlugin#ID_VOLUME} for 159 * instance) 160 * @param roi 161 * the ROI on which the descriptor(s) should be computed 162 * @param sequence 163 * an optional sequence where the pixel size can be retrieved 164 * @return the computed descriptor or <code>null</code> if the descriptor if not found in the 165 * specified set 166 * @throws UnsupportedOperationException 167 * if the type of the given ROI is not supported by this descriptor, or if <code>sequence</code> is 168 * <code>null</code> while the calculation requires it, or if 169 * the specified Z, T or C position are not supported by the descriptor 170 */ 171 public static Object computeDescriptor(String descriptorId, ROI roi, Sequence sequence) 172 { 173 return ROIDescriptor.computeDescriptor(descriptorId, roi, sequence); 174 } 175 176 /** 177 * @deprecated Use {@link ROIStandardDeviationDescriptor} or {@link #computeDescriptor(String, ROI, Sequence)} 178 * method instead. 179 */ 180 @Deprecated 181 public static double getStandardDeviation(Sequence sequence, ROI roi, int z, int t, int c) 182 { 183 try 184 { 185 final SequenceDataIterator it = new SequenceDataIterator(sequence, roi, false, z, t, c); 186 187 long numPixels = 0; 188 double sum = 0; 189 double sum2 = 0; 190 191 // faster to do all calculation in a single iteration run 192 while (!it.done()) 193 { 194 final double value = it.get(); 195 196 sum += value; 197 sum2 += value * value; 198 numPixels++; 199 200 it.next(); 201 } 202 203 if (numPixels > 0) 204 { 205 double x1 = (sum2 / numPixels); 206 double x2 = sum / numPixels; 207 x2 *= x2; 208 209 return Math.sqrt(x1 - x2); 210 } 211 } 212 catch (Exception e) 213 { 214 // we can have exception as the process can be really long 215 // and size modified during this period 216 } 217 218 return 0d; 219 } 220 221 /** 222 * @deprecated Use {@link ROIIntensityDescriptorsPlugin} or {@link #computeDescriptor(String, ROI, Sequence)} method 223 * instead. 224 */ 225 @Deprecated 226 public static IntensityInfo getIntensityInfo(Sequence sequence, ROI roi, int z, int t, int c) 227 { 228 try 229 { 230 final IntensityInfo result = new IntensityInfo(); 231 final SequenceDataIterator it = new SequenceDataIterator(sequence, roi, false, z, t, c); 232 233 long numPixels = 0; 234 double min = Double.MAX_VALUE; 235 double max = -Double.MAX_VALUE; 236 double sum = 0; 237 238 // faster to do all calculation in a single iteration run 239 while (!it.done()) 240 { 241 final double value = it.get(); 242 243 if (value < min) 244 min = value; 245 if (value > max) 246 max = value; 247 sum += value; 248 numPixels++; 249 250 it.next(); 251 } 252 253 if (numPixels > 0) 254 { 255 result.minIntensity = min; 256 result.maxIntensity = max; 257 result.meanIntensity = sum / numPixels; 258 } 259 else 260 { 261 result.minIntensity = 0d; 262 result.maxIntensity = 0d; 263 result.meanIntensity = 0d; 264 } 265 266 return result; 267 } 268 catch (Exception e) 269 { 270 // we can have exception as the process can be really long 271 // and size modified during this period 272 return null; 273 } 274 } 275 276 /** 277 * Returns the number of sequence pixels contained in the specified ROI. 278 * 279 * @param sequence 280 * The sequence we want to get the number of pixel. 281 * @param roi 282 * The ROI define the region where we want to compute the number of pixel. 283 * @param z 284 * The specific Z position (slice) where we want to compute the number of pixel or <code>-1</code> to use the 285 * ROI Z dimension information. 286 * @param t 287 * The specific T position (frame) where we want to compute the number of pixel or <code>-1</code> to use the 288 * ROI T dimension information. 289 * @param c 290 * The specific C position (channel) where we want to compute the number of pixel or <code>-1</code> to use 291 * the ROI C dimension information. 292 */ 293 public static long getNumPixel(Sequence sequence, ROI roi, int z, int t, int c) 294 { 295 return DataIteratorUtil.count(new SequenceDataIterator(sequence, roi, false, z, t, c)); 296 } 297 298 /** 299 * @deprecated Use {@link ROIMinIntensityDescriptor} or {@link #computeDescriptor(String, ROI, Sequence)} method 300 * instead. 301 */ 302 @Deprecated 303 public static double getMinIntensity(Sequence sequence, ROI roi, int z, int t, int c) 304 { 305 return DataIteratorMath.min(new SequenceDataIterator(sequence, roi, false, z, t, c)); 306 } 307 308 /** 309 * @deprecated Use {@link ROIMaxIntensityDescriptor} or {@link #computeDescriptor(String, ROI, Sequence)} method 310 * instead. 311 */ 312 @Deprecated 313 public static double getMaxIntensity(Sequence sequence, ROI roi, int z, int t, int c) 314 { 315 return DataIteratorMath.max(new SequenceDataIterator(sequence, roi, false, z, t, c)); 316 } 317 318 /** 319 * @deprecated Use {@link ROIMeanIntensityDescriptor} or {@link #computeDescriptor(String, ROI, Sequence)} method 320 * instead. 321 */ 322 @Deprecated 323 public static double getMeanIntensity(Sequence sequence, ROI roi, int z, int t, int c) 324 { 325 return DataIteratorMath.mean(new SequenceDataIterator(sequence, roi, false, z, t, c)); 326 } 327 328 /** 329 * @deprecated Use {@link ROISumIntensityDescriptor} or {@link #computeDescriptor(String, ROI, Sequence)} method 330 * instead. 331 */ 332 @Deprecated 333 public static double getSumIntensity(Sequence sequence, ROI roi, int z, int t, int c) 334 { 335 return DataIteratorMath.sum(new SequenceDataIterator(sequence, roi, false, z, t, c)); 336 } 337 338 /** 339 * @deprecated Use {@link ROIStandardDeviationDescriptor} or {@link #computeDescriptor(String, ROI, Sequence)} 340 * method instead. 341 */ 342 @Deprecated 343 public static double getStandardDeviation(Sequence sequence, ROI roi) 344 { 345 return getStandardDeviation(sequence, roi, -1, -1, -1); 346 } 347 348 /** 349 * @deprecated Use {@link ROIIntensityDescriptorsPlugin} or {@link #computeDescriptor(String, ROI, Sequence)} method 350 * instead. 351 */ 352 @Deprecated 353 public static IntensityInfo getIntensityInfo(Sequence sequence, ROI roi) 354 { 355 return getIntensityInfo(sequence, roi, -1, -1, -1); 356 } 357 358 /** 359 * Returns the number of sequence pixels contained in the specified ROI. 360 */ 361 public static long getNumPixel(Sequence sequence, ROI roi) 362 { 363 return getNumPixel(sequence, roi, -1, -1, -1); 364 } 365 366 /** 367 * @deprecated Use {@link ROIMinIntensityDescriptor} or {@link #computeDescriptor(String, ROI, Sequence)} method 368 * instead. 369 */ 370 @Deprecated 371 public static double getMinIntensity(Sequence sequence, ROI roi) 372 { 373 return getMinIntensity(sequence, roi, -1, -1, -1); 374 } 375 376 /** 377 * @deprecated Use {@link ROIMaxIntensityDescriptor} or {@link #computeDescriptor(String, ROI, Sequence)} method 378 * instead. 379 */ 380 @Deprecated 381 public static double getMaxIntensity(Sequence sequence, ROI roi) 382 { 383 return getMaxIntensity(sequence, roi, -1, -1, -1); 384 } 385 386 /** 387 * @deprecated Use {@link ROIMeanIntensityDescriptor} or {@link #computeDescriptor(String, ROI, Sequence)} method 388 * instead. 389 */ 390 @Deprecated 391 public static double getMeanIntensity(Sequence sequence, ROI roi) 392 { 393 return getMeanIntensity(sequence, roi, -1, -1, -1); 394 } 395 396 /** 397 * @deprecated Use {@link ROISumIntensityDescriptor} or {@link #computeDescriptor(String, ROI, Sequence)} method 398 * instead. 399 */ 400 @Deprecated 401 public static double getSumIntensity(Sequence sequence, ROI roi) 402 { 403 return getSumIntensity(sequence, roi, -1, -1, -1); 404 } 405 406 /** 407 * @deprecated Use {@link ROIMassCenterDescriptorsPlugin} or {@link #computeDescriptor(String, ROI, Sequence)} 408 * method instead. 409 */ 410 @Deprecated 411 public static Point5D getMassCenter(ROI roi) 412 { 413 switch (roi.getDimension()) 414 { 415 case 2: 416 final ROI2D roi2d = (ROI2D) roi; 417 final Point2D pt2d = getMassCenter(roi2d); 418 return new Point5D.Double(pt2d.getX(), pt2d.getY(), roi2d.getZ(), roi2d.getT(), roi2d.getC()); 419 420 case 3: 421 final ROI3D roi3d = (ROI3D) roi; 422 final Point3D pt3d = getMassCenter(roi3d); 423 return new Point5D.Double(pt3d.getX(), pt3d.getY(), pt3d.getZ(), roi3d.getT(), roi3d.getC()); 424 425 case 4: 426 final ROI4D roi4d = (ROI4D) roi; 427 final Point4D pt4d = getMassCenter(roi4d); 428 return new Point5D.Double(pt4d.getX(), pt4d.getY(), pt4d.getZ(), pt4d.getT(), roi4d.getC()); 429 430 case 5: 431 return getMassCenter((ROI5D) roi); 432 433 default: 434 return null; 435 } 436 } 437 438 /** 439 * @deprecated Use {@link ROIMassCenterDescriptorsPlugin} or {@link #computeDescriptor(String, ROI, Sequence)} 440 * method instead. 441 */ 442 @Deprecated 443 public static Point2D getMassCenter(ROI2D roi) 444 { 445 double x = 0, y = 0; 446 long len = 0; 447 448 final BooleanMask2D mask = roi.getBooleanMask(true); 449 final boolean m[] = mask.mask; 450 final int h = mask.bounds.height; 451 final int w = mask.bounds.width; 452 453 int off = 0; 454 for (int j = 0; j < h; j++) 455 { 456 for (int i = 0; i < w; i++) 457 { 458 if (m[off++]) 459 { 460 x += i; 461 y += j; 462 len++; 463 } 464 } 465 } 466 467 // get bounds 468 final Rectangle2D bounds2D = roi.getBounds2D(); 469 470 // empty roi --> use bounds center 471 if (len == 0) 472 return new Point2D.Double(bounds2D.getCenterX(), bounds2D.getCenterY()); 473 474 return new Point2D.Double(bounds2D.getX() + (x / len), bounds2D.getY() + (y / len)); 475 } 476 477 /** 478 * @deprecated Use {@link ROIMassCenterDescriptorsPlugin} or {@link #computeDescriptor(String, ROI, Sequence)} 479 * method instead. 480 */ 481 @Deprecated 482 public static Point3D getMassCenter(ROI3D roi) 483 { 484 double x = 0, y = 0, z = 0; 485 long len = 0; 486 final BooleanMask3D mask3d = roi.getBooleanMask(true); 487 488 for (Integer zSlice : mask3d.mask.keySet()) 489 { 490 final int zi = zSlice.intValue(); 491 final double zd = zi; 492 final BooleanMask2D mask = mask3d.getMask2D(zi); 493 final boolean m[] = mask.mask; 494 final double bx = mask.bounds.x; 495 final double by = mask.bounds.y; 496 final int h = mask.bounds.height; 497 final int w = mask.bounds.width; 498 499 int off = 0; 500 for (int j = 0; j < h; j++) 501 { 502 for (int i = 0; i < w; i++) 503 { 504 if (m[off++]) 505 { 506 x += bx + i; 507 y += by + j; 508 z += zd; 509 len++; 510 } 511 } 512 } 513 } 514 515 // get bounds 516 final Rectangle3D bounds3D = roi.getBounds3D(); 517 518 // empty roi --> use bounds center 519 if (len == 0) 520 return new Point3D.Double(bounds3D.getCenterX(), bounds3D.getCenterY(), bounds3D.getCenterZ()); 521 522 return new Point3D.Double((x / len), (y / len), (z / len)); 523 } 524 525 /** 526 * @deprecated Use {@link ROIMassCenterDescriptorsPlugin} or {@link #computeDescriptor(String, ROI, Sequence)} 527 * method instead. 528 */ 529 @Deprecated 530 public static Point4D getMassCenter(ROI4D roi) 531 { 532 final BooleanMask4D mask4d = roi.getBooleanMask(true); 533 double x = 0, y = 0, z = 0, t = 0; 534 long len = 0; 535 536 for (Integer tFrame : mask4d.mask.keySet()) 537 { 538 final int ti = tFrame.intValue(); 539 final double td = ti; 540 final BooleanMask3D mask3d = mask4d.getMask3D(ti); 541 542 for (Integer zSlice : mask3d.mask.keySet()) 543 { 544 final int zi = zSlice.intValue(); 545 final double zd = zi; 546 final BooleanMask2D mask = mask3d.getMask2D(zi); 547 final boolean m[] = mask.mask; 548 final double bx = mask.bounds.x; 549 final double by = mask.bounds.y; 550 final int h = mask.bounds.height; 551 final int w = mask.bounds.width; 552 553 int off = 0; 554 for (int j = 0; j < h; j++) 555 { 556 for (int i = 0; i < w; i++) 557 { 558 if (m[off++]) 559 { 560 x += bx + i; 561 y += by + j; 562 z += zd; 563 t += td; 564 len++; 565 } 566 } 567 } 568 } 569 } 570 571 // get bounds 572 final Rectangle4D bounds4D = roi.getBounds4D(); 573 574 // empty roi --> use bounds center 575 if (len == 0) 576 return new Point4D.Double(bounds4D.getCenterX(), bounds4D.getCenterY(), bounds4D.getCenterZ(), 577 bounds4D.getCenterT()); 578 579 return new Point4D.Double((x / len), (y / len), (z / len), (t / len)); 580 581 } 582 583 /** 584 * @deprecated Use {@link ROIMassCenterDescriptorsPlugin} or {@link #computeDescriptor(String, ROI, Sequence)} 585 * method instead. 586 */ 587 @Deprecated 588 public static Point5D getMassCenter(ROI5D roi) 589 { 590 final BooleanMask5D mask5d = roi.getBooleanMask(true); 591 double x = 0, y = 0, z = 0, t = 0, c = 0; 592 long len = 0; 593 594 for (Integer cChannel : mask5d.mask.keySet()) 595 { 596 final int ci = cChannel.intValue(); 597 final double cd = ci; 598 final BooleanMask4D mask4d = mask5d.getMask4D(ci); 599 600 for (Integer tFrame : mask4d.mask.keySet()) 601 { 602 final int ti = tFrame.intValue(); 603 final double td = ti; 604 final BooleanMask3D mask3d = mask4d.getMask3D(ti); 605 606 for (Integer zSlice : mask3d.mask.keySet()) 607 { 608 final int zi = zSlice.intValue(); 609 final double zd = zi; 610 final BooleanMask2D mask = mask3d.getMask2D(zi); 611 final boolean m[] = mask.mask; 612 final double bx = mask.bounds.x; 613 final double by = mask.bounds.y; 614 final int h = mask.bounds.height; 615 final int w = mask.bounds.width; 616 617 int off = 0; 618 for (int j = 0; j < h; j++) 619 { 620 for (int i = 0; i < w; i++) 621 { 622 if (m[off++]) 623 { 624 x += bx + i; 625 y += by + j; 626 z += zd; 627 t += td; 628 c += cd; 629 len++; 630 } 631 } 632 } 633 } 634 } 635 } 636 637 // get bounds 638 final Rectangle5D bounds5D = roi.getBounds5D(); 639 640 // empty roi --> use bounds center 641 if (len == 0) 642 return new Point5D.Double(bounds5D.getCenterX(), bounds5D.getCenterY(), bounds5D.getCenterZ(), 643 bounds5D.getCenterT(), bounds5D.getCenterC()); 644 645 return new Point5D.Double((x / len), (y / len), (z / len), (t / len), (c / len)); 646 } 647 648 /** 649 * @deprecated 650 */ 651 @Deprecated 652 private static double getMultiplier(Sequence sequence, ROI roi, int dim) 653 { 654 final int dimRoi = roi.getDimension(); 655 656 // cannot give this information for this roi 657 if (dimRoi > dim) 658 return 0d; 659 660 final Rectangle5D boundsRoi = roi.getBounds5D(); 661 double mul = 1d; 662 663 switch (dim) 664 { 665 case 5: 666 if (dimRoi == 4) 667 { 668 final int sizeC = sequence.getSizeC(); 669 670 if ((boundsRoi.getSizeC() == Double.POSITIVE_INFINITY) && (sizeC > 1)) 671 mul *= sizeC; 672 // cannot give this information for this roi 673 else 674 mul = 0d; 675 } 676 case 4: 677 if (dimRoi == 3) 678 { 679 final int sizeT = sequence.getSizeT(); 680 681 if ((boundsRoi.getSizeT() == Double.POSITIVE_INFINITY) && (sizeT > 1)) 682 mul *= sizeT; 683 // cannot give this information for this roi 684 else 685 mul = 0d; 686 } 687 case 3: 688 if (dimRoi == 2) 689 { 690 final int sizeZ = sequence.getSizeZ(); 691 692 if ((boundsRoi.getSizeZ() == Double.POSITIVE_INFINITY) && (sizeZ > 1)) 693 mul *= sizeZ; 694 // cannot give this information for this roi 695 else 696 mul = 0d; 697 } 698 case 2: 699 if (dimRoi == 1) 700 { 701 final int sizeY = sequence.getSizeY(); 702 703 if ((boundsRoi.getSizeY() == Double.POSITIVE_INFINITY) && (sizeY > 1)) 704 mul *= sizeY; 705 // cannot give this information for this roi 706 else 707 mul = 0d; 708 } 709 } 710 711 return mul; 712 } 713 714 /** 715 * @deprecated Use {@link ROIContourDescriptor} or {@link #computeDescriptor(String, ROI, Sequence)} method instead. 716 */ 717 @Deprecated 718 public static String getContourSize(Sequence sequence, double contourPoints, ROI roi, int dim, int roundSignificant) 719 { 720 final double mul = getMultiplier(sequence, roi, dim); 721 722 // 0 means the operation is not supported for this ROI 723 if (mul != 0d) 724 return sequence.calculateSize(MathUtil.roundSignificant(contourPoints, roundSignificant) * mul, dim, 725 dim - 1, 5); 726 727 return ""; 728 } 729 730 /** 731 * @deprecated Use {@link ROIContourDescriptor} or {@link #computeDescriptor(String, ROI, Sequence)} method instead. 732 */ 733 @Deprecated 734 public static String getContourSize(Sequence sequence, ROI roi, int dim, int roundSignificant) 735 { 736 return getContourSize(sequence, roi.getNumberOfContourPoints(), roi, dim, roundSignificant); 737 } 738 739 /** 740 * @deprecated Use {@link ROIContourDescriptor} or {@link #computeDescriptor(String, ROI, Sequence)} method instead. 741 */ 742 @Deprecated 743 public static String getContourSize(Sequence sequence, ROI roi, int dim) 744 { 745 return getContourSize(sequence, roi, dim, 0); 746 } 747 748 /** 749 * @deprecated Use {@link ROIInteriorDescriptor} or {@link #computeDescriptor(String, ROI, Sequence)} method 750 * instead. 751 */ 752 @Deprecated 753 public static String getInteriorSize(Sequence sequence, double interiorPoints, ROI roi, int dim, 754 int roundSignificant) 755 { 756 final double mul = getMultiplier(sequence, roi, dim); 757 758 // 0 means the operation is not supported for this ROI 759 if (mul != 0d) 760 return sequence.calculateSize(MathUtil.roundSignificant(interiorPoints, roundSignificant) * mul, dim, dim, 761 5); 762 763 return ""; 764 } 765 766 /** 767 * @deprecated Use {@link ROIInteriorDescriptor} or {@link #computeDescriptor(String, ROI, Sequence)} method 768 * instead. 769 */ 770 @Deprecated 771 public static String getInteriorSize(Sequence sequence, ROI roi, int dim, int roundSignificant) 772 { 773 return getInteriorSize(sequence, roi.getNumberOfPoints(), roi, dim, roundSignificant); 774 } 775 776 /** 777 * @deprecated Use {@link ROIInteriorDescriptor} or {@link #computeDescriptor(String, ROI, Sequence)} method 778 * instead. 779 */ 780 @Deprecated 781 public static String getInteriorSize(Sequence sequence, ROI roi, int dim) 782 { 783 return getInteriorSize(sequence, roi, dim, 0); 784 } 785 786 /** 787 * @deprecated Use {@link ROIPerimeterDescriptor} or {@link #computeDescriptor(String, ROI, Sequence)} method 788 * instead. 789 */ 790 @Deprecated 791 public static String getPerimeter(Sequence sequence, ROI roi, int roundSignificant) 792 { 793 return getContourSize(sequence, roi, 2, roundSignificant); 794 } 795 796 /** 797 * @deprecated Use {@link ROIPerimeterDescriptor} or {@link #computeDescriptor(String, ROI, Sequence)} method 798 * instead. 799 */ 800 @Deprecated 801 public static String getPerimeter(Sequence sequence, ROI roi) 802 { 803 return getPerimeter(sequence, roi, 0); 804 } 805 806 /** 807 * @deprecated Use {@link ROIAreaDescriptor} or {@link #computeDescriptor(String, ROI, Sequence)} method instead. 808 */ 809 @Deprecated 810 public static String getArea(Sequence sequence, ROI roi, int roundSignificant) 811 { 812 return getInteriorSize(sequence, roi, 2, roundSignificant); 813 } 814 815 /** 816 * @deprecated Use {@link ROIAreaDescriptor} or {@link #computeDescriptor(String, ROI, Sequence)} method instead. 817 */ 818 @Deprecated 819 public static String getArea(Sequence sequence, ROI roi) 820 { 821 return getArea(sequence, roi, 0); 822 } 823 824 /** 825 * @deprecated Use {@link ROISurfaceAreaDescriptor} or {@link #computeDescriptor(String, ROI, Sequence)} method 826 * instead. 827 */ 828 @Deprecated 829 public static String getSurfaceArea(Sequence sequence, ROI roi, int roundSignificant) 830 { 831 return getContourSize(sequence, roi, 3, roundSignificant); 832 } 833 834 /** 835 * @deprecated Use {@link ROISurfaceAreaDescriptor} or {@link #computeDescriptor(String, ROI, Sequence)} method 836 * instead. 837 */ 838 @Deprecated 839 public static String getSurfaceArea(Sequence sequence, ROI roi) 840 { 841 return getSurfaceArea(sequence, roi, 0); 842 } 843 844 /** 845 * @deprecated Use {@link ROIVolumeDescriptor} or {@link #computeDescriptor(String, ROI, Sequence)} method instead. 846 */ 847 @Deprecated 848 public static String getVolume(Sequence sequence, ROI roi, int roundSignificant) 849 { 850 return getInteriorSize(sequence, roi, 3, roundSignificant); 851 } 852 853 /** 854 * @deprecated Use {@link ROIVolumeDescriptor} or {@link #computeDescriptor(String, ROI, Sequence)} method instead. 855 */ 856 @Deprecated 857 public static String getVolume(Sequence sequence, ROI roi) 858 { 859 return getVolume(sequence, roi, 0); 860 } 861 862 /** 863 * Returns the effective ROI number of dimension needed for the specified bounds. 864 */ 865 public static int getEffectiveDimension(Rectangle5D bounds) 866 { 867 int result = 5; 868 869 if (bounds.isInfiniteC() || (bounds.getSizeC() <= 1d)) 870 { 871 result--; 872 if (bounds.isInfiniteT() || (bounds.getSizeT() <= 1d)) 873 { 874 result--; 875 if (bounds.isInfiniteZ() || (bounds.getSizeZ() <= 1d)) 876 result--; 877 } 878 } 879 880 return result; 881 } 882 883 /** 884 * Return 5D dimension for specified operation dimension 885 */ 886 private static Dimension5D.Integer getOpDim(int dim, Rectangle5D.Integer bounds) 887 { 888 final Dimension5D.Integer result = new Dimension5D.Integer(); 889 890 switch (dim) 891 { 892 case 2: // XY ROI with fixed ZTC 893 result.sizeZ = 1; 894 result.sizeT = 1; 895 result.sizeC = 1; 896 break; 897 898 case 3: // XYZ ROI with fixed TC 899 result.sizeZ = bounds.sizeZ; 900 result.sizeT = 1; 901 result.sizeC = 1; 902 break; 903 904 case 4: // XYZT ROI with fixed C 905 result.sizeZ = bounds.sizeZ; 906 result.sizeT = bounds.sizeT; 907 result.sizeC = 1; 908 break; 909 910 default: // XYZTC ROI 911 result.sizeZ = bounds.sizeZ; 912 result.sizeT = bounds.sizeT; 913 result.sizeC = bounds.sizeC; 914 break; 915 } 916 917 return result; 918 } 919 920 /** 921 * Get ROI result for specified 5D mask and operation dimension. 922 */ 923 private static ROI getOpResult(int dim, BooleanMask5D mask, Rectangle5D.Integer bounds) 924 { 925 final ROI result; 926 927 switch (dim) 928 { 929 case 2: // XY ROI with fixed ZTC 930 result = new ROI2DArea(mask.getMask2D(bounds.z, bounds.t, bounds.c)); 931 932 // set ZTC position 933 result.beginUpdate(); 934 try 935 { 936 ((ROI2D) result).setZ(bounds.z); 937 ((ROI2D) result).setT(bounds.t); 938 ((ROI2D) result).setC(bounds.c); 939 } 940 finally 941 { 942 result.endUpdate(); 943 } 944 break; 945 946 case 3: // XYZ ROI with fixed TC 947 result = new ROI3DArea(mask.getMask3D(bounds.t, bounds.c)); 948 949 // set TC position 950 result.beginUpdate(); 951 try 952 { 953 ((ROI3D) result).setT(bounds.t); 954 ((ROI3D) result).setC(bounds.c); 955 } 956 finally 957 { 958 result.endUpdate(); 959 } 960 break; 961 962 case 4: // XYZT ROI with fixed C 963 result = new ROI4DArea(mask.getMask4D(bounds.c)); 964 // set C position 965 ((ROI4D) result).setC(bounds.c); 966 break; 967 968 case 5: // XYZTC ROI 969 result = new ROI5DArea(mask); 970 break; 971 972 default: 973 throw new UnsupportedOperationException( 974 "Can't process boolean operation on a ROI with unknown dimension."); 975 } 976 977 return result; 978 } 979 980 /** 981 * Compute the resulting bounds for <i>union</i> operation between specified ROIs.<br> 982 * It throws an exception if the <i>union</i> operation cannot be done (incompatible dimension). 983 */ 984 public static Rectangle5D getUnionBounds(ROI roi1, ROI roi2) throws UnsupportedOperationException 985 { 986 // null checking 987 if (roi1 == null) 988 { 989 if (roi2 == null) 990 return new Rectangle5D.Double(); 991 return roi2.getBounds5D(); 992 } 993 else if (roi2 == null) 994 return roi1.getBounds5D(); 995 996 final Rectangle5D bounds1 = roi1.getBounds5D(); 997 final Rectangle5D bounds2 = roi2.getBounds5D(); 998 999 // init infinite dim infos 1000 final boolean ic1 = bounds1.isInfiniteC(); 1001 final boolean ic2 = bounds2.isInfiniteC(); 1002 final boolean it1 = bounds1.isInfiniteT(); 1003 final boolean it2 = bounds2.isInfiniteT(); 1004 final boolean iz1 = bounds1.isInfiniteZ(); 1005 final boolean iz2 = bounds2.isInfiniteZ(); 1006 1007 // cannot process union when we have an infinite dimension with a finite one 1008 if ((ic1 ^ ic2) || (it1 ^ it2) || (iz1 ^ iz2)) 1009 throw new UnsupportedOperationException("Can't process union on ROI with different infinite dimension"); 1010 1011 // do union 1012 Rectangle5D.union(bounds1, bounds2, bounds1); 1013 1014 // init infinite dim infos on result 1015 final boolean ic = bounds1.isInfiniteC(); // || (bounds1.getSizeC() <= 1d); 1016 final boolean it = bounds1.isInfiniteT(); // || (bounds1.getSizeT() <= 1d); 1017 final boolean iz = bounds1.isInfiniteZ(); // || (bounds1.getSizeZ() <= 1d); 1018 1019 // cannot process union if C dimension is finite but T or Z is infinite 1020 if (!ic && (it || iz)) 1021 throw new UnsupportedOperationException( 1022 "Can't process union on ROI with a finite C dimension and infinite T or Z dimension"); 1023 // cannot process union if T dimension is finite but Z is infinite 1024 if (!it && iz) 1025 throw new UnsupportedOperationException( 1026 "Can't process union on ROI with a finite T dimension and infinite Z dimension"); 1027 1028 return bounds1; 1029 } 1030 1031 /** 1032 * Compute the resulting bounds for <i>intersection</i> operation between specified ROIs.<br> 1033 * It throws an exception if the <i>intersection</i> operation cannot be done (incompatible dimension). 1034 */ 1035 protected static Rectangle5D getIntersectionBounds(ROI roi1, ROI roi2) throws UnsupportedOperationException 1036 { 1037 // null checking 1038 if ((roi1 == null) || (roi2 == null)) 1039 return new Rectangle5D.Double(); 1040 1041 final Rectangle5D bounds1 = roi1.getBounds5D(); 1042 final Rectangle5D bounds2 = roi2.getBounds5D(); 1043 1044 // do intersection 1045 Rectangle5D.intersect(bounds1, bounds2, bounds1); 1046 1047 // init infinite dim infos 1048 final boolean ic = bounds1.isInfiniteC(); // || (bounds1.getSizeC() <= 1d); 1049 final boolean it = bounds1.isInfiniteT(); // || (bounds1.getSizeT() <= 1d); 1050 final boolean iz = bounds1.isInfiniteZ(); // || (bounds1.getSizeZ() <= 1d); 1051 1052 // cannot process intersection if C dimension is finite but T or Z is infinite 1053 if (!ic && (it || iz)) 1054 throw new UnsupportedOperationException( 1055 "Can't process intersection on ROI with a finite C dimension and infinite T or Z dimension"); 1056 // cannot process intersection if T dimension is finite but Z is infinite 1057 if (!it && iz) 1058 throw new UnsupportedOperationException( 1059 "Can't process intersection on ROI with a finite T dimension and infinite Z dimension"); 1060 1061 return bounds1; 1062 } 1063 1064 /** 1065 * Compute the resulting bounds for <i>subtraction</i> of (roi1 - roi2).<br> 1066 * It throws an exception if the <i>subtraction</i> operation cannot be done (incompatible dimension). 1067 */ 1068 protected static Rectangle5D getSubtractionBounds(ROI roi1, ROI roi2) throws UnsupportedOperationException 1069 { 1070 // null checking 1071 if (roi1 == null) 1072 return new Rectangle5D.Double(); 1073 if (roi2 == null) 1074 return roi1.getBounds5D(); 1075 1076 final Rectangle5D bounds1 = roi1.getBounds5D(); 1077 final Rectangle5D bounds2 = roi2.getBounds5D(); 1078 1079 // init infinite dim infos 1080 final boolean ic1 = bounds1.isInfiniteC(); 1081 final boolean ic2 = bounds2.isInfiniteC(); 1082 final boolean it1 = bounds1.isInfiniteT(); 1083 final boolean it2 = bounds2.isInfiniteT(); 1084 final boolean iz1 = bounds1.isInfiniteZ(); 1085 final boolean iz2 = bounds2.isInfiniteZ(); 1086 1087 // cannot process subtraction when we have an finite dimension on second ROI 1088 // while having a infinite one on the first ROI 1089 if (ic1 && !ic2) 1090 throw new UnsupportedOperationException( 1091 "Can't process subtraction: ROI 1 has infinite C dimension while ROI 2 has a finite one"); 1092 if (it1 && !it2) 1093 throw new UnsupportedOperationException( 1094 "Can't process subtraction: ROI 1 has infinite T dimension while ROI 2 has a finite one"); 1095 if (iz1 && !iz2) 1096 throw new UnsupportedOperationException( 1097 "Can't process subtraction: ROI 1 has infinite Z dimension while ROI 2 has a finite one"); 1098 1099 return bounds1; 1100 } 1101 1102 /** 1103 * Computes union of specified <code>ROI</code> and return result in a new <code>ROI</code>. 1104 */ 1105 public static ROI getUnion(ROI roi1, ROI roi2) throws UnsupportedOperationException 1106 { 1107 // null checking 1108 if (roi1 == null) 1109 { 1110 // return empty ROI 1111 if (roi2 == null) 1112 return new ROI2DArea(); 1113 // return simple copy 1114 return roi2.getCopy(); 1115 } 1116 else if (roi2 == null) 1117 return roi1.getCopy(); 1118 1119 final Rectangle5D bounds5D = getUnionBounds(roi1, roi2); 1120 final int dim = getEffectiveDimension(bounds5D); 1121 1122 // we want integer bounds now 1123 final Rectangle5D.Integer bounds = bounds5D.toInteger(); 1124 final Dimension5D.Integer roiSize = getOpDim(dim, bounds); 1125 // get 3D and 4D bounds 1126 final Rectangle3D.Integer bounds3D = (Rectangle3D.Integer) bounds.toRectangle3D(); 1127 final Rectangle4D.Integer bounds4D = (Rectangle4D.Integer) bounds.toRectangle4D(); 1128 1129 final BooleanMask4D mask5D[] = new BooleanMask4D[roiSize.sizeC]; 1130 1131 for (int c = 0; c < roiSize.sizeC; c++) 1132 { 1133 final BooleanMask3D mask4D[] = new BooleanMask3D[roiSize.sizeT]; 1134 1135 for (int t = 0; t < roiSize.sizeT; t++) 1136 { 1137 final BooleanMask2D mask3D[] = new BooleanMask2D[roiSize.sizeZ]; 1138 1139 for (int z = 0; z < roiSize.sizeZ; z++) 1140 { 1141 mask3D[z] = BooleanMask2D.getUnion( 1142 roi1.getBooleanMask2D(bounds.z + z, bounds.t + t, bounds.c + c, true), 1143 roi2.getBooleanMask2D(bounds.z + z, bounds.t + t, bounds.c + c, true)); 1144 } 1145 1146 mask4D[t] = new BooleanMask3D(new Rectangle3D.Integer(bounds3D), mask3D); 1147 } 1148 1149 mask5D[c] = new BooleanMask4D(new Rectangle4D.Integer(bounds4D), mask4D); 1150 } 1151 1152 // build the 5D result ROI 1153 final BooleanMask5D mask = new BooleanMask5D(bounds, mask5D); 1154 // optimize bounds of the new created mask 1155 mask.optimizeBounds(); 1156 1157 // get result 1158 final ROI result = getOpResult(dim, mask, bounds); 1159 // set name 1160 result.setName("Union"); 1161 1162 return result; 1163 } 1164 1165 /** 1166 * Computes intersection of specified <code>ROI</code> and return result in a new <code>ROI</code>. 1167 */ 1168 public static ROI getIntersection(ROI roi1, ROI roi2) throws UnsupportedOperationException 1169 { 1170 // null checking 1171 if ((roi1 == null) || (roi2 == null)) 1172 // return empty ROI 1173 return new ROI2DArea(); 1174 1175 final Rectangle5D bounds5D = getIntersectionBounds(roi1, roi2); 1176 final int dim = getEffectiveDimension(bounds5D); 1177 1178 // we want integer bounds now 1179 final Rectangle5D.Integer bounds = bounds5D.toInteger(); 1180 final Dimension5D.Integer roiSize = getOpDim(dim, bounds); 1181 // get 2D, 3D and 4D bounds 1182 final Rectangle bounds2D = (Rectangle) bounds.toRectangle2D(); 1183 final Rectangle3D.Integer bounds3D = (Rectangle3D.Integer) bounds.toRectangle3D(); 1184 final Rectangle4D.Integer bounds4D = (Rectangle4D.Integer) bounds.toRectangle4D(); 1185 1186 final BooleanMask4D mask5D[] = new BooleanMask4D[roiSize.sizeC]; 1187 1188 for (int c = 0; c < roiSize.sizeC; c++) 1189 { 1190 final BooleanMask3D mask4D[] = new BooleanMask3D[roiSize.sizeT]; 1191 1192 for (int t = 0; t < roiSize.sizeT; t++) 1193 { 1194 final BooleanMask2D mask3D[] = new BooleanMask2D[roiSize.sizeZ]; 1195 1196 for (int z = 0; z < roiSize.sizeZ; z++) 1197 { 1198 final BooleanMask2D roi1Mask2D = new BooleanMask2D(new Rectangle(bounds2D), 1199 roi1.getBooleanMask2D(bounds2D, bounds.z + z, bounds.t + t, bounds.c + c, true)); 1200 final BooleanMask2D roi2Mask2D = new BooleanMask2D(new Rectangle(bounds2D), 1201 roi2.getBooleanMask2D(bounds2D, bounds.z + z, bounds.t + t, bounds.c + c, true)); 1202 1203 mask3D[z] = BooleanMask2D.getIntersection(roi1Mask2D, roi2Mask2D); 1204 } 1205 1206 mask4D[t] = new BooleanMask3D(new Rectangle3D.Integer(bounds3D), mask3D); 1207 } 1208 1209 mask5D[c] = new BooleanMask4D(new Rectangle4D.Integer(bounds4D), mask4D); 1210 } 1211 1212 // build the 5D result ROI 1213 final BooleanMask5D mask = new BooleanMask5D(bounds, mask5D); 1214 // optimize bounds of the new created mask 1215 mask.optimizeBounds(); 1216 1217 // get result 1218 final ROI result = getOpResult(dim, mask, bounds); 1219 // set name 1220 result.setName("Intersection"); 1221 1222 return result; 1223 } 1224 1225 /** 1226 * Compute exclusive union of specified <code>ROI</code> and return result in a new <code>ROI</code>. 1227 */ 1228 public static ROI getExclusiveUnion(ROI roi1, ROI roi2) throws UnsupportedOperationException 1229 { 1230 // null checking 1231 if (roi1 == null) 1232 { 1233 // return empty ROI 1234 if (roi2 == null) 1235 return new ROI2DArea(); 1236 // return simple copy 1237 return roi2.getCopy(); 1238 } 1239 else if (roi2 == null) 1240 return roi1.getCopy(); 1241 1242 final Rectangle5D bounds5D = getUnionBounds(roi1, roi2); 1243 final int dim = getEffectiveDimension(bounds5D); 1244 1245 // we want integer bounds now 1246 final Rectangle5D.Integer bounds = bounds5D.toInteger(); 1247 final Dimension5D.Integer roiSize = getOpDim(dim, bounds); 1248 // get 3D and 4D bounds 1249 final Rectangle3D.Integer bounds3D = (Rectangle3D.Integer) bounds.toRectangle3D(); 1250 final Rectangle4D.Integer bounds4D = (Rectangle4D.Integer) bounds.toRectangle4D(); 1251 1252 final BooleanMask4D mask5D[] = new BooleanMask4D[roiSize.sizeC]; 1253 1254 for (int c = 0; c < roiSize.sizeC; c++) 1255 { 1256 final BooleanMask3D mask4D[] = new BooleanMask3D[roiSize.sizeT]; 1257 1258 for (int t = 0; t < roiSize.sizeT; t++) 1259 { 1260 final BooleanMask2D mask3D[] = new BooleanMask2D[roiSize.sizeZ]; 1261 1262 for (int z = 0; z < roiSize.sizeZ; z++) 1263 { 1264 mask3D[z] = BooleanMask2D.getExclusiveUnion( 1265 roi1.getBooleanMask2D(bounds.z + z, bounds.t + t, bounds.c + c, true), 1266 roi2.getBooleanMask2D(bounds.z + z, bounds.t + t, bounds.c + c, true)); 1267 } 1268 1269 mask4D[t] = new BooleanMask3D(new Rectangle3D.Integer(bounds3D), mask3D); 1270 } 1271 1272 mask5D[c] = new BooleanMask4D(new Rectangle4D.Integer(bounds4D), mask4D); 1273 } 1274 1275 // build the 5D result ROI 1276 final BooleanMask5D mask = new BooleanMask5D(bounds, mask5D); 1277 // optimize bounds of the new created mask 1278 mask.optimizeBounds(); 1279 1280 // get result 1281 final ROI result = getOpResult(dim, mask, bounds); 1282 // set name 1283 result.setName("Exclusive union"); 1284 1285 return result; 1286 } 1287 1288 /** 1289 * Computes the subtraction of roi1 - roi2 and returns result in a new <code>ROI</code>. 1290 */ 1291 public static ROI getSubtraction(ROI roi1, ROI roi2) throws UnsupportedOperationException 1292 { 1293 // return empty ROI 1294 if (roi1 == null) 1295 return new ROI2DArea(); 1296 // return copy of ROI1 1297 if (roi2 == null) 1298 return roi1.getCopy(); 1299 1300 final Rectangle5D bounds5D = getSubtractionBounds(roi1, roi2); 1301 final int dim = getEffectiveDimension(bounds5D); 1302 1303 // we want integer bounds now 1304 final Rectangle5D.Integer bounds = bounds5D.toInteger(); 1305 final Dimension5D.Integer roiSize = getOpDim(dim, bounds); 1306 // get 3D and 4D bounds 1307 final Rectangle3D.Integer bounds3D = (Rectangle3D.Integer) bounds.toRectangle3D(); 1308 final Rectangle4D.Integer bounds4D = (Rectangle4D.Integer) bounds.toRectangle4D(); 1309 1310 final BooleanMask4D mask5D[] = new BooleanMask4D[roiSize.sizeC]; 1311 1312 for (int c = 0; c < roiSize.sizeC; c++) 1313 { 1314 final BooleanMask3D mask4D[] = new BooleanMask3D[roiSize.sizeT]; 1315 1316 for (int t = 0; t < roiSize.sizeT; t++) 1317 { 1318 final BooleanMask2D mask3D[] = new BooleanMask2D[roiSize.sizeZ]; 1319 1320 for (int z = 0; z < roiSize.sizeZ; z++) 1321 { 1322 mask3D[z] = BooleanMask2D.getSubtraction( 1323 roi1.getBooleanMask2D(bounds.z + z, bounds.t + t, bounds.c + c, true), 1324 roi2.getBooleanMask2D(bounds.z + z, bounds.t + t, bounds.c + c, true)); 1325 } 1326 1327 mask4D[t] = new BooleanMask3D(new Rectangle3D.Integer(bounds3D), mask3D); 1328 } 1329 1330 mask5D[c] = new BooleanMask4D(new Rectangle4D.Integer(bounds4D), mask4D); 1331 } 1332 1333 // build the 5D result ROI 1334 final BooleanMask5D mask = new BooleanMask5D(bounds, mask5D); 1335 // optimize bounds of the new created mask 1336 mask.optimizeBounds(); 1337 1338 // get result 1339 final ROI result = getOpResult(dim, mask, bounds); 1340 // set name 1341 result.setName("Substraction"); 1342 1343 return result; 1344 } 1345 1346 /** 1347 * Merge the specified array of {@link ROI} with the given {@link BooleanOperator}.<br> 1348 * 1349 * @param rois 1350 * ROIs we want to merge. 1351 * @param operator 1352 * {@link BooleanOperator} to apply. 1353 * @return {@link ROI} representing the result of the merge operation. 1354 */ 1355 public static ROI merge(List<? extends ROI> rois, BooleanOperator operator) throws UnsupportedOperationException 1356 { 1357 if (rois.size() == 0) 1358 return null; 1359 1360 ROI result = rois.get(0).getCopy(); 1361 1362 // copy can fail... 1363 if (result != null) 1364 { 1365 switch (operator) 1366 { 1367 case AND: 1368 for (int i = 1; i < rois.size(); i++) 1369 result = result.intersect(rois.get(i), true); 1370 break; 1371 case OR: 1372 for (int i = 1; i < rois.size(); i++) 1373 result = result.add(rois.get(i), true); 1374 break; 1375 case XOR: 1376 for (int i = 1; i < rois.size(); i++) 1377 result = result.exclusiveAdd(rois.get(i), true); 1378 break; 1379 } 1380 } 1381 1382 // for (int i = 1; i < rois.size(); i++) 1383 // { 1384 // final ROI roi = rois.get(i); 1385 // 1386 // switch (operator) 1387 // { 1388 // case AND: 1389 // result = result.getIntersection(roi); 1390 // break; 1391 // case OR: 1392 // result = result.getUnion(roi); 1393 // break; 1394 // case XOR: 1395 // result = result.getExclusiveUnion(roi); 1396 // break; 1397 // } 1398 // } 1399 1400 return result; 1401 } 1402 1403 /** 1404 * Builds and returns a ROI corresponding to the union of the specified ROI list. 1405 */ 1406 public static ROI getUnion(List<? extends ROI> rois) throws UnsupportedOperationException 1407 { 1408 return merge(rois, BooleanOperator.OR); 1409 } 1410 1411 /** 1412 * Builds and returns a ROI corresponding to the exclusive union of the specified ROI list. 1413 */ 1414 public static ROI getExclusiveUnion(List<? extends ROI> rois) throws UnsupportedOperationException 1415 { 1416 return merge(rois, BooleanOperator.XOR); 1417 } 1418 1419 /** 1420 * Builds and returns a ROI corresponding to the intersection of the specified ROI list. 1421 */ 1422 public static ROI getIntersection(List<? extends ROI> rois) throws UnsupportedOperationException 1423 { 1424 return merge(rois, BooleanOperator.AND); 1425 } 1426 1427 /** 1428 * Subtract the content of the roi2 from the roi1 and return the result as a new {@link ROI}.<br> 1429 * This is equivalent to: <code>roi1.getSubtraction(roi2)</code> 1430 * 1431 * @return {@link ROI} representing the result of subtraction. 1432 */ 1433 public static ROI subtract(ROI roi1, ROI roi2) throws UnsupportedOperationException 1434 { 1435 return roi1.getSubtraction(roi2); 1436 } 1437 1438 /** 1439 * Converts the specified ROI to a ROI Point ({@link ROI2DPoint} or {@link ROI3DPoint}) representing the mass center of the input ROI. 1440 * 1441 * @return the ROI point representing the mass center of the input ROI. 1442 */ 1443 public static ROI convertToPoint(ROI roi) 1444 { 1445 final ROI result; 1446 final Point5D pt = ROIMassCenterDescriptorsPlugin.computeMassCenter(roi); 1447 1448 if (roi instanceof ROI2D) 1449 { 1450 result = new ROI2DPoint(pt.getX(), pt.getY()); 1451 ((ROI2DPoint) result).setZ(((ROI2D) roi).getZ()); 1452 ((ROI2DPoint) result).setT(((ROI2D) roi).getT()); 1453 ((ROI2DPoint) result).setC(((ROI2D) roi).getC()); 1454 } 1455 else if (roi instanceof ROI3D) 1456 { 1457 result = new ROI3DPoint(pt.getX(), pt.getY(), pt.getZ()); 1458 ((ROI3DPoint) result).setT(((ROI3D) roi).getT()); 1459 ((ROI3DPoint) result).setC(((ROI3D) roi).getC()); 1460 } 1461 else 1462 { 1463 result = new ROI3DPoint(pt.getX(), pt.getY(), pt.getZ()); 1464 ((ROI3DPoint) result).setT((int) pt.getT()); 1465 ((ROI3DPoint) result).setC((int) pt.getC()); 1466 } 1467 1468 // preserve properties 1469 ROIUtil.copyROIProperties(roi, result, true); 1470 1471 return result; 1472 } 1473 1474 /** 1475 * Converts the specified ROI to a 2D ellipse type ROI centered on the mass center of the input ROI. 1476 * 1477 * @return the 2D ellipse ROI centered on the mass center of the input ROI. 1478 */ 1479 public static ROI2DEllipse convertToEllipse(ROI roi, double radiusX, double radiusY) 1480 { 1481 final Point5D pt = ROIMassCenterDescriptorsPlugin.computeMassCenter(roi); 1482 final double x = pt.getX(); 1483 final double y = pt.getY(); 1484 final ROI2DEllipse result = new ROI2DEllipse(x - radiusX, y - radiusY, x + radiusX, y + radiusY); 1485 1486 if (roi instanceof ROI2D) 1487 { 1488 result.setZ(((ROI2D) roi).getZ()); 1489 result.setT(((ROI2D) roi).getT()); 1490 result.setC(((ROI2D) roi).getC()); 1491 } 1492 else if (roi instanceof ROI3D) 1493 { 1494 result.setZ((int) pt.getZ()); 1495 result.setT(((ROI3D) roi).getT()); 1496 result.setC(((ROI3D) roi).getC()); 1497 } 1498 else 1499 { 1500 result.setZ((int) pt.getZ()); 1501 result.setT((int) pt.getT()); 1502 result.setC((int) pt.getC()); 1503 } 1504 1505 // preserve properties 1506 ROIUtil.copyROIProperties(roi, result, true); 1507 1508 return result; 1509 } 1510 1511 /** 1512 * Converts the specified ROI to a 2D rectangle type ROI centered on the mass center of the input ROI. 1513 * 1514 * @return the 2D rectangle ROI centered on the mass center of the input ROI. 1515 */ 1516 public static ROI2DRectangle convertToRectangle(ROI roi, double width, double height) 1517 { 1518 final Point5D pt = ROIMassCenterDescriptorsPlugin.computeMassCenter(roi); 1519 final double x = pt.getX(); 1520 final double y = pt.getY(); 1521 final double rw = width / 2; 1522 final double rh = height / 2; 1523 final ROI2DRectangle result = new ROI2DRectangle(x - rw, y - rh, x + rw, y + rh); 1524 1525 if (roi instanceof ROI2D) 1526 { 1527 result.setZ(((ROI2D) roi).getZ()); 1528 result.setT(((ROI2D) roi).getT()); 1529 result.setC(((ROI2D) roi).getC()); 1530 } 1531 else if (roi instanceof ROI3D) 1532 { 1533 result.setZ((int) pt.getZ()); 1534 result.setT(((ROI3D) roi).getT()); 1535 result.setC(((ROI3D) roi).getC()); 1536 } 1537 else 1538 { 1539 result.setZ((int) pt.getZ()); 1540 result.setT((int) pt.getT()); 1541 result.setC((int) pt.getC()); 1542 } 1543 1544 // preserve properties 1545 ROIUtil.copyROIProperties(roi, result, true); 1546 1547 return result; 1548 } 1549 1550 /** 1551 * Converts the specified 2D ROI to 3D Stack ROI (ROI3DStack) by stacking it along the Z axis given zMin and zMax 1552 * (inclusive) parameters. 1553 * 1554 * @return the converted 3D stack ROI or <code>null</code> if the input ROI was null 1555 */ 1556 public static ROI convertToStack(ROI2D roi, int zMin, int zMax) 1557 { 1558 ROI result = null; 1559 1560 if (roi instanceof ROI2DRectangle) 1561 result = new ROI3DStackRectangle(((ROI2DRectangle) roi).getRectangle(), zMin, zMax); 1562 else if (roi instanceof ROI2DEllipse) 1563 result = new ROI3DStackEllipse(((ROI2DEllipse) roi).getEllipse(), zMin, zMax); 1564 else if (roi instanceof ROI2DPolygon) 1565 result = new ROI3DStackPolygon(((ROI2DPolygon) roi).getPolygon2D(), zMin, zMax); 1566 else if (roi instanceof ROI2DArea) 1567 result = new ROI3DArea(((ROI2DArea) roi).getBooleanMask(true), zMin, zMax); 1568 else if (roi != null) 1569 result = new ROI3DArea(roi.getBooleanMask2D(roi.getZ(), roi.getT(), roi.getC(), true), zMin, zMax); 1570 1571 if ((roi != null) && (result != null)) 1572 { 1573 // unselect all control points 1574 result.unselectAllPoints(); 1575 // keep original ROI informations 1576 result.setName(roi.getName() + STACK_SUFFIX); 1577 copyROIProperties(roi, result, false); 1578 } 1579 1580 return result; 1581 } 1582 1583 /** 1584 * Converts the specified ROI to a boolean mask type ROI (ROI Area). 1585 * 1586 * @return the ROI Area corresponding to the input ROI.<br> 1587 * If the ROI is already of boolean mask type then it's directly returned without any conversion. 1588 */ 1589 public static ROI convertToMask(ROI roi) 1590 { 1591 // no conversion needed 1592 if ((roi instanceof ROI2DArea) || (roi instanceof ROI3DArea) || (roi instanceof ROI4DArea) 1593 || (roi instanceof ROI5DArea)) 1594 return roi; 1595 1596 final Rectangle5D bounds5D = roi.getBounds5D(); 1597 final int dim = getEffectiveDimension(bounds5D); 1598 1599 // we want integer bounds now 1600 final Rectangle5D.Integer bounds = bounds5D.toInteger(); 1601 final Dimension5D.Integer roiSize = getOpDim(dim, bounds); 1602 // get 2D, 3D and 4D bounds 1603 final Rectangle bounds2D = (Rectangle) bounds.toRectangle2D(); 1604 final Rectangle3D.Integer bounds3D = (Rectangle3D.Integer) bounds.toRectangle3D(); 1605 final Rectangle4D.Integer bounds4D = (Rectangle4D.Integer) bounds.toRectangle4D(); 1606 1607 // build 5D mask result 1608 final BooleanMask4D mask5D[] = new BooleanMask4D[roiSize.sizeC]; 1609 1610 for (int c = 0; c < roiSize.sizeC; c++) 1611 { 1612 final BooleanMask3D mask4D[] = new BooleanMask3D[roiSize.sizeT]; 1613 1614 for (int t = 0; t < roiSize.sizeT; t++) 1615 { 1616 final BooleanMask2D mask3D[] = new BooleanMask2D[roiSize.sizeZ]; 1617 1618 for (int z = 0; z < roiSize.sizeZ; z++) 1619 mask3D[z] = new BooleanMask2D(new Rectangle(bounds2D), 1620 roi.getBooleanMask2D(bounds2D, bounds.z + z, bounds.t + t, bounds.c + c, true)); 1621 1622 mask4D[t] = new BooleanMask3D(new Rectangle3D.Integer(bounds3D), mask3D); 1623 } 1624 1625 mask5D[c] = new BooleanMask4D(new Rectangle4D.Integer(bounds4D), mask4D); 1626 } 1627 1628 // build the 5D result ROI 1629 final BooleanMask5D mask = new BooleanMask5D(bounds, mask5D); 1630 // optimize bounds of the new created mask 1631 mask.optimizeBounds(); 1632 1633 // get result 1634 final ROI result = getOpResult(dim, mask, bounds); 1635 1636 // keep original ROI informations 1637 String newName = roi.getName() + MASK_SUFFIX; 1638 // check if we can shorter name 1639 final String cancelableSuffix = SHAPE_SUFFIX + MASK_SUFFIX; 1640 if (newName.endsWith(cancelableSuffix)) 1641 newName = newName.substring(0, newName.length() - cancelableSuffix.length()); 1642 // set name 1643 result.setName(newName); 1644 // copy properties 1645 copyROIProperties(roi, result, false); 1646 1647 return result; 1648 } 1649 1650 /** 1651 * Converts the specified ROI to a shape type ROI (ROI Polygon or ROI Mesh). 1652 * 1653 * @param roi 1654 * the roi to convert to shape type ROI 1655 * @param maxDeviation 1656 * maximum allowed deviation/distance of resulting ROI polygon from the input ROI contour (in pixel). 1657 * Use <code>-1</code> for automatic maximum deviation calculation. 1658 * @return the ROI Polygon or ROI Mesh corresponding to the input ROI.<br> 1659 * If the ROI is already of shape type then it's directly returned without any conversion. 1660 */ 1661 public static ROI convertToShape(ROI roi, double maxDeviation) throws UnsupportedOperationException 1662 { 1663 if (roi instanceof ROI2DShape) 1664 return roi; 1665 1666 if (roi instanceof ROI2D) 1667 { 1668 final ROI2D roi2d = (ROI2D) roi; 1669 1670 // get contour points in connected order 1671 final List<Point> points = roi2d.getBooleanMask(true).getConnectedContourPoints(); 1672 1673 // convert to point2D and center points in observed pixel. 1674 final List<Point2D> points2D = new ArrayList<Point2D>(points.size()); 1675 for (Point pt : points) 1676 points2D.add(new Point2D.Double(pt.x + 0.5d, pt.y + 0.5d)); 1677 1678 final double dev; 1679 1680 // auto deviation 1681 if (maxDeviation < 0) 1682 { 1683 // compute it from ROI size 1684 final Rectangle2D bnd = roi2d.getBounds2D(); 1685 dev = Math.log10(Math.sqrt(bnd.getWidth() * bnd.getHeight())) / Math.log10(3); 1686 } 1687 else 1688 dev = maxDeviation; 1689 1690 // convert to ROI polygon 1691 final ROI2DPolygon result = new ROI2DPolygon(Polygon2D.getPolygon2D(points2D, dev)); 1692 1693 // keep original ROI informations 1694 String newName = roi.getName() + SHAPE_SUFFIX; 1695 // check if we can shorter name 1696 final String cancelableSuffix = MASK_SUFFIX + SHAPE_SUFFIX; 1697 if (newName.endsWith(cancelableSuffix)) 1698 newName = newName.substring(0, newName.length() - cancelableSuffix.length()); 1699 // set name 1700 result.setName(newName); 1701 // copy properties 1702 copyROIProperties(roi, result, false); 1703 1704 return result; 1705 } 1706 1707 if (roi instanceof ROI3D) 1708 { 1709 // not yet supported 1710 throw new UnsupportedOperationException("ROIUtil.convertToShape(ROI): Operation not supported for 3D ROI."); 1711 1712 } 1713 1714 throw new UnsupportedOperationException( 1715 "ROIUtil.convertToShape(ROI): Operation not supported for this ROI: " + roi.getName()); 1716 } 1717 1718 /** 1719 * Returns connected component from specified ROI as a list of ROI (Area type). 1720 */ 1721 public static List<ROI> getConnectedComponents(ROI roi) throws UnsupportedOperationException 1722 { 1723 final List<ROI> result = new ArrayList<ROI>(); 1724 1725 if (roi instanceof ROI2D) 1726 { 1727 final ROI2D roi2d = (ROI2D) roi; 1728 int ind = 0; 1729 1730 for (BooleanMask2D component : roi2d.getBooleanMask(true).getComponents()) 1731 { 1732 final ROI2DArea componentRoi = new ROI2DArea(component); 1733 1734 if (!componentRoi.isEmpty()) 1735 { 1736 // keep original ROI informations 1737 componentRoi.setName(roi.getName() + OBJECT_SUFFIX + " #" + ind++); 1738 copyROIProperties(roi, componentRoi, false); 1739 1740 result.add(componentRoi); 1741 } 1742 } 1743 1744 return result; 1745 } 1746 1747 if (roi instanceof ROI3D) 1748 { 1749 // TODO: add label extractor implementation here 1750 1751 // final ROI3D roi3d = (ROI3D) roi; 1752 // int ind = 0; 1753 // 1754 // for (BooleanMask3D component : roi3d.getBooleanMask(true).getComponents()) 1755 // { 1756 // final ROI2DArea componentRoi = new ROI2DArea(component); 1757 // 1758 // if (!componentRoi.isEmpty()) 1759 // { 1760 // // keep original ROI informations 1761 // componentRoi.setName(roi.getName() + " object #" + ind++); 1762 // copyROIProperties(roi, componentRoi, false); 1763 // 1764 // result.add(componentRoi); 1765 // } 1766 // } 1767 1768 // not yet supported 1769 throw new UnsupportedOperationException( 1770 "ROIUtil.getConnectedComponents(ROI): Operation not supported yet for 3D ROI."); 1771 } 1772 1773 throw new UnsupportedOperationException( 1774 "ROIUtil.getConnectedComponents(ROI): Operation not supported for this ROI: " + roi.getName()); 1775 } 1776 1777 static boolean computePolysFromLine(Line2D line, Point2D edgePt1, Point2D edgePt2, Polygon2D poly1, Polygon2D poly2, 1778 boolean inner) 1779 { 1780 final Line2D edgeLine = new Line2D.Double(edgePt1, edgePt2); 1781 1782 // they intersect ? 1783 if (edgeLine.intersectsLine(line)) 1784 { 1785 final Point2D intersection = Line2DUtil.getIntersection(edgeLine, line); 1786 1787 // are we inside poly2 ? 1788 if (inner) 1789 { 1790 // add intersection to poly2 1791 poly2.addPoint(intersection); 1792 // add intersection and pt2 to poly1 1793 poly1.addPoint(intersection); 1794 poly1.addPoint(edgePt2); 1795 } 1796 else 1797 { 1798 // add intersection to poly1 1799 poly1.addPoint(intersection); 1800 // add intersection and pt2 to poly2 1801 poly2.addPoint(intersection); 1802 poly2.addPoint(edgePt2); 1803 } 1804 1805 // we changed region 1806 return !inner; 1807 } 1808 1809 // inside poly2 --> add point to poly2 1810 if (inner) 1811 poly2.addPoint(edgePt2); 1812 else 1813 poly1.addPoint(edgePt2); 1814 1815 // same region 1816 return inner; 1817 } 1818 1819 /** 1820 * Cut the specified ROI with the given Line2D (extended to ROI bounds) and return the 2 resulting ROI in a 1821 * list.<br> 1822 * If the specified ROI cannot be cut by the given Line2D then <code>null</code> is returned. 1823 */ 1824 public static List<ROI> split(ROI roi, Line2D line) 1825 { 1826 final Rectangle2D bounds2d = roi.getBounds5D().toRectangle2D(); 1827 // need to enlarge bounds a bit to avoid roundness issues on line intersection 1828 final Rectangle2D extendedBounds2d = Rectangle2DUtil.getScaledRectangle(bounds2d, 1.1d, true); 1829 // enlarge line to ROI bounds 1830 final Line2D extendedLine = Rectangle2DUtil.getIntersectionLine(extendedBounds2d, line); 1831 1832 // if the extended line intersects the ROI bounds 1833 if ((extendedLine != null) && bounds2d.intersectsLine(extendedLine)) 1834 { 1835 final List<ROI> result = new ArrayList<ROI>(); 1836 final Point2D topLeft = new Point2D.Double(bounds2d.getMinX(), bounds2d.getMinY()); 1837 final Point2D topRight = new Point2D.Double(bounds2d.getMaxX(), bounds2d.getMinY()); 1838 final Point2D bottomRight = new Point2D.Double(bounds2d.getMaxX(), bounds2d.getMaxY()); 1839 final Point2D bottomLeft = new Point2D.Double(bounds2d.getMinX(), bounds2d.getMaxY()); 1840 final Polygon2D poly1 = new Polygon2D(); 1841 final Polygon2D poly2 = new Polygon2D(); 1842 boolean inner; 1843 1844 // add first point to poly1 1845 poly1.addPoint(topLeft); 1846 // we are inside poly1 for now 1847 inner = false; 1848 1849 // compute the 2 rectangle part (polygon) from top, right, bottom and left lines 1850 inner = computePolysFromLine(extendedLine, topLeft, topRight, poly1, poly2, inner); 1851 inner = computePolysFromLine(extendedLine, topRight, bottomRight, poly1, poly2, inner); 1852 inner = computePolysFromLine(extendedLine, bottomRight, bottomLeft, poly1, poly2, inner); 1853 inner = computePolysFromLine(extendedLine, bottomLeft, topLeft, poly1, poly2, inner); 1854 1855 // get intersection result from both polygon 1856 final ROI roiPart1 = new ROI2DPolygon(poly1).getIntersection(roi); 1857 final ROI roiPart2 = new ROI2DPolygon(poly2).getIntersection(roi); 1858 1859 // keep original ROI informations 1860 roiPart1.setName(roi.getName() + PART_SUFFIX + " #1"); 1861 copyROIProperties(roi, roiPart1, false); 1862 roiPart2.setName(roi.getName() + PART_SUFFIX + " #2"); 1863 copyROIProperties(roi, roiPart2, false); 1864 1865 // add to result list 1866 result.add(roiPart1); 1867 result.add(roiPart2); 1868 1869 return result; 1870 } 1871 1872 return null; 1873 } 1874 1875 /** 1876 * Convert a list of ROI into a binary / labeled Sequence. 1877 * 1878 * @param inputRois 1879 * list of ROI to convert 1880 * @param sizeX 1881 * the wanted size X of output Sequence, if set to <code>0</code> then Sequence size X is computed 1882 * automatically from 1883 * the global ROI bounds. 1884 * @param sizeY 1885 * the wanted size Y of output Sequence, if set to <code>0</code> then Sequence size Y is computed 1886 * automatically from 1887 * the global ROI bounds. 1888 * @param sizeC 1889 * the wanted size C of output Sequence, if set to <code>0</code> then Sequence size C is computed 1890 * automatically from 1891 * the global ROI bounds. 1892 * @param sizeZ 1893 * the wanted size Z of output Sequence, if set to <code>0</code> then Sequence size Z is computed 1894 * automatically from 1895 * the global ROI bounds. 1896 * @param sizeT 1897 * the wanted size T of output Sequence, if set to <code>0</code> then Sequence size T is computed 1898 * automatically from 1899 * the global ROI bounds. 1900 * @param dataType 1901 * the wanted dataType of output Sequence 1902 * @param label 1903 * if set to <code>true</code> then each ROI will be draw as a separate label (value) in the sequence 1904 * starting from 1. 1905 */ 1906 public static Sequence convertToSequence(List<ROI> inputRois, int sizeX, int sizeY, int sizeC, int sizeZ, int sizeT, 1907 DataType dataType, boolean label) 1908 { 1909 final List<ROI> rois = new ArrayList<ROI>(); 1910 final Rectangle5D bounds = new Rectangle5D.Double(); 1911 1912 try 1913 { 1914 // compute the union of all ROI 1915 final ROI roi = ROIUtil.merge(inputRois, BooleanOperator.OR); 1916 // get bounds of result 1917 bounds.add(roi.getBounds5D()); 1918 // add this single ROI to list 1919 rois.add(roi); 1920 } 1921 catch (Exception e) 1922 { 1923 for (ROI roi : inputRois) 1924 { 1925 // compute global bounds 1926 if (roi != null) 1927 { 1928 bounds.add(roi.getBounds5D()); 1929 rois.add(roi); 1930 } 1931 } 1932 } 1933 1934 int sX = sizeX; 1935 int sY = sizeY; 1936 int sC = sizeC; 1937 int sZ = sizeZ; 1938 int sT = sizeT; 1939 1940 if (sX == 0) 1941 sX = (int) bounds.getSizeX(); 1942 if (sY == 0) 1943 sY = (int) bounds.getSizeY(); 1944 if (sC == 0) 1945 sC = (bounds.isInfiniteC() ? 1 : (int) bounds.getSizeC()); 1946 if (sZ == 0) 1947 sZ = (bounds.isInfiniteZ() ? 1 : (int) bounds.getSizeZ()); 1948 if (sT == 0) 1949 sT = (bounds.isInfiniteT() ? 1 : (int) bounds.getSizeT()); 1950 1951 // empty base dimension and empty result --> generate a empty 320x240 image 1952 if (sX == 0) 1953 sX = 320; 1954 if (sY == 0) 1955 sY = 240; 1956 if (sC == 0) 1957 sC = 1; 1958 if (sZ == 0) 1959 sZ = 1; 1960 if (sT == 0) 1961 sT = 1; 1962 1963 final Sequence out = new Sequence("ROI conversion"); 1964 1965 out.beginUpdate(); 1966 try 1967 { 1968 for (int t = 0; t < sT; t++) 1969 for (int z = 0; z < sZ; z++) 1970 out.setImage(t, z, new IcyBufferedImage(sX, sY, sC, dataType)); 1971 1972 double fillValue = 1d; 1973 1974 // set value from ROI(s) 1975 for (ROI roi : rois) 1976 { 1977 if (!roi.getBounds5D().isEmpty()) 1978 DataIteratorUtil.set(new SequenceDataIterator(out, roi), fillValue); 1979 1980 if (label) 1981 fillValue += 1d; 1982 } 1983 1984 // notify data changed 1985 out.dataChanged(); 1986 } 1987 finally 1988 { 1989 out.endUpdate(); 1990 } 1991 1992 return out; 1993 } 1994 1995 /** 1996 * Convert a list of ROI into a binary / labeled Sequence. 1997 * 1998 * @param inputRois 1999 * list of ROI to convert 2000 * @param sequence 2001 * the sequence used to define the wanted sequence dimension in return.<br> 2002 * If this field is <code>null</code> then the global ROI bounds will be used to define the Sequence 2003 * dimension 2004 * @param label 2005 * if set to <code>true</code> then each ROI will be draw as a separate label (value) in the sequence 2006 * starting from 1. 2007 */ 2008 public static Sequence convertToSequence(List<ROI> inputRois, Sequence sequence, boolean label) 2009 { 2010 if (sequence == null) 2011 return convertToSequence(inputRois, 0, 0, 0, 0, 0, 2012 label ? ((inputRois.size() > 255) ? DataType.USHORT : DataType.UBYTE) : DataType.UBYTE, label); 2013 2014 return convertToSequence(inputRois, sequence.getSizeX(), sequence.getSizeY(), 1, sequence.getSizeZ(), 2015 sequence.getSizeT(), sequence.getDataType_(), label); 2016 } 2017 2018 /** 2019 * Convert a single ROI into a binary / labeled Sequence. 2020 * 2021 * @param inputRoi 2022 * ROI to convert 2023 * @param sequence 2024 * the sequence used to define the wanted sequence dimension in return.<br> 2025 * If this field is <code>null</code> then the global ROI bounds will be used to define the Sequence 2026 * dimension 2027 */ 2028 public static Sequence convertToSequence(ROI inputRoi, Sequence sequence) 2029 { 2030 return convertToSequence(CollectionUtil.createArrayList(inputRoi), sequence, false); 2031 } 2032 2033 /** 2034 * Scale (3D) the given ROI by specified X,Y,Z factors.<br> 2035 * Only {@link ROI2DShape} and {@link ROI3DShape} are supported ! 2036 * 2037 * @param roi 2038 * input ROI we want to rescale 2039 * @throws UnsupportedOperationException 2040 * if input ROI is not ROI2DShape or ROI3DShape (scaling supported only for these ROI) 2041 */ 2042 public static void scale(ROI roi, double scaleX, double scaleY, double scaleZ) throws UnsupportedOperationException 2043 { 2044 // shape ROI --> can rescale easily 2045 if (roi instanceof ROI2DRectShape) 2046 { 2047 final ROI2DRectShape roi2DRectShape = (ROI2DRectShape) roi; 2048 2049 roi2DRectShape.beginUpdate(); 2050 try 2051 { 2052 final Rectangle2D bounds = roi2DRectShape.getBounds2D(); 2053 2054 // reshape directly 2055 bounds.setFrame(bounds.getX() * scaleX, bounds.getY() * scaleY, bounds.getWidth() * scaleX, 2056 bounds.getHeight() * scaleY); 2057 roi2DRectShape.setBounds2D(bounds); 2058 2059 final int z = roi2DRectShape.getZ(); 2060 2061 // re scale Z position if needed 2062 if ((z != -1) && (scaleZ != 1d)) 2063 roi2DRectShape.setZ((int) (z * scaleZ)); 2064 } 2065 finally 2066 { 2067 roi2DRectShape.endUpdate(); 2068 } 2069 } 2070 else if (roi instanceof ROI2DShape) 2071 { 2072 final ROI2DShape roi2DShape = (ROI2DShape) roi; 2073 2074 roi2DShape.beginUpdate(); 2075 try 2076 { 2077 // adjust control point position directly 2078 for (Anchor2D pt : roi2DShape.getControlPoints()) 2079 { 2080 final Point2D pos = pt.getPosition(); 2081 // change control point position 2082 pt.setPosition(pos.getX() * scaleX, pos.getY() * scaleY); 2083 } 2084 2085 final int z = roi2DShape.getZ(); 2086 2087 // re scale Z position if needed 2088 if ((z != -1) && (scaleZ != 1d)) 2089 roi2DShape.setZ((int) (z * scaleZ)); 2090 } 2091 finally 2092 { 2093 roi2DShape.endUpdate(); 2094 } 2095 } 2096 else if (roi instanceof ROI3DShape) 2097 { 2098 final ROI3DShape roi3DShape = (ROI3DShape) roi; 2099 2100 roi3DShape.beginUpdate(); 2101 try 2102 { 2103 // adjust control point position directly 2104 for (Anchor3D pt : roi3DShape.getControlPoints()) 2105 { 2106 final Point3D pos = pt.getPosition(); 2107 // change control point position 2108 pt.setPosition(pos.getX() * scaleX, pos.getY() * scaleY, pos.getZ() * scaleZ); 2109 } 2110 } 2111 finally 2112 { 2113 roi3DShape.endUpdate(); 2114 } 2115 } 2116 else 2117 throw new UnsupportedOperationException("ROIUtil.scale: cannot rescale " + roi.getSimpleClassName() + " !"); 2118 } 2119 2120 /** 2121 * Scale (2D) the given ROI by specified X/Y factor.<br> 2122 * Only {@link ROI2DShape} and {@link ROI3DShape} are supported ! 2123 * 2124 * @param roi 2125 * input ROI we want to rescale 2126 * @throws UnsupportedOperationException 2127 * if input ROI is not ROI2DShape or ROI3DShape (scaling supported only for these ROI) 2128 */ 2129 public static void scale(ROI roi, double scaleX, double scaleY) throws UnsupportedOperationException 2130 { 2131 scale(roi, scaleX, scaleY, 1d); 2132 } 2133 2134 /** 2135 * Scale the given ROI by specified scale factor.<br> 2136 * Only {@link ROI2DShape} and {@link ROI3DShape} are supported ! 2137 * 2138 * @param roi 2139 * input ROI we want to rescale 2140 * @throws UnsupportedOperationException 2141 * if input ROI is not ROI2DShape or ROI3DShape (scaling supported only for these ROI) 2142 */ 2143 public static void scale(ROI roi, double scale) throws UnsupportedOperationException 2144 { 2145 scale(roi, scale, scale, scale); 2146 } 2147 2148 /** 2149 * Create and returns a new ROI which is a 2x up/down scaled version of the input ROI.<br> 2150 * Note that the returned ROI can be ROI2DArea or ROI3DArea if original ROI format doesn't support 2X scale 2151 * operation. 2152 * 2153 * @param roi 2154 * input ROI we want to get the up scaled form 2155 * @param scaleOnZ 2156 * Set to <code>true</code> to scale as well on Z dimension (XY dimension only otherwise) 2157 * @param down 2158 * Set to <code>true</code> for down scaling and <code>false</code> for up scaling operation 2159 * @throws UnsupportedOperationException 2160 * if input ROI is ROI4D or ROI5D (up scaling not supported for these ROI) 2161 */ 2162 public static ROI get2XScaled(ROI roi, boolean scaleOnZ, boolean down) throws UnsupportedOperationException 2163 { 2164 if (roi == null) 2165 return null; 2166 2167 final double scaling = down ? 0.5d : 2d; 2168 ROI result = roi.getCopy(); 2169 2170 // shape ROI --> can rescale easily 2171 if ((result instanceof ROI2DShape) || (result instanceof ROI3DShape)) 2172 scale(result, scaling, scaling, scaleOnZ ? scaling : 1d); 2173 else if (result instanceof ROI2D) 2174 { 2175 final ROI2DArea roi2DArea; 2176 2177 if (result instanceof ROI2DArea) 2178 { 2179 roi2DArea = (ROI2DArea) result; 2180 2181 // scale 2182 if (down) 2183 roi2DArea.downscale(); 2184 else 2185 roi2DArea.upscale(); 2186 2187 // scale Z position if wanted 2188 if ((roi2DArea.getZ() != -1) && scaleOnZ) 2189 roi2DArea.setZ((int) (roi2DArea.getZ() * scaling)); 2190 } 2191 else 2192 { 2193 final BooleanMask2D bm = ((ROI2D) result).getBooleanMask(true); 2194 2195 // scale 2196 if (down) 2197 roi2DArea = new ROI2DArea(bm.downscale()); 2198 else 2199 roi2DArea = new ROI2DArea(bm.upscale()); 2200 2201 // get original position 2202 final Point5D pos = result.getPosition5D(); 2203 2204 // restore Z,T,C position 2205 if (Double.isInfinite(pos.getZ())) 2206 roi2DArea.setZ(-1); 2207 else 2208 roi2DArea.setZ((int) (pos.getZ() * (scaleOnZ ? scaling : 1d))); 2209 if (Double.isInfinite(pos.getT())) 2210 roi2DArea.setT(-1); 2211 else 2212 roi2DArea.setT((int) pos.getT()); 2213 if (Double.isInfinite(pos.getC())) 2214 roi2DArea.setC(-1); 2215 else 2216 roi2DArea.setC((int) pos.getC()); 2217 2218 // copy properties 2219 copyROIProperties(result, roi2DArea, true); 2220 2221 result = roi2DArea; 2222 } 2223 } 2224 else if (result instanceof ROI3D) 2225 { 2226 final ROI3DArea roi3DArea; 2227 2228 // we want a ROI2DArea 2229 if (result instanceof ROI3DArea) 2230 { 2231 roi3DArea = (ROI3DArea) result; 2232 2233 // scale 2234 if (down) 2235 { 2236 if (scaleOnZ) 2237 roi3DArea.downscale(); 2238 else 2239 roi3DArea.downscale2D(); 2240 } 2241 else 2242 { 2243 if (scaleOnZ) 2244 roi3DArea.upscale(); 2245 else 2246 roi3DArea.upscale2D(); 2247 } 2248 } 2249 else 2250 { 2251 final BooleanMask3D bm = ((ROI3D) result).getBooleanMask(true); 2252 2253 // scale 2254 if (down) 2255 { 2256 if (scaleOnZ) 2257 roi3DArea = new ROI3DArea(bm.downscale()); 2258 else 2259 roi3DArea = new ROI3DArea(bm.downscale2D()); 2260 } 2261 else 2262 { 2263 if (scaleOnZ) 2264 roi3DArea = new ROI3DArea(bm.upscale()); 2265 else 2266 roi3DArea = new ROI3DArea(bm.upscale2D()); 2267 } 2268 2269 // get original position 2270 final Point5D pos = result.getPosition5D(); 2271 2272 // restore T,C position 2273 if (Double.isInfinite(pos.getT())) 2274 roi3DArea.setT(-1); 2275 else 2276 roi3DArea.setT((int) pos.getT()); 2277 if (Double.isInfinite(pos.getC())) 2278 roi3DArea.setC(-1); 2279 else 2280 roi3DArea.setC((int) pos.getC()); 2281 2282 // copy properties 2283 copyROIProperties(result, roi3DArea, true); 2284 2285 result = roi3DArea; 2286 } 2287 } 2288 // 4D or 5D ROI --> scaling not supported 2289 else 2290 throw new UnsupportedOperationException("ROIUtil.get2XScaled: cannot rescale ROI4D or ROI5D !"); 2291 2292 return result; 2293 } 2294 2295 /** 2296 * Create and returns a new ROI which is a 2x up scaled version of the input ROI.<br> 2297 * Note that the returned ROI can be ROI2DArea or ROI3DArea if original ROI format doesn't support up scale 2298 * operation. 2299 * 2300 * @param roi 2301 * input ROI we want to get the up scaled form 2302 * @param scaleOnZ 2303 * Set to <code>true</code> to scale as well on Z dimension (XY dimension only otherwise) 2304 * @throws UnsupportedOperationException 2305 * if input ROI is ROI4D or ROI5D (up scaling not supported for these ROI) 2306 */ 2307 public static ROI getUpscaled(ROI roi, boolean scaleOnZ) throws UnsupportedOperationException 2308 { 2309 return get2XScaled(roi, scaleOnZ, false); 2310 } 2311 2312 /** 2313 * Create and returns a new ROI which is a 2x down scaled version of the input ROI.<br> 2314 * Note that the returned ROI can be ROI2DArea or ROI3DArea if original ROI format doesn't support up scale 2315 * operation. 2316 * 2317 * @param roi 2318 * input ROI we want to get the up scaled form 2319 * @param scaleOnZ 2320 * Set to <code>true</code> to scale as well on Z dimension (XY dimension only otherwise) 2321 * @throws UnsupportedOperationException 2322 * if input ROI is ROI4D or ROI5D (up scaling not supported for these ROI) 2323 */ 2324 public static ROI getDownscaled(ROI roi, boolean scaleOnZ) throws UnsupportedOperationException 2325 { 2326 return get2XScaled(roi, scaleOnZ, true); 2327 } 2328 2329 /** 2330 * Create a copy of the specified ROI coming from <i>source</i> sequence adjusted to the <i>destination</i> sequence.<br> 2331 * The resulting ROI coordinates can be different if the {@link Sequence#getPosition()} are not identical on the 2 sequences.<br> 2332 * The resulting ROI can be scaled if the {@link Sequence#getPixelSize()} are not identical on the 2 sequences<br> 2333 * Note that the returned ROI can have a Boolean Mask format if we can't re-use original ROI format, also the scale operation may not be possible depending 2334 * the original ROI format. 2335 * 2336 * @param roi 2337 * input ROI we want to adjust 2338 * @param source 2339 * the source sequence where the ROI was initially generated (should contains valid <i>origin</i> information, see Sequence#getOriginXXX(} methods) 2340 * @param destination 2341 * the destination sequence where we want to copy the ROI (should contains valid <i>origin</i> information, see Sequence#getOriginXXX(} methods) 2342 * @param translate 2343 * if we allow the returned ROI to be translated compared to the original ROI 2344 * @param scale 2345 * if we allow the returned ROI to be scaled compared to the original ROI 2346 * @param ignoreErrorOnScale 2347 * ignore unsupported scaling operation, in which case the result ROI won't be correctly scaled 2348 * @return adjusted ROI 2349 * @throws UnsupportedOperationException 2350 * if input ROI is ROI4D or ROI5D while scaling is required (scaling not supported for these ROI) and <code>ignoreErrorOnScale</code> is set to 2351 * <code>FALSE</code> 2352 */ 2353 public static ROI adjustToSequence(ROI roi, Sequence source, Sequence destination, boolean translate, boolean scale, 2354 boolean ignoreErrorOnScale) throws UnsupportedOperationException 2355 { 2356 if (roi == null) 2357 return null; 2358 2359 // create a copy 2360 ROI result = roi.getCopy(); 2361 2362 if (scale) 2363 { 2364 final double scaleX = source.getPixelSizeX() / destination.getPixelSizeX(); 2365 final double scaleY = source.getPixelSizeY() / destination.getPixelSizeY(); 2366 final double scaleZ = source.getPixelSizeZ() / destination.getPixelSizeZ(); 2367 2368 // ROI is a 2D or 3D shape ? --> use easy scaling 2369 if ((result instanceof ROI2DShape) || (result instanceof ROI3DShape)) 2370 scale(result, scaleX, scaleY, scaleZ); 2371 else 2372 { 2373 boolean doRescale = true; 2374 boolean doRescaleZ = true; 2375 2376 if (scaleX != scaleY) 2377 { 2378 doRescale = false; 2379 if (ignoreErrorOnScale) 2380 System.out.println( 2381 "[Warning] ROIUtil.adjustToSequence: cannot rescale ROI with different X/Y scale ratio."); 2382 else 2383 throw new UnsupportedOperationException( 2384 "ROIUtil.adjustToSequence: cannot rescale ROI (different X/Y scale ratio) !"); 2385 } 2386 2387 // get log2 of scaleX/Y (round it a bit) 2388 final double resDelta = MathUtil.round(Math.log(scaleX) / Math.log(2), 1); 2389 2390 // too far from 2^x scaling 2391 if (Math.round(resDelta) != resDelta) 2392 { 2393 doRescale = false; 2394 if (ignoreErrorOnScale) 2395 System.out.println( 2396 "[Warning] ROIUtil.adjustToSequence: cannot rescale ROI with scale XY = " + scaleX); 2397 else 2398 throw new UnsupportedOperationException( 2399 "ROIUtil.adjustToSequence: cannot rescale ROI (scale XY = " + scaleX + ") !"); 2400 } 2401 2402 // get log2 of scaleZ (round it a bit) 2403 final double resDeltaZ = MathUtil.round(Math.log(scaleZ) / Math.log(2), 1); 2404 2405 if (Math.round(resDeltaZ) != resDeltaZ) 2406 { 2407 doRescaleZ = false; 2408 if (ignoreErrorOnScale) 2409 System.out.println("[Warning] ROIUtil.adjustToSequence: ignoring ROI Z rescaling (scale Z = " 2410 + scaleZ + ")"); 2411 else 2412 throw new UnsupportedOperationException( 2413 "ROIUtil.adjustToSequence: cannot rescale ROI (scale Z = " + scaleZ + ") !"); 2414 } 2415 2416 final boolean zScaling = resDeltaZ != 0d; 2417 2418 // Z rescaling needed ? --> we need to have same XY and Z scale ratio 2419 if (zScaling && (resDeltaZ != resDelta)) 2420 { 2421 doRescaleZ = false; 2422 if (ignoreErrorOnScale) 2423 System.out.println("[Warning] ROIUtil.adjustToSequence: ignoring ROI Z rescaling (scale XY = " 2424 + scaleX + " while scale Z = " + scaleZ + ")"); 2425 else 2426 throw new UnsupportedOperationException( 2427 "ROIUtil.adjustToSequence: cannot rescale ROI (scale XY = " + scaleX 2428 + " while scale Z = " + scaleZ + ") !"); 2429 } 2430 2431 try 2432 { 2433 if (doRescale) 2434 { 2435 int i = (int) resDelta; 2436 2437 // destination resolution level > source resolution level 2438 if (resDelta > 0) 2439 { 2440 // down scaling 2441 while (i-- > 0) 2442 result = getUpscaled(result, zScaling && doRescaleZ); 2443 } 2444 else 2445 { 2446 // up scaling 2447 while (i++ < 0) 2448 result = getDownscaled(result, zScaling && doRescaleZ); 2449 } 2450 } 2451 } 2452 catch (UnsupportedOperationException e) 2453 { 2454 // we should propagate it then 2455 if (!ignoreErrorOnScale) 2456 throw e; 2457 } 2458 } 2459 } 2460 2461 // can set position ? --> relocate it 2462 if (translate && result.canSetPosition()) 2463 { 2464 // get current position 2465 final Point5D pos = result.getPosition5D(); 2466 2467 // can change it Z position ? 2468 if (!Double.isInfinite(pos.getZ())) 2469 { 2470 // we just want to get offset as scaling already taken care of converting relative ROI position 2471 final Point3D offset = SequenceUtil.convertPoint(new Point3D.Double(), source, destination); 2472 2473 // compute position in destination 2474 pos.setX(pos.getX() + offset.getX()); 2475 pos.setY(pos.getY() + offset.getY()); 2476 pos.setZ(pos.getZ() + offset.getZ()); 2477 } 2478 else 2479 { 2480 // we just want to get offset as scaling already taken care of converting relative ROI position 2481 final Point2D offset = SequenceUtil.convertPoint(new Point2D.Double(), source, destination); 2482 2483 // compute position in destination 2484 pos.setX(pos.getX() + offset.getX()); 2485 pos.setY(pos.getY() + offset.getY()); 2486 } 2487 2488 // can change it ? (we don't scale T dimension) 2489 if (!Double.isInfinite(pos.getT())) 2490 { 2491 // get time interval in ms 2492 final double timeIntervalMs = destination.getTimeInterval() * 1000d; 2493 2494 if (timeIntervalMs > 0d) 2495 { 2496 // get delta in timestamp (ms) 2497 final double deltaT = source.getPositionT() - destination.getPositionT(); 2498 // get wanted destination T offset 2499 final double tOffset = deltaT / timeIntervalMs; 2500 2501 // assume correct T range is ~1000 2502 if ((tOffset > -1000d) && (tOffset < 1000d)) 2503 pos.setT(Math.min(999, Math.max(0, pos.getT() + tOffset))); 2504 } 2505 } 2506 2507 // set back position 2508 result.setPosition5D(pos); 2509 } 2510 2511 return result; 2512 } 2513 2514 /** 2515 * Create a copy of the specified ROI coming from <i>source</i> sequence adjusted to the <i>destination</i> sequence.<br> 2516 * The resulting ROI coordinates can be different if the {@link Sequence#getPosition()} are not identical on the 2 sequences.<br> 2517 * The resulting ROI can be scaled if the {@link Sequence#getPixelSize()} are not identical on the 2 sequences<br> 2518 * Note that the returned ROI can have a Boolean Mask format if we can't re-use original ROI format, also the scale operation may not be possible depending 2519 * the original ROI format. 2520 * 2521 * @param roi 2522 * input ROI we want to adjust 2523 * @param source 2524 * the source sequence where the ROI was initially generated (should contains valid <i>origin</i> information, see Sequence#getOriginXXX(} methods) 2525 * @param destination 2526 * the destination sequence where we want to copy the ROI (should contains valid <i>origin</i> information, see Sequence#getOriginXXX(} methods) 2527 * @param translate 2528 * if we allow the returned ROI to be translated compared to the original ROI 2529 * @param scale 2530 * if we allow the returned ROI to be scaled compared to the original ROI 2531 * @return adjusted ROI 2532 * @throws UnsupportedOperationException 2533 * if input ROI is ROI4D or ROI5D while scaling is required (scaling not supported for these ROI) and <code>ignoreErrorOnScale</code> is set to 2534 * <code>FALSE</code> 2535 */ 2536 public static ROI adjustToSequence(ROI roi, Sequence source, Sequence destination, boolean translate, boolean scale) 2537 throws UnsupportedOperationException 2538 { 2539 return adjustToSequence(roi, source, destination, translate, scale, false); 2540 } 2541 2542 /** 2543 * Create a copy of the specified ROI coming from <i>source</i> sequence adjusted to the <i>destination</i> sequence.<br> 2544 * The resulting ROI coordinates can be different if the {@link Sequence#getPosition()} are not identical on the 2 sequences.<br> 2545 * The resulting ROI can be scaled if the {@link Sequence#getPixelSize()} are not identical on the 2 sequences<br> 2546 * Note that the returned ROI can have a Boolean Mask format if we can't re-use original ROI format, also the scale operation may not be possible depending 2547 * the original ROI format. 2548 * 2549 * @param roi 2550 * input ROI we want to adjust 2551 * @param source 2552 * the source sequence where the ROI was initially generated (should contains valid <i>origin</i> information, see Sequence#getOriginXXX(} methods) 2553 * @param destination 2554 * the destination sequence where we want to copy the ROI (should contains valid <i>origin</i> information, see Sequence#getOriginXXX(} methods) 2555 * @return adjusted ROI 2556 * @throws UnsupportedOperationException 2557 * if input ROI is ROI4D or ROI5D while scaling is required (scaling not supported for these ROI) 2558 */ 2559 public static ROI adjustToSequence(ROI roi, Sequence source, Sequence destination) 2560 throws UnsupportedOperationException 2561 { 2562 return adjustToSequence(roi, source, destination, true, true); 2563 } 2564 2565 // /** 2566 // * Create and returns a new ROI adjusted to the specified sequence if it represents a sub region of another 2567 // * Sequence.<br> 2568 // * You need to use this function when you generated the ROI on the origin Sequence and want to have it adjusted to 2569 // * the given sub region Sequence coordinates.<br> 2570 // * ROI coordinates can be affected if the {@link Sequence#getOriginXYRegion()} is not <code>null</code>.<br> 2571 // * If {@link Sequence#getOriginResolution()} is not <code>0</code> then the returned ROI will be down scaled to fit 2572 // * the Sequence image resolution.<br> 2573 // * Note that the returned ROI can have a Boolean Mask format if we can't re-use original ROI format.. 2574 // * 2575 // * @param roi 2576 // * input ROI we want to get the adjusted form 2577 // * @param subSequence 2578 // * the sequence representing the sub region of the origin Sequence (it should contains <i>origin</i> 2579 // * information, see Sequence#getOriginXXX(} methods) 2580 // * @return adjusted ROI 2581 // * @throws UnsupportedOperationException 2582 // * if input ROI is ROI4D or ROI5D while scaling is required (scaling not supported for these ROI) 2583 // */ 2584 // public static ROI adjustToSequence(ROI roi, Sequence subSequence) throws UnsupportedOperationException 2585 // { 2586 // if (roi == null) 2587 // return null; 2588 // 2589 // ROI result = roi.getCopy(); 2590 // 2591 // if (subSequence != null) 2592 // { 2593 // int res = subSequence.getOriginResolution(); 2594 // final double scaleFactor = 1d / Math.pow(2d, res); 2595 // 2596 // // down scaling 2597 // while (res-- > 0) 2598 // result = getDownscaled(result, false); 2599 // 2600 // // can set position ? --> relocate it 2601 // if (result.canSetPosition()) 2602 // { 2603 // // get current position 2604 // final Point5D pos = result.getPosition5D(); 2605 // 2606 // final Rectangle originPos = subSequence.getOriginXYRegion(); 2607 // // sub region ? 2608 // if (originPos != null) 2609 // { 2610 // pos.setX(pos.getX() - (originPos.getX() * scaleFactor)); 2611 // pos.setY(pos.getY() - (originPos.getY() * scaleFactor)); 2612 // } 2613 // 2614 // final int zMin = subSequence.getOriginZMin(); 2615 // // sub Z stack part ? 2616 // if (zMin != -1) 2617 // { 2618 // // can change it ? (we don't scale Z dimension) 2619 // if (!Double.isInfinite(pos.getZ())) 2620 // pos.setZ(pos.getZ() - zMin); 2621 // } 2622 // 2623 // final int tMin = subSequence.getOriginTMin(); 2624 // // sub T sequence part ? 2625 // if (tMin != -1) 2626 // { 2627 // // can change it ? (we don't scale T dimension) 2628 // if (!Double.isInfinite(pos.getT())) 2629 // pos.setT(pos.getT() - tMin); 2630 // } 2631 // 2632 // final int c = subSequence.getOriginChannel(); 2633 // // sub channel ? 2634 // if (c != -1) 2635 // { 2636 // // can change it ? (we don't scale C dimension) 2637 // if (!Double.isInfinite(pos.getC())) 2638 // pos.setC(pos.getC() - c); 2639 // } 2640 // 2641 // // set back position 2642 // result.setPosition5D(pos); 2643 // } 2644 // } 2645 // 2646 // return result; 2647 // } 2648 // 2649 // /** 2650 // * Create and returns a new ROI adjusted to the origin sequence given the sub region Sequence.<br> 2651 // * You need to use this function when you generated the ROI on the given sub region Sequence and want to have it 2652 // * back in the origin Sequence coordinates.<br> 2653 // * ROI coordinates can be affected if the {@link Sequence#getOriginXYRegion()} is not <code>null</code>.<br> 2654 // * If {@link Sequence#getOriginResolution()} is not <code>0</code> then the returned ROI will be up scaled to fit 2655 // * the original image resolution.<br> 2656 // * Note that the returned ROI can have a Boolean Mask format if we can't re-use original ROI format. 2657 // * 2658 // * @param roi 2659 // * input ROI we want to get the adjusted form 2660 // * @param subSequence 2661 // * the sequence representing the sub region of the origin Sequence (it should contains <i>origin</i> 2662 // * information, see Sequence#getOriginXXX(} methods) 2663 // * @return adjusted ROI 2664 // * @throws UnsupportedOperationException 2665 // * if input ROI is ROI4D or ROI5D while scaling is required (scaling not supported for these ROI) 2666 // */ 2667 // public static ROI adjustToOriginSequence(ROI roi, Sequence subSequence) throws UnsupportedOperationException 2668 // { 2669 // if (roi == null) 2670 // return null; 2671 // 2672 // ROI result = roi.getCopy(); 2673 // 2674 // if (subSequence != null) 2675 // { 2676 // int res = subSequence.getOriginResolution(); 2677 // 2678 // // up scaling (2D) 2679 // while (res-- > 0) 2680 // result = getUpscaled(result, false); 2681 // 2682 // // can set position ? --> relocate it 2683 // if (result.canSetPosition()) 2684 // { 2685 // // get current position 2686 // final Point5D pos = result.getPosition5D(); 2687 // 2688 // final Rectangle originPos = subSequence.getOriginXYRegion(); 2689 // // sub region ? 2690 // if (originPos != null) 2691 // { 2692 // pos.setX(pos.getX() + originPos.getX()); 2693 // pos.setY(pos.getY() + originPos.getY()); 2694 // } 2695 // 2696 // final int zMin = subSequence.getOriginZMin(); 2697 // // sub Z stack part ? 2698 // if (zMin != -1) 2699 // { 2700 // // can change it ? 2701 // if (!Double.isInfinite(pos.getZ())) 2702 // pos.setZ(pos.getZ() + zMin); 2703 // } 2704 // 2705 // final int tMin = subSequence.getOriginTMin(); 2706 // // sub T sequence part ? 2707 // if (tMin != -1) 2708 // { 2709 // // can change it ? 2710 // if (!Double.isInfinite(pos.getT())) 2711 // pos.setT(pos.getT() + tMin); 2712 // } 2713 // 2714 // final int c = subSequence.getOriginChannel(); 2715 // // sub channel ? 2716 // if (c != -1) 2717 // { 2718 // // can change it ? 2719 // if (!Double.isInfinite(pos.getC())) 2720 // pos.setC(pos.getC() + c); 2721 // } 2722 // 2723 // // set back position 2724 // result.setPosition5D(pos); 2725 // } 2726 // } 2727 // 2728 // return result; 2729 // } 2730 2731 /** 2732 * Copy properties (name, color...) from <code>source</code> ROI and apply it to <code>destination</code> ROI. 2733 */ 2734 public static void copyROIProperties(ROI source, ROI destination, boolean copyName) 2735 { 2736 if ((source == null) || (destination == null)) 2737 return; 2738 2739 if (copyName) 2740 destination.setName(source.getName()); 2741 destination.setColor(source.getColor()); 2742 destination.setOpacity(source.getOpacity()); 2743 destination.setStroke(source.getStroke()); 2744 destination.setReadOnly(source.isReadOnly()); 2745 destination.setSelected(source.isSelected()); 2746 destination.setShowName(source.getShowName()); 2747 destination.setGroupId(source.getGroupId()); 2748 2749 // copy extended properties 2750 for (Entry<String, String> propertyEntry : source.getProperties().entrySet()) 2751 destination.setProperty(propertyEntry.getKey(), propertyEntry.getValue()); 2752 } 2753}