001/* 002 * Copyright 2010-2018 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.file; 020 021import java.io.IOException; 022import java.nio.channels.ClosedByInterruptException; 023import java.util.ArrayList; 024import java.util.Collection; 025import java.util.Collections; 026import java.util.HashMap; 027import java.util.List; 028import java.util.Map; 029 030import icy.gui.frame.progress.FileFrame; 031import icy.sequence.DimensionId; 032import icy.sequence.MetaDataUtil; 033import icy.type.DataType; 034import icy.util.StringUtil; 035import icy.util.StringUtil.AlphanumComparator; 036import ome.xml.meta.OMEXMLMetadata; 037import plugins.kernel.importer.LociImporterPlugin; 038 039/** 040 * This class is an utility class aim to help in grouping a list of <i>file path</id> representing image to form a complete and valid Sequence. 041 * 042 * @author Stephane 043 */ 044public class SequenceFileSticher 045{ 046 public static class SequenceType 047 { 048 public int sizeX; 049 public int sizeY; 050 public int sizeZ; 051 public int sizeT; 052 public int sizeC; 053 public DataType dataType; 054 public double pixelSizeX; 055 public double pixelSizeY; 056 public double pixelSizeZ; 057 public double timeInterval; 058 059 // internal 060 int hc; 061 062 public SequenceType() 063 { 064 super(); 065 066 // undetermined 067 sizeX = 0; 068 sizeY = 0; 069 sizeZ = 0; 070 sizeT = 0; 071 sizeC = 0; 072 dataType = null; 073 pixelSizeX = 0d; 074 pixelSizeY = 0d; 075 pixelSizeZ = 0d; 076 timeInterval = 0d; 077 } 078 079 void computeHashCode() 080 { 081 hc = (sizeX << 0) ^ (sizeY << 4) ^ (sizeZ << 8) ^ (sizeT << 12) ^ (sizeC << 16) 082 ^ (Float.floatToIntBits((float) pixelSizeX) << 20) 083 ^ (Float.floatToIntBits((float) pixelSizeY) << 24) ^ (Float.floatToIntBits((float) pixelSizeZ) >> 4) 084 ^ (Float.floatToIntBits((float) timeInterval) >> 8) 085 ^ ((dataType != null) ? (dataType.ordinal() >> 12) : 0); 086 } 087 088 @Override 089 public int hashCode() 090 { 091 return hc; 092 } 093 094 @Override 095 public boolean equals(Object obj) 096 { 097 if (obj instanceof SequenceType) 098 { 099 final SequenceType st = (SequenceType) obj; 100 101 return (st.sizeX == sizeX) && (st.sizeY == sizeY) && (st.sizeZ == sizeZ) && (st.sizeT == sizeT) 102 && (st.sizeC == sizeC) && (st.pixelSizeX == pixelSizeX) && (st.pixelSizeY == pixelSizeY) 103 && (st.pixelSizeZ == pixelSizeZ) && (st.timeInterval == timeInterval) 104 && (st.dataType == dataType); 105 } 106 107 return super.equals(obj); 108 } 109 } 110 111 public static class SequenceIdent 112 { 113 /** 114 * base path pattern (identical part of the path in this group) 115 */ 116 public final String base; 117 /** 118 * Series index for this group 119 */ 120 public final int series; 121 /** 122 * base image type for this group (correspond to the type of each image) 123 */ 124 public final SequenceType baseType; 125 /** 126 * Compatible importer capable of loading this image group 127 */ 128 public final SequenceFileImporter importer; 129 130 private final int hc; 131 132 public SequenceIdent(String base, int series, SequenceType type, SequenceFileImporter importer) 133 { 134 super(); 135 136 this.base = base; 137 this.series = series; 138 this.baseType = type; 139 this.importer = importer; 140 141 hc = base.hashCode() ^ series; 142 } 143 144 public SequenceIdent(String base, int series) 145 { 146 this(base, series, null, null); 147 } 148 149 @Override 150 public int hashCode() 151 { 152 return hc; 153 } 154 155 @Override 156 public boolean equals(Object obj) 157 { 158 if (obj instanceof SequenceIdent) 159 { 160 final SequenceIdent ident = (SequenceIdent) obj; 161 162 boolean result = base.equals(ident.base) && (series == ident.series); 163 164 // test baseType only if defined 165 if (result && (baseType != null) && (ident.baseType != null)) 166 result &= baseType.equals(ident.baseType); 167 168 return result; 169 } 170 171 return super.equals(obj); 172 } 173 } 174 175 public static class SequenceAbsolutePosition implements Comparable<SequenceAbsolutePosition> 176 { 177 // absolute position from metadata 178 public double posX; 179 public double posY; 180 public double posZ; 181 public double posT; 182 public double indX; 183 public double indY; 184 public double indZ; 185 public double indT; 186 187 // internal 188 protected int hc; 189 190 public SequenceAbsolutePosition() 191 { 192 super(); 193 194 // undetermined 195 posX = -1d; 196 posY = -1d; 197 posZ = -1d; 198 posT = -1d; 199 indX = -1d; 200 indY = -1d; 201 indZ = -1d; 202 indT = -1d; 203 } 204 205 /** 206 * Set index X from absolute position and sequence properties (pixel size, dimension) 207 */ 208 public void setIndexX(SequenceType type) 209 { 210 // -1 mean not set 211 if (posX != -1d) 212 { 213 // get pixel position if possible 214 final double pixPos; 215 216 if (type.pixelSizeX > 0d) 217 // we use * 2 to avoid index duplication because of rounding, anyway we will fix interval later 218 pixPos = (2d * posX) / type.pixelSizeX; 219 else 220 // use absolute position as pixel pos 221 pixPos = posX; 222 223 // index = (pixel position) / (image size) 224 indX = Math.round(pixPos / type.sizeX); 225 } 226 } 227 228 /** 229 * Set index X from absolute position and sequence properties (pixel size, dimension) 230 */ 231 public void setIndexY(SequenceType type) 232 { 233 // -1 mean not set 234 if (posY != -1d) 235 { 236 // get pixel position if possible 237 final double pixPos; 238 239 if (type.pixelSizeY > 0d) 240 // we use * 2 to avoid index duplication because of rounding, anyway we will fix interval later 241 pixPos = (2d * posY) / type.pixelSizeY; 242 else 243 // use absolute position as pixel pos 244 pixPos = posY; 245 246 // index = (pixel position) / (image size) 247 indY = Math.round(pixPos / type.sizeY); 248 } 249 } 250 251 /** 252 * Set index X from absolute position and sequence properties (pixel size, dimension) 253 */ 254 public void setIndexZ(SequenceType type) 255 { 256 // -1 mean not set 257 if (posZ != -1d) 258 { 259 // get pixel position if possible 260 final double pixPos; 261 262 if (type.pixelSizeZ > 0d) 263 // we use * 2 to avoid index duplication because of rounding, anyway we will fix interval later 264 pixPos = (2d * posZ) / type.pixelSizeZ; 265 else 266 // use absolute position as pixel pos 267 pixPos = posZ; 268 269 // index = (pixel position) / (image size) 270 indZ = Math.round(pixPos / type.sizeZ); 271 } 272 } 273 274 /** 275 * Set index T from absolute time position and sequence properties (pixel size, dimension) 276 */ 277 public void setIndexT(SequenceType type) 278 { 279 // -1 mean not set 280 if (posT != -1d) 281 { 282 // get time position in second if possible 283 final double timePos; 284 285 if (type.timeInterval > 0d) 286 // we use * 2 to avoid index duplication because of rounding, anyway we will fix interval later 287 timePos = (2d * posT) / type.timeInterval; 288 else 289 // use absolute position as time pos 290 timePos = posT; 291 292 // index = (time position) / (image size) 293 indT = Math.round(timePos / type.sizeT); 294 } 295 } 296 297 public void clearIndX() 298 { 299 indX = -1d; 300 } 301 302 public void clearIndY() 303 { 304 indY = -1d; 305 } 306 307 public void clearIndZ() 308 { 309 indZ = -1d; 310 } 311 312 public void clearIndT() 313 { 314 indT = -1d; 315 } 316 317 public DimensionId getDifference(SequenceAbsolutePosition sap) 318 { 319 if (compare(indT, sap.indT) != 0) 320 return DimensionId.T; 321 if (compare(indZ, sap.indZ) != 0) 322 return DimensionId.Z; 323 if (compare(indY, sap.indY) != 0) 324 return DimensionId.Y; 325 if (compare(indX, sap.indX) != 0) 326 return DimensionId.X; 327 328 return null; 329 } 330 331 public void computeHashCode() 332 { 333 hc = Float.floatToIntBits((float) indX) ^ (Float.floatToIntBits((float) indY) << 8) 334 ^ (Float.floatToIntBits((float) indZ) << 16) ^ (Float.floatToIntBits((float) indT) << 24) 335 ^ Float.floatToIntBits((float) posX) ^ (Float.floatToIntBits((float) posY) >> 8) 336 ^ (Float.floatToIntBits((float) posZ) >> 16) ^ (Float.floatToIntBits((float) posT) >> 24); 337 } 338 339 @Override 340 public int hashCode() 341 { 342 return hc; 343 } 344 345 @Override 346 public boolean equals(Object obj) 347 { 348 if (obj instanceof SequenceAbsolutePosition) 349 { 350 final SequenceAbsolutePosition sap = (SequenceAbsolutePosition) obj; 351 352 return (sap.indX == indX) && (sap.indY == indY) && (sap.indZ == indZ) && (sap.indT == indT); 353 } 354 355 return super.equals(obj); 356 } 357 358 @Override 359 public int compareTo(SequenceAbsolutePosition sap) 360 { 361 int result = compare(indT, sap.indT); 362 if (result == 0) 363 result = compare(indZ, sap.indZ); 364 if (result == 0) 365 result = compare(indY, sap.indY); 366 if (result == 0) 367 result = compare(indX, sap.indX); 368 369 return result; 370 } 371 372 } 373 374 public static class SequenceIndexPosition implements Comparable<SequenceIndexPosition> 375 { 376 public int x; 377 public int y; 378 public int z; 379 public int t; 380 public int c; 381 382 public SequenceIndexPosition() 383 { 384 super(); 385 386 // undetermined 387 x = -1; 388 y = -1; 389 z = -1; 390 t = -1; 391 c = -1; 392 } 393 394 public DimensionId getDifference(SequenceIndexPosition sip) 395 { 396 if (SequenceFileSticher.compare(t, sip.t) != 0) 397 return DimensionId.T; 398 if (SequenceFileSticher.compare(z, sip.z) != 0) 399 return DimensionId.Z; 400 if (SequenceFileSticher.compare(c, sip.c) != 0) 401 return DimensionId.C; 402 if (SequenceFileSticher.compare(y, sip.y) != 0) 403 return DimensionId.Y; 404 if (SequenceFileSticher.compare(x, sip.x) != 0) 405 return DimensionId.X; 406 407 return null; 408 } 409 410 @Override 411 public int hashCode() 412 { 413 return x ^ (y << 6) ^ (z << 12) ^ (t << 18) ^ (c << 24); 414 } 415 416 @Override 417 public boolean equals(Object obj) 418 { 419 if (obj instanceof SequenceIndexPosition) 420 { 421 final SequenceIndexPosition sip = (SequenceIndexPosition) obj; 422 423 return (sip.x == x) && (sip.y == y) && (sip.z == z) && (sip.t == t) && (sip.c == c); 424 } 425 426 return super.equals(obj); 427 } 428 429 public int compare(SequenceIndexPosition sip) 430 { 431 int result = 0; 432 433 if (result == 0) 434 result = SequenceFileSticher.compare(t, sip.t); 435 if (result == 0) 436 result = SequenceFileSticher.compare(z, sip.z); 437 if (result == 0) 438 result = SequenceFileSticher.compare(c, sip.c); 439 if (result == 0) 440 result = SequenceFileSticher.compare(y, sip.y); 441 if (result == 0) 442 result = SequenceFileSticher.compare(x, sip.x); 443 444 return result; 445 } 446 447 @Override 448 public int compareTo(SequenceIndexPosition sip) 449 { 450 return compare(sip); 451 } 452 } 453 454 public static class SequencePosition implements Comparable<SequencePosition> 455 { 456 // /** 457 // * file path 458 // */ 459 // final String path; 460 // 461 // /** 462 // * importer for this path 463 // */ 464 // SequenceFileImporter importer; 465 // /** 466 // * metadata for this path 467 // */ 468 // OMEXMLMetadata metadata; 469 470 // /** 471 // * Absolute position 472 // */ 473 // final SequenceAbsolutePosition absPos; 474 475 /** 476 * index position 477 */ 478 final SequenceIndexPosition indPos; 479 480 // /** 481 // * size & type info 482 // */ 483 // final SequenceType type; 484 485 /** 486 * helper to find position 487 */ 488 FilePosition filePosition; 489 490 // public SequencePosition(String path) 491 // { 492 // super(); 493 // 494 // // type = new SequenceType(); 495 // filePosition = new FilePosition(path); 496 // indPos = new SequenceIndexPosition(); 497 // 498 // // not affected 499 // // absPos = new SequenceAbsolutePosition(); 500 // // importer = null; 501 // // metadata = null; 502 // } 503 504 public SequencePosition(FilePosition filePosition) 505 { 506 super(); 507 508 // type = new SequenceType(); 509 this.filePosition = filePosition; 510 indPos = new SequenceIndexPosition(); 511 512 // not affected 513 // absPos = new SequenceAbsolutePosition(); 514 // importer = null; 515 // metadata = null; 516 } 517 518 public String getBase() 519 { 520 return filePosition.base; 521 } 522 523 public String getPath() 524 { 525 return filePosition.path; 526 } 527 528 public int getIndexS() 529 { 530 int result = filePosition.getValue(DimensionId.NULL); 531 532 // if not defined then series = 0 533 if (result == -1) 534 result = 0; 535 536 return result; 537 } 538 539 public int getIndexX() 540 { 541 if (indPos.x != -1) 542 return indPos.x; 543 544 // default 545 return 0; 546 } 547 548 public int getIndexY() 549 { 550 if (indPos.y != -1) 551 return indPos.x; 552 553 // default 554 return 0; 555 } 556 557 public int getIndexC() 558 { 559 if (indPos.c != -1) 560 return indPos.c; 561 562 // default 563 return 0; 564 } 565 566 public int getIndexZ() 567 { 568 if (indPos.z != -1) 569 return indPos.z; 570 571 // default 572 return 0; 573 } 574 575 public int getIndexT() 576 { 577 if (indPos.t != -1) 578 return indPos.t; 579 580 // default 581 return 0; 582 } 583 584 // public int getSizeZ() 585 // { 586 // if (type.sizeZ != -1) 587 // return type.sizeZ; 588 // 589 // // default 590 // return 1; 591 // } 592 // 593 // public int getSizeT() 594 // { 595 // if (type.sizeT != -1) 596 // return type.sizeT; 597 // 598 // // default 599 // return 1; 600 // } 601 // 602 // public int getSizeC() 603 // { 604 // if (type.sizeC != -1) 605 // return type.sizeC; 606 // 607 // // default 608 // return 1; 609 // } 610 611 public int compareSeries(SequencePosition sp) 612 { 613 return filePosition.compareSeries(sp.filePosition); 614 } 615 616 public DimensionId getDifference(SequencePosition sp) 617 { 618 // not the same series (always compare first) 619 if (compareSeries(sp) != 0) 620 return DimensionId.NULL; 621 622 // DimensionId result = absPos.getDifference(sp.absPos); 623 // if (result == null) 624 DimensionId result = filePosition.getDifference(sp.filePosition, false); 625 if (result == null) 626 result = indPos.getDifference(indPos); 627 628 return result; 629 } 630 631 @Override 632 public int compareTo(SequencePosition sp) 633 { 634 int result = compareSeries(sp); 635 if (result == 0) 636 // result = absPos.compareTo(sp.absPos); 637 // if (result == 0) 638 result = filePosition.compare(sp.filePosition, false); 639 if (result == 0) 640 result = indPos.compare(sp.indPos); 641 642 return result; 643 } 644 645 @Override 646 public String toString() 647 { 648 return "Path=" + getPath() + " Position=[S:" + getIndexS() + " T:" + getIndexT() + " Z:" + getIndexZ() 649 + " C:" + getIndexC() + " Y:" + getIndexY() + " X:" + getIndexX() + "]"; 650 } 651 } 652 653 /** 654 * Class used to build a FilePosition from an <i>path</i> 655 * 656 * @author Stephane 657 */ 658 public static class FilePosition implements Comparable<FilePosition> 659 { 660 /** 661 * Class representing a position for a specific dimension. 662 * 663 * @author Stephane 664 */ 665 private static class PositionChunk 666 { 667 /** X dimension prefixes */ 668 static final String[] prefixesX = {"x", "xpos", "posx", "xposition", "positionx"}; 669 670 /** Y dimension prefixes */ 671 static final String[] prefixesY = {"y", "ypos", "posy", "yposition", "positiony"}; 672 673 /** Depth (Z) dimension prefixes (taken from Bio-Formats for almost) */ 674 static final String[] prefixesZ = {"fp", "sec", "z", "zs", "plane", "focal", "focalplane"}; 675 676 /** Time (T) dimension prefixes (taken from Bio-Formats for almost) */ 677 static final String[] prefixesT = {"t", "tl", "tp", "time", "frame"}; 678 679 /** Channel (C) dimension prefixes (taken from Bio-Formats for almost) */ 680 static final String[] prefixesC = {"c", "ch", "channel", "b", "band", "w", "wl", "wave", "wavelength"}; 681 682 /** Series (S)dimension prefixes (taken from Bio-Formats for almost) */ 683 static final String[] prefixesS = {"s", "series", "sp", "f", "field"}; 684 685 public DimensionId dim; 686 public int value; 687 688 PositionChunk(String prefix, int value) 689 { 690 super(); 691 692 dim = null; 693 if (!StringUtil.isEmpty(prefix)) 694 { 695 final String prefixLC = prefix.toLowerCase(); 696 697 if (dim == null) 698 dim = getDim(prefixLC, prefixesX, DimensionId.X); 699 if (dim == null) 700 dim = getDim(prefixLC, prefixesY, DimensionId.Y); 701 if (dim == null) 702 dim = getDim(prefixLC, prefixesZ, DimensionId.Z); 703 if (dim == null) 704 dim = getDim(prefixLC, prefixesT, DimensionId.T); 705 if (dim == null) 706 dim = getDim(prefixLC, prefixesC, DimensionId.C); 707 if (dim == null) 708 dim = getDim(prefixLC, prefixesS, DimensionId.NULL); 709 } 710 711 this.value = value; 712 } 713 714 private static DimensionId getDim(String prefix, String prefixes[], DimensionId d) 715 { 716 for (String p : prefixes) 717 if (prefix.endsWith(p)) 718 return d; 719 720 return null; 721 } 722 } 723 724 final String path; 725 final String base; 726 final List<PositionChunk> chunks; 727 728 FilePosition(String path) 729 { 730 super(); 731 732 this.path = path; 733 this.base = getBase(path); 734 735 chunks = new ArrayList<PositionChunk>(); 736 737 build(); 738 } 739 740 private void build() 741 { 742 // we need to extract position from filename (not from the complete path) 743 final String name = FileUtil.getFileName(path); 744 final int len = name.length(); 745 746 // int value; 747 int index = 0; 748 while (index < len) 749 { 750 // get starting digit char index 751 final int startInd = StringUtil.getNextDigitCharIndex(name, index); 752 753 // we find a digit char ? 754 if (startInd >= 0) 755 { 756 // get ending digit char index 757 int endInd = StringUtil.getNextNonDigitCharIndex(name, startInd); 758 if (endInd < 0) 759 endInd = len; 760 761 // add number only if < 100000 (else it can be a date or id...) 762 // if ((endInd - startInd) < 6) 763 // { 764 // get prefix 765 final String prefix = getPositionPrefix(name, startInd - 1); 766 // get value 767 final int value = StringUtil.parseInt(name.substring(startInd, endInd), -1); 768 769 // add the position info 770 addChunk(prefix, value); 771 // } 772 773 // adjust index 774 index = endInd; 775 } 776 else 777 index = len; 778 } 779 } 780 781 private static String getBase(String path) 782 { 783 final String folder = FileUtil.getDirectory(path, true); 784 785 // we extract position from filename (not from the complete path) 786 String result = FileUtil.getFileName(path); 787 int pos = 0; 788 789 while (pos < result.length()) 790 { 791 final int st = StringUtil.getNextDigitCharIndex(result, pos); 792 793 if (st != -1) 794 { 795 // get ending digit char index 796 int end = StringUtil.getNextNonDigitCharIndex(result, st); 797 if (end < 0) 798 end = result.length(); 799 // final int size = end - st; 800 801 // remove number from name if number size < 6 802 // if (size < 6) 803 result = result.substring(0, st) + result.substring(end); 804 // pass to next 805 // else 806 // pos = end; 807 } 808 else 809 // done 810 break; 811 } 812 813 return folder + result; 814 } 815 816 private static String getPositionPrefix(String text, int ind) 817 { 818 if ((ind >= 0) && (ind < text.length())) 819 { 820 // we have a letter at this position 821 if (Character.isLetter(text.charAt(ind))) 822 // get complete prefix 823 return text.substring(StringUtil.getPreviousNonLetterCharIndex(text, ind) + 1, ind + 1); 824 } 825 826 return ""; 827 } 828 829 private void addChunk(String prefix, int value) 830 { 831 final PositionChunk chunk = new PositionChunk(prefix, value); 832 // get the previous chunk for this dimension 833 final PositionChunk previousChunk = getChunk(chunk.dim, false); 834 835 // // already have a chunk for this dimension ? --> remove it (keep last found) 836 // if (previousChunk != null) 837 // removeChunk(previousChunk); 838 // already have a chunk for this dimension --> detach its from current dim (keep last found) 839 if (previousChunk != null) 840 previousChunk.dim = null; 841 842 // add the chunk 843 chunks.add(chunk); 844 } 845 846 private boolean removeChunk(PositionChunk chunk) 847 { 848 return chunks.remove(chunk); 849 } 850 851 boolean removeChunk(DimensionId dim) 852 { 853 return removeChunk(getChunk(dim, true)); 854 } 855 856 public int getValue(DimensionId dim) 857 { 858 final PositionChunk chunk = getChunk(dim, true); 859 860 if (chunk != null) 861 return chunk.value; 862 863 // -1 --> dimension not affected 864 return -1; 865 } 866 867 boolean isUnknowDim(DimensionId dim) 868 { 869 return getChunk(dim, false) == null; 870 } 871 872 public PositionChunk getChunk(DimensionId dim, boolean allowUnknown) 873 { 874 if (dim != null) 875 { 876 for (PositionChunk chunk : chunks) 877 if (chunk.dim == dim) 878 return chunk; 879 880 if (allowUnknown) 881 return getChunkFromUnknown(dim); 882 } 883 884 return null; 885 } 886 887 /** 888 * Try to attribute given dimension position from unknown chunk(x).<br> 889 * Work only for Z, T and C dimension (unlikely to have unaffected X and Y dimension) 890 */ 891 private PositionChunk getChunkFromUnknown(DimensionId dim) 892 { 893 final boolean hasCChunk = (getChunk(DimensionId.C, false) != null); 894 final boolean hasZChunk = (getChunk(DimensionId.Z, false) != null); 895 final boolean hasTChunk = (getChunk(DimensionId.T, false) != null); 896 897 // priority order for affectation: T, Z, C 898 switch (dim) 899 { 900 case Z: 901 // shouldn't happen (Z already affected) 902 if (hasZChunk) 903 return null; 904 905 // T chunk present --> Z = unknown[0] 906 if (hasTChunk) 907 return getUnknownChunk(0); 908 909 // T chunk not present --> T = unknown[0]; Z = unknown[1] 910 return getUnknownChunk(1); 911 912 case T: 913 // shouldn't happen (T already affected) 914 if (hasTChunk) 915 return null; 916 917 // T = unknown[0] 918 return getUnknownChunk(0); 919 920 case C: 921 // shouldn't happen (C already affected) 922 if (hasCChunk) 923 return null; 924 925 if (hasTChunk) 926 { 927 // T and Z chunk present --> C = unknown[0] 928 if (hasZChunk) 929 return getUnknownChunk(0); 930 931 // T chunk present --> Z = unknown[0]; C = unknown[1] 932 return getUnknownChunk(1); 933 } 934 // Z chunk present --> T = unknown[0]; C = unknown[1] 935 else if (hasZChunk) 936 return getUnknownChunk(1); 937 938 // no other chunk present --> T = unknown[0]; Z = unknown[1]; C = unknown[2] 939 return getUnknownChunk(2); 940 } 941 942 return null; 943 } 944 945 private PositionChunk getUnknownChunk(int i) 946 { 947 int ind = 0; 948 949 for (PositionChunk chunk : chunks) 950 { 951 if (chunk.dim == null) 952 { 953 if (ind == i) 954 return chunk; 955 956 ind++; 957 } 958 } 959 960 return null; 961 } 962 963 int getUnknownChunkCount() 964 { 965 int result = 0; 966 967 for (PositionChunk chunk : chunks) 968 if (chunk.dim == null) 969 result++; 970 971 return result; 972 } 973 974 public int compareSeries(FilePosition ipb) 975 { 976 int result = 0; 977 final String bn1 = base; 978 final String bn2 = ipb.base; 979 980 // can compare on base name ? 981 if (!StringUtil.isEmpty(bn1) && !StringUtil.isEmpty(bn2)) 982 result = bn1.compareTo(bn2); 983 984 // compare on series path position 985 if (result == 0) 986 result = SequenceFileSticher.compare(getValue(DimensionId.NULL), ipb.getValue(DimensionId.NULL)); 987 988 return result; 989 } 990 991 public int compare(FilePosition ipb, boolean compareSeries) 992 { 993 int result = 0; 994 995 // always compare series first 996 if (compareSeries) 997 result = compareSeries(ipb); 998 999 if (result == 0) 1000 result = SequenceFileSticher.compare(getValue(DimensionId.T), ipb.getValue(DimensionId.T)); 1001 if (result == 0) 1002 result = SequenceFileSticher.compare(getValue(DimensionId.Z), ipb.getValue(DimensionId.Z)); 1003 if (result == 0) 1004 result = SequenceFileSticher.compare(getValue(DimensionId.C), ipb.getValue(DimensionId.C)); 1005 if (result == 0) 1006 result = SequenceFileSticher.compare(getValue(DimensionId.Y), ipb.getValue(DimensionId.Y)); 1007 if (result == 0) 1008 result = SequenceFileSticher.compare(getValue(DimensionId.X), ipb.getValue(DimensionId.X)); 1009 1010 return result; 1011 } 1012 1013 public DimensionId getDifference(FilePosition ipb, boolean compareSeries) 1014 { 1015 if (compareSeries) 1016 { 1017 // always compare series first 1018 if (compareSeries(ipb) != 0) 1019 return DimensionId.NULL; 1020 } 1021 1022 if (SequenceFileSticher.compare(getValue(DimensionId.T), ipb.getValue(DimensionId.T)) != 0) 1023 return DimensionId.T; 1024 if (SequenceFileSticher.compare(getValue(DimensionId.Z), ipb.getValue(DimensionId.Z)) != 0) 1025 return DimensionId.Z; 1026 if (SequenceFileSticher.compare(getValue(DimensionId.C), ipb.getValue(DimensionId.C)) != 0) 1027 return DimensionId.C; 1028 if (SequenceFileSticher.compare(getValue(DimensionId.Y), ipb.getValue(DimensionId.Y)) != 0) 1029 return DimensionId.Y; 1030 if (SequenceFileSticher.compare(getValue(DimensionId.X), ipb.getValue(DimensionId.X)) != 0) 1031 return DimensionId.X; 1032 1033 return null; 1034 } 1035 1036 @Override 1037 public int compareTo(FilePosition ipb) 1038 { 1039 return compare(ipb, false); 1040 } 1041 1042 @Override 1043 public String toString() 1044 { 1045 return "FilePosition [S:" + getValue(DimensionId.NULL) + " C:" + getValue(DimensionId.C) + " T:" 1046 + getValue(DimensionId.T) + " Z:" + getValue(DimensionId.Z) + " Y:" + getValue(DimensionId.Y) 1047 + " X:" + getValue(DimensionId.X) + "]"; 1048 } 1049 } 1050 1051 public static class SequenceFileGroup 1052 { 1053 public final SequenceIdent ident; 1054 public final List<SequencePosition> positions; 1055 1056 // final sequence dimension 1057 public int totalSizeX; 1058 public int totalSizeY; 1059 public int totalSizeZ; 1060 public int totalSizeT; 1061 public int totalSizeC; 1062 1063 /** 1064 * Internal use only, use {@link SequenceFileSticher#groupFiles(SequenceFileImporter, Collection, boolean, FileFrame)} instead. 1065 */ 1066 public SequenceFileGroup(SequenceIdent ident) 1067 { 1068 super(); 1069 1070 this.ident = ident; 1071 positions = new ArrayList<SequencePosition>(); 1072 1073 // we will compute them with buildIndexesFromPositions 1074 totalSizeX = 0; 1075 totalSizeY = 0; 1076 totalSizeZ = 0; 1077 totalSizeT = 0; 1078 totalSizeC = 0; 1079 } 1080 1081 // void cleanFixedAbsPos() 1082 // { 1083 // // nothing to do 1084 // if (positions.isEmpty()) 1085 // return; 1086 // 1087 // final SequencePosition fpos = positions.get(0); 1088 // 1089 // // init 1090 // double indX = fpos.absPos.indX; 1091 // double indY = fpos.absPos.indY; 1092 // double indZ = fpos.absPos.indZ; 1093 // double indT = fpos.absPos.indT; 1094 // boolean posXChanged = false; 1095 // boolean posYChanged = false; 1096 // boolean posZChanged = false; 1097 // boolean posTChanged = false; 1098 // 1099 // for (int i = 1; i < positions.size(); i++) 1100 // { 1101 // final SequencePosition pos = positions.get(i); 1102 // 1103 // if (indX != pos.absPos.indX) 1104 // posXChanged = true; 1105 // if (indY != pos.absPos.indY) 1106 // posYChanged = true; 1107 // if (indZ != pos.absPos.indZ) 1108 // posZChanged = true; 1109 // if (indT != pos.absPos.indT) 1110 // posTChanged = true; 1111 // } 1112 // 1113 // for (SequencePosition pos : positions) 1114 // { 1115 // // fixed X position --> useless 1116 // if (!posXChanged) 1117 // pos.absPos.clearIndX(); 1118 // // fixed Y position --> useless 1119 // if (!posYChanged) 1120 // pos.absPos.clearIndY(); 1121 // // fixed Z position --> useless 1122 // if (!posZChanged) 1123 // pos.absPos.clearIndZ(); 1124 // // fixed T position --> useless 1125 // if (!posTChanged) 1126 // pos.absPos.clearIndT(); 1127 // } 1128 // } 1129 1130 void checkZTDimIdPos() 1131 { 1132 final boolean zMulti = ident.baseType.sizeZ > 1; 1133 final boolean tMulti = ident.baseType.sizeT > 1; 1134 1135 // determine if we need to swap out Z and T position (if only one of the dimension can move) 1136 if (tMulti ^ zMulti) 1137 { 1138 boolean tSet = false; 1139 boolean tCanChange = true; 1140 boolean zSet = false; 1141 boolean zCanChange = true; 1142 1143 for (SequencePosition pos : positions) 1144 { 1145 final FilePosition idPos = pos.filePosition; 1146 1147 if (idPos != null) 1148 { 1149 if (idPos.getValue(DimensionId.T) != -1) 1150 tSet = true; 1151 if (idPos.getValue(DimensionId.Z) != -1) 1152 zSet = true; 1153 1154 if (!idPos.isUnknowDim(DimensionId.T)) 1155 tCanChange = false; 1156 if (!idPos.isUnknowDim(DimensionId.Z)) 1157 zCanChange = false; 1158 } 1159 } 1160 1161 // Z and T position are not fixed, and one of the dimension , try to swap if possible 1162 if (tCanChange && zCanChange) 1163 { 1164 boolean swapZT = false; 1165 1166 // multi T but single Z 1167 if (tMulti) 1168 { 1169 // T position set but can be swapped with Z 1170 if (tSet && tCanChange && !zSet) 1171 swapZT = true; 1172 } 1173 else 1174 // multi Z but single T 1175 { 1176 // Z position set but can be swapped with T 1177 if (zSet && zCanChange && !tSet) 1178 swapZT = true; 1179 } 1180 1181 // swap T and Z dimension 1182 if (swapZT) 1183 { 1184 for (SequencePosition pos : positions) 1185 { 1186 final FilePosition idPos = pos.filePosition; 1187 1188 if (idPos != null) 1189 { 1190 final FilePosition.PositionChunk zChunk = idPos.getChunk(DimensionId.Z, true); 1191 final FilePosition.PositionChunk tChunk = idPos.getChunk(DimensionId.T, true); 1192 1193 // swap dim 1194 if (zChunk != null) 1195 zChunk.dim = DimensionId.T; 1196 if (tChunk != null) 1197 tChunk.dim = DimensionId.Z; 1198 } 1199 } 1200 } 1201 } 1202 } 1203 } 1204 1205 void buildIndexesAndSizesFromPositions(boolean findPosition) 1206 { 1207 final int size = positions.size(); 1208 1209 // nothing to do 1210 if (size <= 0) 1211 return; 1212 1213 final SequenceType baseType = ident.baseType; 1214 1215 // store final sequence dimension 1216 final int sc = baseType.sizeC; 1217 final int st = baseType.sizeT; 1218 final int sz = baseType.sizeZ; 1219 final int sy = baseType.sizeY; 1220 final int sx = baseType.sizeX; 1221 1222 // compact indexes 1223 int t = 0; 1224 int z = 0; 1225 int c = 0; 1226 int y = 0; 1227 int x = 0; 1228 int mt = 0; 1229 int mz = 0; 1230 int mc = 0; 1231 int my = 0; 1232 int mx = 0; 1233 1234 SequencePosition previous = positions.get(0); 1235 SequenceIndexPosition indPos = previous.indPos; 1236 1237 indPos.t = t; 1238 indPos.z = z; 1239 indPos.c = c; 1240 indPos.y = y; 1241 indPos.x = x; 1242 1243 for (int i = 1; i < size; i++) 1244 { 1245 final SequencePosition current = positions.get(i); 1246 DimensionId diff = null; 1247 1248 // if we don't want real position we just use T ordering 1249 if (findPosition) 1250 diff = previous.getDifference(current); 1251 1252 // default = T dimension 1253 if (diff == null) 1254 diff = DimensionId.T; 1255 1256 // base path changed 1257 switch (diff) 1258 { 1259 // // series position change (shouldn't arrive, group are here for that) 1260 // case NULL: 1261 // s++; 1262 // // keep maximum 1263 // ms = Math.max(ms, s); 1264 // // reset others indexes 1265 // t = 0; 1266 // z = 0; 1267 // c = 0; 1268 // y = 0; 1269 // x = 0; 1270 // break; 1271 1272 // T position changed (default case) 1273 case T: 1274 default: 1275 t += st; 1276 // keep maximum 1277 mt = Math.max(mt, t); 1278 // reset others indexes 1279 z = 0; 1280 c = 0; 1281 y = 0; 1282 x = 0; 1283 break; 1284 1285 // Z position changed 1286 case Z: 1287 z += sz; 1288 // keep maximum 1289 mz = Math.max(mz, z); 1290 // reset others indexes 1291 c = 0; 1292 y = 0; 1293 x = 0; 1294 break; 1295 1296 // C position changed 1297 case C: 1298 c += sc; 1299 // keep maximum 1300 mc = Math.max(mc, c); 1301 // reset others indexes 1302 y = 0; 1303 x = 0; 1304 break; 1305 1306 // Y position changed 1307 case Y: 1308 y++; 1309 // keep maximum 1310 my = Math.max(my, y); 1311 // reset others indexes 1312 x = 0; 1313 break; 1314 1315 // X position changed 1316 case X: 1317 x++; 1318 // keep maximum 1319 mx = Math.max(mx, x); 1320 break; 1321 } 1322 1323 // update current position 1324 indPos = current.indPos; 1325 indPos.t = t; 1326 indPos.z = z; 1327 indPos.c = c; 1328 indPos.y = y; 1329 indPos.x = x; 1330 1331 // keep trace of last position 1332 previous = current; 1333 } 1334 1335 // we want size for each dimension 1336 mt++; 1337 mz++; 1338 mc++; 1339 my++; 1340 mx++; 1341 1342 // normally we want the equality here 1343 if ((mt * mz * mc * my * mx) != size) 1344 { 1345 // note that this can happen when thread is interrupted so just put a warning here 1346 System.err.println("Warning: SequenceFileSticher - number of image doesn't match: " + size 1347 + " (expected = " + (mt * mz * mc * my * mx) + ")"); 1348 } 1349 1350 // store final sequence dimension 1351 totalSizeC = mc * sc; 1352 totalSizeT = mt * st; 1353 totalSizeZ = mz * sz; 1354 totalSizeY = my * sy; 1355 totalSizeX = mx * sx; 1356 } 1357 1358 /** 1359 * Return all contained path in this group 1360 */ 1361 public List<String> getPaths() 1362 { 1363 final List<String> results = new ArrayList<String>(); 1364 1365 for (SequencePosition pos : positions) 1366 results.add(pos.getPath()); 1367 1368 return results; 1369 } 1370 1371 } 1372 1373 /** 1374 * Same as {@link SequenceFileSticher#groupFiles(SequenceFileImporter, Collection, boolean, FileFrame)} except it does several groups if all image file path 1375 * cannot be grouped to form a single Sequence.<br> 1376 * The grouping is done using the path name information (recognizing and parsing specific patterns in the path) and assume file shares the same properties 1377 * (dimensions).<br> 1378 * The method returns a set of {@link SequenceFileGroup} where each group define a Sequence.<br> 1379 * 1380 * @param importer 1381 * {@link SequenceFileImporter} to use to open image.<br> 1382 * If set to <i>null</i> the method automatically try to find a compatible {@link SequenceFileImporter}. 1383 * @param paths 1384 * image file paths we want to group 1385 * @param findPosition 1386 * if true we try to determine the X, Y, Z, T and C image position otherwise a simple ascending T ordering is done 1387 * @param loadingFrame 1388 * Loading dialog if any to show progress 1389 * @see #groupFiles(SequenceFileImporter, Collection, boolean, FileFrame) 1390 */ 1391 public static Collection<SequenceFileGroup> groupAllFiles(SequenceFileImporter importer, Collection<String> paths, 1392 boolean findPosition, FileFrame loadingFrame) 1393 { 1394 final List<String> sortedPaths = Loader.cleanNonImageFile(new ArrayList<String>(paths)); 1395 1396 if (sortedPaths.isEmpty()) 1397 return new ArrayList<SequenceFileGroup>(); 1398 1399 // final List<FilePosition> filePositions = new ArrayList<FilePosition>(); 1400 1401 if (loadingFrame != null) 1402 loadingFrame.setAction("Sort paths..."); 1403 1404 // sort paths on name using smart sorter 1405 if (sortedPaths.size() > 1) 1406 Collections.sort(sortedPaths, new AlphanumComparator()); 1407 1408 // we do a 1st pass to build all FilePosition 1409 if (loadingFrame != null) 1410 loadingFrame.setAction("Extracting positions from paths..."); 1411 1412 // group FilePosition by 'base' path 1413 final Map<String, List<FilePosition>> pathPositionsMap = new HashMap<String, List<FilePosition>>(); 1414 1415 // build FilePosition 1416 for (String path : sortedPaths) 1417 { 1418 final FilePosition filePosition = new FilePosition(path); 1419 final String base = filePosition.base; 1420 1421 // we want to group by 'base' path 1422 List<FilePosition> positions = pathPositionsMap.get(base); 1423 1424 // list not yet created ? 1425 if (positions == null) 1426 { 1427 // create and add it 1428 positions = new ArrayList<FilePosition>(); 1429 pathPositionsMap.put(base, positions); 1430 } 1431 1432 // add it 1433 positions.add(filePosition); 1434 // // add it to global list as well 1435 // filePositions.add(filePosition); 1436 } 1437 1438 final Map<SequenceIdent, SequenceFileGroup> result = new HashMap<SequenceIdent, SequenceFileGroup>(); 1439 1440 // clean FilePosition grouped by base path and add them to group 1441 for (List<FilePosition> positions : pathPositionsMap.values()) 1442 { 1443 // remove position information which never change 1444 while (cleanPositions(positions, DimensionId.NULL)) 1445 ; 1446 while (cleanPositions(positions, DimensionId.T)) 1447 ; 1448 while (cleanPositions(positions, DimensionId.Z)) 1449 ; 1450 while (cleanPositions(positions, DimensionId.C)) 1451 ; 1452 while (cleanPositions(positions, DimensionId.Y)) 1453 ; 1454 while (cleanPositions(positions, DimensionId.X)) 1455 ; 1456 1457 // add position to group(s) 1458 for (FilePosition pos : positions) 1459 addToGroup(result, new SequencePosition(pos), importer); 1460 } 1461 1462 /* 1463 * if (loadingFrame != null) 1464 * loadingFrame.setAction("Get positions information from metadata..."); 1465 * 1466 * SequenceFileImporter imp = importer; 1467 * int indT = 0; 1468 * 1469 * for (int i = 0; i < sortedPaths.size(); i++) 1470 * { 1471 * final String path = sortedPaths.get(i); 1472 * final SequencePosition position = new SequencePosition(path); 1473 * final SequenceType type = position.type; 1474 * final SequenceAbsolutePosition absPos = position.absPos; 1475 * final SequenceIndexPosition indPos = position.indPos; 1476 * 1477 * // try to open the image 1478 * imp = tryOpen(imp, path); 1479 * 1480 * // correctly opened ? 1481 * if (imp != null) 1482 * { 1483 * try 1484 * { 1485 * // get metadata 1486 * final OMEXMLMetadata meta = imp.getOMEXMLMetaData(); 1487 * 1488 * // set type information 1489 * type.sizeX = MetaDataUtil.getSizeX(meta, 0); 1490 * type.sizeY = MetaDataUtil.getSizeY(meta, 0); 1491 * type.sizeZ = MetaDataUtil.getSizeZ(meta, 0); 1492 * type.sizeT = MetaDataUtil.getSizeT(meta, 0); 1493 * type.sizeC = MetaDataUtil.getSizeC(meta, 0); 1494 * type.dataType = MetaDataUtil.getDataType(meta, 0); 1495 * // use -1 as default value to detect when position is not set 1496 * type.pixelSizeX = MetaDataUtil.getPixelSizeX(meta, 0, 0d); 1497 * type.pixelSizeY = MetaDataUtil.getPixelSizeY(meta, 0, 0d); 1498 * type.pixelSizeZ = MetaDataUtil.getPixelSizeZ(meta, 0, 0d); 1499 * type.timeInterval = MetaDataUtil.getTimeInterval(meta, 0, 0d); 1500 * // can compute hash code 1501 * type.computeHashCode(); 1502 * 1503 * if (findPosition) 1504 * { 1505 * // use -1 as default value to detect when position is not set 1506 * absPos.posX = MathUtil.roundSignificant(MetaDataUtil.getPositionX(meta, 0, 0, 0, 0, -1d), 1507 * 5); 1508 * absPos.posY = MathUtil.roundSignificant(MetaDataUtil.getPositionY(meta, 0, 0, 0, 0, -1d), 1509 * 5); 1510 * absPos.posZ = MathUtil.roundSignificant(MetaDataUtil.getPositionZ(meta, 0, 0, 0, 0, -1d), 1511 * 5); 1512 * absPos.posT = MathUtil.roundSignificant(MetaDataUtil.getPositionT(meta, 0, 0, 0, 0, -1d), 1513 * 5); 1514 * // try to compute index from absolute and pixel size info 1515 * absPos.setIndexX(type); 1516 * absPos.setIndexY(type); 1517 * absPos.setIndexZ(type); 1518 * absPos.setIndexT(type); 1519 * // can compute hash code 1520 * absPos.computeHashCode(); 1521 * } 1522 * 1523 * // store importer & metadata object 1524 * position.importer = imp; 1525 * position.metadata = meta; 1526 * } 1527 * catch (Throwable t) 1528 * { 1529 * // error while retrieve metadata 1530 * t.printStackTrace(); 1531 * } 1532 * finally 1533 * { 1534 * try 1535 * { 1536 * // close importer 1537 * imp.close(); 1538 * } 1539 * catch (IOException e) 1540 * { 1541 * // just ignore... 1542 * } 1543 * } 1544 * 1545 * // store filePosition in position object 1546 * if (findPosition) 1547 * position.filePosition = filePositions.get(i); 1548 * else 1549 * { 1550 * indPos.x = 0; 1551 * indPos.y = 0; 1552 * indPos.z = 0; 1553 * // simple T ordering 1554 * indPos.t = indT; 1555 * indPos.c = 0; 1556 * 1557 * // next T position 1558 * indT += type.sizeT; 1559 * } 1560 * 1561 * // add to result map (important to have position informations first) 1562 * addToGroup(result, position); 1563 * } 1564 * } 1565 * 1566 */ 1567 if (loadingFrame != null) 1568 loadingFrame.setAction("Cleanup up positions and rebuilding indexes..."); 1569 1570 // need to improve position informations 1571 for (SequenceFileGroup group : result.values()) 1572 { 1573 // // clean absolute positions 1574 // group.cleanFixedAbsPos(); 1575 // check if we can revert Z and T dimension from FilePosition 1576 if (findPosition) 1577 { 1578 group.checkZTDimIdPos(); 1579 // sort group positions on cleaned up position (S, T, Z, C, Y, X order) 1580 Collections.sort(group.positions); 1581 } 1582 1583 // build final index position from internal position (absolute or path) 1584 group.buildIndexesAndSizesFromPositions(findPosition); 1585 } 1586 1587 return result.values(); 1588 } 1589 1590 /** 1591 * Take a list of image file path as input and try to group them to form a unique Sequence.<br> 1592 * The grouping is done using the path name information (recognizing and parsing specific patterns in the path) and assume file shares the same properties 1593 * (dimensions).<br> 1594 * The method returns the "biggest" group found, use {@link SequenceFileSticher#groupAllFiles(SequenceFileImporter, Collection, boolean, FileFrame)} to 1595 * retrieve all possible groups.<br> 1596 * 1597 * @param importer 1598 * {@link SequenceFileImporter} to use to open image.<br> 1599 * If set to <i>null</i> the method automatically try to find a compatible 1600 * {@link SequenceFileImporter} 1601 * @param paths 1602 * image file paths we want to group 1603 * @param findPosition 1604 * if true we try to determine the X, Y, Z, T and C image position otherwise a simple ascending T ordering is done 1605 * @param loadingFrame 1606 * Loading dialog if any to show progress 1607 * @see #groupAllFiles(SequenceFileImporter, Collection, boolean, FileFrame) 1608 */ 1609 public static SequenceFileGroup groupFiles(SequenceFileImporter importer, Collection<String> paths, 1610 boolean findPosition, FileFrame loadingFrame) 1611 { 1612 SequenceFileGroup result = null; 1613 1614 for (SequenceFileGroup group : groupAllFiles(importer, paths, findPosition, loadingFrame)) 1615 { 1616 if (result == null) 1617 result = group; 1618 else if (result.positions.size() < group.positions.size()) 1619 result = group; 1620 } 1621 1622 return result; 1623 } 1624 1625 static int compare(double v1, double v2) 1626 { 1627 // can compare ? 1628 if ((v1 != -1d) && (v2 != -1d)) 1629 { 1630 if (v1 < v2) 1631 return -1; 1632 else if (v1 > v2) 1633 return 1; 1634 } 1635 1636 return 0; 1637 } 1638 1639 /** 1640 * Returns opened {@link SequenceFileImporter} or <i>null</i> if we can't open the given path 1641 */ 1642 @SuppressWarnings("resource") 1643 static SequenceFileImporter tryOpen(SequenceFileImporter importer, String path) 1644 { 1645 final boolean tryAnotherImporter; 1646 SequenceFileImporter imp; 1647 1648 // importer not defined ? 1649 if (importer == null) 1650 { 1651 // try to find a compatible file importer 1652 imp = Loader.getSequenceFileImporter(path, true); 1653 // we don't need to try another importer 1654 tryAnotherImporter = false; 1655 } 1656 else 1657 { 1658 // use given importer 1659 imp = importer; 1660 // we may need to test another importer 1661 tryAnotherImporter = true; 1662 } 1663 1664 // we have an importer ? 1665 if (imp != null) 1666 { 1667 // disable original metadata for LOCI importer 1668 if (imp instanceof LociImporterPlugin) 1669 { 1670 // disable grouping and extra metadata 1671 ((LociImporterPlugin) imp).setGroupFiles(false); 1672 ((LociImporterPlugin) imp).setReadOriginalMetadata(false); 1673 } 1674 1675 try 1676 { 1677 // try to open it (require default metadata otherwise pixel size may miss) 1678 imp.open(path, 0); 1679 } 1680 catch (ClosedByInterruptException e) 1681 { 1682 // interrupted --> just return null 1683 return null; 1684 } 1685 catch (Throwable t) 1686 { 1687 // can't be opened... try with an other importer 1688 if (tryAnotherImporter) 1689 return tryOpen(null, path); 1690 1691 // can't open importer 1692 return null; 1693 } 1694 } 1695 1696 return imp; 1697 } 1698 1699 // private static void addToGroup(Map<SequenceIdent, SequenceFileGroup> groups, SequencePosition position) 1700 // { 1701 // final SequenceIdent ident = new SequenceIdent(position.getBase(), position.getSeriesFromPath(), position.type, 1702 // position.importer); 1703 // SequenceFileGroup group = groups.get(ident); 1704 // 1705 // // group not yet created ? 1706 // if (group == null) 1707 // { 1708 // // create and add it 1709 // group = new SequenceFileGroup(ident); 1710 // groups.put(ident, group); 1711 // } 1712 // 1713 // // add to the group 1714 // group.positions.add(position); 1715 // } 1716 1717 private static void addToGroup(Map<SequenceIdent, SequenceFileGroup> groups, SequencePosition position, 1718 SequenceFileImporter importer) 1719 { 1720 SequenceFileGroup group = groups.get(new SequenceIdent(position.getBase(), position.getIndexS())); 1721 1722 // no group yet for this base path 1723 if (group == null) 1724 { 1725 // get complete ident for this position 1726 final SequenceIdent ident = getSequenceIdent(importer, position); 1727 1728 // can't add this position... 1729 if (ident == null) 1730 return; 1731 1732 // create and add it 1733 group = new SequenceFileGroup(ident); 1734 groups.put(ident, group); 1735 } 1736 1737 // add to the group 1738 group.positions.add(position); 1739 } 1740 1741 /** 1742 * Build and return sequence ident for specified {@link SequencePosition} 1743 */ 1744 private static SequenceIdent getSequenceIdent(SequenceFileImporter importer, SequencePosition position) 1745 { 1746 // try to open the image 1747 final SequenceFileImporter imp = tryOpen(importer, position.getPath()); 1748 1749 // can't open it (or interrupted) ? --> return null 1750 if (imp == null) 1751 return null; 1752 1753 try 1754 { 1755 // get metadata 1756 final OMEXMLMetadata meta = imp.getOMEXMLMetaData(); 1757 final SequenceType type = new SequenceType(); 1758 1759 // set type information 1760 type.sizeX = MetaDataUtil.getSizeX(meta, 0); 1761 type.sizeY = MetaDataUtil.getSizeY(meta, 0); 1762 type.sizeZ = MetaDataUtil.getSizeZ(meta, 0); 1763 type.sizeT = MetaDataUtil.getSizeT(meta, 0); 1764 type.sizeC = MetaDataUtil.getSizeC(meta, 0); 1765 type.dataType = MetaDataUtil.getDataType(meta, 0); 1766 // use -1 as default value to detect when position is not set 1767 type.pixelSizeX = MetaDataUtil.getPixelSizeX(meta, 0, 0d); 1768 type.pixelSizeY = MetaDataUtil.getPixelSizeY(meta, 0, 0d); 1769 type.pixelSizeZ = MetaDataUtil.getPixelSizeZ(meta, 0, 0d); 1770 type.timeInterval = MetaDataUtil.getTimeInterval(meta, 0, 0d); 1771 // can compute hash code 1772 type.computeHashCode(); 1773 1774 return new SequenceIdent(position.getBase(), position.getIndexS(), type, imp); 1775 } 1776 catch (Throwable t) 1777 { 1778 // error while retrieve metadata 1779 t.printStackTrace(); 1780 return null; 1781 } 1782 finally 1783 { 1784 try 1785 { 1786 // close importer 1787 imp.close(); 1788 } 1789 catch (IOException e) 1790 { 1791 // just ignore... 1792 } 1793 } 1794 } 1795 1796 private static boolean cleanPositions(Collection<FilePosition> filePositions, DimensionId dim) 1797 { 1798 // remove fixed dim 1799 int value = -1; 1800 for (FilePosition position : filePositions) 1801 { 1802 final int v = position.getValue(dim); 1803 1804 if (v != -1) 1805 { 1806 if (value == -1) 1807 value = v; 1808 else if (value != v) 1809 { 1810 // variable --> stop 1811 value = -1; 1812 break; 1813 } 1814 } 1815 } 1816 1817 // fixed dimension ? --> remove it 1818 if (value != -1) 1819 { 1820 for (FilePosition position : filePositions) 1821 { 1822 if (position.getValue(dim) != -1) 1823 position.removeChunk(dim); 1824 } 1825 1826 return true; 1827 } 1828 1829 return false; 1830 } 1831}