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.file; 020 021import java.awt.Rectangle; 022import java.io.File; 023import java.io.IOException; 024import java.lang.reflect.InvocationTargetException; 025import java.util.ArrayList; 026import java.util.Arrays; 027import java.util.Collection; 028import java.util.HashMap; 029import java.util.HashSet; 030import java.util.List; 031import java.util.Map; 032import java.util.Map.Entry; 033import java.util.Set; 034import java.util.TreeMap; 035 036import icy.common.exception.UnsupportedFormatException; 037import icy.file.SequenceFileSticher.SequenceFileGroup; 038import icy.file.SequenceFileSticher.SequenceIdent; 039import icy.file.SequenceFileSticher.SequencePosition; 040import icy.gui.dialog.ImporterSelectionDialog; 041import icy.gui.dialog.SeriesSelectionDialog; 042import icy.gui.frame.progress.FailedAnnounceFrame; 043import icy.gui.frame.progress.FileFrame; 044import icy.gui.menu.ApplicationMenu; 045import icy.image.ChannelPosition; 046import icy.image.IcyBufferedImage; 047import icy.image.ImagePosition; 048import icy.image.ImageProvider; 049import icy.main.Icy; 050import icy.plugin.PluginDescriptor; 051import icy.plugin.PluginLauncher; 052import icy.plugin.PluginLoader; 053import icy.preferences.GeneralPreferences; 054import icy.sequence.MetaDataUtil; 055import icy.sequence.Sequence; 056import icy.sequence.SequenceIdImporter; 057import icy.sequence.SequenceImporter; 058import icy.sequence.SequencePersistent; 059import icy.sequence.SequenceUtil; 060import icy.system.IcyExceptionHandler; 061import icy.system.SystemUtil; 062import icy.system.thread.ThreadUtil; 063import icy.type.DataType; 064import icy.type.collection.CollectionUtil; 065import icy.util.OMEUtil; 066import icy.util.StringUtil; 067import icy.util.XMLUtil; 068import loci.formats.FormatException; 069import loci.formats.IFormatReader; 070import loci.formats.ImageReader; 071import loci.formats.meta.MetadataStore; 072import loci.formats.ome.OMEXMLMetadataImpl; 073import ome.xml.meta.OMEXMLMetadata; 074import plugins.kernel.importer.LociImporterPlugin; 075 076/** 077 * Sequence / Image loader class. 078 * 079 * @author Fabrice de Chaumont & Stephane 080 */ 081public class Loader 082{ 083 /** 084 * @deprecated Use {@link SequenceFileSticher#groupFiles(SequenceFileImporter, Collection, boolean, FileFrame)} instead 085 */ 086 @Deprecated 087 public static class FilePosition extends ChannelPosition 088 { 089 public final String path; 090 public String basePath; 091 int s; 092 093 public FilePosition(String path, String basePath, int s, int t, int z, int c) 094 { 095 super(t, z, c); 096 097 this.s = s; 098 this.path = path; 099 this.basePath = basePath; 100 } 101 102 /** 103 * @deprecated Use {@link #FilePosition(String, String, int, int, int, int)} instead. 104 */ 105 @Deprecated 106 public FilePosition(String path, int t, int z, int c) 107 { 108 super(t, z, c); 109 110 this.path = path; 111 basePath = ""; 112 } 113 114 public FilePosition(String path) 115 { 116 super(); 117 118 this.path = path; 119 } 120 121 public FilePosition(FilePosition fp) 122 { 123 this(fp.path, fp.basePath, fp.s, fp.t, fp.z, fp.c); 124 } 125 126 public int getS() 127 { 128 return s; 129 } 130 131 public void setS(int s) 132 { 133 this.s = s; 134 } 135 136 public void set(int s, int t, int z, int c) 137 { 138 super.set(t, z, c); 139 this.s = s; 140 } 141 142 @Override 143 public int compareTo(ImagePosition o) 144 { 145 if (o instanceof FilePosition) 146 { 147 int result = basePath.compareTo(((FilePosition) o).basePath); 148 149 if (result != 0) 150 return result; 151 152 final int sp = ((FilePosition) o).s; 153 154 if (s > sp) 155 return 1; 156 if (s < sp) 157 return -1; 158 } 159 160 return super.compareTo(o); 161 } 162 163 @Override 164 public String toString() 165 { 166 return "File=" + path + " Position=[S:" + s + " T:" + t + " Z:" + z + " C:" + c + "]"; 167 } 168 } 169 170 // private final static Set<String> nonImageExtensions = new 171 // HashSet<String>(CollectionUtil.asList(new String[] { 172 // "xml", "txt", "pdf", "xls", "doc", "docx", "rtf", "exe", "wav", "mp3", "app"})); 173 /** 174 * XML, XLS and TXT file can be image metadata files used to open the whole image, accept it ! 175 */ 176 private final static Set<String> nonImageExtensions = new HashSet<String>( 177 CollectionUtil.asList(new String[] {"pdf", "doc", "docx", "rtf", "exe", "wav", "mp3", "app"})); 178 179 // keep trace of reported / warned plugin 180 private static Set<String> reportedImporterPlugins = new HashSet<String>(); 181 private static Set<String> warnedImporterPlugins = new HashSet<String>(); 182 183 private static void handleImporterError(PluginDescriptor plugin, Throwable t) 184 { 185 final String pluginId = plugin.getName() + " " + plugin.getVersion(); 186 187 if (t instanceof UnsupportedClassVersionError) 188 { 189 if (!warnedImporterPlugins.contains(pluginId)) 190 { 191 // show a specific message in the output console 192 System.err.println("Plugin '" + plugin.getName() + "' " + plugin.getVersion() 193 + " is not compatible with java " + ((int) Math.floor(SystemUtil.getJavaVersionAsNumber()))); 194 System.err.println("You need to install a newer version of java to use it."); 195 196 // add to the list of warned plugins 197 warnedImporterPlugins.add(pluginId); 198 } 199 } 200 else 201 { 202 if (!reportedImporterPlugins.contains(pluginId)) 203 { 204 // show a message in the output console 205 IcyExceptionHandler.showErrorMessage(t, false, true); 206 // and send an error report (silent as we don't want a dialog appearing here) 207 IcyExceptionHandler.report(plugin, IcyExceptionHandler.getErrorMessage(t, true)); 208 209 // add to the list of warned plugins 210 reportedImporterPlugins.add(pluginId); 211 } 212 } 213 } 214 215 /** 216 * Returns all available resource importer. 217 */ 218 public static List<Importer> getImporters() 219 { 220 final List<PluginDescriptor> plugins = PluginLoader.getPlugins(Importer.class); 221 final List<Importer> result = new ArrayList<Importer>(); 222 223 for (PluginDescriptor plugin : plugins) 224 { 225 try 226 { 227 // add the importer 228 result.add((Importer) PluginLauncher.create(plugin)); 229 } 230 catch (Throwable t) 231 { 232 handleImporterError(plugin, t); 233 } 234 } 235 236 return result; 237 } 238 239 /** 240 * Returns all available resource (non image) importer which take file as input. 241 */ 242 public static List<FileImporter> getFileImporters() 243 { 244 final List<PluginDescriptor> plugins = PluginLoader.getPlugins(FileImporter.class); 245 final List<FileImporter> result = new ArrayList<FileImporter>(); 246 247 for (PluginDescriptor plugin : plugins) 248 { 249 try 250 { 251 // add the importer 252 result.add((FileImporter) PluginLauncher.create(plugin)); 253 } 254 catch (Throwable t) 255 { 256 handleImporterError(plugin, t); 257 } 258 } 259 260 return result; 261 } 262 263 /** 264 * Returns a Map containing the appropriate file importer for the specified file.<br> 265 * A file can be absent from the returned Map when no importer support it.<br> 266 * 267 * @param importers 268 * the base list of importer we want to test to open file. 269 * @param paths 270 * the list of file we want to retrieve importer for. 271 * @param useFirstFound 272 * if set to <code>true</code> then the first matching importer is automatically selected 273 * otherwise a dialog appears to let the user to choose the correct importer when 274 * severals importers match for a file. 275 */ 276 public static Map<FileImporter, List<String>> getFileImporters(List<FileImporter> importers, List<String> paths, 277 boolean useFirstFound) 278 { 279 final Map<FileImporter, List<String>> result = new HashMap<FileImporter, List<String>>(importers.size()); 280 final Map<String, FileImporter> extensionImporters = new HashMap<String, FileImporter>(importers.size()); 281 282 for (String path : paths) 283 { 284 final String ext = FileUtil.getFileExtension(path, false); 285 FileImporter imp; 286 287 // try to get importer from extension first 288 imp = extensionImporters.get(ext); 289 290 // do not exist yet 291 if (imp == null) 292 { 293 // find it 294 imp = getFileImporter(importers, path, useFirstFound); 295 // set the importer for this extension 296 if (imp != null) 297 extensionImporters.put(ext, imp); 298 } 299 300 // importer found for this path ? 301 if (imp != null) 302 { 303 // retrieve current list of path for this importer 304 List<String> list = result.get(imp); 305 306 // do not exist yet --> create it 307 if (list == null) 308 { 309 list = new ArrayList<String>(); 310 // set the list for this importer 311 result.put(imp, list); 312 } 313 314 // add path to the list 315 list.add(path); 316 } 317 } 318 319 return result; 320 } 321 322 /** 323 * Returns a Map containing the appropriate file importer for the specified file.<br> 324 * A file can be absent from the returned Map when no importer support it.<br> 325 * 326 * @param paths 327 * the list of file we want to retrieve importer for. 328 * @param useFirstFound 329 * if set to <code>true</code> then the first matching importer is automatically selected 330 * otherwise a dialog appears to let the user to choose the correct importer when 331 * severals importers match for a file. 332 */ 333 public static Map<FileImporter, List<String>> getFileImporters(List<String> paths, boolean useFirstFound) 334 { 335 return getFileImporters(getFileImporters(), paths, useFirstFound); 336 } 337 338 /** 339 * Returns all file importer which can open the specified file. 340 */ 341 public static List<FileImporter> getFileImporters(List<FileImporter> importers, String path) 342 { 343 final List<FileImporter> result = new ArrayList<FileImporter>(importers.size()); 344 345 for (FileImporter importer : importers) 346 if (importer.acceptFile(path)) 347 result.add(importer); 348 349 return result; 350 } 351 352 /** 353 * Returns all file importer which can open the specified file. 354 */ 355 public static List<FileImporter> getFileImporters(String path) 356 { 357 return getFileImporters(getFileImporters(), path); 358 } 359 360 /** 361 * Returns the appropriate file importer for the specified file.<br> 362 * Returns <code>null</code> if no importer can open the file. 363 * 364 * @param importers 365 * the base list of importer we want to test to open file. 366 * @param path 367 * the file we want to retrieve importer for. 368 * @param useFirstFound 369 * if set to <code>true</code> then the first matching importer is automatically selected 370 * otherwise a dialog appears to let the user to choose the correct importer when 371 * severals importers match. 372 * @see #getFileImporters(List, String) 373 */ 374 public static FileImporter getFileImporter(List<FileImporter> importers, String path, boolean useFirstFound) 375 { 376 final List<FileImporter> result = new ArrayList<FileImporter>(importers.size()); 377 378 for (FileImporter importer : importers) 379 { 380 if (importer.acceptFile(path)) 381 { 382 if (useFirstFound) 383 return importer; 384 385 result.add(importer); 386 } 387 } 388 389 // let user select the good importer 390 return selectFileImporter(result, path); 391 } 392 393 /** 394 * Returns the appropriate file importer for the specified file.<br> 395 * Returns <code>null</code> if no importer can open the file. 396 * 397 * @param path 398 * the file we want to retrieve importer for. 399 * @param useFirstFound 400 * if set to <code>true</code> then the first matching importer is automatically selected 401 * otherwise a dialog appears to let the user to choose the correct importer when 402 * severals importers match. 403 * @see #getFileImporters(String) 404 */ 405 public static FileImporter getFileImporter(String path, boolean useFirstFound) 406 { 407 return getFileImporter(getFileImporters(), path, useFirstFound); 408 } 409 410 /** 411 * Display a dialog to let the user select the appropriate file importer for the specified file. 412 */ 413 public static FileImporter selectFileImporter(final List<FileImporter> importers, final String path) 414 { 415 if (importers.size() == 0) 416 return null; 417 if (importers.size() == 1) 418 return importers.get(0); 419 420 if (Icy.getMainInterface().isHeadLess()) 421 return importers.get(0); 422 423 final Object result[] = new Object[1]; 424 425 // use invokeNow carefully ! 426 ThreadUtil.invokeNow(new Runnable() 427 { 428 @Override 429 public void run() 430 { 431 // get importer 432 result[0] = new ImporterSelectionDialog(importers, path).getSelectedImporter(); 433 } 434 }); 435 436 return (FileImporter) result[0]; 437 } 438 439 /** 440 * Returns all available sequence importer (different from {@link SequenceIdImporter} or {@link SequenceFileImporter}). 441 */ 442 public static List<SequenceImporter> getSequenceImporters() 443 { 444 final List<PluginDescriptor> plugins = PluginLoader.getPlugins(SequenceImporter.class); 445 final List<SequenceImporter> result = new ArrayList<SequenceImporter>(); 446 447 for (PluginDescriptor plugin : plugins) 448 { 449 try 450 { 451 // add the importer 452 result.add((SequenceImporter) PluginLauncher.create(plugin)); 453 } 454 catch (Throwable t) 455 { 456 handleImporterError(plugin, t); 457 } 458 } 459 460 return result; 461 } 462 463 /** 464 * Returns all available sequence importer which take path as input.<br> 465 * If you want to get specifically importer which use path as input, then you need to use {@link #getSequenceFileImporters()} instead. 466 * 467 * @see #getSequenceFileImporters() 468 */ 469 public static List<SequenceIdImporter> getSequenceIdImporters() 470 { 471 final List<PluginDescriptor> plugins = PluginLoader.getPlugins(SequenceIdImporter.class); 472 final List<SequenceIdImporter> result = new ArrayList<SequenceIdImporter>(); 473 474 for (PluginDescriptor plugin : plugins) 475 { 476 try 477 { 478 // add the importer 479 result.add((SequenceIdImporter) PluginLauncher.create(plugin)); 480 } 481 catch (Throwable t) 482 { 483 handleImporterError(plugin, t); 484 } 485 } 486 487 return result; 488 } 489 490 /** 491 * Returns all available sequence importer which take file as input. 492 * 493 * @see #getSequenceIdImporters() 494 */ 495 public static List<SequenceFileImporter> getSequenceFileImporters() 496 { 497 final List<PluginDescriptor> plugins = PluginLoader.getPlugins(SequenceFileImporter.class); 498 final List<SequenceFileImporter> result = new ArrayList<SequenceFileImporter>(); 499 500 for (PluginDescriptor plugin : plugins) 501 { 502 try 503 { 504 // add the importer 505 result.add((SequenceFileImporter) PluginLauncher.create(plugin)); 506 } 507 catch (Throwable t) 508 { 509 handleImporterError(plugin, t); 510 } 511 } 512 513 return result; 514 } 515 516 /** 517 * Returns a Map containing the appropriate sequence file importer for the specified list of file path.<br> 518 * A file can be absent from the returned Map when no importer support it.<br> 519 * 520 * @param importers 521 * the base list of importer we want to test to open file. 522 * @param paths 523 * the list of path we want to find importer for. 524 * @param useFirstFound 525 * if set to <code>true</code> then the first matching importer is automatically selected 526 * otherwise a dialog appears to let the user to choose the correct importer when 527 * severals importers match for a path. 528 */ 529 @SuppressWarnings("resource") 530 public static <T extends SequenceFileImporter> Map<T, List<String>> getSequenceFileImporters(List<T> importers, 531 List<String> paths, boolean useFirstFound) 532 { 533 final Map<T, List<String>> result = new HashMap<T, List<String>>(importers.size()); 534 final Map<String, T> extensionImporters = new HashMap<String, T>(importers.size()); 535 T imp = null; 536 537 for (String path : paths) 538 { 539 // get path extension (useful for path path type) 540 final String ext = FileUtil.getFileExtension(path, false); 541 542 // have an extension ? --> try to get importer from extension first 543 if (!StringUtil.isEmpty(ext)) 544 imp = extensionImporters.get(ext); 545 546 // have an importer for this path extension ? --> test it 547 if ((imp != null) && !imp.acceptFile(path)) 548 imp = null; 549 550 // do not exist yet 551 if (imp == null) 552 { 553 // find it 554 imp = getSequenceFileImporter(importers, path, useFirstFound); 555 // set the importer for this extension 556 if ((imp != null) && !StringUtil.isEmpty(ext)) 557 extensionImporters.put(ext, imp); 558 } 559 560 // importer found for this path ? 561 if (imp != null) 562 { 563 // retrieve current list of path for this importer 564 List<String> list = result.get(imp); 565 566 // do not exist yet --> create it 567 if (list == null) 568 { 569 list = new ArrayList<String>(); 570 // set the list for this importer 571 result.put(imp, list); 572 } 573 574 // add path to the list 575 list.add(path); 576 } 577 } 578 579 return result; 580 } 581 582 /** 583 * Returns a Map containing the appropriate sequence file importer for the specified file.<br> 584 * A file can be absent from the returned Map when no importer support it.<br> 585 * 586 * @param paths 587 * the list of file we want to retrieve importer for. 588 * @param grouped 589 * if set to <code>true</code> then we want to group the files so we only need to find the first appropriate importer 590 * and don't test for other files (only 1 importer will be returned for all files). 591 * @param useFirstFound 592 * if set to <code>true</code> then the first matching importer is automatically selected 593 * otherwise a dialog appears to let the user to choose the correct importer when severals importers match for a file. 594 */ 595 @SuppressWarnings("resource") 596 public static Map<SequenceFileImporter, List<String>> getSequenceFileImporters(SequenceFileImporter defaultImporter, 597 List<String> paths, boolean grouped, boolean useFirstFound) 598 { 599 if (paths.isEmpty()) 600 return new HashMap<SequenceFileImporter, List<String>>(); 601 602 final Map<SequenceFileImporter, List<String>> result; 603 604 // have a default importer specified ? --> use it for all files 605 if (defaultImporter != null) 606 { 607 result = new HashMap<SequenceFileImporter, List<String>>(); 608 result.put(defaultImporter, paths); 609 return result; 610 } 611 612 // grouped ? --> find the first valid importer 613 if (grouped) 614 { 615 result = new HashMap<SequenceFileImporter, List<String>>(); 616 617 for (String path : paths) 618 { 619 final SequenceFileImporter imp = getSequenceFileImporter(path, useFirstFound); 620 621 if (imp != null) 622 { 623 // use first valid importer for all files 624 result.put(imp, paths); 625 break; 626 } 627 } 628 629 return result; 630 } 631 632 // get importers for each path 633 return getSequenceFileImporters(paths, useFirstFound); 634 } 635 636 /** 637 * Returns a Map containing the appropriate sequence file importer for the specified list of file path.<br> 638 * A path can be absent from the returned Map when no importer support it.<br> 639 * 640 * @param paths 641 * the list of path we want to retrieve importer for. 642 * @param useFirstFound 643 * if set to <code>true</code> then the first matching importer is automatically selected 644 * otherwise a dialog appears to let the user to choose the correct importer when 645 * severals importers match for a file. 646 */ 647 public static Map<SequenceFileImporter, List<String>> getSequenceFileImporters(List<String> paths, 648 boolean useFirstFound) 649 { 650 return getSequenceFileImporters(getSequenceFileImporters(), paths, useFirstFound); 651 } 652 653 /** 654 * Returns all sequence file importer which can open the specified path. 655 */ 656 public static <T extends SequenceFileImporter> List<T> getSequenceFileImporters(List<T> importers, String path) 657 { 658 final List<T> result = new ArrayList<T>(importers.size()); 659 660 for (T importer : importers) 661 if (importer.acceptFile(path)) 662 result.add(importer); 663 664 return result; 665 } 666 667 /** 668 * Returns all sequence file importer which can open the specified file path. 669 */ 670 public static List<SequenceFileImporter> getSequenceFileImporters(String path) 671 { 672 return getSequenceFileImporters(getSequenceFileImporters(), path); 673 } 674 675 /** 676 * Returns the appropriate sequence file importer for the specified path.<br> 677 * Depending the parameters it will open a dialog to let the user choose the importer to use 678 * when severals match.<br> 679 * Returns <code>null</code> if no importer can open the specified path. 680 * 681 * @param importers 682 * the base list of importer we want to test to open file path. 683 * @param path 684 * the path we want to retrieve importer for. 685 * @param useFirstFound 686 * if set to <code>true</code> then the first matching importer is automatically selected 687 * otherwise a dialog appears to let the user to choose the correct importer when 688 * severals importers match. 689 * @see #getSequenceFileImporters(List, String) 690 */ 691 public static <T extends SequenceFileImporter> T getSequenceFileImporter(List<T> importers, String path, 692 boolean useFirstFound) 693 { 694 final List<T> result = new ArrayList<T>(importers.size()); 695 696 for (T importer : importers) 697 { 698 if (importer.acceptFile(path)) 699 { 700 if (useFirstFound) 701 return importer; 702 703 result.add(importer); 704 } 705 } 706 707 // let user select the good importer 708 return selectSequenceFileImporter(result, path); 709 } 710 711 /** 712 * Returns the appropriate sequence file importer for the specified file path.<br> 713 * Depending the parameters it will open a dialog to let the user choose the importer to use 714 * when severals match.<br> 715 * Returns <code>null</code> if no importer can open the specified file path. 716 * 717 * @param path 718 * the file path we want to retrieve importer for. 719 * @param useFirstFound 720 * if set to <code>true</code> then the first matching importer is automatically selected 721 * otherwise a dialog appears to let the user to choose the correct importer when 722 * severals importers match. 723 * @see #getSequenceFileImporters(String) 724 */ 725 public static SequenceFileImporter getSequenceFileImporter(String path, boolean useFirstFound) 726 { 727 return getSequenceFileImporter(getSequenceFileImporters(), path, useFirstFound); 728 } 729 730 /** 731 * Display a dialog to let the user select the appropriate sequence file importer for the given file path. 732 */ 733 @SuppressWarnings("unchecked") 734 public static <T extends SequenceFileImporter> T selectSequenceFileImporter(final List<T> importers, 735 final String path) 736 { 737 if (importers.size() == 0) 738 return null; 739 if (importers.size() == 1) 740 return importers.get(0); 741 742 // no choice, take first one 743 if (Icy.getMainInterface().isHeadLess()) 744 return importers.get(0); 745 746 final Object result[] = new Object[1]; 747 748 // use invokeNow carefully ! 749 ThreadUtil.invokeNow(new Runnable() 750 { 751 @Override 752 public void run() 753 { 754 // get importer 755 final ImporterSelectionDialog selectionDialog = new ImporterSelectionDialog(importers, path); 756 757 if (!selectionDialog.isCanceled()) 758 result[0] = selectionDialog.getSelectedImporter(); 759 else 760 result[0] = null; 761 } 762 }); 763 764 return (T) result[0]; 765 } 766 767 /** 768 * @deprecated Use {@link #getSequenceFileImporter(List, String, boolean)} 769 */ 770 @Deprecated 771 public static SequenceFileImporter getSequenceFileImporter(List<SequenceFileImporter> importers, String path) 772 { 773 return getSequenceFileImporter(importers, path, true); 774 } 775 776 /** 777 * @deprecated Use {@link #getSequenceFileImporter(String, boolean)} 778 */ 779 @Deprecated 780 public static SequenceFileImporter getSequenceFileImporter(String path) 781 { 782 return getSequenceFileImporter(path, true); 783 } 784 785 static SequenceFileImporter cloneSequenceFileImporter(SequenceFileImporter importer) 786 throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, 787 NoSuchMethodException, SecurityException 788 { 789 if (importer == null) 790 return null; 791 792 final SequenceFileImporter result = importer.getClass().getDeclaredConstructor().newInstance(); 793 794 if (result instanceof LociImporterPlugin) 795 { 796 final LociImporterPlugin srcImp = (LociImporterPlugin) importer; 797 final LociImporterPlugin resImp = (LociImporterPlugin) result; 798 799 resImp.setGroupFiles(srcImp.isGroupFiles()); 800 resImp.setReadOriginalMetadata(srcImp.getReadOriginalMetadata()); 801 } 802 803 return result; 804 } 805 806 /** 807 * Returns <code>true</code> if the specified path describes a file type (from extension) which 808 * is well known to 809 * not be an image file.<br> 810 * For instance <i>.exe</i>, <i>.wav</i> or <i>.doc</i> file cannot specify an image file so we 811 * can quickly discard them (extension based exclusion) 812 */ 813 public static boolean canDiscardImageFile(String path) 814 { 815 final String ext = FileUtil.getFileExtension(path, false).toLowerCase(); 816 817 return nonImageExtensions.contains(ext); 818 } 819 820 /** 821 * Returns true if the specified file is a supported image file. 822 */ 823 public static boolean isSupportedImageFile(String path) 824 { 825 return getSequenceFileImporter(path, true) != null; 826 } 827 828 /** 829 * @deprecated Use {@link #isSupportedImageFile(String)} instead. 830 */ 831 @Deprecated 832 public static boolean isImageFile(String path) 833 { 834 return isSupportedImageFile(path); 835 } 836 837 /** 838 * Returns path which are supported by the specified imported for the given list of paths. 839 */ 840 public static List<String> getSupportedFiles(SequenceFileImporter importer, List<String> paths) 841 { 842 final List<String> result = new ArrayList<String>(); 843 844 for (String path : paths) 845 { 846 if (importer.acceptFile(path)) 847 result.add(path); 848 } 849 850 return result; 851 } 852 853 /** 854 * Check if we can open the given image plane resolution (XY size < 2^31).<br> 855 * If the image plane is too large the method throw an exception with an informative error 856 * message about the encountered limitation. 857 * 858 * @param resolution 859 * wanted image resolution: a value of <code>0</code> means full resolution of the 860 * original image while value <code>1</code> correspond to the resolution / 2.<br> 861 * Formula: <code>resolution / 2^value</code><br> 862 * @param sizeX 863 * width of the image region we want to load 864 * @param sizeY 865 * height of the image region we want to load 866 * @param messageSuffix 867 * message suffix for the exception if wanted 868 * @throws UnsupportedOperationException 869 * if the XY plane size is >= 2^31 pixels 870 * @return the number of pixels of the image plane 871 */ 872 public static long checkOpeningPlane(int resolution, int sizeX, int sizeY, String messageSuffix) 873 throws UnsupportedOperationException 874 { 875 // size of XY plane 876 long sizeXY = (long) sizeX * (long) sizeY; 877 // wanted resolution 878 sizeXY /= Math.pow(4, resolution); 879 880 // we can't handle that plane size 881 if (sizeXY > Integer.MAX_VALUE) 882 throw new UnsupportedOperationException( 883 "Cannot open image with a XY plane size >= 2^31." + ((messageSuffix != null) ? messageSuffix : "")); 884 885 return sizeXY; 886 } 887 888 /** 889 * Check if we have enough resource to open the image defined by the given size information and 890 * wanted resolution.<br> 891 * If the image is too large to be displayed at full resolution (XY plane size > 2^31) or if we 892 * don't have enough 893 * memory to store the whole image the method throw an exception with an informative error 894 * message about the 895 * encountered limitation. 896 * 897 * @param resolution 898 * wanted image resolution: a value of <code>0</code> means full resolution of the 899 * original image while value <code>1</code> correspond to the resolution / 2.<br> 900 * Formula: <code>resolution / 2^value</code><br> 901 * @param sizeX 902 * width of the image region we want to load 903 * @param sizeY 904 * height of the image region we want to load 905 * @param sizeC 906 * number of channel we want to load 907 * @param sizeZ 908 * number of slice we want to load (can be different from original image sizeZ) 909 * @param sizeT 910 * number of frame we want to load (can be different from original image sizeT) 911 * @param dataType 912 * pixel data type of the image we want to load 913 * @param messageSuffix 914 * message suffix for the exception if wanted 915 * @throws UnsupportedOperationException 916 * if the XY plane size is >= 2^31 pixels 917 * @throws OutOfMemoryError 918 * if there is not enough memory to open the image 919 */ 920 public static void checkOpening(int resolution, int sizeX, int sizeY, int sizeC, int sizeZ, int sizeT, 921 DataType dataType, String messageSuffix) throws UnsupportedOperationException, OutOfMemoryError 922 { 923 final long sizeXY = checkOpeningPlane(resolution, sizeX, sizeY, messageSuffix); 924 925 // get free memory 926 long freeInByte = SystemUtil.getJavaFreeMemory() - (16 * 1024 * 1024); 927 // check that we have enough memory for the whole image and for the ARGB image used for 928 // display (sizeXY * 4) 929 long sizeInByte = (sizeXY * sizeC * sizeZ * sizeT * dataType.getSize()) + (sizeXY * 4); 930 931 // not enough memory to store the whole image ? 932 if (sizeInByte > freeInByte) 933 { 934 // try to release some memory 935 System.gc(); 936 // get updated free memory 937 freeInByte = SystemUtil.getJavaFreeMemory() - (16 * 1024 * 1024); 938 } 939 940 // still not enough memory ? 941 if (sizeInByte > freeInByte) 942 throw new OutOfMemoryError("Not enough memory to open the wanted image resolution." 943 + ((messageSuffix != null) ? messageSuffix : "")); 944 } 945 946 /** 947 * Check if we have enough resource to open the image defined by the given metadata information, series index and 948 * wanted resolution.<br> 949 * If the image is too large to be displayed at full resolution (XY plane size > 2^31) or if we* don't have enough 950 * memory to store the whole image the method throw an exception with an informative error message about the 951 * encountered limitation. 952 * 953 * @param meta 954 * metadata of the image 955 * @param series 956 * series index 957 * @param resolution 958 * wanted image resolution: a value of <code>0</code> means full resolution of the 959 * original image while value 960 * <code>1</code> correspond to the resolution / 2.<br> 961 * Formula: <code>resolution / 2^value</code><br> 962 * @param sizeZ 963 * number of slice we want to load (can be different from original image sizeZ) 964 * @param sizeT 965 * number of frame we want to load (can be different from original image sizeT) 966 * @param messageSuffix 967 * message suffix for the exception if wanted 968 * @throws UnsupportedOperationException 969 * if the XY plane size is >= 2^31 pixels 970 * @throws OutOfMemoryError 971 * if there is not enough memory to open the image 972 */ 973 public static void checkOpening(OMEXMLMetadata meta, int series, int resolution, int sizeZ, int sizeT, 974 String messageSuffix) throws UnsupportedOperationException, OutOfMemoryError 975 { 976 checkOpening(resolution, MetaDataUtil.getSizeX(meta, series), MetaDataUtil.getSizeY(meta, series), 977 MetaDataUtil.getSizeC(meta, series), sizeZ, sizeT, MetaDataUtil.getDataType(meta, series), 978 messageSuffix); 979 } 980 981 /** 982 * Check if we have enough resource to open the image defined by the given metadata information, series index and 983 * wanted resolution.<br> 984 * If the image is too large to be displayed at full resolution (XY plane size > 2^31) or if we don't have enough 985 * memory to store the whole image the method throw an exception with an informative error message about the 986 * encountered limitation. 987 * 988 * @param meta 989 * metadata of the image 990 * @param series 991 * series index 992 * @param resolution 993 * wanted image resolution: a value of <code>0</code> means full resolution of the 994 * original image while value 995 * <code>1</code> correspond to the resolution / 2.<br> 996 * Formula: <code>resolution / 2^value</code><br> 997 * @param messageSuffix 998 * message suffix for the exception if wanted 999 * @throws UnsupportedOperationException 1000 * if the XY plane size is >= 2^31 pixels 1001 * @throws OutOfMemoryError 1002 * if there is not enough memory to open the image 1003 */ 1004 public static void checkOpening(OMEXMLMetadata meta, int series, int resolution, String messageSuffix) 1005 throws UnsupportedOperationException, OutOfMemoryError 1006 { 1007 checkOpening(meta, series, resolution, MetaDataUtil.getSizeZ(meta, series), MetaDataUtil.getSizeT(meta, series), 1008 messageSuffix); 1009 } 1010 1011 // /** 1012 // * Returns the best resolution to use from the given metadata information and series index.<br> 1013 // * If the image is too large to be displayed at full resolution (XY plane size > 2^31) or if 1014 // we don't have enough 1015 // * memory to store the whole image (depending wanted constraint) then a sub resolution index 1016 // is returned.<br> 1017 // * A return value of <code>0</code> means full resolution of the original image while value 1018 // <code>1</code> 1019 // * correspond to the resolution / 2.<br> 1020 // * Formula: <code>resolution / 2^value</code><br> 1021 // * 1022 // * @param meta 1023 // * metadata of the image 1024 // * @param series 1025 // * series index 1026 // * @param showMessage 1027 // * show announce frame or message in the output log when one size constraint is meet and 1028 // induce resolution 1029 // * decrease 1030 // */ 1031 // public static int getBestResolution(OMEXMLMetadataImpl meta, int series, boolean showMessage) 1032 // { 1033 // // easy trick to disable message display when needed 1034 // boolean warningDisplayed = !showMessage; 1035 // // default resolution to open (full resolution) 1036 // int resolution = 0; 1037 // // size of XY plane 1038 // long sizeXY = (long) MetaDataUtil.getSizeX(meta, series) * (long) MetaDataUtil.getSizeY(meta, 1039 // series); 1040 // 1041 // // we can't handle that plane size 1042 // if (sizeXY > Integer.MAX_VALUE) 1043 // { 1044 // if (!warningDisplayed) 1045 // { 1046 // // notify we can't open that image at full resolution 1047 // if (!Icy.getMainInterface().isHeadLess()) 1048 // new AnnounceFrame("XY plane size is >= 2^31, try to open sub resolution of the image...", 1049 // 10); 1050 // else 1051 // System.out.println("XY plane size is >= 2^31, try to open sub resolution of the image..."); 1052 // 1053 // warningDisplayed = true; 1054 // } 1055 // 1056 // // reduce resolution until XY plane size is acceptable 1057 // do 1058 // { 1059 // resolution++; 1060 // sizeXY /= 4; 1061 // } 1062 // while (sizeXY > Integer.MAX_VALUE); 1063 // } 1064 // 1065 // // get free memory 1066 // long freeInByte = SystemUtil.getJavaFreeMemory() - (16 * 1024 * 1024); 1067 // // check that we have enough memory for the whole image and the ARGB image used for display 1068 // (sizeXY * 4) 1069 // long sizeInByte = MetaDataUtil.getDataSize(meta, series, resolution) + (sizeXY * 4); 1070 // 1071 // // not enough memory to store the whole image ? 1072 // if (sizeInByte > freeInByte) 1073 // { 1074 // // try to release some memory 1075 // System.gc(); 1076 // // get updated free memory 1077 // freeInByte = SystemUtil.getJavaFreeMemory() - (16 * 1024 * 1024); 1078 // } 1079 // 1080 // // still not enough memory ? 1081 // if (sizeInByte > freeInByte) 1082 // { 1083 // if (!warningDisplayed) 1084 // { 1085 // // display an information message that we can only load a sub resolution of the image 1086 // if (!Icy.getMainInterface().isHeadLess()) 1087 // new AnnounceFrame( 1088 // "Not enough memory to open full resolution of the image, try to open sub resolution...", 10); 1089 // else 1090 // System.out 1091 // .println("Not enough memory to open full resolution of the image, try to open sub 1092 // resolution..."); 1093 // 1094 // warningDisplayed = true; 1095 // } 1096 // 1097 // // reduce image resolution so the whole image fit in about 70% of available memory (safe) 1098 // freeInByte = (int) (freeInByte * 0.7d); 1099 // do 1100 // { 1101 // resolution++; 1102 // sizeInByte /= 4; 1103 // } 1104 // while (sizeInByte > freeInByte); 1105 // } 1106 // 1107 // return resolution; 1108 // } 1109 1110 /** 1111 * @deprecated Use {@link #getSequenceFileImporters(String)} instead. 1112 */ 1113 @SuppressWarnings("resource") 1114 @Deprecated 1115 public static IFormatReader getReader(String path) throws FormatException, IOException 1116 { 1117 return new ImageReader().getReader(path); 1118 } 1119 1120 /** 1121 * Loads and returns metadata of the specified image file with given importer.<br> 1122 * It can returns <code>null</code> if the specified file is not a valid or supported) image 1123 * file. 1124 */ 1125 public static OMEXMLMetadata getOMEXMLMetaData(SequenceFileImporter importer, String path) 1126 throws UnsupportedFormatException, IOException 1127 { 1128 if (importer.open(path, 0)) 1129 { 1130 try 1131 { 1132 return importer.getOMEXMLMetaData(); 1133 } 1134 finally 1135 { 1136 importer.close(); 1137 } 1138 } 1139 1140 return null; 1141 } 1142 1143 /** 1144 * Loads and returns metadata of the specified image file.<br> 1145 * It can returns <code>null</code> if the specified file is not a valid or supported) image 1146 * file. 1147 */ 1148 public static OMEXMLMetadata getOMEXMLMetaData(String path) throws UnsupportedFormatException, IOException 1149 { 1150 OMEXMLMetadata result; 1151 UnsupportedFormatException lastError = null; 1152 1153 for (SequenceFileImporter importer : getSequenceFileImporters(path)) 1154 { 1155 try 1156 { 1157 result = getOMEXMLMetaData(importer, path); 1158 1159 if (result != null) 1160 return result; 1161 } 1162 catch (UnsupportedFormatException e) 1163 { 1164 lastError = e; 1165 } 1166 } 1167 1168 throw new UnsupportedFormatException("Image file '" + path + "' is not supported :\n", lastError); 1169 } 1170 1171 /** 1172 * @deprecated Use {@link #getOMEXMLMetaData(SequenceFileImporter, String)} instead. 1173 */ 1174 @Deprecated 1175 public static OMEXMLMetadataImpl getMetaData(SequenceFileImporter importer, String path) 1176 throws UnsupportedFormatException, IOException 1177 { 1178 if (importer.open(path, 0)) 1179 { 1180 try 1181 { 1182 return importer.getMetaData(); 1183 } 1184 finally 1185 { 1186 importer.close(); 1187 } 1188 } 1189 1190 return null; 1191 } 1192 1193 /** 1194 * @throws IOException 1195 * @deprecated Use {@link #getOMEXMLMetaData(String)} instead 1196 */ 1197 @Deprecated 1198 public static OMEXMLMetadataImpl getMetaData(String path) throws UnsupportedFormatException, IOException 1199 { 1200 OMEXMLMetadataImpl result; 1201 UnsupportedFormatException lastError = null; 1202 1203 for (SequenceFileImporter importer : getSequenceFileImporters(path)) 1204 { 1205 try 1206 { 1207 result = getMetaData(importer, path); 1208 1209 if (result != null) 1210 return result; 1211 } 1212 catch (UnsupportedFormatException e) 1213 { 1214 lastError = e; 1215 } 1216 } 1217 1218 throw new UnsupportedFormatException("Image file '" + path + "' is not supported :\n", lastError); 1219 } 1220 1221 /** 1222 * @deprecated Use {@link #getOMEXMLMetaData(String)} instead. 1223 */ 1224 @Deprecated 1225 public static OMEXMLMetadataImpl getMetaData(File file) throws UnsupportedFormatException, IOException 1226 { 1227 return getMetaData(file.getAbsolutePath()); 1228 } 1229 1230 /** 1231 * @deprecated Use {@link #getOMEXMLMetaData(String)} instead. 1232 */ 1233 @Deprecated 1234 protected static OMEXMLMetadataImpl getMetaData(IFormatReader reader, String path) 1235 throws FormatException, IOException 1236 { 1237 // prepare meta data store structure 1238 reader.setMetadataStore((MetadataStore) OMEUtil.createOMEXMLMetadata()); 1239 // load file with LOCI library 1240 reader.setId(path); 1241 1242 return (OMEXMLMetadataImpl) reader.getMetadataStore(); 1243 } 1244 1245 // /** 1246 // * Use the given importer to load and return metadata of the specified image file. 1247 // * 1248 // * @throws UnsupportedFormatException 1249 // * @throws IOException 1250 // */ 1251 // public static OMEXMLMetadataImpl getMetaData(SequenceFileImporter importer, File file) 1252 // throws UnsupportedFormatException, IOException 1253 // { 1254 // // load current file and add to results 1255 // return importer.getMetaData(file); 1256 // } 1257 1258 /** 1259 * Returns a thumbnail of the specified image file path.<br> 1260 * It can return <code>null</code> if the specified file is not a valid or supported image file. 1261 * 1262 * @param importer 1263 * Importer used to open and load the thumbnail from the image file. 1264 * @param path 1265 * image file path. 1266 * @param series 1267 * Series index we want to retrieve thumbnail from (for multi series image).<br> 1268 * Set to 0 if unsure. 1269 */ 1270 public static IcyBufferedImage loadThumbnail(SequenceFileImporter importer, String path, int series) 1271 throws UnsupportedFormatException, IOException 1272 { 1273 if (importer.open(path, 0)) 1274 { 1275 try 1276 { 1277 return importer.getThumbnail(series); 1278 } 1279 finally 1280 { 1281 importer.close(); 1282 } 1283 } 1284 1285 return null; 1286 } 1287 1288 /** 1289 * Returns a thumbnail of the specified image file path. 1290 * 1291 * @param path 1292 * image file path. 1293 * @param series 1294 * Series index we want to retrieve thumbnail from (for multi series image).<br> 1295 * Set to 0 if unsure. 1296 */ 1297 public static IcyBufferedImage loadThumbnail(String path, int series) throws UnsupportedFormatException, IOException 1298 { 1299 IcyBufferedImage result; 1300 UnsupportedFormatException lastError = null; 1301 1302 for (SequenceFileImporter importer : getSequenceFileImporters(path)) 1303 { 1304 try 1305 { 1306 result = loadThumbnail(importer, path, series); 1307 1308 if (result != null) 1309 return result; 1310 } 1311 catch (UnsupportedFormatException e) 1312 { 1313 lastError = e; 1314 } 1315 } 1316 1317 throw new UnsupportedFormatException("Image file '" + path + "' is not supported :\n", lastError); 1318 } 1319 1320 /** 1321 * @deprecated Use {@link IcyBufferedImage#createFrom(IFormatReader, int, int)} instead. 1322 */ 1323 @Deprecated 1324 public static IcyBufferedImage loadImage(IFormatReader reader, int z, int t) throws FormatException, IOException 1325 { 1326 // return an icy image 1327 return IcyBufferedImage.createFrom(reader, z, t); 1328 } 1329 1330 /** 1331 * @deprecated Use {@link IcyBufferedImage#createFrom(IFormatReader, int, int)} with Z and T 1332 * parameters set to 0. 1333 */ 1334 @Deprecated 1335 public static IcyBufferedImage loadImage(IFormatReader reader) throws FormatException, IOException 1336 { 1337 // return an icy image 1338 return IcyBufferedImage.createFrom(reader, 0, 0); 1339 } 1340 1341 /** 1342 * @deprecated Use {@link #loadImage(String, int, int)} instead. 1343 */ 1344 @Deprecated 1345 public static IcyBufferedImage loadImage(File file, int z, int t) throws FormatException, IOException 1346 { 1347 return loadImage(file.getAbsolutePath(), z, t); 1348 } 1349 1350 /** 1351 * @deprecated Use {@link #loadImage(String)} instead. 1352 */ 1353 @Deprecated 1354 public static IcyBufferedImage loadImage(File file) throws UnsupportedFormatException, IOException 1355 { 1356 return loadImage(file.getAbsolutePath()); 1357 } 1358 1359 /** 1360 * @deprecated Use {@link #loadImage(String, int, int, int)} instead. 1361 */ 1362 @Deprecated 1363 public static IcyBufferedImage loadImage(String path, int z, int t) throws FormatException, IOException 1364 { 1365 final IFormatReader reader = getReader(path); 1366 1367 // disable file grouping 1368 reader.setGroupFiles(false); 1369 // set file path 1370 reader.setId(path); 1371 try 1372 { 1373 // return an icy image 1374 return IcyBufferedImage.createFrom(reader, z, t); 1375 } 1376 finally 1377 { 1378 // close reader 1379 reader.close(); 1380 } 1381 } 1382 1383 /** 1384 * Load and return the image at given position from the specified file path.<br> 1385 * For lower image level access, you can use importer methods. 1386 * 1387 * @param importer 1388 * Importer used to open and load the image file. 1389 * @param path 1390 * image file path. 1391 * @param series 1392 * Series index we want to retrieve image from (for multi series image).<br> 1393 * Set to 0 if unsure (default). 1394 * @param z 1395 * Z position of the image to open. 1396 * @param t 1397 * T position of the image to open. 1398 * @throws IOException 1399 * @throws UnsupportedFormatException 1400 */ 1401 public static IcyBufferedImage loadImage(SequenceFileImporter importer, String path, int series, int z, int t) 1402 throws UnsupportedFormatException, IOException 1403 { 1404 if ((importer == null) || !importer.open(path, 0)) 1405 throw new UnsupportedFormatException("Image file '" + path + "' is not supported !"); 1406 1407 try 1408 { 1409 return importer.getImage(series, z, t); 1410 } 1411 finally 1412 { 1413 importer.close(); 1414 } 1415 } 1416 1417 /** 1418 * Load and return the image at given position from the specified file path.<br> 1419 * For lower image level access, you can use {@link #getSequenceFileImporter(String, boolean)} 1420 * method and 1421 * directly work through the returned {@link ImageProvider} interface. 1422 * 1423 * @param path 1424 * image file path. 1425 * @param series 1426 * Series index we want to retrieve image from (for multi series image).<br> 1427 * Set to 0 if unsure (default). 1428 * @param z 1429 * Z position of the image to open. 1430 * @param t 1431 * T position of the image to open. 1432 * @throws IOException 1433 * @throws UnsupportedFormatException 1434 */ 1435 public static IcyBufferedImage loadImage(String path, int series, int z, int t) 1436 throws UnsupportedFormatException, IOException 1437 { 1438 return loadImage(getSequenceFileImporter(path, true), path, series, z, t); 1439 } 1440 1441 /** 1442 * Load and return a single image from the specified file path.<br> 1443 * If the specified file contains severals image the first image is returned. 1444 */ 1445 public static IcyBufferedImage loadImage(String path) throws UnsupportedFormatException, IOException 1446 { 1447 return loadImage(path, 0, 0, 0); 1448 } 1449 1450 /** 1451 * @deprecated Use {@link #loadSequences(File[], int, boolean, boolean, boolean)} instead. 1452 */ 1453 @Deprecated 1454 public static Sequence[] loadSequences(File[] files, int[] series, boolean separate, boolean autoOrder, 1455 boolean showProgress) 1456 { 1457 final List<Sequence> result = new ArrayList<Sequence>(); 1458 final List<String> paths = FileUtil.toPaths(CollectionUtil.asList(files)); 1459 1460 if (series == null) 1461 result.addAll(loadSequences(paths, -1, separate, autoOrder, false, showProgress)); 1462 else 1463 { 1464 for (int s : series) 1465 result.addAll(loadSequences(paths, s, separate, autoOrder, false, showProgress)); 1466 } 1467 1468 return result.toArray(new Sequence[result.size()]); 1469 } 1470 1471 /** 1472 * @deprecated Use {@link #loadSequences(File[], int[], boolean, boolean, boolean)} instead. 1473 */ 1474 @Deprecated 1475 public static List<Sequence> loadSequences(List<File> files, List<Integer> series, boolean separate, 1476 boolean autoOrder, boolean showProgress) 1477 { 1478 final int[] seriesArray; 1479 1480 if (series != null) 1481 { 1482 seriesArray = new int[series.size()]; 1483 1484 for (int i = 0; i < seriesArray.length; i++) 1485 seriesArray[i] = series.get(i).intValue(); 1486 } 1487 else 1488 { 1489 seriesArray = new int[1]; 1490 seriesArray[0] = 0; 1491 } 1492 1493 return Arrays.asList( 1494 loadSequences(files.toArray(new File[files.size()]), seriesArray, separate, autoOrder, showProgress)); 1495 } 1496 1497 /** 1498 * @deprecated Use {@link #loadSequences(File[], int[], boolean, boolean, boolean)} instead. 1499 */ 1500 @Deprecated 1501 public static List<Sequence> loadSequences(List<File> files, List<Integer> series, boolean separate, 1502 boolean showProgress) 1503 { 1504 return loadSequences(files, series, separate, true, showProgress); 1505 } 1506 1507 /** 1508 * @deprecated Use {@link #loadSequences(File[], int[], boolean, boolean, boolean)} instead. 1509 */ 1510 @Deprecated 1511 public static List<Sequence> loadSequences(List<File> files, List<Integer> series, boolean separate) 1512 { 1513 return loadSequences(files, series, separate, true, true); 1514 } 1515 1516 /** 1517 * @deprecated Use {@link #loadSequences(File[], int[], boolean, boolean, boolean)} instead. 1518 */ 1519 @Deprecated 1520 public static List<Sequence> loadSequences(List<File> files, List<Integer> series) 1521 { 1522 return loadSequences(files, series, false, true, true); 1523 } 1524 1525 /** 1526 * @deprecated Use {@link #loadSequences(File[], int[], boolean, boolean, boolean)} instead. 1527 */ 1528 @Deprecated 1529 public static List<Sequence> loadSequences(List<File> files, boolean separate, boolean showProgress) 1530 { 1531 return loadSequences(files, null, separate, true, showProgress); 1532 } 1533 1534 /** 1535 * @deprecated Use {@link #loadSequences(File[], int[], boolean, boolean, boolean)} instead. 1536 */ 1537 @Deprecated 1538 public static List<Sequence> loadSequences(List<File> files, boolean separate) 1539 { 1540 return loadSequences(files, null, separate, true, true); 1541 } 1542 1543 /** 1544 * @deprecated Use {@link #loadSequences(File[], int[], boolean, boolean, boolean)} instead. 1545 */ 1546 @Deprecated 1547 public static List<Sequence> loadSequences(List<File> files, boolean separate, boolean display, boolean addToRecent) 1548 { 1549 return loadSequences(files, null, separate, true, true); 1550 } 1551 1552 /** 1553 * @deprecated Use {@link #loadSequence(File, int, boolean)} instead. 1554 */ 1555 @Deprecated 1556 public static Sequence[] loadSequences(File file, int[] series, boolean showProgress) 1557 { 1558 return loadSequences(new File[] {file}, series, false, true, showProgress); 1559 } 1560 1561 /** 1562 * @deprecated Use {@link #loadSequences(File, int[], boolean)} instead. 1563 */ 1564 @Deprecated 1565 public static List<Sequence> loadSequences(File file, List<Integer> series, boolean showProgress) 1566 { 1567 final int[] seriesArray; 1568 1569 if (series != null) 1570 { 1571 seriesArray = new int[series.size()]; 1572 1573 for (int i = 0; i < seriesArray.length; i++) 1574 seriesArray[i] = series.get(i).intValue(); 1575 } 1576 else 1577 { 1578 seriesArray = new int[1]; 1579 seriesArray[0] = 0; 1580 } 1581 1582 return Arrays.asList(loadSequences(new File[] {file}, seriesArray, false, true, showProgress)); 1583 } 1584 1585 /** 1586 * @deprecated Use {@link #loadSequences(File, int[], boolean)} instead. 1587 */ 1588 @Deprecated 1589 public static List<Sequence> loadSequences(File file, List<Integer> series) 1590 { 1591 return loadSequences(file, series, true); 1592 } 1593 1594 /** 1595 * @deprecated Use {@link #loadSequences(File, int[], boolean)} instead. 1596 */ 1597 @Deprecated 1598 public static List<Sequence> loadSequences(File file, List<Integer> series, boolean display, boolean addToRecent) 1599 { 1600 return loadSequences(file, series, true); 1601 } 1602 1603 /** 1604 * @deprecated Use {@link #loadSequence(File, int, boolean)} instead. 1605 */ 1606 @Deprecated 1607 public static Sequence loadSequence(File file, boolean showProgress) 1608 { 1609 return loadSequence(new File[] {file}, -1, showProgress); 1610 } 1611 1612 /** 1613 * @deprecated Use {@link #loadSequence(File, int, boolean)} instead. 1614 */ 1615 @Deprecated 1616 public static Sequence loadSequence(File file) 1617 { 1618 return loadSequence(new File[] {file}, -1, true); 1619 } 1620 1621 /** 1622 * @deprecated Use {@link #loadSequences(List, int, boolean, boolean, boolean, boolean)} 1623 * instead. 1624 */ 1625 @Deprecated 1626 public static Sequence[] loadSequences(File[] files, int series, boolean separate, boolean autoOrder, 1627 boolean showProgress) 1628 { 1629 final List<Sequence> result = loadSequences(FileUtil.toPaths(CollectionUtil.asList(files)), series, separate, 1630 autoOrder, false, showProgress); 1631 return result.toArray(new Sequence[result.size()]); 1632 } 1633 1634 /** 1635 * Load a sequence from the specified list of file and returns it.<br> 1636 * As the function can take sometime you should not call it from the AWT EDT.<br> 1637 * The function can return null if no sequence can be loaded from the specified files. 1638 * 1639 * @param files 1640 * List of image file to load. 1641 * @param series 1642 * Series index to load (for multi series sequence), set to 0 if unsure (default). 1643 * @param showProgress 1644 * Show progression of loading process. 1645 */ 1646 @Deprecated 1647 public static Sequence loadSequence(File[] files, int series, boolean showProgress) 1648 { 1649 final Sequence[] result = loadSequences(files, series, false, true, showProgress); 1650 1651 if (result.length > 0) 1652 return result[0]; 1653 1654 return null; 1655 } 1656 1657 /** 1658 * @deprecated Use {@link #loadSequence(String, int, boolean)} instead. 1659 */ 1660 @Deprecated 1661 public static Sequence loadSequence(File file, int series, boolean showProgress) 1662 { 1663 return loadSequence(new File[] {file}, series, showProgress); 1664 } 1665 1666 /** 1667 * Load a list of sequence from the specified list of file with the given {@link SequenceFileImporter} and returns them.<br> 1668 * As the function can take sometime you should not call it from the AWT EDT.<br> 1669 * The method returns an empty array if an error occurred or if no file could be opened (not supported).<br> 1670 * If the user cancelled the action (series selection dialog) then it returns <code>null</code>. 1671 * 1672 * @param importer 1673 * Importer used to open and load image files.<br> 1674 * If set to <code>null</code> the loader will search for a compatible importer and if 1675 * several importers match the user will have to select the appropriate one from a 1676 * selection dialog. 1677 * @param paths 1678 * List of image file to load. 1679 * @param series 1680 * Series index to load (for multi series sequence), set to 0 if unsure (default).<br> 1681 * -1 is a special value so it gives a chance to the user<br> 1682 * to select the series to open from a series selector dialog. 1683 * @param forceVolatile 1684 * If set to <code>true</code> then image data is forced to volatile (see {@link IcyBufferedImage#isVolatile()}).<br> 1685 * Note that if you don't have enough memory to load the whole Sequence into memory then image data are always made volatile. 1686 * @param separate 1687 * Force image to be loaded in separate sequence. 1688 * @param autoOrder 1689 * Try to order image in sequence from their filename 1690 * @param addToRecent 1691 * If set to true the files list will be traced in recent opened sequence. 1692 * @param showProgress 1693 * Show progression of loading process. 1694 */ 1695 @SuppressWarnings("resource") 1696 public static List<Sequence> loadSequences(SequenceFileImporter importer, List<String> paths, int series, 1697 boolean forceVolatile, boolean separate, boolean autoOrder, boolean addToRecent, boolean showProgress) 1698 { 1699 final List<Sequence> result = new ArrayList<Sequence>(); 1700 1701 // detect if this is a complete folder load 1702 final boolean directory = (paths.size() == 1) && new File(paths.get(0)).isDirectory(); 1703 // explode path list 1704 final List<String> singlePaths = cleanNonImageFile(explode(paths)); 1705 final boolean adjSeparate = (singlePaths.size() <= 1) || separate; 1706 // get the sequence importers first 1707 final Map<SequenceFileImporter, List<String>> sequenceFileImporters = getSequenceFileImporters(importer, 1708 singlePaths, !adjSeparate, false); 1709 1710 for (Entry<SequenceFileImporter, List<String>> entry : sequenceFileImporters.entrySet()) 1711 { 1712 final SequenceFileImporter imp = entry.getKey(); 1713 final List<String> currPaths = entry.getValue(); 1714 final boolean dir = directory && (sequenceFileImporters.size() == 1) 1715 && (currPaths.size() == singlePaths.size()); 1716 1717 // load sequence 1718 result.addAll(loadSequences(imp, currPaths, series, forceVolatile, adjSeparate, autoOrder, dir, addToRecent, 1719 showProgress)); 1720 1721 // remove loaded files 1722 singlePaths.removeAll(currPaths); 1723 } 1724 1725 // remove remaining XML persistence files... 1726 for (int i = singlePaths.size() - 1; i >= 0; i--) 1727 if (SequencePersistent.isValidXMLPersitence(singlePaths.get(i))) 1728 singlePaths.remove(i); 1729 1730 // remaining files ? 1731 if (singlePaths.size() > 0) 1732 { 1733 // get first found importer for remaining files 1734 final Map<SequenceFileImporter, List<String>> importers = getSequenceFileImporters(singlePaths, true); 1735 1736 // user canceled action for these paths so we remove them 1737 for (List<String> values : importers.values()) 1738 singlePaths.removeAll(values); 1739 1740 if (singlePaths.size() > 0) 1741 { 1742 // just log in console 1743 System.err.println("No compatible importer found for the following files:"); 1744 for (String path : singlePaths) 1745 System.err.println(path); 1746 System.err.println(); 1747 } 1748 } 1749 1750 // return sequences 1751 return result; 1752 } 1753 1754 /** 1755 * Load a list of sequence from the specified list of file with the given {@link SequenceFileImporter} and returns them.<br> 1756 * As the function can take sometime you should not call it from the AWT EDT.<br> 1757 * The method returns an empty array if an error occurred or if no file could be opened (not supported).<br> 1758 * If the user cancelled the action (series selection dialog) then it returns <code>null</code>. 1759 * 1760 * @param importer 1761 * Importer used to open and load image files.<br> 1762 * If set to <code>null</code> the loader will search for a compatible importer and if 1763 * several importers match the user will have to select the appropriate one from a 1764 * selection dialog. 1765 * @param paths 1766 * List of image file to load. 1767 * @param series 1768 * Series index to load (for multi series sequence), set to 0 if unsure (default).<br> 1769 * -1 is a special value so it gives a chance to the user<br> 1770 * to select the series to open from a series selector dialog. 1771 * @param separate 1772 * Force image to be loaded in separate sequence. 1773 * @param autoOrder 1774 * Try to order image in sequence from their filename 1775 * @param addToRecent 1776 * If set to true the files list will be traced in recent opened sequence. 1777 * @param showProgress 1778 * Show progression of loading process. 1779 */ 1780 @SuppressWarnings("resource") 1781 public static List<Sequence> loadSequences(SequenceFileImporter importer, List<String> paths, int series, 1782 boolean separate, boolean autoOrder, boolean addToRecent, boolean showProgress) 1783 { 1784 return loadSequences(importer, paths, series, false, separate, autoOrder, addToRecent, showProgress); 1785 } 1786 1787 /** 1788 * Loads a list of sequence from the specified list of file and returns them.<br> 1789 * As the function can take sometime you should not call it from the AWT EDT.<br> 1790 * The method returns an empty array if an error occurred or if no file could not be opened (not 1791 * supported).<br> 1792 * If several importers match to open a file the user will have to select the appropriate one 1793 * from a selection dialog. 1794 * 1795 * @param paths 1796 * List of image file to load. 1797 * @param series 1798 * Series index to load (for multi series sequence), set to 0 if unsure (default).<br> 1799 * -1 is a special value so it gives a chance to the user<br> 1800 * to select the series to open from a series selector dialog. 1801 * @param separate 1802 * Force image to be loaded in separate sequence. 1803 * @param autoOrder 1804 * Try to order image in sequence from their filename 1805 * @param addToRecent 1806 * If set to true the files list will be traced in recent opened sequence. 1807 * @param showProgress 1808 * Show progression of loading process. 1809 */ 1810 public static List<Sequence> loadSequences(List<String> paths, int series, boolean separate, boolean autoOrder, 1811 boolean addToRecent, boolean showProgress) 1812 { 1813 return loadSequences(null, paths, series, separate, autoOrder, addToRecent, showProgress); 1814 } 1815 1816 /** 1817 * Load a sequence from the specified list of file and returns it.<br> 1818 * The function try to guess image ordering from file name and metadata.<br> 1819 * As the function can take sometime you should not call it from the AWT EDT.<br> 1820 * The function can return null if no sequence can be loaded from the specified files. 1821 * 1822 * @param importer 1823 * Importer used to load the image file (can be null for automatic selection). 1824 * @param paths 1825 * List of image file to load. 1826 * @param addToRecent 1827 * If set to true the files list will be traced in recent opened sequence. 1828 * @param showProgress 1829 * Show progression of loading process. 1830 * @see #getSequenceFileImporter(String, boolean) 1831 */ 1832 public static Sequence loadSequence(SequenceFileImporter importer, List<String> paths, boolean addToRecent, 1833 boolean showProgress) 1834 { 1835 final ApplicationMenu mainMenu; 1836 final FileFrame loadingFrame; 1837 Sequence result = null; 1838 1839 if (addToRecent) 1840 mainMenu = Icy.getMainInterface().getApplicationMenu(); 1841 else 1842 mainMenu = null; 1843 if (showProgress && !Icy.getMainInterface().isHeadLess()) 1844 loadingFrame = new FileFrame("Loading", null); 1845 else 1846 loadingFrame = null; 1847 1848 try 1849 { 1850 // we want only one group 1851 final SequenceFileGroup group = SequenceFileSticher.groupFiles(importer, cleanNonImageFile(paths), 1852 showProgress, loadingFrame); 1853 // do group loading 1854 result = internalLoadGroup(group, false, false, mainMenu, loadingFrame); 1855 // load sequence XML data 1856 if (GeneralPreferences.getSequencePersistence()) 1857 result.loadXMLData(); 1858 } 1859 catch (Throwable t) 1860 { 1861 // just show the error 1862 IcyExceptionHandler.showErrorMessage(t, true); 1863 1864 if (loadingFrame != null) 1865 new FailedAnnounceFrame((t instanceof OutOfMemoryError) ? t.getMessage() 1866 : "Failed to open file(s), see the console output for more details."); 1867 } 1868 finally 1869 { 1870 if (loadingFrame != null) 1871 loadingFrame.close(); 1872 } 1873 1874 return result; 1875 } 1876 1877 /** 1878 * Load a sequence from the specified list of file and returns it.<br> 1879 * As the function can take sometime you should not call it from the AWT EDT.<br> 1880 * The function can return null if no sequence can be loaded from the specified files. 1881 * 1882 * @param importer 1883 * Importer used to load the image file (can be null for automatic selection). 1884 * @param paths 1885 * List of image file to load. 1886 * @param showProgress 1887 * Show progression of loading process. 1888 * @see #getSequenceFileImporter(String, boolean) 1889 */ 1890 public static Sequence loadSequence(SequenceFileImporter importer, List<String> paths, boolean showProgress) 1891 { 1892 return loadSequence(importer, paths, false, showProgress); 1893 } 1894 1895 /** 1896 * @deprecated Use {@link #loadSequence(SequenceFileImporter, List, boolean, boolean)} instead 1897 */ 1898 @Deprecated 1899 public static Sequence loadSequence(List<?> files, boolean display, boolean addToRecent) 1900 { 1901 return loadSequence(files, true); 1902 } 1903 1904 /** 1905 * Load a sequence from the specified list of file and returns it.<br> 1906 * As the function can take sometime you should not call it from the AWT EDT.<br> 1907 * The function can return null if no sequence can be loaded from the specified files.<br> 1908 * If several importers match to open a file the user will have to select the appropriate one 1909 * from a selection dialog. 1910 * 1911 * @param files 1912 * List of image file to load (can be String or File object). 1913 * @param showProgress 1914 * Show progression of loading process. 1915 */ 1916 @SuppressWarnings("unchecked") 1917 public static Sequence loadSequence(List<?> files, boolean showProgress) 1918 { 1919 if (files.size() == 0) 1920 return null; 1921 1922 final List<String> paths; 1923 1924 if (files.get(0) instanceof File) 1925 paths = FileUtil.toPaths((List<File>) files); 1926 else 1927 paths = (List<String>) files; 1928 1929 return loadSequence(null, paths, showProgress); 1930 } 1931 1932 /** 1933 * Load a sequence from the specified list of file and returns it.<br> 1934 * As the function can take sometime you should not call it from the AWT EDT.<br> 1935 * The function can return null if no sequence can be loaded from the specified files.<br> 1936 * If several importers match to open a file the user will have to select the appropriate one 1937 * from a selection dialog. 1938 * 1939 * @param files 1940 * List of image file to load (can be String or File object). 1941 */ 1942 public static Sequence loadSequence(List<?> files) 1943 { 1944 return loadSequence(files, true); 1945 } 1946 1947 /** 1948 * Loads the specified image file and return it as a Sequence, it can return <code>null</code> 1949 * if an error occured.<br> 1950 * As this method can take sometime, you should not call it from the EDT. 1951 * 1952 * @param importer 1953 * Importer used to load the image file.<br> 1954 * If set to <code>null</code> the loader will search for a compatible importer and if 1955 * several importers 1956 * match the user will have to select the appropriate one from a selection dialog if 1957 * <code>showProgress</code> parameter is set to <code>true</code> otherwise the first 1958 * compatible importer will be automatically used. 1959 * @param path 1960 * Image file to load. 1961 * @param series 1962 * Series index to load (for multi series sequence), set to 0 if unsure (default).<br> 1963 * -1 is a special value so it gives a chance to the user to select series to open from a 1964 * series selector dialog. 1965 * @param addToRecent 1966 * If set to true the path will be traced in recent opened sequence. 1967 * @param showProgress 1968 * Show progression of loading process. 1969 */ 1970 public static Sequence loadSequence(SequenceFileImporter importer, String path, int series, boolean addToRecent, 1971 boolean showProgress) 1972 { 1973 return loadSequence(importer, path, series, 0, null, -1, -1, -1, -1, -1, addToRecent, showProgress); 1974 } 1975 1976 /** 1977 * Load a sequence from the specified file.<br> 1978 * As the function can take sometime you should not call it from the AWT EDT. 1979 * 1980 * @param importer 1981 * Importer used to load the image file.<br> 1982 * If set to <code>null</code> the loader will search for a compatible importer and if 1983 * several importers 1984 * match the user will have to select the appropriate one from a selection dialog if 1985 * <code>showProgress</code> parameter is set to <code>true</code> otherwise the first 1986 * compatible importer will be automatically used. 1987 * @param path 1988 * Image file to load. 1989 * @param series 1990 * Series index to load (for multi series sequence), set to 0 if unsure (default). 1991 * @param showProgress 1992 * Show progression of loading process. 1993 */ 1994 public static Sequence loadSequence(SequenceFileImporter importer, String path, int series, boolean showProgress) 1995 { 1996 return loadSequence(importer, path, series, false, showProgress); 1997 } 1998 1999 /** 2000 * Load a sequence from the specified file.<br> 2001 * As the function can take sometime you should not call it from the AWT EDT.<br> 2002 * If several importers match to open the file the user will have to select the appropriate one 2003 * from a selection dialog if <code>showProgress</code> parameter is set to <code>true</code> 2004 * otherwise the first 2005 * compatible importer is automatically used. 2006 * 2007 * @param path 2008 * Image file to load. 2009 * @param series 2010 * Series index to load (for multi series sequence), set to 0 if unsure (default). 2011 * @param showProgress 2012 * Show progression of loading process. 2013 */ 2014 public static Sequence loadSequence(String path, int series, boolean showProgress) 2015 { 2016 return loadSequence(null, path, series, showProgress); 2017 } 2018 2019 /** 2020 * @deprecated Use {@link #load(List, boolean, boolean, boolean)} instead. 2021 */ 2022 @Deprecated 2023 public static void load(List<File> files) 2024 { 2025 load(files.toArray(new File[files.size()]), false, true, true); 2026 } 2027 2028 /** 2029 * @deprecated Use {@link #load(List, boolean, boolean, boolean)} instead. 2030 */ 2031 @Deprecated 2032 public static void load(List<File> files, boolean separate) 2033 { 2034 load(files.toArray(new File[files.size()]), separate, true, true); 2035 } 2036 2037 /** 2038 * @deprecated Use {@link #load(List, boolean, boolean, boolean)} instead. 2039 */ 2040 @Deprecated 2041 public static void load(List<File> files, boolean separate, boolean showProgress) 2042 { 2043 load(files.toArray(new File[files.size()]), separate, true, showProgress); 2044 } 2045 2046 // /** 2047 // * @deprecated Use {@link #load(File[], boolean, boolean, boolean)} instead. 2048 // */ 2049 // @Deprecated 2050 // public static void load(List<File> files, boolean separate, boolean autoOrder, boolean 2051 // showProgress) 2052 // { 2053 // load(files.toArray(new File[files.size()]), separate, autoOrder, showProgress); 2054 // } 2055 2056 /** 2057 * @deprecated Use {@link #load(String, boolean)} instead. 2058 */ 2059 @Deprecated 2060 public static void load(File file) 2061 { 2062 load(new File[] {file}, false, false, true); 2063 } 2064 2065 /** 2066 * @deprecated Use {@link #load(List, boolean, boolean, boolean)} instead. 2067 */ 2068 @Deprecated 2069 public static void load(final File[] files, final boolean separate, final boolean autoOrder, 2070 final boolean showProgress) 2071 { 2072 // asynchronous call 2073 ThreadUtil.bgRun(new Runnable() 2074 { 2075 @Override 2076 public void run() 2077 { 2078 // load sequence 2079 final Sequence[] sequences = loadSequences(files, -1, separate, autoOrder, showProgress); 2080 // and display them 2081 for (Sequence seq : sequences) 2082 Icy.getMainInterface().addSequence(seq); 2083 } 2084 }); 2085 } 2086 2087 /** 2088 * @deprecated Use {@link #load(String, boolean)} instead. 2089 */ 2090 @Deprecated 2091 public static void load(File file, boolean showProgress) 2092 { 2093 load(new File[] {file}, false, false, showProgress); 2094 } 2095 2096 /** 2097 * Load the specified files with the given {@link FileImporter}.<br> 2098 * The loading process is asynchronous.<br> 2099 * The FileImporter is responsible to make the loaded files available in the application.<br> 2100 * This method should be used only for non image file. 2101 * 2102 * @param importer 2103 * Importer used to open and load files.<br> 2104 * If set to <code>null</code> the loader will search for a compatible importer and if 2105 * several importers match the user will have to select the appropriate one from a 2106 * selection dialog. 2107 * @param paths 2108 * list of file to load 2109 * @param showProgress 2110 * Show progression in loading process 2111 */ 2112 public static void load(final FileImporter importer, final List<String> paths, final boolean showProgress) 2113 { 2114 // asynchronous call 2115 ThreadUtil.bgRun(new Runnable() 2116 { 2117 @Override 2118 public void run() 2119 { 2120 // explode path list 2121 final List<String> singlePaths = explode(paths); 2122 2123 if (singlePaths.size() > 0) 2124 { 2125 // get the file importer now for remaining file 2126 final Map<FileImporter, List<String>> fileImporters; 2127 2128 // importer not defined --> find the appropriate importers 2129 if (importer == null) 2130 fileImporters = getFileImporters(singlePaths, false); 2131 else 2132 { 2133 fileImporters = new HashMap<FileImporter, List<String>>(1); 2134 fileImporters.put(importer, new ArrayList<String>(singlePaths)); 2135 } 2136 2137 for (Entry<FileImporter, List<String>> entry : fileImporters.entrySet()) 2138 { 2139 final FileImporter importer = entry.getKey(); 2140 final List<String> currPaths = entry.getValue(); 2141 2142 // load files 2143 loadFiles(importer, paths, true, showProgress); 2144 2145 // remove loaded files 2146 singlePaths.removeAll(currPaths); 2147 } 2148 } 2149 2150 // remaining files ? 2151 if (singlePaths.size() > 0) 2152 { 2153 // get first found importer for remaining files 2154 final Map<SequenceFileImporter, List<String>> importers = getSequenceFileImporters(singlePaths, 2155 true); 2156 2157 // user canceled action for these paths so we remove them 2158 for (List<String> values : importers.values()) 2159 singlePaths.removeAll(values); 2160 2161 if (singlePaths.size() > 0) 2162 { 2163 // just log in console 2164 System.err.println("No compatible importer found for the following files:"); 2165 for (String path : singlePaths) 2166 System.err.println(path); 2167 System.err.println(); 2168 } 2169 } 2170 } 2171 }); 2172 } 2173 2174 /** 2175 * Load the specified files with the given {@link FileImporter}.<br> 2176 * The FileImporter is responsible to make the loaded files available in the application.<br> 2177 * This method should be used only for non image file. 2178 * 2179 * @param importer 2180 * Importer used to open and load image files. 2181 * @param paths 2182 * list of file to load 2183 * @param addToRecent 2184 * If set to true the files list will be traced in recent opened files. 2185 * @param showProgress 2186 * Show progression in loading process 2187 */ 2188 static void loadFiles(FileImporter importer, List<String> paths, boolean addToRecent, boolean showProgress) 2189 { 2190 final ApplicationMenu mainMenu; 2191 final FileFrame loadingFrame; 2192 2193 if (addToRecent) 2194 mainMenu = Icy.getMainInterface().getApplicationMenu(); 2195 else 2196 mainMenu = null; 2197 if (showProgress && !Icy.getMainInterface().isHeadLess()) 2198 { 2199 loadingFrame = new FileFrame("Loading", null); 2200 loadingFrame.setLength(paths.size()); 2201 loadingFrame.setPosition(0); 2202 } 2203 else 2204 loadingFrame = null; 2205 2206 try 2207 { 2208 // load each file in a separate sequence 2209 for (String path : paths) 2210 { 2211 if (loadingFrame != null) 2212 loadingFrame.incPosition(); 2213 2214 // load current file 2215 importer.load(path, loadingFrame); 2216 2217 // add as separate item to recent file list 2218 if (mainMenu != null) 2219 mainMenu.addRecentLoadedFile(new File(FileUtil.getGenericPath(path))); 2220 } 2221 } 2222 catch (Throwable t) 2223 { 2224 // just show the error 2225 IcyExceptionHandler.showErrorMessage(t, true); 2226 if (loadingFrame != null) 2227 new FailedAnnounceFrame("Failed to open file(s), see the console output for more details."); 2228 } 2229 finally 2230 { 2231 if (loadingFrame != null) 2232 loadingFrame.close(); 2233 } 2234 } 2235 2236 /** 2237 * Load the specified image file with the given {@link SequenceFileImporter}.<br> 2238 * The loading process is asynchronous.<br> 2239 * The resulting sequence is automatically displayed when the process complete. 2240 * 2241 * @param importer 2242 * Importer used to load the image file.<br> 2243 * If set to <code>null</code> the loader will search for a compatible importer and if 2244 * several importers match the user will have to select the appropriate one from a selection dialog if 2245 * <code>showProgress</code> parameter is set to <code>true</code> otherwise the first 2246 * compatible importer will be automatically used. 2247 * @param path 2248 * image file to load 2249 * @param series 2250 * Series index to load (for multi series sequence), set to 0 if unsure (default).<br> 2251 * -1 is a special value so it gives a chance to the user to select series to open from a 2252 * series selector dialog. 2253 * @param resolution 2254 * Wanted resolution level for the image (use 0 if unsure), useful for large image<br> 2255 * The retrieved image resolution is equal to 2256 * <code>image.resolution / (2^resolution)</code><br> 2257 * So for instance level 0 is the default/full image resolution while level 1 is base 2258 * image 2259 * resolution / 2 and so on... 2260 * @param region 2261 * The 2D region of the image we want to retrieve.<br> 2262 * If set to <code>null</code> then the whole XY plane of the image is returned. 2263 * @param minZ 2264 * the minimum Z position of the image (slice) we want retrieve (inclusive).<br> 2265 * Set to -1 to retrieve the whole stack. 2266 * @param maxZ 2267 * the maximum Z position of the image (slice) we want retrieve (inclusive).<br> 2268 * Set to -1 to retrieve the whole stack. 2269 * @param minT 2270 * the minimum T position of the image (frame) we want retrieve (inclusive).<br> 2271 * Set to -1 to retrieve the whole timelaps. 2272 * @param maxT 2273 * the maximum T position of the image (frame) we want retrieve (inclusive).<br> 2274 * Set to -1 to retrieve the whole timelaps. 2275 * @param channel 2276 * C position of the image (channel) we want retrieve (-1 means all channel). 2277 * @param forceVolatile 2278 * If set to <code>true</code> then image data is forced to volatile (see {@link IcyBufferedImage#isVolatile()}).<br> 2279 * Note that if you don't have enough memory to load the whole Sequence into memory then image data are always made volatile. 2280 * @param separate 2281 * Force image to be loaded in separate sequence if possible (disable stitching if any) 2282 * @param addToRecent 2283 * If set to true the files list will be traced in recent opened sequence. 2284 * @param showProgress 2285 * Show progression in loading process 2286 */ 2287 public static void load(final SequenceFileImporter importer, final String path, final int series, 2288 final int resolution, final Rectangle region, final int minZ, final int maxZ, final int minT, 2289 final int maxT, final int channel, final boolean forceVolatile, final boolean separate, 2290 final boolean addToRecent, final boolean showProgress) 2291 { 2292 // asynchronous call 2293 ThreadUtil.bgRun(new Runnable() 2294 { 2295 @Override 2296 public void run() 2297 { 2298 // normal opening operation ? 2299 if ((resolution == 0) && (region == null) && (minZ <= 0) && (maxZ == -1) && (minT <= 0) && (maxT == -1) 2300 && (channel == -1)) 2301 { 2302 // load sequence (we use this method to allow multi series opening) 2303 final List<Sequence> sequences = loadSequences( 2304 (importer == null) ? getSequenceFileImporter(path, !showProgress) : importer, 2305 CollectionUtil.asList(path), series, forceVolatile, separate, false, false, addToRecent, 2306 showProgress); 2307 2308 // and display them 2309 for (Sequence sequence : sequences) 2310 if (sequence != null) 2311 Icy.getMainInterface().addSequence(sequence); 2312 } 2313 else 2314 { 2315 // load sequence 2316 final Sequence sequence = loadSequence(importer, path, series, resolution, region, minZ, maxZ, minT, 2317 maxT, channel, forceVolatile, addToRecent, showProgress); 2318 2319 // and display it 2320 if (sequence != null) 2321 Icy.getMainInterface().addSequence(sequence); 2322 } 2323 } 2324 }); 2325 } 2326 2327 /** 2328 * Load the specified image file with the given {@link SequenceFileImporter}.<br> 2329 * The loading process is asynchronous.<br> 2330 * The resulting sequence is automatically displayed when the process complete. 2331 * 2332 * @param importer 2333 * Importer used to load the image file.<br> 2334 * If set to <code>null</code> the loader will search for a compatible importer and if 2335 * several importers match the user will have to select the appropriate one from a selection dialog if 2336 * <code>showProgress</code> parameter is set to <code>true</code> otherwise the first 2337 * compatible importer will be automatically used. 2338 * @param path 2339 * image file to load 2340 * @param series 2341 * Series index to load (for multi series sequence), set to 0 if unsure (default).<br> 2342 * -1 is a special value so it gives a chance to the user to select series to open from a 2343 * series selector dialog. 2344 * @param resolution 2345 * Wanted resolution level for the image (use 0 if unsure), useful for large image<br> 2346 * The retrieved image resolution is equal to 2347 * <code>image.resolution / (2^resolution)</code><br> 2348 * So for instance level 0 is the default/full image resolution while level 1 is base 2349 * image 2350 * resolution / 2 and so on... 2351 * @param region 2352 * The 2D region of the image we want to retrieve.<br> 2353 * If set to <code>null</code> then the whole XY plane of the image is returned. 2354 * @param minZ 2355 * the minimum Z position of the image (slice) we want retrieve (inclusive).<br> 2356 * Set to -1 to retrieve the whole stack. 2357 * @param maxZ 2358 * the maximum Z position of the image (slice) we want retrieve (inclusive).<br> 2359 * Set to -1 to retrieve the whole stack. 2360 * @param minT 2361 * the minimum T position of the image (frame) we want retrieve (inclusive).<br> 2362 * Set to -1 to retrieve the whole timelaps. 2363 * @param maxT 2364 * the maximum T position of the image (frame) we want retrieve (inclusive).<br> 2365 * Set to -1 to retrieve the whole timelaps. 2366 * @param channel 2367 * C position of the image (channel) we want retrieve (-1 means all channel). 2368 * @param separate 2369 * Force image to be loaded in separate sequence if possible (disable stitching if any) 2370 * @param addToRecent 2371 * If set to true the files list will be traced in recent opened sequence. 2372 * @param showProgress 2373 * Show progression in loading process 2374 */ 2375 public static void load(final SequenceFileImporter importer, final String path, final int series, 2376 final int resolution, final Rectangle region, final int minZ, final int maxZ, final int minT, 2377 final int maxT, final int channel, final boolean separate, final boolean addToRecent, 2378 final boolean showProgress) 2379 { 2380 load(importer, path, series, resolution, region, minZ, maxZ, minT, maxT, channel, false, separate, addToRecent, 2381 showProgress); 2382 } 2383 2384 /** 2385 * Load the specified image file list (try to group them as much as possible).<br> 2386 * The loading process is asynchronous.<br> 2387 * The resulting sequence is automatically displayed when the process complete. 2388 * 2389 * @param paths 2390 * list of file we want to group to build a to load 2391 * @param resolution 2392 * Wanted resolution level for the image (use 0 if unsure), useful for large image<br> 2393 * The retrieved image resolution is equal to 2394 * <code>image.resolution / (2^resolution)</code><br> 2395 * So for instance level 0 is the default/full image resolution while level 1 is base 2396 * image 2397 * resolution / 2 and so on... 2398 * @param region 2399 * The 2D region of the image we want to retrieve.<br> 2400 * If set to <code>null</code> then the whole XY plane of the image is returned. 2401 * @param minZ 2402 * the minimum Z position of the image (slice) we want retrieve (inclusive).<br> 2403 * Set to -1 to retrieve the whole stack. 2404 * @param maxZ 2405 * the maximum Z position of the image (slice) we want retrieve (inclusive).<br> 2406 * Set to -1 to retrieve the whole stack. 2407 * @param minT 2408 * the minimum T position of the image (frame) we want retrieve (inclusive).<br> 2409 * Set to -1 to retrieve the whole timelaps. 2410 * @param maxT 2411 * the maximum T position of the image (frame) we want retrieve (inclusive).<br> 2412 * Set to -1 to retrieve the whole timelaps. 2413 * @param channel 2414 * C position of the image (channel) we want retrieve (-1 means all channel). 2415 * @param directory 2416 * specify is the source is a single complete directory 2417 * @param addToRecent 2418 * If set to true the files list will be traced in recent opened sequence. 2419 * @param showProgress 2420 * Show progression in loading process 2421 */ 2422 public static void load(final List<String> paths, final int resolution, final Rectangle region, final int minZ, 2423 final int maxZ, final int minT, final int maxT, final int channel, final boolean directory, 2424 final boolean addToRecent, final boolean showProgress) 2425 { 2426 // asynchronous call 2427 ThreadUtil.bgRun(new Runnable() 2428 { 2429 @Override 2430 public void run() 2431 { 2432 final ApplicationMenu mainMenu; 2433 final FileFrame loadingFrame; 2434 2435 if (addToRecent) 2436 mainMenu = Icy.getMainInterface().getApplicationMenu(); 2437 else 2438 mainMenu = null; 2439 if (showProgress && !Icy.getMainInterface().isHeadLess()) 2440 loadingFrame = new FileFrame("Loading", null); 2441 else 2442 loadingFrame = null; 2443 2444 try 2445 { 2446 // load sequence from group with advanced options 2447 for (SequenceFileGroup group : SequenceFileSticher.groupAllFiles(null, cleanNonImageFile(paths), 2448 true, loadingFrame)) 2449 { 2450 final Sequence sequence = internalLoadGroup(group, resolution, region, minZ, maxZ, minT, maxT, 2451 channel, false, directory, mainMenu, loadingFrame); 2452 2453 if (sequence != null) 2454 { 2455 // load sequence XML data 2456 if (GeneralPreferences.getSequencePersistence()) 2457 sequence.loadXMLData(); 2458 // and display it 2459 Icy.getMainInterface().addSequence(sequence); 2460 } 2461 } 2462 } 2463 catch (Throwable t) 2464 { 2465 // just show the error 2466 IcyExceptionHandler.showErrorMessage(t, true); 2467 2468 if (loadingFrame != null) 2469 new FailedAnnounceFrame((t instanceof OutOfMemoryError) ? t.getMessage() 2470 : "Failed to open file(s), see the console output for more details."); 2471 } 2472 finally 2473 { 2474 if (loadingFrame != null) 2475 loadingFrame.close(); 2476 } 2477 } 2478 }); 2479 } 2480 2481 /** 2482 * Load the specified image file group (built using SequenceFileSticher class).<br> 2483 * The loading process is asynchronous.<br> 2484 * The resulting sequence is automatically displayed when the process complete. 2485 * 2486 * @param group 2487 * Sequence file group built using {@link SequenceFileSticher}. 2488 * @param resolution 2489 * Wanted resolution level for the image (use 0 if unsure), useful for large image<br> 2490 * The retrieved image resolution is equal to 2491 * <code>image.resolution / (2^resolution)</code><br> 2492 * So for instance level 0 is the default/full image resolution while level 1 is base 2493 * image 2494 * resolution / 2 and so on... 2495 * @param region 2496 * The 2D region of the image we want to retrieve.<br> 2497 * If set to <code>null</code> then the whole XY plane of the image is returned. 2498 * @param minZ 2499 * the minimum Z position of the image (slice) we want retrieve (inclusive).<br> 2500 * Set to -1 to retrieve the whole stack. 2501 * @param maxZ 2502 * the maximum Z position of the image (slice) we want retrieve (inclusive).<br> 2503 * Set to -1 to retrieve the whole stack. 2504 * @param minT 2505 * the minimum T position of the image (frame) we want retrieve (inclusive).<br> 2506 * Set to -1 to retrieve the whole timelaps. 2507 * @param maxT 2508 * the maximum T position of the image (frame) we want retrieve (inclusive).<br> 2509 * Set to -1 to retrieve the whole timelaps. 2510 * @param channel 2511 * C position of the image (channel) we want retrieve (-1 means all channel). 2512 * @param directory 2513 * specify is the source is a single complete directory 2514 * @param addToRecent 2515 * If set to true the files list will be traced in recent opened sequence. 2516 * @param showProgress 2517 * Show progression in loading process 2518 */ 2519 public static void load(final SequenceFileGroup group, final int resolution, final Rectangle region, final int minZ, 2520 final int maxZ, final int minT, final int maxT, final int channel, final boolean directory, 2521 final boolean addToRecent, final boolean showProgress) 2522 { 2523 // asynchronous call 2524 ThreadUtil.bgRun(new Runnable() 2525 { 2526 @Override 2527 public void run() 2528 { 2529 final ApplicationMenu mainMenu; 2530 final FileFrame loadingFrame; 2531 2532 if (addToRecent) 2533 mainMenu = Icy.getMainInterface().getApplicationMenu(); 2534 else 2535 mainMenu = null; 2536 if (showProgress && !Icy.getMainInterface().isHeadLess()) 2537 loadingFrame = new FileFrame("Loading", null); 2538 else 2539 loadingFrame = null; 2540 2541 try 2542 { 2543 // load sequence from group with advanced options 2544 final Sequence sequence = internalLoadGroup(group, resolution, region, minZ, maxZ, minT, maxT, 2545 channel, false, directory, mainMenu, loadingFrame); 2546 2547 if (sequence != null) 2548 { 2549 // load sequence XML data 2550 if (GeneralPreferences.getSequencePersistence()) 2551 sequence.loadXMLData(); 2552 // and display it 2553 Icy.getMainInterface().addSequence(sequence); 2554 } 2555 } 2556 catch (Throwable t) 2557 { 2558 // just show the error 2559 IcyExceptionHandler.showErrorMessage(t, true); 2560 2561 if (loadingFrame != null) 2562 new FailedAnnounceFrame((t instanceof OutOfMemoryError) ? t.getMessage() 2563 : "Failed to open file(s), see the console output for more details."); 2564 } 2565 finally 2566 { 2567 if (loadingFrame != null) 2568 loadingFrame.close(); 2569 } 2570 } 2571 }); 2572 } 2573 2574 /** 2575 * Load the specified image files with the given {@link SequenceFileImporter}.<br> 2576 * The loading process is asynchronous.<br> 2577 * If <i>separate</i> is false the loader try to set image in the same sequence.<br> 2578 * If <i>separate</i> is true each image is loaded in a separate sequence.<br> 2579 * The resulting sequences are automatically displayed when the process complete. 2580 * 2581 * @param importer 2582 * Importer used to open and load image files.<br> 2583 * If set to <code>null</code> the loader will search for a compatible importer and if 2584 * several importers match the user will have to select the appropriate one from a 2585 * selection dialog. 2586 * @param paths 2587 * list of image file to load 2588 * @param separate 2589 * Force image to be loaded in separate sequence 2590 * @param autoOrder 2591 * Try to order image in sequence from their filename 2592 * @param showProgress 2593 * Show progression in loading process 2594 */ 2595 public static void load(final SequenceFileImporter importer, final List<String> paths, final boolean separate, 2596 final boolean autoOrder, final boolean showProgress) 2597 { 2598 // asynchronous call 2599 ThreadUtil.bgRun(new Runnable() 2600 { 2601 @Override 2602 public void run() 2603 { 2604 // load sequence 2605 final List<Sequence> sequences = loadSequences(importer, paths, -1, separate, autoOrder, true, 2606 showProgress); 2607 // and display them 2608 for (Sequence seq : sequences) 2609 Icy.getMainInterface().addSequence(seq); 2610 } 2611 }); 2612 } 2613 2614 /** 2615 * Load the specified files (asynchronous process) by using automatically the appropriate 2616 * {@link FileImporter} or {@link SequenceFileImporter}. If several importers match to open the 2617 * file the user will have to select the appropriate one from a selection dialog.<br> 2618 * <br> 2619 * If the specified files are image files:<br> 2620 * When <i>separate</i> is <code>false</code> the loader try to set image in the same 2621 * sequence.<br> 2622 * When <i>separate</i> is <code>true</code> each image is loaded in a separate sequence.<br> 2623 * The resulting sequences are automatically displayed when the process complete. 2624 * 2625 * @param paths 2626 * list of file to load 2627 * @param forceVolatile 2628 * If set to <code>true</code> then image data is forced to volatile (see {@link IcyBufferedImage#isVolatile()}).<br> 2629 * Note that if you don't have enough memory to load the whole Sequence into memory then image data are always made volatile. 2630 * @param separate 2631 * Force image to be loaded in separate sequence (image files only) 2632 * @param autoOrder 2633 * Try to order image in sequence from their filename (image files only) 2634 * @param showProgress 2635 * Show progression in loading process 2636 */ 2637 public static void load(final List<String> paths, final boolean forceVolatile, final boolean separate, 2638 final boolean autoOrder, final boolean showProgress) 2639 { 2640 // asynchronous call 2641 ThreadUtil.bgRun(new Runnable() 2642 { 2643 @SuppressWarnings("resource") 2644 @Override 2645 public void run() 2646 { 2647 // detect if this is a complete folder load 2648 final boolean directory = (paths.size() == 1) && new File(paths.get(0)).isDirectory(); 2649 // explode path list 2650 final List<String> singlePaths = cleanNonImageFile(explode(paths)); 2651 final boolean adjSeparate = (singlePaths.size() <= 1) || separate; 2652 final Map<SequenceFileImporter, List<String>> sequenceFileImporters = getSequenceFileImporters(null, 2653 singlePaths, !adjSeparate, false); 2654 2655 for (Entry<SequenceFileImporter, List<String>> entry : sequenceFileImporters.entrySet()) 2656 { 2657 final SequenceFileImporter importer = entry.getKey(); 2658 final List<String> currPaths = entry.getValue(); 2659 final boolean dir = directory && (sequenceFileImporters.size() == 1) 2660 && (currPaths.size() == singlePaths.size()); 2661 2662 // load sequence 2663 final List<Sequence> sequences = loadSequences(importer, currPaths, -1, forceVolatile, adjSeparate, 2664 autoOrder, dir, true, showProgress); 2665 // and display them 2666 for (Sequence seq : sequences) 2667 Icy.getMainInterface().addSequence(seq); 2668 2669 // remove loaded files 2670 singlePaths.removeAll(currPaths); 2671 } 2672 2673 if (singlePaths.size() > 0) 2674 { 2675 // get the file importer now for remaining file 2676 final Map<FileImporter, List<String>> fileImporters = getFileImporters(singlePaths, false); 2677 2678 for (Entry<FileImporter, List<String>> entry : fileImporters.entrySet()) 2679 { 2680 final FileImporter importer = entry.getKey(); 2681 final List<String> currPaths = entry.getValue(); 2682 2683 // load files 2684 loadFiles(importer, paths, true, showProgress); 2685 2686 // remove loaded files 2687 singlePaths.removeAll(currPaths); 2688 } 2689 } 2690 2691 // remaining files ? 2692 if (singlePaths.size() > 0) 2693 { 2694 // get first found importer for remaining files 2695 final Map<SequenceFileImporter, List<String>> importers = getSequenceFileImporters(singlePaths, 2696 true); 2697 2698 // user canceled action for these paths so we remove them 2699 for (List<String> values : importers.values()) 2700 singlePaths.removeAll(values); 2701 2702 if (singlePaths.size() > 0) 2703 { 2704 // just log in console 2705 System.err.println("No compatible importer found for the following files:"); 2706 for (String path : singlePaths) 2707 System.err.println(path); 2708 System.err.println(); 2709 } 2710 } 2711 } 2712 }); 2713 } 2714 2715 /** 2716 * Load the specified files (asynchronous process) by using automatically the appropriate 2717 * {@link FileImporter} or {@link SequenceFileImporter}. If several importers match to open the 2718 * file the user will have to select the appropriate one from a selection dialog.<br> 2719 * <br> 2720 * If the specified files are image files:<br> 2721 * When <i>separate</i> is <code>false</code> the loader try to set image in the same 2722 * sequence.<br> 2723 * When <i>separate</i> is <code>true</code> each image is loaded in a separate sequence.<br> 2724 * The resulting sequences are automatically displayed when the process complete. 2725 * 2726 * @param paths 2727 * list of file to load 2728 * @param separate 2729 * Force image to be loaded in separate sequence (image files only) 2730 * @param autoOrder 2731 * Try to order image in sequence from their filename (image files only) 2732 * @param showProgress 2733 * Show progression in loading process 2734 */ 2735 public static void load(final List<String> paths, final boolean separate, final boolean autoOrder, 2736 final boolean showProgress) 2737 { 2738 load(paths, false, separate, autoOrder, showProgress); 2739 } 2740 2741 /** 2742 * Load the specified file (asynchronous process) by using automatically the appropriate 2743 * {@link FileImporter} or 2744 * {@link SequenceFileImporter}. If several importers match to open the 2745 * file the user will have to select the appropriate one from a selection dialog.<br> 2746 * <br> 2747 * If the specified file is an image file, the resulting sequence is automatically displayed 2748 * when process complete. 2749 * 2750 * @param path 2751 * file to load 2752 * @param showProgress 2753 * Show progression of loading process. 2754 */ 2755 public static void load(String path, boolean showProgress) 2756 { 2757 load(CollectionUtil.createArrayList(path), false, false, showProgress); 2758 } 2759 2760 /** 2761 * @deprecated Use {@link #loadSequences(List, int, boolean, boolean, boolean, boolean)} 2762 * instead. 2763 */ 2764 @Deprecated 2765 static Sequence[] loadSequences(SequenceFileImporter importer, File[] files, int series, boolean separate, 2766 boolean autoOrder, boolean directory, boolean addToRecent, boolean showProgress) 2767 { 2768 final List<String> paths = cleanNonImageFile(CollectionUtil.asList(FileUtil.toPaths(files))); 2769 final List<Sequence> result = loadSequences(importer, paths, series, false, separate, autoOrder, directory, 2770 addToRecent, showProgress); 2771 return result.toArray(new Sequence[result.size()]); 2772 } 2773 2774 /** 2775 * Loads the specified image file and return it as a Sequence, it can return <code>null</code> 2776 * if an error occured.<br> 2777 * As this method can take sometime, you should not call it from the EDT. 2778 * 2779 * @param importer 2780 * Importer used to load the image file.<br> 2781 * If set to <code>null</code> the loader will search for a compatible importer and if 2782 * several importers 2783 * match the user will have to select the appropriate one from a selection dialog if 2784 * <code>showProgress</code> parameter is set to <code>true</code> otherwise the first 2785 * compatible importer will be automatically used. 2786 * @param path 2787 * image file to load 2788 * @param series 2789 * Series index to load (for multi series sequence), set to 0 if unsure (default).<br> 2790 * -1 is a special value so it gives a chance to the user to select series to open from a 2791 * series selector dialog. 2792 * @param resolution 2793 * Wanted resolution level for the image (use 0 if unsure), useful for large image<br> 2794 * The retrieved image resolution is equal to <code>image.resolution / (2^resolution)</code><br> 2795 * So for instance level 0 is the default/full image resolution while level 1 is base image resolution / 2 2796 * and so on... 2797 * @param region 2798 * The 2D region of the image we want to retrieve (in full image resolution).<br> 2799 * If set to <code>null</code> then the whole XY plane of the image is returned. 2800 * @param minZ 2801 * the minimum Z position of the image (slice) we want retrieve (inclusive).<br> 2802 * Set to -1 to retrieve the whole stack. 2803 * @param maxZ 2804 * the maximum Z position of the image (slice) we want retrieve (inclusive).<br> 2805 * Set to -1 to retrieve the whole stack. 2806 * @param minT 2807 * the minimum T position of the image (frame) we want retrieve (inclusive).<br> 2808 * Set to -1 to retrieve the whole timelaps. 2809 * @param maxT 2810 * the maximum T position of the image (frame) we want retrieve (inclusive).<br> 2811 * Set to -1 to retrieve the whole timelaps. 2812 * @param channel 2813 * C position of the image (channel) we want retrieve (-1 means all channel). 2814 * @param forceVolatile 2815 * If set to <code>true</code> then image data is forced to volatile (see {@link IcyBufferedImage#isVolatile()}).<br> 2816 * Note that if you don't have enough memory to load the whole Sequence into memory then image data are always made volatile. 2817 * @param addToRecent 2818 * If set to true the files list will be traced in recent opened sequence. 2819 * @param showProgress 2820 * Show progression in loading process 2821 */ 2822 public static Sequence loadSequence(SequenceFileImporter importer, String path, int series, int resolution, 2823 Rectangle region, int minZ, int maxZ, int minT, int maxT, int channel, boolean forceVolatile, 2824 boolean addToRecent, boolean showProgress) 2825 { 2826 final ApplicationMenu mainMenu; 2827 final FileFrame loadingFrame; 2828 final Sequence result; 2829 2830 if (addToRecent) 2831 mainMenu = Icy.getMainInterface().getApplicationMenu(); 2832 else 2833 mainMenu = null; 2834 if (showProgress && !Icy.getMainInterface().isHeadLess()) 2835 loadingFrame = new FileFrame("Loading", path); 2836 else 2837 loadingFrame = null; 2838 2839 // importer is not specified ? --> get a compatible one. 2840 final SequenceFileImporter imp = (importer == null) ? getSequenceFileImporter(path, !showProgress) : importer; 2841 2842 try 2843 { 2844 // open image 2845 imp.open(path, 0); 2846 2847 // get metadata 2848 final OMEXMLMetadata meta = imp.getOMEXMLMetaData(); 2849 // clean the metadata 2850 MetaDataUtil.clean(meta); 2851 2852 // series selection 2853 int selectedSerie; 2854 2855 // give the opportunity to select the series (single one) to open ? 2856 if (series == -1) 2857 { 2858 try 2859 { 2860 // series selection (create a new importer instance as selectSerie(..) does async processes) 2861 selectedSerie = selectSerie(cloneSequenceFileImporter(imp), path, meta, 0); 2862 } 2863 catch (Throwable t) 2864 { 2865 IcyExceptionHandler.showErrorMessage(t, true, true); 2866 System.err.print("Opening first series by default..."); 2867 selectedSerie = 0; 2868 } 2869 2870 // user cancelled action in the series selection ? null = cancel 2871 if (selectedSerie == -1) 2872 return null; 2873 } 2874 else 2875 selectedSerie = series; 2876 2877 // load the image 2878 result = internalLoadSingle(imp, meta, selectedSerie, resolution, region, minZ, maxZ, minT, maxT, channel, 2879 forceVolatile, loadingFrame); 2880 2881 // Don't close importer on success ! we want to keep it inside the sequence. 2882 // We will close it when finalizing the sequence... 2883 // imp.close(); 2884 2885 // add as separate item to recent file list 2886 if (mainMenu != null) 2887 mainMenu.addRecentLoadedFile(new File(FileUtil.getGenericPath(path))); 2888 2889 // TODO: restore colormap --> try to recover colormap 2890 2891 // load sequence XML data 2892 if (GeneralPreferences.getSequencePersistence()) 2893 result.loadXMLData(); 2894 } 2895 catch (Throwable t) 2896 { 2897 try 2898 { 2899 // close importer when error happen (not stored in Sequence so we need to close it manually) 2900 imp.close(); 2901 } 2902 catch (Exception e) 2903 { 2904 // ignore 2905 } 2906 2907 // just show the error 2908 IcyExceptionHandler.showErrorMessage(t, true); 2909 2910 if (loadingFrame != null) 2911 { 2912 new FailedAnnounceFrame((t instanceof OutOfMemoryError) ? t.getMessage() 2913 : "Failed to open file(s), see the console output for more details."); 2914 } 2915 2916 return null; 2917 } 2918 finally 2919 { 2920 if (loadingFrame != null) 2921 loadingFrame.close(); 2922 } 2923 2924 return result; 2925 } 2926 2927 /** 2928 * Loads the specified image file and return it as a Sequence, it can return <code>null</code> 2929 * if an error occured.<br> 2930 * As this method can take sometime, you should not call it from the EDT. 2931 * 2932 * @param importer 2933 * Importer used to load the image file.<br> 2934 * If set to <code>null</code> the loader will search for a compatible importer and if 2935 * several importers 2936 * match the user will have to select the appropriate one from a selection dialog if 2937 * <code>showProgress</code> parameter is set to <code>true</code> otherwise the first 2938 * compatible importer will be automatically used. 2939 * @param path 2940 * image file to load 2941 * @param series 2942 * Series index to load (for multi series sequence), set to 0 if unsure (default).<br> 2943 * -1 is a special value so it gives a chance to the user to select series to open from a 2944 * series selector dialog. 2945 * @param resolution 2946 * Wanted resolution level for the image (use 0 if unsure), useful for large image<br> 2947 * The retrieved image resolution is equal to <code>image.resolution / (2^resolution)</code><br> 2948 * So for instance level 0 is the default/full image resolution while level 1 is base image resolution / 2 2949 * and so on... 2950 * @param region 2951 * The 2D region of the image we want to retrieve (in full image resolution).<br> 2952 * If set to <code>null</code> then the whole XY plane of the image is returned. 2953 * @param minZ 2954 * the minimum Z position of the image (slice) we want retrieve (inclusive).<br> 2955 * Set to -1 to retrieve the whole stack. 2956 * @param maxZ 2957 * the maximum Z position of the image (slice) we want retrieve (inclusive).<br> 2958 * Set to -1 to retrieve the whole stack. 2959 * @param minT 2960 * the minimum T position of the image (frame) we want retrieve (inclusive).<br> 2961 * Set to -1 to retrieve the whole timelaps. 2962 * @param maxT 2963 * the maximum T position of the image (frame) we want retrieve (inclusive).<br> 2964 * Set to -1 to retrieve the whole timelaps. 2965 * @param channel 2966 * C position of the image (channel) we want retrieve (-1 means all channel). 2967 * @param addToRecent 2968 * If set to true the files list will be traced in recent opened sequence. 2969 * @param showProgress 2970 * Show progression in loading process 2971 */ 2972 public static Sequence loadSequence(SequenceFileImporter importer, String path, int series, int resolution, 2973 Rectangle region, int minZ, int maxZ, int minT, int maxT, int channel, boolean addToRecent, 2974 boolean showProgress) 2975 { 2976 return loadSequence(importer, path, series, resolution, region, minZ, maxZ, minT, maxT, channel, false, 2977 addToRecent, showProgress); 2978 } 2979 2980 /** 2981 * Load a sequence from the specified list of file and returns it.<br> 2982 * The function try to guess image ordering from file name and metadata.<br> 2983 * 2984 * @param importer 2985 * Importer used to load the image files.<br> 2986 * If set to <code>null</code> the loader will take the first compatible importer found. 2987 * @param paths 2988 * list of file where to load image from 2989 * @param resolution 2990 * Wanted resolution level for the image (use 0 if unsure), useful for large image<br> 2991 * The retrieved image resolution is equal to 2992 * <code>image.resolution / (2^resolution)</code><br> 2993 * So for instance level 0 is the default/full image resolution while level 1 is base 2994 * image 2995 * resolution / 2 and so on... 2996 * @param region 2997 * The 2D region of the image we want to retrieve.<br> 2998 * If set to <code>null</code> then the whole XY plane of the image is returned. 2999 * @param minZ 3000 * the minimum Z position of the image (slice) we want retrieve (inclusive).<br> 3001 * Set to -1 to retrieve the whole stack. 3002 * @param maxZ 3003 * the maximum Z position of the image (slice) we want retrieve (inclusive).<br> 3004 * Set to -1 to retrieve the whole stack. 3005 * @param minT 3006 * the minimum T position of the image (frame) we want retrieve (inclusive).<br> 3007 * Set to -1 to retrieve the whole timelaps. 3008 * @param maxT 3009 * the maximum T position of the image (frame) we want retrieve (inclusive).<br> 3010 * Set to -1 to retrieve the whole timelaps. 3011 * @param channel 3012 * C position of the image (channel) we want retrieve (-1 means all channel). 3013 * @param forceVolatile 3014 * If set to <code>true</code> then image data is forced to volatile (see {@link IcyBufferedImage#isVolatile()}).<br> 3015 * Note that if you don't have enough memory to load the whole Sequence into memory then image data are always made volatile. 3016 * @param directory 3017 * specify is the source is a single complete directory 3018 * @param addToRecent 3019 * If set to true the files list will be traced in recent opened sequence. 3020 * @param showProgress 3021 * Show progression in loading process 3022 * @see #getSequenceFileImporter(String, boolean) 3023 */ 3024 public static Sequence loadSequence(SequenceFileImporter importer, List<String> paths, int resolution, 3025 Rectangle region, int minZ, int maxZ, int minT, int maxT, int channel, boolean forceVolatile, 3026 boolean directory, boolean addToRecent, boolean showProgress) 3027 { 3028 final ApplicationMenu mainMenu; 3029 final FileFrame loadingFrame; 3030 Sequence result = null; 3031 3032 if (addToRecent) 3033 mainMenu = Icy.getMainInterface().getApplicationMenu(); 3034 else 3035 mainMenu = null; 3036 if (showProgress && !Icy.getMainInterface().isHeadLess()) 3037 loadingFrame = new FileFrame("Loading", null); 3038 else 3039 loadingFrame = null; 3040 3041 try 3042 { 3043 // we want only one group 3044 final SequenceFileGroup group = SequenceFileSticher.groupFiles(importer, cleanNonImageFile(paths), true, 3045 loadingFrame); 3046 // do group loading 3047 result = internalLoadGroup(group, resolution, region, minZ, maxZ, minT, maxT, channel, forceVolatile, 3048 directory, mainMenu, loadingFrame); 3049 // load sequence XML data 3050 if (GeneralPreferences.getSequencePersistence()) 3051 result.loadXMLData(); 3052 } 3053 catch (Throwable t) 3054 { 3055 // just show the error 3056 IcyExceptionHandler.showErrorMessage(t, true); 3057 3058 if (loadingFrame != null) 3059 new FailedAnnounceFrame((t instanceof OutOfMemoryError) ? t.getMessage() 3060 : "Failed to open file(s), see the console output for more details."); 3061 } 3062 finally 3063 { 3064 if (loadingFrame != null) 3065 loadingFrame.close(); 3066 } 3067 3068 return result; 3069 } 3070 3071 /** 3072 * Load a sequence from the specified list of file and returns it.<br> 3073 * The function try to guess image ordering from file name and metadata.<br> 3074 * 3075 * @param paths 3076 * list of file where to load image from 3077 * @param resolution 3078 * Wanted resolution level for the image (use 0 if unsure), useful for large image<br> 3079 * The retrieved image resolution is equal to 3080 * <code>image.resolution / (2^resolution)</code><br> 3081 * So for instance level 0 is the default/full image resolution while level 1 is base 3082 * image 3083 * resolution / 2 and so on... 3084 * @param region 3085 * The 2D region of the image we want to retrieve.<br> 3086 * If set to <code>null</code> then the whole XY plane of the image is returned. 3087 * @param minZ 3088 * the minimum Z position of the image (slice) we want retrieve (inclusive).<br> 3089 * Set to -1 to retrieve the whole stack. 3090 * @param maxZ 3091 * the maximum Z position of the image (slice) we want retrieve (inclusive).<br> 3092 * Set to -1 to retrieve the whole stack. 3093 * @param minT 3094 * the minimum T position of the image (frame) we want retrieve (inclusive).<br> 3095 * Set to -1 to retrieve the whole timelaps. 3096 * @param maxT 3097 * the maximum T position of the image (frame) we want retrieve (inclusive).<br> 3098 * Set to -1 to retrieve the whole timelaps. 3099 * @param channel 3100 * C position of the image (channel) we want retrieve (-1 means all channel). 3101 * @param forceVolatile 3102 * If set to <code>true</code> then image data is forced to volatile (see {@link IcyBufferedImage#isVolatile()}).<br> 3103 * Note that if you don't have enough memory to load the whole Sequence into memory then image data are always made volatile. 3104 * @param directory 3105 * specify is the source is a single complete directory 3106 * @param addToRecent 3107 * If set to true the files list will be traced in recent opened sequence. 3108 * @param showProgress 3109 * Show progression in loading process 3110 * @see #getSequenceFileImporter(String, boolean) 3111 */ 3112 public static Sequence loadSequence(List<String> paths, int resolution, Rectangle region, int minZ, int maxZ, 3113 int minT, int maxT, int channel, boolean forceVolatile, boolean directory, boolean addToRecent, 3114 boolean showProgress) 3115 { 3116 return loadSequence(null, paths, resolution, region, minZ, maxZ, minT, maxT, channel, forceVolatile, directory, 3117 addToRecent, showProgress); 3118 } 3119 3120 /** 3121 * Load a sequence from the specified list of file and returns it.<br> 3122 * The function try to guess image ordering from file name and metadata.<br> 3123 * 3124 * @param paths 3125 * list of file where to load image from 3126 * @param resolution 3127 * Wanted resolution level for the image (use 0 if unsure), useful for large image<br> 3128 * The retrieved image resolution is equal to 3129 * <code>image.resolution / (2^resolution)</code><br> 3130 * So for instance level 0 is the default/full image resolution while level 1 is base 3131 * image 3132 * resolution / 2 and so on... 3133 * @param region 3134 * The 2D region of the image we want to retrieve.<br> 3135 * If set to <code>null</code> then the whole XY plane of the image is returned. 3136 * @param minZ 3137 * the minimum Z position of the image (slice) we want retrieve (inclusive).<br> 3138 * Set to -1 to retrieve the whole stack. 3139 * @param maxZ 3140 * the maximum Z position of the image (slice) we want retrieve (inclusive).<br> 3141 * Set to -1 to retrieve the whole stack. 3142 * @param minT 3143 * the minimum T position of the image (frame) we want retrieve (inclusive).<br> 3144 * Set to -1 to retrieve the whole timelaps. 3145 * @param maxT 3146 * the maximum T position of the image (frame) we want retrieve (inclusive).<br> 3147 * Set to -1 to retrieve the whole timelaps. 3148 * @param channel 3149 * C position of the image (channel) we want retrieve (-1 means all channel). 3150 * @param directory 3151 * specify is the source is a single complete directory 3152 * @param addToRecent 3153 * If set to true the files list will be traced in recent opened sequence. 3154 * @param showProgress 3155 * Show progression in loading process 3156 * @see #getSequenceFileImporter(String, boolean) 3157 */ 3158 public static Sequence loadSequence(List<String> paths, int resolution, Rectangle region, int minZ, int maxZ, 3159 int minT, int maxT, int channel, boolean directory, boolean addToRecent, boolean showProgress) 3160 { 3161 return loadSequence(null, paths, resolution, region, minZ, maxZ, minT, maxT, channel, false, directory, 3162 addToRecent, showProgress); 3163 } 3164 3165 /** 3166 * Loads the specified image files and return them as list of sequence.<br> 3167 * If 'separate' is false the loader try to set images in the same sequence.<br> 3168 * If separate is true each image is loaded in a separate sequence.<br> 3169 * As this method can take sometime, you should not call it from the EDT.<br> 3170 * 3171 * @param importer 3172 * Importer used to open and load images (cannot be <code>null</code> here) except when separate is false 3173 * @param paths 3174 * list of image file to load 3175 * @param series 3176 * Series index to load (for multi series sequence), set to 0 if unsure (default).<br> 3177 * -1 is a special value so it gives a chance to the user to select series to open from a 3178 * series selector dialog. 3179 * @param forceVolatile 3180 * If set to <code>true</code> then image data is forced to volatile (see {@link IcyBufferedImage#isVolatile()}).<br> 3181 * Note that if you don't have enough memory to load the whole Sequence into memory then image data are always made volatile. 3182 * @param separate 3183 * Force image to be loaded in separate sequence (also disable stitching if possible) 3184 * @param autoOrder 3185 * If set to true then images are automatically orderer from their filename. 3186 * @param directory 3187 * Specify is the source is a single complete directory 3188 * @param addToRecent 3189 * If set to true the files list will be traced in recent opened sequence. 3190 * @param showProgress 3191 * Show progression in loading process 3192 */ 3193 static List<Sequence> loadSequences(SequenceFileImporter importer, List<String> paths, int series, 3194 boolean forceVolatile, boolean separate, boolean autoOrder, boolean directory, boolean addToRecent, 3195 boolean showProgress) 3196 { 3197 final List<Sequence> result = new ArrayList<Sequence>(); 3198 3199 // nothing to load 3200 if (paths.size() <= 0) 3201 return result; 3202 3203 final ApplicationMenu mainMenu; 3204 final FileFrame loadingFrame; 3205 3206 if (addToRecent) 3207 mainMenu = Icy.getMainInterface().getApplicationMenu(); 3208 else 3209 mainMenu = null; 3210 if (showProgress && !Icy.getMainInterface().isHeadLess()) 3211 loadingFrame = new FileFrame("Loading", null); 3212 else 3213 loadingFrame = null; 3214 3215 try 3216 { 3217 final List<String> remainingFiles = new ArrayList<String>(paths); 3218 3219 // load each file in a separate sequence 3220 if (separate || (paths.size() <= 1)) 3221 { 3222 if (loadingFrame != null) 3223 { 3224 // each file can contains several image so we use 100 "inter-step" 3225 loadingFrame.setLength(paths.size() * 100d); 3226 loadingFrame.setPosition(0d); 3227 } 3228 3229 // force un-grouping when 'separate' is true 3230 if (separate && (importer instanceof LociImporterPlugin)) 3231 ((LociImporterPlugin) importer).setGroupFiles(false); 3232 3233 // load each file in a separate sequence 3234 for (String path : paths) 3235 { 3236 // load the file 3237 final List<Sequence> sequences = internalLoadSingle(importer, path, series, forceVolatile, 3238 !separate, loadingFrame); 3239 3240 // special case where loading was interrupted 3241 if (sequences == null) 3242 { 3243 // no error 3244 remainingFiles.clear(); 3245 break; 3246 } 3247 3248 if (sequences.size() > 0) 3249 { 3250 // add sequences to result 3251 result.addAll(sequences); 3252 // remove path from remaining 3253 remainingFiles.remove(path); 3254 // add as separate item to recent file list 3255 if (mainMenu != null) 3256 mainMenu.addRecentLoadedFile(new File(FileUtil.getGenericPath(path))); 3257 } 3258 3259 // interrupt loading 3260 if ((loadingFrame != null) && loadingFrame.isCancelRequested()) 3261 { 3262 // no error 3263 remainingFiles.clear(); 3264 break; 3265 } 3266 } 3267 } 3268 // grouped loading 3269 else 3270 { 3271 // get file group 3272 final Collection<SequenceFileGroup> groups = SequenceFileSticher.groupAllFiles(importer, paths, 3273 autoOrder, loadingFrame); 3274 3275 for (SequenceFileGroup group : groups) 3276 { 3277 final Sequence sequence = internalLoadGroup(group, forceVolatile, directory, mainMenu, 3278 loadingFrame); 3279 3280 // loading interrupted ? 3281 if ((loadingFrame != null) && loadingFrame.isCancelRequested()) 3282 { 3283 // no error 3284 remainingFiles.clear(); 3285 break; 3286 } 3287 3288 // remove all paths from the group in remaining list 3289 for (SequencePosition pos : group.positions) 3290 remainingFiles.remove(pos.getPath()); 3291 3292 // add to result 3293 result.add(sequence); 3294 } 3295 3296 // if (loadingFrame != null) 3297 // { 3298 // loadingFrame.setAction("Loading"); 3299 // // each file can contains several image so we use 100 "inter step" 3300 // loadingFrame.setLength(filePositions.size() * 100d); 3301 // loadingFrame.setPosition(0d); 3302 // } 3303 // 3304 // for (FilePosition filePos : filePositions) 3305 // { 3306 // final String path = filePos.path; 3307 // // load the file 3308 // final List<Sequence> sequences = internalLoadSingle(importer, path, series, loadingFrame); 3309 // 3310 // // special case where loading was interrupted 3311 // if (sequences == null) 3312 // { 3313 // // no error 3314 // remainingFiles.clear(); 3315 // break; 3316 // } 3317 // 3318 // final int s = filePos.getS(); 3319 // final int z = filePos.getZ(); 3320 // final int t = filePos.getT(); 3321 // final int c = filePos.getC(); 3322 // boolean concat; 3323 // 3324 // // special case of single result --> try to concatenate to last sequence 3325 // if ((sequences.size() == 1) && !map.isEmpty()) 3326 // { 3327 // final Sequence seq = sequences.get(0); 3328 // final int sizeZ = seq.getSizeZ(); 3329 // final int sizeT = seq.getSizeT(); 3330 // final int sizeC = seq.getSizeC(); 3331 // 3332 // concat = true; 3333 // // concatenation restriction 3334 // if (lastS != s) 3335 // concat = false; 3336 // if ((sizeZ > 1) && (z > 0)) 3337 // concat = false; 3338 // if ((sizeT > 1) && (t > 0)) 3339 // concat = false; 3340 // if ((sizeC > 1) && (c > 0)) 3341 // concat = false; 3342 // 3343 // if (concat) 3344 // { 3345 // // find last sequence for this channel 3346 // final Sequence lastSequence = map.get(Integer.valueOf(c)); 3347 // 3348 // // determine if concatenation is possible 3349 // if ((lastSequence != null) && !lastSequence.isCompatible(seq.getFirstImage())) 3350 // concat = false; 3351 // } 3352 // 3353 // // update series index 3354 // lastS = s; 3355 // } 3356 // else 3357 // concat = false; 3358 // 3359 // // sequence correctly loaded ? 3360 // if (sequences.size() > 0) 3361 // { 3362 // if (concat) 3363 // { 3364 // final Sequence seq = sequences.get(0); 3365 // // find last sequence for this channel 3366 // Sequence lastSequence = map.get(Integer.valueOf(c)); 3367 // 3368 // // concatenate 3369 // lastSequence = concatenateSequence(lastSequence, seq, t > 0, z > 0); 3370 // // store the merged sequence for this channel 3371 // map.put(Integer.valueOf(c), lastSequence); 3372 // } 3373 // else 3374 // { 3375 // // concatenate sequences in map and add it to result list 3376 // addSequences(result, map); 3377 // // if on first channel then put the last sequence result in the map 3378 // if (c == 0) 3379 // map.put(Integer.valueOf(0), sequences.remove(sequences.size() - 1)); 3380 // // and add the rest to the list 3381 // if (sequences.size() > 0) 3382 // result.addAll(sequences); 3383 // } 3384 // 3385 // // remove path from remaining 3386 // remainingFiles.remove(path); 3387 // } 3388 // 3389 // // interrupt loading 3390 // if ((loadingFrame != null) && loadingFrame.isCancelRequested()) 3391 // { 3392 // // no error 3393 // remainingFiles.clear(); 3394 // break; 3395 // } 3396 // } 3397 // 3398 // // concatenate last sequences in map and add it to result list 3399 // addSequences(result, map); 3400 // 3401 // // add as one item to recent file list 3402 // if (mainMenu != null) 3403 // { 3404 // // set only the directory entry 3405 // if (directory) 3406 // mainMenu.addRecentFile(FileUtil.getDirectory(paths.get(0), false)); 3407 // else 3408 // mainMenu.addRecentFile(paths); 3409 // } 3410 } 3411 3412 // TODO: restore colormap --> try to recover colormap 3413 3414 if (remainingFiles.size() > 0) 3415 { 3416 System.err.println("Cannot open the following file(s) (format not supported):"); 3417 for (String path : remainingFiles) 3418 System.err.println(path); 3419 3420 if (loadingFrame != null) 3421 { 3422 new FailedAnnounceFrame( 3423 "Some file(s) could not be opened (format not supported). See the console output for more details."); 3424 } 3425 } 3426 3427 // load sequence XML data 3428 if (GeneralPreferences.getSequencePersistence()) 3429 { 3430 for (Sequence seq : result) 3431 seq.loadXMLData(); 3432 } 3433 } 3434 catch (Throwable t) 3435 { 3436 // just show the error 3437 IcyExceptionHandler.showErrorMessage(t, true); 3438 3439 if (loadingFrame != null) 3440 new FailedAnnounceFrame((t instanceof OutOfMemoryError) ? t.getMessage() 3441 : "Failed to open file(s), see the console output for more details."); 3442 } 3443 finally 3444 { 3445 if (loadingFrame != null) 3446 loadingFrame.close(); 3447 } 3448 3449 return result; 3450 } 3451 3452 /** 3453 * Concatenate the <i>src</i> sequence to the <i>dest</i> one. 3454 */ 3455 static Sequence concatenateSequence(Sequence dest, Sequence src, boolean onT, boolean onZ) 3456 { 3457 if (dest == null) 3458 return src; 3459 if (src == null) 3460 return dest; 3461 3462 final int dst; 3463 final int dsz; 3464 final int sst = src.getSizeT(); 3465 final int ssz = src.getSizeZ(); 3466 3467 if (onT) 3468 { 3469 if (onZ) 3470 { 3471 dst = dest.getSizeT() - 1; 3472 dsz = dest.getSizeZ(dst); 3473 } 3474 else 3475 { 3476 dst = dest.getSizeT(); 3477 dsz = 0; 3478 } 3479 } 3480 else 3481 { 3482 dst = 0; 3483 dsz = onZ ? dest.getSizeZ() : 0; 3484 } 3485 3486 // put 'dest' in update state to avoid useless recalculations 3487 if (!dest.isUpdating()) 3488 dest.beginUpdate(); 3489 3490 for (int t = 0; t < sst; t++) 3491 for (int z = 0; z < ssz; z++) 3492 dest.setImage(t + dst, z + dsz, src.getImage(t, z)); 3493 3494 return dest; 3495 } 3496 3497 static void addSequences(List<Sequence> result, TreeMap<Integer, Sequence> map) 3498 { 3499 if (!map.isEmpty()) 3500 { 3501 // get all sequence from the map orderer by channel 3502 final Collection<Sequence> sequencesC = map.values(); 3503 3504 // remove update state 3505 for (Sequence seq : sequencesC) 3506 if (seq.isUpdating()) 3507 seq.endUpdate(); 3508 3509 final Sequence sequences[] = sequencesC.toArray(new Sequence[sequencesC.size()]); 3510 3511 // several sequences ? 3512 if (sequences.length > 1) 3513 { 3514 // concatenate sequences on C dimension 3515 final Sequence merged = SequenceUtil.concatC(sequences); 3516 // better to keep name from first image 3517 merged.setName(sequences[0].getName()); 3518 // then add the result to the list 3519 result.add(merged); 3520 } 3521 else 3522 result.add(sequences[0]); 3523 3524 // better to not merge the C channel after all 3525 // result.addAll(sequencesC); 3526 3527 // clear the map 3528 map.clear(); 3529 } 3530 } 3531 3532 /** 3533 * <b>Internal use only !</b><br> 3534 * Load image(s) from the given importer and parameters and return the result as a Sequence.<br> 3535 * If <i>loadingFrame</i> is not <code>null</code> then it has 100 steps allocated to the 3536 * loading of current path. 3537 * 3538 * @param importer 3539 * Opened importer used to load the image file (cannot be <code>null</code> here) 3540 * @param metadata 3541 * Metadata of the image 3542 * @param series 3543 * Series index to load (for multi series sequence), set to 0 if unsure (default). 3544 * @param resolution 3545 * Wanted resolution level for the image (use 0 if unsure), useful for large image<br> 3546 * The retrieved image resolution is equal to <code>image.resolution / (2^resolution)</code><br> 3547 * So for instance level 0 is the default/full image resolution while level 1 is base image resolution / 2 3548 * and so on... 3549 * @param region 3550 * The 2D region of the image we want to retrieve (in full image resolution).<br> 3551 * If set to <code>null</code> then the whole XY plane of the image is returned. 3552 * @param minZ 3553 * the minimum Z position of the image (slice) we want retrieve (inclusive).<br> 3554 * Set to -1 to retrieve the whole stack. 3555 * @param maxZ 3556 * the maximum Z position of the image (slice) we want retrieve (inclusive).<br> 3557 * Set to -1 to retrieve the whole stack. 3558 * @param minT 3559 * the minimum T position of the image (frame) we want retrieve (inclusive).<br> 3560 * Set to -1 to retrieve the whole timelaps. 3561 * @param maxT 3562 * the maximum T position of the image (frame) we want retrieve (inclusive).<br> 3563 * Set to -1 to retrieve the whole timelaps. 3564 * @param channel 3565 * C position of the image (channel) we want retrieve (-1 means all channel). 3566 * @param forceVolatile 3567 * If set to <code>true</code> then image data is forced to volatile (see {@link IcyBufferedImage#isVolatile()}).<br> 3568 * Note that if you don't have enough memory to load the whole Sequence into memory then image data are always made volatile. 3569 * @param loadingFrame 3570 * the loading frame used to display progress of the operation (can be null).<br> 3571 * Caller should allocate 100 positions for the internal single load process. 3572 * @return the Sequence object or <code>null</code> 3573 */ 3574 public static Sequence internalLoadSingle(SequenceIdImporter importer, OMEXMLMetadata metadata, int series, 3575 int resolution, Rectangle region, int minZ, int maxZ, int minT, int maxT, int channel, 3576 boolean forceVolatile, FileFrame loadingFrame) 3577 throws IOException, UnsupportedFormatException, OutOfMemoryError 3578 { 3579 final int imgSizeX = MetaDataUtil.getSizeX(metadata, series); 3580 final int imgSizeY = MetaDataUtil.getSizeY(metadata, series); 3581 3582 final Rectangle adjRegion; 3583 3584 if (region != null) 3585 adjRegion = new Rectangle(0, 0, imgSizeX, imgSizeY).intersection(region); 3586 else 3587 adjRegion = null; 3588 3589 final int sizeX = (adjRegion == null) ? imgSizeX : adjRegion.width; 3590 final int sizeY = (adjRegion == null) ? imgSizeY : adjRegion.height; 3591 final int sizeZ = MetaDataUtil.getSizeZ(metadata, series); 3592 final int sizeT = MetaDataUtil.getSizeT(metadata, series); 3593 final int sizeC = MetaDataUtil.getSizeC(metadata, series); 3594 final DataType dataType = MetaDataUtil.getDataType(metadata, series); 3595 3596 final int adjMinZ, adjMaxZ; 3597 final int adjMinT, adjMaxT; 3598 3599 if (minZ < 0) 3600 adjMinZ = 0; 3601 else 3602 adjMinZ = Math.min(minZ, sizeZ); 3603 if (maxZ < 0) 3604 adjMaxZ = sizeZ - 1; 3605 else 3606 adjMaxZ = Math.min(maxZ, sizeZ - 1); 3607 if (minT < 0) 3608 adjMinT = 0; 3609 else 3610 adjMinT = Math.min(minT, sizeT); 3611 if (maxT < 0) 3612 adjMaxT = sizeT - 1; 3613 else 3614 adjMaxT = Math.min(maxT, sizeT - 1); 3615 3616 // we want volatile image 3617 boolean volatileImage = forceVolatile || GeneralPreferences.getVirtualMode(); 3618 3619 try 3620 { 3621 // volatile image ? --> we just need to check the plane size 3622 if (volatileImage) 3623 checkOpeningPlane(resolution, sizeX, sizeY, 3624 " Try to open a sub resolution or sub part of the image only."); 3625 else 3626 // check that can open the image 3627 checkOpening(resolution, sizeX, sizeY, (channel == -1) ? sizeC : 1, (adjMaxZ - adjMinZ) + 1, 3628 (adjMaxT - adjMinT) + 1, dataType, 3629 " Try to open a sub resolution or sub part of the image only."); 3630 } 3631 catch (OutOfMemoryError e) 3632 { 3633 // force volatile image if we don't have enough memory to open the image 3634 volatileImage = true; 3635 } 3636 3637 // create result sequence with desired series metadata 3638 final Sequence result = new Sequence(OMEUtil.createOMEXMLMetadata(metadata, series)); 3639 3640 // setup sequence properties and metadata from the opening setting 3641 setupSequence(result, importer, MetaDataUtil.getNumSeries(metadata) > 1, series, adjRegion, resolution, sizeZ, 3642 sizeT, sizeC, adjMinZ, adjMaxZ, adjMinT, adjMaxT, channel); 3643 3644 // number of image to process 3645 final int numImage = ((adjMaxZ - adjMinZ) + 1) * ((adjMaxT - adjMinT) + 1); 3646 3647 if (numImage > 0) 3648 { 3649 // set local length for loader frame 3650 final double progressStep = 100d / numImage; 3651 double progress = 0d; 3652 boolean first = true; 3653 final int resDivisor = Math.max(1, (int) Math.pow(2, resolution)); 3654 final int adjSizeX = sizeX / resDivisor; 3655 final int adjSizeY = sizeY / resDivisor; 3656 3657 if (loadingFrame != null) 3658 progress = loadingFrame.getPosition(); 3659 3660 result.beginUpdate(); 3661 try 3662 { 3663 for (int t = adjMinT; t <= adjMaxT; t++) 3664 { 3665 for (int z = adjMinZ; z <= adjMaxZ; z++) 3666 { 3667 if (loadingFrame != null) 3668 { 3669 // cancel requested ? --> stop loading here... 3670 if (loadingFrame.isCancelRequested()) 3671 return result; 3672 3673 // special group importer ? --> use internal file path 3674 if (importer instanceof SequenceFileGroupImporter) 3675 loadingFrame.setFilename(((SequenceFileGroupImporter) importer).getPath(z, t, 3676 (channel != -1) ? channel : 0)); 3677 } 3678 3679 final IcyBufferedImage image; 3680 3681 // need to load one image at least to get the colormap information (stored in image colormodel) 3682 if (first) 3683 { 3684 // load image now 3685 if (channel == -1) 3686 image = importer.getImage(series, resolution, adjRegion, z, t); 3687 else 3688 image = importer.getImage(series, resolution, adjRegion, z, t, channel); 3689 3690 // don't forget to set volatile state 3691 image.setVolatile(volatileImage); 3692 // first image has been loaded now 3693 first = false; 3694 } 3695 else 3696 // use empty image for now (lazy loading) 3697 image = new IcyBufferedImage(adjSizeX, adjSizeY, sizeC, dataType, volatileImage); 3698 3699 // set image source information for delayed image data loading 3700 image.setImageSourceInfo(importer, series, resolution, adjRegion, t, z, channel); 3701 // set image into the sequence 3702 result.setImage(t - adjMinT, z - adjMinZ, image); 3703 3704 progress += progressStep; 3705 3706 // notify progress to loader frame 3707 if (loadingFrame != null) 3708 loadingFrame.setPosition(progress); 3709 } 3710 } 3711 } 3712 finally 3713 { 3714 result.endUpdate(); 3715 } 3716 } 3717 3718 return result; 3719 } 3720 3721 /** 3722 * <b>Internal use only !</b><br> 3723 * Load a single file and return result as Sequence list (for multi series).<br> 3724 * If <i>loadingFrame</i> is not <code>null</code> then it has 100 steps allocated to the 3725 * loading of current path. 3726 * 3727 * @param importer 3728 * Importer to use to open and load images (cannot be <code>null</code> here) 3729 * @param path 3730 * image file to load 3731 * @param series 3732 * Series index to load (for multi series sequence), set to 0 if unsure (default).<br> 3733 * -1 is a special value so it gives a chance to the user to select series to open from a 3734 * series selector dialog. 3735 * @param forceVolatile 3736 * If set to <code>true</code> then image data is forced to volatile (see {@link IcyBufferedImage#isVolatile()}).<br> 3737 * Note that if you don't have enough memory to load the whole Sequence into memory then image data are always made volatile. 3738 * @param groupSeries 3739 * Enable series grouping into the same sequence (appended in T dimension), only meaningful if <code>series</code> = -1 3740 * @param loadingFrame 3741 * the loading frame used to display progress of the operation (can be null) 3742 * @throws IOException 3743 */ 3744 static List<Sequence> internalLoadSingle(SequenceFileImporter importer, String path, int series, 3745 boolean forceVolatile, boolean groupSeries, FileFrame loadingFrame) 3746 throws IOException, UnsupportedFormatException, OutOfMemoryError 3747 { 3748 final double endStep; 3749 3750 if (loadingFrame != null) 3751 { 3752 loadingFrame.setFilename(path); 3753 // 100 step reserved to load this image 3754 endStep = loadingFrame.getPosition() + 100d; 3755 } 3756 else 3757 endStep = 0d; 3758 3759 final List<Sequence> result = new ArrayList<Sequence>(); 3760 3761 try 3762 { 3763 // prepare image loading for this file 3764 if (!importer.open(path, 0)) 3765 throw new UnsupportedFormatException( 3766 "Image file '" + path + "' is not supported by " + importer.toString() + " importer."); 3767 3768 // get metadata 3769 final OMEXMLMetadata meta = importer.getOMEXMLMetaData(); 3770 // clean the metadata 3771 MetaDataUtil.clean(meta); 3772 3773 // series selection 3774 int selectedSeries[]; 3775 3776 // give the opportunity to select the series(s) to open ? 3777 if (series == -1) 3778 { 3779 try 3780 { 3781 // try to group series 3782 if (groupSeries) 3783 selectedSeries = groupSeries(meta); 3784 // series selection (create a new importer instance as selectSerie(..) does async processes) 3785 else 3786 selectedSeries = selectSeries(cloneSequenceFileImporter(importer), path, meta, 0, false); 3787 } 3788 catch (Throwable t) 3789 { 3790 IcyExceptionHandler.showErrorMessage(t, true, true); 3791 System.err.print("Opening first series by default..."); 3792 selectedSeries = new int[] {0}; 3793 } 3794 3795 // user cancelled action in the series selection ? null = cancel 3796 if (selectedSeries.length == 0) 3797 return null; 3798 } 3799 else 3800 selectedSeries = new int[] {series}; 3801 3802 // add sequence to result 3803 for (int s : selectedSeries) 3804 { 3805 final Sequence seq = internalLoadSingle(importer, meta, s, 0, null, -1, -1, -1, -1, -1, forceVolatile, 3806 loadingFrame); 3807 3808 // group series together 3809 if ((result.size() > 0) && groupSeries) 3810 concatenateSequence(result.get(0), seq, true, false); 3811 else 3812 // just add to list 3813 result.add(seq); 3814 } 3815 3816 // grouped series ? 3817 if (groupSeries && (selectedSeries.length > 1)) 3818 { 3819 final Sequence seq = result.get(0); 3820 final String imageName = MetaDataUtil.getName(meta, 0); 3821 3822 // reset to metadata name 3823 if (StringUtil.isEmpty(imageName)) 3824 seq.setName(Sequence.DEFAULT_NAME + StringUtil.toString(seq.getId(), 3)); 3825 else 3826 seq.setName(imageName); 3827 } 3828 3829 // Don't close importer on success ! we want to keep it inside the sequence. 3830 // We will close it when finalizing the sequence... 3831 // importer.close(); 3832 } 3833 catch (Throwable t) 3834 { 3835 // close importer when error happen (not stored in Sequence so we need to close it manually) 3836 importer.close(); 3837 3838 // the importer is supposed to support this file --> re throw the exception 3839 if (importer.acceptFile(path)) 3840 throw t; 3841 } 3842 finally 3843 { 3844 if (loadingFrame != null) 3845 loadingFrame.setPosition(endStep); 3846 } 3847 3848 return result; 3849 } 3850 3851 /** 3852 * <b>Internal use only !</b><br> 3853 * Load the specified image file group and return a single Sequence from it.<br> 3854 * As this method can take sometime, you should not call it from the EDT.<br> 3855 * 3856 * @param group 3857 * image path group we want to load 3858 * @param resolution 3859 * Wanted resolution level for the image (use 0 if unsure), useful for large image<br> 3860 * The retrieved image resolution is equal to <code>image.resolution / (2^resolution)</code><br> 3861 * So for instance level 0 is the default/full image resolution while level 1 is base image resolution / 2 3862 * and so on... 3863 * @param region 3864 * The 2D region of the image we want to retrieve (in full image resolution).<br> 3865 * If set to <code>null</code> then the whole XY plane of the image is returned. 3866 * @param minZ 3867 * the minimum Z position of the image (slice) we want retrieve (inclusive).<br> 3868 * Set to -1 to retrieve the whole stack. 3869 * @param maxZ 3870 * the maximum Z position of the image (slice) we want retrieve (inclusive).<br> 3871 * Set to -1 to retrieve the whole stack. 3872 * @param minT 3873 * the minimum T position of the image (frame) we want retrieve (inclusive).<br> 3874 * Set to -1 to retrieve the whole timelaps. 3875 * @param maxT 3876 * the maximum T position of the image (frame) we want retrieve (inclusive).<br> 3877 * Set to -1 to retrieve the whole timelaps. 3878 * @param channel 3879 * C position of the image (channel) we want retrieve (-1 means all channel). 3880 * @param forceVolatile 3881 * If set to <code>true</code> then image data is forced to volatile (see {@link IcyBufferedImage#isVolatile()}).<br> 3882 * Note that if you don't have enough memory to load the whole Sequence into memory then image data are always made volatile. 3883 * @param directory 3884 * specify is the source is a single complete directory 3885 * @param mainMenu 3886 * menu object used to store recent file (can be null) 3887 * @param loadingFrame 3888 * the loading frame used to cancel / display progress of the operation (can be null) 3889 * @Return the loaded Sequence (or <code>null<code> if loading was canceled) 3890 */ 3891 static Sequence internalLoadGroup(SequenceFileGroup group, int resolution, Rectangle region, int minZ, int maxZ, 3892 int minT, int maxT, int channel, boolean forceVolatile, boolean directory, ApplicationMenu mainMenu, 3893 FileFrame loadingFrame) throws UnsupportedFormatException, IOException 3894 { 3895 final double endStep; 3896 Sequence result; 3897 3898 if (loadingFrame != null) 3899 { 3900 loadingFrame.setFilename(group.ident.base); 3901 loadingFrame.setAction("Loading image group"); 3902 // 100 step reserved to load this image 3903 endStep = loadingFrame.getPosition() + 100d; 3904 } 3905 else 3906 endStep = 0d; 3907 3908 // use the special group importer 3909 final SequenceFileGroupImporter groupImporter = new SequenceFileGroupImporter(); 3910 3911 try 3912 { 3913 // open image group (can't fail) 3914 groupImporter.open(group, 0); 3915 3916 // get metadata 3917 final OMEXMLMetadata meta = groupImporter.getOMEXMLMetaData(); 3918 // clean the metadata 3919 MetaDataUtil.clean(meta); 3920 3921 result = internalLoadSingle(groupImporter, meta, 0, resolution, region, minZ, maxZ, minT, maxT, channel, 3922 forceVolatile, loadingFrame); 3923 3924 // directory load ? 3925 if (directory) 3926 { 3927 // get directory without last separator 3928 final String fileDir = FileUtil.getDirectory(group.ident.base, false); 3929 3930 // set sequence name and filename to directory 3931 result.setName(FileUtil.getFileName(fileDir, false)); 3932 result.setFilename(fileDir); 3933 3934 // add as one item to recent file list 3935 if (mainMenu != null) 3936 mainMenu.addRecentFile(fileDir); 3937 } 3938 // normal file loading 3939 else 3940 { 3941 if (mainMenu != null) 3942 mainMenu.addRecentFile(group.getPaths()); 3943 } 3944 3945 // Don't close importer on success ! we want to keep it inside the sequence. 3946 // We will close it when finalizing the sequence... 3947 // groupImporter.close(); 3948 } 3949 catch (Throwable t) 3950 { 3951 // close importer when error happen (not stored in Sequence so we need to close it manually) 3952 groupImporter.close(); 3953 3954 // re throw 3955 throw t; 3956 } 3957 finally 3958 { 3959 if (loadingFrame != null) 3960 loadingFrame.setPosition(endStep); 3961 } 3962 3963 return result; 3964 } 3965 3966 /** 3967 * <b>Internal use only !</b><br> 3968 * Load the specified image file group and return a single Sequence from it.<br> 3969 * As this method can take sometime, you should not call it from the EDT.<br> 3970 * 3971 * @param group 3972 * image path group we want to load 3973 * @param directory 3974 * specify is the source is a single complete directory 3975 * @param mainMenu 3976 * menu object used to store recent file (can be null) 3977 * @param forceVolatile 3978 * If set to <code>true</code> then image data is forced to volatile (see {@link IcyBufferedImage#isVolatile()}).<br> 3979 * Note that if you don't have enough memory to load the whole Sequence into memory then image data are always made volatile. 3980 * @param loadingFrame 3981 * the loading frame used to cancel / display progress of the operation (can be null) 3982 * @Return the loaded Sequence (or <code>null<code> if loading was canceled) 3983 */ 3984 static Sequence internalLoadGroup(SequenceFileGroup group, boolean forceVolatile, boolean directory, 3985 ApplicationMenu mainMenu, FileFrame loadingFrame) throws UnsupportedFormatException, IOException 3986 { 3987 return internalLoadGroup(group, 0, null, -1, -1, -1, -1, -1, forceVolatile, directory, mainMenu, loadingFrame); 3988 } 3989 3990 public static String getSequenceName(Sequence sequence, String path, boolean multiSerie, int series) 3991 { 3992 // default name 3993 String name = FileUtil.getFileName(path, false); 3994 3995 // default name used --> use better name 3996 if (sequence.isDefaultName()) 3997 { 3998 // multi series image --> add series info 3999 if (multiSerie) 4000 name += " - series " + StringUtil.toString(series); 4001 } 4002 else 4003 { 4004 // multi series image --> adjust name to keep file name info 4005 if (multiSerie) 4006 name += " - " + sequence.getName(); 4007 else 4008 // just use sequence metadata name 4009 name = sequence.getName(); 4010 } 4011 4012 return name; 4013 } 4014 4015 /** 4016 * Setup the specified sequence object given the different opening informations 4017 * 4018 * @param sequence 4019 * sequence to adjust properties 4020 * @param importer 4021 * image path 4022 * @param multiSerie 4023 * <code>true</code> if this Sequence comes from a multi series dataset 4024 * @param series 4025 * series index 4026 * @param region 4027 * Rectangle region we want to load from original image (full resolution) 4028 * @param resolution 4029 * Resolution level to open 4030 * @param sizeZ 4031 * original image sizeZ 4032 * @param sizeT 4033 * original image sizeT 4034 * @param sizeC 4035 * original image sizeC 4036 * @param minZ 4037 * minimum Z slice wanted 4038 * @param maxZ 4039 * maximum Z slice wanted 4040 * @param minT 4041 * minimum T frame wanted 4042 * @param maxT 4043 * maximum T frame wanted 4044 * @param channel 4045 * channel we want to load (-1 for all) 4046 */ 4047 public static void setupSequence(Sequence sequence, SequenceIdImporter importer, boolean multiSerie, int series, 4048 Rectangle region, int resolution, int sizeZ, int sizeT, int sizeC, int minZ, int maxZ, int minT, int maxT, 4049 int channel) 4050 { 4051 final String path = FileUtil.getGenericPath(importer.getOpened()); 4052 // get default name 4053 String name = getSequenceName(sequence, path, multiSerie, series); 4054 4055 // original pixel size 4056 final double psx = sequence.getPixelSizeX(); 4057 final double psy = sequence.getPixelSizeY(); 4058 final double psz = sequence.getPixelSizeZ(); 4059 // original position 4060 final double posX = sequence.getPositionX(); 4061 final double posY = sequence.getPositionY(); 4062 final double posZ = sequence.getPositionZ(); 4063 // original time stamp (in ms) 4064 final long posT = sequence.getPositionT(); 4065 4066 // get sequence metadata 4067 final OMEXMLMetadata metadata = sequence.getOMEXMLMetadata(); 4068 4069 // cleanup planes 4070 for (int t = sizeT - 1; t >= 0; t--) 4071 { 4072 for (int z = sizeZ - 1; z >= 0; z--) 4073 { 4074 for (int c = 0; c < sizeC; c++) 4075 { 4076 if ((t < minT) || (t > maxT) || (z < minZ) || (z > maxZ)) 4077 MetaDataUtil.removePlane(metadata, 0, maxT, maxZ, c); 4078 } 4079 } 4080 } 4081 4082 // single channel extraction ? 4083 if (channel != -1) 4084 { 4085 // adjust origin channel 4086 sequence.setOriginChannel(channel); 4087 4088 // clean channels and remaining planes 4089 for (int c = 0; c < sizeC; c++) 4090 { 4091 if (c != channel) 4092 { 4093 MetaDataUtil.removePlanes(metadata, 0, -1, -1, c); 4094 MetaDataUtil.removeChannel(metadata, 0, c); 4095 } 4096 } 4097 } 4098 4099 // adjust position X,Y 4100 if (region != null) 4101 { 4102 // set origin region 4103 sequence.setOriginXYRegion(region); 4104 // adjust position 4105 sequence.setPositionX(posX + (region.x * psx)); 4106 sequence.setPositionY(posY + (region.y * psy)); 4107 } 4108 // adjust position Z 4109 if (minZ > 0) 4110 sequence.setPositionZ(posZ + (minZ * psz)); 4111 // adjust position T 4112 if (minT > 0) 4113 sequence.setTimeStamp( 4114 posT + (long) (sequence.getPositionTOffset(minT, minZ, Math.max(0, channel)) * 1000d)); 4115 4116 // using sub resolution ? 4117 if (resolution > 0) 4118 { 4119 final int divider = (int) Math.pow(2, resolution); 4120 4121 // adjust origin resolution 4122 sequence.setOriginResolution(resolution); 4123 // adjust pixel size 4124 sequence.setPixelSizeX(psx * divider); 4125 sequence.setPixelSizeY(psy * divider); 4126 4127 // adjust name 4128 name += " - binning=" + StringUtil.toString(divider); 4129 } 4130 4131 // adjust Z Range 4132 if ((minZ > 0) || (maxZ < (sizeZ - 1))) 4133 { 4134 sequence.setOriginZMin(minZ); 4135 sequence.setOriginZMax(maxZ); 4136 4137 // adjust name 4138 if (minZ == maxZ) 4139 name += " - Z" + StringUtil.toString(minZ); 4140 else 4141 name += " - Z[" + StringUtil.toString(minZ) + "-" + StringUtil.toString(maxZ) + "]"; 4142 } 4143 // adjust T Range 4144 if ((minT > 0) || (maxT < (sizeT - 1))) 4145 { 4146 sequence.setOriginTMin(minT); 4147 sequence.setOriginTMax(maxT); 4148 4149 // adjust name 4150 if (minT == maxT) 4151 name += " - T" + StringUtil.toString(minT); 4152 else 4153 name += " - T[" + StringUtil.toString(minT) + "-" + StringUtil.toString(maxT) + "]"; 4154 } 4155 4156 // need to adjust name for channel ? 4157 if (channel != -1) 4158 name += " - C" + StringUtil.toString(channel); 4159 4160 // set final name and filename 4161 sequence.setName(name); 4162 sequence.setFilename(path); 4163 4164 // set importer (for caching / delayed loading...) 4165 sequence.setImageProvider(importer); 4166 } 4167 4168 /** 4169 * Display the Series Selection frame for the given image and returns selected series(s).<br> 4170 * Returns a 0 length array if user canceled series selection. 4171 */ 4172 public static int[] selectSeries(final SequenceIdImporter importer, final String path, final OMEXMLMetadata meta, 4173 int defaultSerie, final boolean singleSelection) throws UnsupportedFormatException, IOException 4174 { 4175 final int serieCount = MetaDataUtil.getNumSeries(meta); 4176 final int[] tmp = new int[serieCount + 1]; 4177 4178 if (serieCount > 0) 4179 { 4180 tmp[0] = 1; 4181 4182 // multi series, display selection dialog 4183 if (serieCount > 1) 4184 { 4185 // allow user to select series to open 4186 if (!Icy.getMainInterface().isHeadLess()) 4187 { 4188 final Exception[] exception = new Exception[1]; 4189 exception[0] = null; 4190 4191 // use invokeNow carefully ! 4192 ThreadUtil.invokeNow(new Runnable() 4193 { 4194 @Override 4195 public void run() 4196 { 4197 try 4198 { 4199 final int[] series = new SeriesSelectionDialog(importer, path, meta, singleSelection) 4200 .getSelectedSeries(); 4201 // get result 4202 tmp[0] = series.length; 4203 System.arraycopy(series, 0, tmp, 1, series.length); 4204 } 4205 catch (Exception e) 4206 { 4207 exception[0] = e; 4208 } 4209 } 4210 }); 4211 4212 // propagate exception 4213 if (exception[0] instanceof UnsupportedFormatException) 4214 throw (UnsupportedFormatException) exception[0]; 4215 else if (exception[0] instanceof IOException) 4216 throw (IOException) exception[0]; 4217 } 4218 // use the pre selected series 4219 else 4220 tmp[1] = defaultSerie; 4221 } 4222 // only 1 series so open it 4223 else 4224 tmp[1] = 0; 4225 } 4226 4227 // copy back result to adjusted array 4228 final int[] result = new int[tmp[0]]; 4229 4230 System.arraycopy(tmp, 1, result, 0, result.length); 4231 4232 return result; 4233 } 4234 4235 /** 4236 * Try to group series with similar images properties (XYZC dimension) starting from first image and return the list of grouped series index. 4237 */ 4238 public static int[] groupSeries(OMEXMLMetadata meta) 4239 { 4240 final List<Integer> result = new ArrayList<Integer>(); 4241 4242 final int sizeS = MetaDataUtil.getNumSeries(meta); 4243 if (sizeS > 0) 4244 { 4245 final int sizeX = MetaDataUtil.getSizeX(meta, 0); 4246 final int sizeY = MetaDataUtil.getSizeY(meta, 0); 4247 final int sizeZ = MetaDataUtil.getSizeZ(meta, 0); 4248 final int sizeC = MetaDataUtil.getSizeC(meta, 0); 4249 final DataType dataType = MetaDataUtil.getDataType(meta, 0); 4250 4251 result.add(Integer.valueOf(0)); 4252 4253 // we can group series only if size T == 1 (as we group on T dimension) 4254 if (MetaDataUtil.getSizeT(meta, 0) == 1) 4255 { 4256 for (int s = 1; s < sizeS; s++) 4257 { 4258 final int sx = MetaDataUtil.getSizeX(meta, s); 4259 final int sy = MetaDataUtil.getSizeY(meta, s); 4260 final int sz = MetaDataUtil.getSizeZ(meta, s); 4261 final int sc = MetaDataUtil.getSizeC(meta, s); 4262 final DataType dt = MetaDataUtil.getDataType(meta, s); 4263 4264 if ((sx == sizeX) && (sy == sizeY) && (sz == sizeZ) && (sc == sizeC) && (dt == dataType) 4265 && (MetaDataUtil.getSizeT(meta, s) == 1)) 4266 result.add(Integer.valueOf(s)); 4267 } 4268 } 4269 } 4270 4271 final int[] ires = new int[result.size()]; 4272 4273 for (int i = 0; i < ires.length; i++) 4274 ires[i] = result.get(i).intValue(); 4275 4276 return ires; 4277 } 4278 4279 /** 4280 * Display the Series Selection frame for the given image and returns selected series(s).<br> 4281 * Returns a 0 length array if user canceled series selection. 4282 */ 4283 public static int[] selectSeries(final SequenceFileImporter importer, final String path, final OMEXMLMetadata meta, 4284 int defaultSerie, final boolean singleSelection) throws UnsupportedFormatException, IOException 4285 { 4286 return selectSeries((SequenceIdImporter) importer, path, meta, defaultSerie, singleSelection); 4287 } 4288 4289 /** 4290 * Display the Series Selection frame for the given image and returns selected series(s).<br> 4291 * Returns a 0 length array if user canceled series selection. 4292 */ 4293 public static int[] selectSeries(final SequenceFileImporter importer, final String path, 4294 final OMEXMLMetadataImpl meta, int defaultSerie, boolean singleSelection) 4295 throws UnsupportedFormatException, IOException 4296 { 4297 return selectSeries(importer, path, (OMEXMLMetadata) meta, defaultSerie, singleSelection); 4298 } 4299 4300 /** 4301 * Display the Series Selection frame for the given image and return the selected series (single selection).<br> 4302 * Returns <code>-1</code> if user canceled series selection. 4303 */ 4304 public static int selectSerie(final SequenceFileImporter importer, final String path, final OMEXMLMetadata meta, 4305 int defaultSerie) throws UnsupportedFormatException, IOException 4306 { 4307 final int selected[] = selectSeries(importer, path, meta, defaultSerie, true); 4308 4309 if (selected.length > 0) 4310 return selected[0]; 4311 4312 return -1; 4313 } 4314 4315 /** 4316 * @deprecated Use {@link #selectSerie(SequenceFileImporter, String, OMEXMLMetadata, int)} instead 4317 */ 4318 @Deprecated 4319 public static int selectSerie(final SequenceFileImporter importer, final String path, final OMEXMLMetadataImpl meta, 4320 int defaultSerie) throws UnsupportedFormatException, IOException 4321 { 4322 return selectSerie(importer, path, (OMEXMLMetadata) meta, defaultSerie); 4323 } 4324 4325 static List<String> explode(List<String> paths) 4326 { 4327 return FileUtil.toPaths(FileUtil.explode(FileUtil.toFiles(paths), null, true, false)); 4328 } 4329 4330 /** 4331 * Remove invalid image files from the list of files 4332 */ 4333 public static List<String> cleanNonImageFile(List<String> paths) 4334 { 4335 final List<String> result = new ArrayList<String>(); 4336 4337 // extensions based exclusion 4338 for (String path : paths) 4339 { 4340 // no image file or XML persistence --> ignore 4341 if (canDiscardImageFile(path)) 4342 continue; 4343 4344 // XML file ? 4345 if (FileUtil.getFileExtension(path, false).toLowerCase().equals(XMLUtil.FILE_EXTENSION)) 4346 { 4347 // ignore persistence files 4348 if (SequencePersistent.isValidXMLPersitence(path)) 4349 continue; 4350 } 4351 4352 result.add(path); 4353 } 4354 4355 return result; 4356 } 4357 4358 /** 4359 * @deprecated Use {@link SequenceFileSticher#groupAllFiles(SequenceFileImporter, Collection, boolean, FileFrame)} instead 4360 */ 4361 @Deprecated 4362 public static List<FilePosition> getFilePositions(List<String> paths, boolean dimOrder, FileFrame loadingFrame) 4363 { 4364 final List<FilePosition> result = new ArrayList<FilePosition>(paths.size()); 4365 4366 // use new path grouper method 4367 final Collection<SequenceFileGroup> groups = SequenceFileSticher.groupAllFiles(null, paths, dimOrder, 4368 loadingFrame); 4369 4370 // just build FilePosition from contained SequencePosition 4371 for (SequenceFileGroup group : groups) 4372 { 4373 final SequenceIdent ident = group.ident; 4374 4375 for (SequencePosition pos : group.positions) 4376 result.add(new FilePosition(pos.getPath(), ident.base, 0, pos.getIndexT(), pos.getIndexZ(), 4377 pos.getIndexC())); 4378 } 4379 4380 return result; 4381 } 4382 4383 /** 4384 * @deprecated Use {@link SequenceFileSticher#groupAllFiles(SequenceFileImporter, Collection, boolean, FileFrame)} instead 4385 */ 4386 @Deprecated 4387 public static List<FilePosition> getFilePositions(List<String> paths, boolean dimOrder) 4388 { 4389 return getFilePositions(paths, dimOrder, null); 4390 } 4391}