Changeset 4089


Ignore:
Timestamp:
06/02/08 14:38:02 (12 years ago)
Author:
curtis
Message:

Rewritten OME-TIFF reader:

  • Works with the latest OME-TIFF specification.
  • Handles UUIDs, but does not search for constituent files when FileName attributes are missing yet.
  • May or may not work with OME server; needs testing.
File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/loci/formats/in/OMETiffReader.java

    r4056 r4089  
    88 
    99This program is free software; you can redistribute it and/or modify 
    10 it under the terms of the GNU Library General Public License as published by 
     10it under the terms of the GNU General Public License as published by 
    1111the Free Software Foundation; either version 3 of the License, or 
    1212(at your option) any later version. 
     
    1515but WITHOUT ANY WARRANTY; without even the implied warranty of 
    1616MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
    17 GNU Library General Public License for more details. 
    18  
    19 You should have received a copy of the GNU Library General Public License 
     17GNU General Public License for more details. 
     18 
     19You should have received a copy of the GNU General Public License 
    2020along with this program; if not, write to the Free Software 
    2121Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
     
    2424package loci.formats.in; 
    2525 
    26 import java.io.*; 
     26import java.io.File; 
     27import java.io.IOException; 
    2728import java.util.*; 
    28 import javax.xml.parsers.*; 
    2929import loci.formats.*; 
    30 import loci.formats.meta.MetadataStore; 
    31 import org.xml.sax.*; 
    32 import org.xml.sax.helpers.DefaultHandler; 
     30import loci.formats.meta.MetadataRetrieve; 
    3331 
    3432/** 
     
    4139 * <a href="https://skyking.microscopy.wisc.edu/svn/java/trunk/loci/formats/in/OMETiffReader.java">SVN</a></dd></dl> 
    4240 */ 
    43 public class OMETiffReader extends BaseTiffReader { 
    44  
    45   // -- Constants -- 
    46  
    47   /** Factory for generating SAX parsers. */ 
    48   public static final SAXParserFactory SAX_FACTORY = 
    49     SAXParserFactory.newInstance(); 
     41public class OMETiffReader extends FormatReader { 
    5042 
    5143  // -- Fields -- 
    5244 
     45  /** Mapping from series and plane numbers to files and IFD entries. */ 
     46  protected OMETiffPlane[][] info; // dimensioned [numSeries][numPlanes] 
     47 
    5348  /** List of used files. */ 
    54   private String[] usedFiles; 
    55  
    56   /** List of Image IDs. */ 
    57   private Vector imageIDs; 
    58  
    59   /** List of Pixels IDs. */ 
    60   private Vector pixelsIDs; 
    61  
    62   /** List of TiffData UUIDs. */ 
    63   private Vector planeUUIDs; 
    64  
    65   /** List of file UUIDs. */ 
    66   private Vector fileUUIDs; 
    67  
    68   private Vector tempIfdMap, tempFileMap, tempIfdCount; 
    69   private int currentFile, currentSeries, seriesCount; 
    70   private int[] numIFDs; 
    71   private int[][] ifdMap, fileMap; 
    72   private boolean uuids, lsids, isWiscScan; 
    73   private Hashtable[][] usedIFDs; 
    74   private Hashtable keyMetadata, temporaryKeyMetadata; 
    75   private String directory; 
     49  protected String[] used; 
    7650 
    7751  // -- Constructor -- 
    7852 
     53  /** Constructs a new OME-TIFF reader. */ 
    7954  public OMETiffReader() { 
    80     super("OME-TIFF", new String[] {"tif", "tiff"}); 
    81     blockCheckLen = 1048576; 
    82     suffixSufficient = false; 
    83   } 
    84  
    85   // -- Internal BaseTiffReader API methods -- 
    86  
    87   /* @see BaseTiffReader#initStandardMetadata() */ 
    88   protected void initStandardMetadata() throws FormatException, IOException { 
    89     super.initStandardMetadata(); 
    90  
    91     keyMetadata = new Hashtable(); 
    92     temporaryKeyMetadata = new Hashtable(); 
    93  
    94     OMETiffHandler handler = new OMETiffHandler(false); 
    95     String comment = DataTools.sanitizeXML(TiffTools.getComment(ifds[0])); 
    96  
    97     currentSeries = -1; 
    98     seriesCount = 0; 
    99     imageIDs = null; 
    100     pixelsIDs = null; 
    101     planeUUIDs = null; 
    102     fileUUIDs = null; 
    103     lsids = true; 
    104  
    105     tempIfdMap = new Vector(); 
    106     tempFileMap = new Vector(); 
    107     tempIfdCount = new Vector(); 
    108  
    109     Location l = new Location(currentId).getAbsoluteFile().getParentFile(); 
    110     directory = l.getAbsolutePath(); 
    111     if (!directory.endsWith(File.separator)) directory += File.separator; 
    112  
    113     try { 
    114       SAXParser parser = SAX_FACTORY.newSAXParser(); 
    115       parser.parse(new ByteArrayInputStream(comment.getBytes()), handler); 
    116     } 
    117     catch (ParserConfigurationException exc) { 
    118       throw new FormatException(exc); 
    119     } 
    120     catch (SAXException exc) { 
    121       throw new FormatException(exc); 
    122     } 
    123  
    124     // MAJOR HACK : check for OME-XML in the comment of the second IFD 
    125     // There is a version of WiscScan which writes OME-XML to every IFD, 
    126     // but with SizeZ and SizeT equal to 1. 
    127  
    128     String s = null; 
    129     if (ifds.length > 1) { 
    130       s = TiffTools.getComment(ifds[1]); 
    131     } 
    132     isWiscScan = s != null && s.indexOf("ome.xsd") != -1; 
    133  
    134     // look for additional files in the dataset 
    135     Vector files = new Vector(); 
    136     String[] fileList = new File(currentId).exists() ? l.list() : 
    137       (String[]) Location.getIdMap().keySet().toArray(new String[0]); 
    138  
    139     int oldSeriesCount = seriesCount; 
    140  
    141     for (int i=0; i<fileList.length; i++) { 
    142       String check = fileList[i].toLowerCase(); 
    143       if (checkSuffix(fileList[i], TiffReader.TIFF_SUFFIXES)) { 
    144         status("Checking " + fileList[i]); 
    145         Location file = new Location(fileList[i]); 
    146         if (!file.exists()) file = new Location(l, fileList[i]); 
    147         String iid = file.getAbsolutePath(); 
    148         String icomment = TiffTools.getComment(iid); 
    149         if (icomment == null || icomment.trim().length() == 0) continue; 
    150         if (!icomment.trim().startsWith("<?xml")) continue; 
    151         boolean addToList = true; 
    152         if (uuids) { 
    153           currentSeries = -1; 
    154           seriesCount = 0; 
    155           handler = new OMETiffHandler(true); 
    156           try { 
    157             SAXParser parser = SAX_FACTORY.newSAXParser(); 
    158             parser.parse( 
    159               new ByteArrayInputStream(icomment.getBytes()), handler); 
    160           } 
    161           catch (ParserConfigurationException exc) { 
    162             throw new FormatException(exc); 
    163           } 
    164           catch (SAXException exc) { 
    165             throw new FormatException(exc); 
    166           } 
    167           addToList = planeUUIDs.contains(fileUUIDs.get(fileUUIDs.size() - 1)); 
    168         } 
    169         // the rest of this logic is a hack for obsolete OME-TIFF files 
    170         else if (lsids && imageIDs != null) { 
    171           for (int k=0; k<imageIDs.size(); k++) { 
    172             if (icomment.indexOf((String) imageIDs.get(k)) == -1) { 
    173               addToList = false; 
    174               break; 
    175             } 
    176           } 
    177           if (addToList) { 
    178             for (int k=0; k<pixelsIDs.size(); k++) { 
    179               if (icomment.indexOf((String) pixelsIDs.get(k)) == -1) { 
    180                 addToList = false; 
    181                 break; 
    182               } 
    183             } 
    184           } 
    185         } 
    186         else { 
    187           // check key pieces of metadata for consistency 
    188           currentSeries = -1; 
    189           seriesCount = 0; 
    190           handler = new OMETiffHandler(true); 
    191           try { 
    192             SAXParser parser = SAX_FACTORY.newSAXParser(); 
    193             parser.parse( 
    194               new ByteArrayInputStream(icomment.getBytes()), handler); 
    195           } 
    196           catch (ParserConfigurationException exc) { 
    197             throw new FormatException(exc); 
    198           } 
    199           catch (SAXException exc) { 
    200             throw new FormatException(exc); 
    201           } 
    202  
    203           Enumeration keys = keyMetadata.keys(); 
    204           while (keys.hasMoreElements()) { 
    205             String key = keys.nextElement().toString(); 
    206             if (!keyMetadata.get(key).equals(temporaryKeyMetadata.get(key))) { 
    207               addToList = false; 
    208               break; 
    209             } 
    210           } 
    211           temporaryKeyMetadata.clear(); 
    212         } 
    213  
    214         if (addToList || iid.endsWith(File.separator + currentId) || 
    215           (iid.endsWith(currentId) && currentId.startsWith(File.separator))) 
    216         { 
    217           files.add(iid); 
    218         } 
    219       } 
    220     } 
    221  
    222     // parse grouped files 
    223  
    224     seriesCount = oldSeriesCount; 
    225  
    226     ifdMap = new int[seriesCount][]; 
    227     fileMap = new int[seriesCount][]; 
    228     numIFDs = new int[seriesCount]; 
    229  
    230     for (int i=0; i<seriesCount; i++) { 
    231       int ii = ((Integer) tempIfdCount.get(i)).intValue(); 
    232       ifdMap[i] = new int[ii]; 
    233       fileMap[i] = new int[ii]; 
    234     } 
    235  
    236     // copy temp IFD/file maps 
    237  
    238     for (int i=0; i<tempIfdMap.size(); i++) { 
    239       Vector v = (Vector) tempIfdMap.get(i); 
    240       int ifdCount = ((Integer) tempIfdCount.get(i)).intValue(); 
    241       for (int j=0; j<ifdCount; j++) { 
    242         ifdMap[i][j] = ((Integer) v.get(j)).intValue(); 
    243       } 
    244       numIFDs[i] = ifdCount; 
    245     } 
    246  
    247     for (int i=0; i<tempFileMap.size(); i++) { 
    248       Vector v = (Vector) tempFileMap.get(i); 
    249       int ifdCount = ((Integer) tempIfdCount.get(i)).intValue(); 
    250       for (int j=0; j<ifdCount; j++) { 
    251         fileMap[i][j] = ((Integer) v.get(j)).intValue(); 
    252       } 
    253     } 
    254  
    255     usedFiles = (String[]) files.toArray(new String[0]); 
    256     usedIFDs = new Hashtable[usedFiles.length][]; 
    257  
    258     for (int i=0; i<usedFiles.length; i++) { 
    259       if (usedFiles[i].endsWith(currentId)) { 
    260         usedIFDs[i] = ifds; 
    261         continue; 
    262       } 
    263       status("Parsing " + usedFiles[i]); 
    264       currentSeries = -1; 
    265       tempIfdMap = null; 
    266       tempFileMap = null; 
    267       tempIfdCount = null; 
    268       currentFile = i; 
    269  
    270       RandomAccessStream ras = new RandomAccessStream(usedFiles[i]); 
    271       usedIFDs[i] = TiffTools.getIFDs(ras); 
    272       ras.close(); 
    273       String c = TiffTools.getComment(usedIFDs[i][0]); 
    274       handler = new OMETiffHandler(false); 
    275       try { 
    276         SAXParser parser = SAX_FACTORY.newSAXParser(); 
    277         parser.parse(new ByteArrayInputStream(c.getBytes()), handler); 
    278       } 
    279       catch (ParserConfigurationException exc) { 
    280         throw new FormatException(exc); 
    281       } 
    282       catch (SAXException exc) { 
    283         throw new FormatException(exc); 
    284       } 
    285     } 
    286  
    287     for (int i=0; i<getSeriesCount(); i++) { 
    288       if (numIFDs != null && lsids) { 
    289         if (numIFDs[i] < core.imageCount[i]) { 
    290           LogTools.println("Too few IFDs; got " + numIFDs[i] + 
    291             ", expected " + core.imageCount[i]); 
    292         } 
    293         else if (numIFDs[i] > core.imageCount[i]) { 
    294           LogTools.println("Too many IFDs; got " + numIFDs[i] + 
    295             ", expected " + core.imageCount[i]); 
    296         } 
    297       } 
    298       else if (core.imageCount[i] > ifdMap[i].length) { 
    299         core.imageCount[i] = ifds.length; 
    300         if (core.sizeZ[i] > ifds.length) { 
    301           core.sizeZ[i] = ifds.length / (core.rgb[i] ? core.sizeC[i] : 1); 
    302           core.sizeT[i] = 1; 
    303           if (!core.rgb[i]) core.sizeC[i] = 1; 
    304         } 
    305         else if (core.sizeT[i] > ifds.length) { 
    306           core.sizeT[i] = ifds.length / (core.rgb[i] ? core.sizeC[i] : 1); 
    307           core.sizeZ[i] = 1; 
    308           if (!core.rgb[i]) core.sizeC[i] = 1; 
    309         } 
    310       } 
    311       else if (core.imageCount[i] != 
    312         core.sizeZ[i] * core.sizeC[i] * core.sizeT[i]) 
    313       { 
    314         if (!core.rgb[i]) { 
    315           core.imageCount[i] = core.sizeZ[i] * core.sizeC[i] * core.sizeT[i]; 
    316         } 
    317         else core.imageCount[i] = core.sizeZ[i] * core.sizeT[i]; 
    318       } 
    319  
    320       if (core.imageCount[i] == core.sizeZ[i] * core.sizeT[i] && 
    321         core.sizeC[i] > 1) 
    322       { 
    323         core.rgb[i] = true; 
    324       } 
    325     } 
    326   } 
    327  
    328   /* @see BaseTiffReader#initMetadataStore() */ 
    329   protected void initMetadataStore() { 
    330     String comment = TiffTools.getComment(ifds[0]); 
    331     metadata.remove("Comment"); 
    332     MetadataStore store = getMetadataStore(); 
    333     MetadataTools.convertMetadata(comment, store); 
     55    super("OME-TIFF", new String[] {"ome.tif", "ome.tiff"}); 
    33456  } 
    33557 
    33658  // -- IFormatReader API methods -- 
    337  
    338   /* @see loci.formats.IFormatReader#fileGroupOption(String) */ 
    339   public int fileGroupOption(String id) throws FormatException, IOException { 
    340     return MUST_GROUP; 
    341   } 
    342  
    343   /* @see loci.formats.IFormatReader#isThisType(String, boolean) */ 
    344   public boolean isThisType(String name, boolean open) { 
    345     if (!open && !checkSuffix(name, suffixes)) return false; 
    346     try { 
    347       RandomAccessStream s = new RandomAccessStream(name); 
    348       byte[] buf = new byte[blockCheckLen]; 
    349       s.seek(0); 
    350       s.read(buf); 
    351       boolean passFirst = isThisType(buf); 
    352       s.seek(s.length() - blockCheckLen); 
    353       s.read(buf); 
    354       boolean passSecond = isThisType(buf); 
    355       s.close(); 
    356       return passFirst || passSecond; 
    357     } 
    358     catch (IOException e) { 
    359       if (debug) LogTools.trace(e); 
    360     } 
    361     return false; 
    362   } 
    36359 
    36460  /* @see loci.formats.IFormatReader#isThisType(byte[]) */ 
    36561  public boolean isThisType(byte[] block) { 
    366     try { 
    367       RandomAccessStream ras = new RandomAccessStream(block); 
    368       Hashtable ifd = TiffTools.getFirstIFD(ras); 
    369       ras.close(); 
    370       if (ifd == null) throw new IOException(); 
    371  
    372       String comment = TiffTools.getComment(ifd); 
    373       return comment != null && comment.indexOf("ome.xsd") >= 0; 
    374     } 
    375     catch (IOException e) { 
    376       if (debug) LogTools.trace(e); 
    377     } 
    378     catch (ArrayIndexOutOfBoundsException e) { 
    379       if (debug) LogTools.trace(e); 
    380     } 
    381     return new String(block).indexOf("ome.xsd") != -1; 
     62    return TiffTools.isValidHeader(block); 
     63  } 
     64 
     65  /* 
     66   * @see loci.formats.IFormatReader#openBytes(int, byte[], int, int, int, int) 
     67   */ 
     68  public byte[] openBytes(int no, byte[] buf, int x, int y, 
     69    int width, int height) throws FormatException, IOException 
     70  { 
     71    FormatTools.assertId(currentId, true, 1); 
     72    FormatTools.checkPlaneNumber(this, no); 
     73    IFormatReader r = info[series][no].reader; 
     74    r.setId(info[series][no].id); 
     75    return r.openBytes(info[series][no].ifd, buf, x, y, width, height); 
    38276  } 
    38377 
     
    38579  public String[] getUsedFiles() { 
    38680    FormatTools.assertId(currentId, true, 1); 
    387     return usedFiles; 
    388   } 
    389  
    390   /** 
    391    * @see loci.formats.IFormatReader#openBytes(int, byte[], int, int, int, int) 
    392    */ 
    393   public byte[] openBytes(int no, byte[] buf, int x, int y, int w, int h) 
    394     throws FormatException, IOException 
    395   { 
    396     FormatTools.assertId(currentId, true, 1); 
    397     FormatTools.checkPlaneNumber(this, no); 
    398     FormatTools.checkBufferSize(this, buf.length, w, h); 
    399  
    400     int ifd = ifdMap[series][no]; 
    401     int fileIndex = fileMap[series][no]; 
    402  
    403     in = new RandomAccessStream(usedFiles[fileIndex]); 
    404     TiffTools.getSamples(usedIFDs[fileIndex][ifd], in, buf, x, y, w, h); 
    405     in.close(); 
    406     return buf; 
    407   } 
    408  
    409   // -- IFormatHandler API methods -- 
    410  
    411   /* @see loci.formats.IFormatHandler#close() */ 
    412   public void close() throws IOException { 
    413     super.close(); 
    414     usedFiles = null; 
    415     usedIFDs = null; 
    416     imageIDs = pixelsIDs = planeUUIDs = fileUUIDs = null; 
    417     tempIfdMap = tempFileMap = tempIfdCount = null; 
    418     numIFDs = null; 
    419     ifdMap = fileMap = null; 
    420     lsids = isWiscScan = false; 
    421   } 
    422  
    423   // -- Helper class -- 
    424  
    425   /** SAX handler for parsing XML. */ 
    426   private class OMETiffHandler extends DefaultHandler { 
    427     private String order; 
    428     private int sizeZ, sizeC, sizeT; 
    429     private int imageCount = 0; 
    430     private boolean foundDescription = false, foundDate = false; 
    431     private boolean foundUUID = false; 
    432     private boolean minimal; 
    433     private int currentIFD, ifdCount; 
    434  
    435     public OMETiffHandler(boolean minimal) { 
    436       // flag is true if we just want to scan for metadata, without populating 
    437       // anything 
    438       this.minimal = minimal; 
    439     } 
    440  
    441     public void characters(char[] ch, int start, int length) { 
    442       String key = null; 
    443       if (foundDescription) { 
    444         key = "Description " + (imageCount - 1); 
    445         foundDescription = false; 
    446       } 
    447       if (foundDate) { 
    448         key = "CreationDate " + (imageCount - 1); 
    449         foundDate = false; 
    450       } 
    451       if (key != null) { 
    452         if (!keyMetadata.containsKey(key)) { 
    453           keyMetadata.put(key, new String(ch, start, length)); 
    454         } 
    455         else temporaryKeyMetadata.put(key, new String(ch, start, length)); 
     81    return used; 
     82  } 
     83 
     84  // -- Internal FormatReader API methods -- 
     85 
     86  /* @see loci.formats.FormatReader#initFile(String) */ 
     87  protected void initFile(String id) throws FormatException, IOException { 
     88    if (debug) debug("OMETiffReader.initFile(" + id + ")"); 
     89    // normalize file name 
     90    super.initFile(normalizeFilename(null, id)); 
     91    id = currentId; 
     92    String dir = new File(id).getParent(); 
     93 
     94    // parse and populate OME-XML metadata 
     95    RandomAccessStream ras = new RandomAccessStream(id); 
     96    Hashtable firstIFD = TiffTools.getFirstIFD(ras); 
     97    ras.close(); 
     98    String xml = TiffTools.getComment(firstIFD); 
     99    MetadataRetrieve meta = (MetadataRetrieve) 
     100      MetadataTools.createOMEXMLMetadata(xml); 
     101    String currentUUID = meta.getUUID(); 
     102    MetadataTools.convertMetadata(meta, metadataStore); 
     103 
     104    // determine series count from Image and Pixels elements 
     105    int seriesCount = 0; 
     106    int imageCount = meta.getImageCount(); 
     107    for (int i=0; i<imageCount; i++) seriesCount += meta.getPixelsCount(i); 
     108    info = new OMETiffPlane[seriesCount][]; 
     109 
     110    // compile list of file/UUID mappings 
     111    Hashtable files = new Hashtable(); 
     112    boolean needSearch = false; 
     113    for (int i=0; i<imageCount; i++) { 
     114      int pixelsCount = meta.getPixelsCount(i); 
     115      for (int p=0; p<pixelsCount; p++) { 
     116        int tiffDataCount = meta.getTiffDataCount(i, p); 
     117        for (int td=0; td<tiffDataCount; td++) { 
     118          String uuid = meta.getTiffDataUUID(i, p, td); 
     119          String filename = null; 
     120          if (uuid == null) { 
     121            // no UUID means that TiffData element refers to this file 
     122            uuid = ""; 
     123            filename = id; 
     124          } 
     125          else { 
     126            filename = meta.getTiffDataFileName(i, p, td); 
     127            if (filename == null) { 
     128              if (uuid.equals(currentUUID)) { 
     129                // UUID references this file 
     130                filename = id; 
     131              } 
     132              else { 
     133                // will need to search for this UUID 
     134                filename = ""; 
     135                needSearch = true; 
     136              } 
     137            } 
     138            else filename = normalizeFilename(dir, filename); 
     139          } 
     140          String existing = (String) files.get(uuid); 
     141          if (existing == null) files.put(uuid, filename); 
     142          else if (!existing.equals(filename)) { 
     143            throw new FormatException("Inconsistent UUID filenames"); 
     144          } 
     145        } 
    456146      } 
    457147    } 
    458148 
    459     public void startElement(String uri, String localName, String qName, 
    460       Attributes attributes) 
    461     { 
    462       if (qName.equals("OME")) { 
    463         String uuid = attributes.getValue("UUID"); 
    464         if (fileUUIDs == null) fileUUIDs = new Vector(); 
    465         fileUUIDs.add(uuid); 
    466       } 
    467       else if (qName.equals("Image")) { 
    468         String id = attributes.getValue("ID"); 
    469         if (!minimal) { 
    470           if (id.startsWith("urn:lsid:")) { 
    471             if (imageIDs == null) imageIDs = new Vector(); 
    472             imageIDs.add(id); 
    473           } 
    474           else lsids = false; 
    475         } 
    476  
    477         imageCount++; 
    478       } 
    479       else if (qName.equals("CreationDate")) { 
    480         foundDate = true; 
    481       } 
    482       else if (qName.equals("Description")) { 
    483         foundDescription = true; 
    484       } 
    485       else if (qName.equals("UUID")) { 
    486         foundUUID = true; 
    487  
    488         if (usedFiles != null && fileMap != null) { 
    489           String filename = directory + attributes.getValue("FileName"); 
    490           int pos = -1; 
    491           for (int q=0; q<usedFiles.length; q++) { 
    492             if (usedFiles[q].equals(filename)) { 
    493               pos = q; 
    494               break; 
    495             } 
    496           } 
    497           fileMap[currentSeries][currentIFD] = pos; 
    498           for (int i=1; i<ifdCount; i++) { 
    499             fileMap[currentSeries][currentIFD + i] = pos; 
    500           } 
    501         } 
    502       } 
    503       else if (qName.equals("Pixels")) { 
    504         if (minimal) return; 
    505         currentSeries++; 
    506         String id = attributes.getValue("ID"); 
    507         if (id.startsWith("urn:lsid:")) { 
    508           if (pixelsIDs == null) pixelsIDs = new Vector(); 
    509           pixelsIDs.add(id); 
    510         } 
    511         else lsids = false; 
    512  
    513         order = attributes.getValue("DimensionOrder"); 
    514         sizeZ = Integer.parseInt(attributes.getValue("SizeZ")); 
    515         sizeC = Integer.parseInt(attributes.getValue("SizeC")); 
    516         sizeT = Integer.parseInt(attributes.getValue("SizeT")); 
    517         if (tempIfdCount != null) { 
    518           int count = sizeZ * sizeC * sizeT; 
    519           try { 
    520             if (TiffTools.getPhotometricInterpretation(ifds[0]) == 
    521               TiffTools.RGB) 
    522             { 
    523               int nFiles = usedFiles == null ? 1 : usedFiles.length; 
    524               count = (int) Math.min(count, ifds.length * nFiles); 
    525             } 
    526           } 
    527           catch (FormatException e) { 
    528             if (debug) LogTools.trace(e); 
    529           } 
    530           tempIfdCount.add(new Integer(count)); 
    531         } 
    532  
    533         if (sizeZ < 1) sizeZ = 1; 
    534         if (sizeC < 1) sizeC = 1; 
    535         if (sizeT < 1) sizeT = 1; 
    536  
    537         if (core.sizeZ.length <= currentSeries) { 
    538           CoreMetadata tempCore = new CoreMetadata(currentSeries + 1); 
    539           int ss = core.sizeX.length; 
    540           System.arraycopy(core.sizeX, 0, tempCore.sizeX, 0, ss); 
    541           System.arraycopy(core.sizeY, 0, tempCore.sizeY, 0, ss); 
    542           System.arraycopy(core.sizeZ, 0, tempCore.sizeZ, 0, ss); 
    543           System.arraycopy(core.sizeC, 0, tempCore.sizeC, 0, ss); 
    544           System.arraycopy(core.sizeT, 0, tempCore.sizeT, 0, ss); 
    545           System.arraycopy(core.thumbSizeX, 0, tempCore.thumbSizeX, 0, ss); 
    546           System.arraycopy(core.thumbSizeY, 0, tempCore.thumbSizeY, 0, ss); 
    547           System.arraycopy(core.pixelType, 0, tempCore.pixelType, 0, ss); 
    548           System.arraycopy(core.imageCount, 0, tempCore.imageCount, 0, ss); 
    549           System.arraycopy(core.currentOrder, 0, tempCore.currentOrder, 0, ss); 
    550           System.arraycopy(core.orderCertain, 0, tempCore.orderCertain, 0, ss); 
    551           System.arraycopy(core.rgb, 0, tempCore.rgb, 0, ss); 
    552           System.arraycopy(core.littleEndian, 0, tempCore.littleEndian, 0, ss); 
    553           System.arraycopy(core.interleaved, 0, tempCore.interleaved, 0, ss); 
    554           System.arraycopy(core.indexed, 0, tempCore.indexed, 0, ss); 
    555           System.arraycopy(core.falseColor, 0, tempCore.falseColor, 0, ss); 
    556           System.arraycopy(core.metadataComplete, 0, 
    557             tempCore.metadataComplete, 0, ss); 
    558           core = tempCore; 
    559         } 
    560  
    561         core.sizeX[currentSeries] = 
    562           Integer.parseInt(attributes.getValue("SizeX")); 
    563         core.sizeY[currentSeries] = 
    564           Integer.parseInt(attributes.getValue("SizeY")); 
    565         core.sizeZ[currentSeries] = sizeZ; 
    566         core.sizeC[currentSeries] = sizeC; 
    567         core.sizeT[currentSeries] = sizeT; 
    568         core.currentOrder[currentSeries] = order; 
    569         core.rgb[currentSeries] = isRGB(); 
    570         core.indexed[currentSeries] = isIndexed(); 
    571         core.falseColor[currentSeries] = isFalseColor(); 
    572  
    573         int sc = core.sizeC[currentSeries]; 
    574         if (core.rgb[currentSeries] && !core.indexed[currentSeries]) { 
    575           if (sc <= 3) sc = 1; 
    576           else sc /= 3; 
    577         } 
    578         core.imageCount[currentSeries] = 
    579           core.sizeZ[currentSeries] * sc * core.sizeT[currentSeries]; 
    580         core.pixelType[currentSeries] = 
    581           FormatTools.pixelTypeFromString(attributes.getValue("PixelType")); 
    582         if (core.pixelType[currentSeries] == FormatTools.INT8 || 
    583           core.pixelType[currentSeries] == FormatTools.INT16 || 
    584           core.pixelType[currentSeries] == FormatTools.INT32) 
    585         { 
    586           core.pixelType[currentSeries]++; 
    587         } 
    588  
    589         if (isWiscScan) core.sizeT[currentSeries] = core.imageCount[0]; 
    590  
    591         core.orderCertain[currentSeries] = true; 
    592  
    593         seriesCount++; 
    594       } 
    595       else if (qName.equals("TiffData")) { 
    596         if (minimal) return; 
    597         String ifd = attributes.getValue("IFD"); 
    598         String numPlanes = attributes.getValue("NumPlanes"); 
    599         String z = attributes.getValue("FirstZ"); 
    600         String c = attributes.getValue("FirstC"); 
    601         String t = attributes.getValue("FirstT"); 
    602         if (ifd == null || ifd.equals("")) ifd = "0"; 
    603         if (numPlanes == null || numPlanes.equals("")) { 
    604           if (usedIFDs != null) numPlanes = "" + usedIFDs[currentSeries].length; 
    605           else numPlanes = "" + ifds.length; 
    606         } 
    607         if (z == null || z.equals("")) z = "0"; 
    608         if (c == null || c.equals("")) c = "0"; 
    609         if (t == null || t.equals("")) t = "0"; 
    610  
    611         try { 
    612           if (usedIFDs != null && usedIFDs[currentFile] != null) { 
    613             int f = Integer.parseInt(ifd); 
    614             int x = (int) TiffTools.getImageWidth(usedIFDs[currentFile][f]); 
    615             int y = (int) TiffTools.getImageLength(usedIFDs[currentFile][f]); 
    616             if (x != core.sizeX[currentSeries]) { 
    617               LogTools.println("Mismatched width: got " + 
    618                 core.sizeX[currentSeries] + ", expected " + x); 
    619               core.sizeX[currentSeries] = x; 
    620             } 
    621             if (y != core.sizeY[currentSeries]) { 
    622               LogTools.println("Mismatched height: got " + 
    623                 core.sizeY[currentSeries] + ", expected " + y); 
    624               core.sizeY[currentSeries] = y; 
    625             } 
    626           } 
    627         } 
    628         catch (FormatException e) { 
    629           if (debug) LogTools.trace(e); 
    630         } 
    631  
    632         ifdCount = Integer.parseInt(numPlanes); 
    633         int totalIFDs = ifdCount * (usedFiles == null ? 1 : usedFiles.length); 
    634         try { 
    635           if (TiffTools.getPhotometricInterpretation(ifds[0]) == TiffTools.RGB) 
    636           { 
    637             // account for RGB planes 
    638             int div = (sizeZ * sizeC * sizeT) / totalIFDs; 
    639             if (sizeC / div > 0) sizeC /= div; 
    640           } 
    641         } 
    642         catch (FormatException e) { 
    643           if (debug)  LogTools.trace(e); 
    644         } 
    645         int idx = FormatTools.getIndex(order, sizeZ, sizeC, sizeT, 
    646           sizeZ * sizeC * sizeT, Integer.parseInt(z), Integer.parseInt(c), 
    647           Integer.parseInt(t)); 
    648         currentIFD = idx; 
    649  
    650         if (tempIfdMap != null) { 
    651           Vector v = new Vector(sizeZ * sizeC * sizeT); 
    652           Vector y = new Vector(sizeZ * sizeC * sizeT); 
    653           if (tempIfdMap.size() >= seriesCount && tempIfdMap.size() > 0) { 
    654             v = (Vector) tempIfdMap.get(seriesCount - 1); 
    655             y = (Vector) tempFileMap.get(seriesCount - 1); 
    656           } 
    657           else { 
    658             for (int i=0; i<sizeZ*sizeC*sizeT; i++) { 
    659               v.add(new Integer(-1)); 
    660               y.add(new Integer(-1)); 
    661             } 
    662           } 
    663  
    664           v.setElementAt(new Integer(ifd), idx); 
    665           y.setElementAt(new Integer(0), idx); 
    666  
    667           for (int i=1; i<ifdCount; i++) { 
    668             if (idx + i < v.size()) { 
    669               v.setElementAt(new Integer(Integer.parseInt(ifd) + i), idx + i); 
    670               y.setElementAt(new Integer(0), idx + i); 
    671             } 
    672             else { 
    673               int diff = idx + i - v.size(); 
    674               for (int q=0; q<diff; q++) { 
    675                 v.add(new Integer(-1)); 
    676                 y.add(new Integer(-1)); 
    677               } 
    678               v.add(new Integer(Integer.parseInt(ifd) + i)); 
    679               y.add(new Integer(0)); 
    680             } 
    681           } 
    682  
    683           if (tempIfdMap.size() >= seriesCount) { 
    684             tempIfdMap.setElementAt(v, seriesCount - 1); 
    685             tempFileMap.setElementAt(y, seriesCount - 1); 
    686           } 
    687           else { 
    688             tempIfdMap.add(v); 
    689             tempFileMap.add(y); 
    690           } 
    691         } 
    692         else { 
    693           ifdMap[currentSeries][idx] = Integer.parseInt(ifd); 
    694           fileMap[currentSeries][idx] = currentFile; 
    695           for (int i=1; i<ifdCount; i++) { 
    696             if (idx + i < ifdMap[currentSeries].length) { 
    697               ifdMap[currentSeries][idx + i] = ifdMap[currentSeries][idx] + i; 
    698               fileMap[currentSeries][idx + i] = currentFile; 
    699             } 
    700           } 
    701         } 
    702       } 
    703       else if (qName.equals("OriginalMetadata")) { 
    704         addMeta(attributes.getValue("name"), attributes.getValue("value")); 
     149    // search for missing filenames 
     150    if (needSearch) { 
     151      Enumeration en = files.keys(); 
     152      while (en.hasMoreElements()) { 
     153        String uuid = (String) en.nextElement(); 
     154        String filename = (String) files.get(uuid); 
     155        if (filename.equals("")) { 
     156          // TODO search... 
     157          // should scan only other .ome.tif files 
     158          // to make this work with OME server may be a little tricky? 
     159          throw new FormatException("Unmatched UUID: " + uuid); 
     160        } 
    705161      } 
    706162    } 
     163 
     164    // build list of used files 
     165    Enumeration en = files.keys(); 
     166    int numUUIDs = files.size(); 
     167    HashSet fileSet = new HashSet(); // ensure no duplicate filenames 
     168    for (int i=0; i<numUUIDs; i++) { 
     169      String uuid = (String) en.nextElement(); 
     170      String filename = (String) files.get(uuid); 
     171      fileSet.add(filename); 
     172    } 
     173    used = new String[fileSet.size()]; 
     174    Iterator iter = fileSet.iterator(); 
     175    for (int i=0; i<used.length; i++) used[i] = (String) iter.next(); 
     176 
     177    // HACK - for efficiency, assume all IFDs of all 
     178    // constituent files have the same samples per pixel 
     179    int samples = TiffTools.getSamplesPerPixel(firstIFD); 
     180 
     181    // process TiffData elements 
     182    Hashtable readers = new Hashtable(); 
     183    int s = 0; 
     184    for (int i=0; i<imageCount; i++) { 
     185      if (debug) { 
     186        debug("Image[" + i + "] {"); 
     187        debug("  id = " + meta.getImageID(i)); 
     188      } 
     189      int pixelsCount = meta.getPixelsCount(i); 
     190      for (int p=0; p<pixelsCount; p++) { 
     191        if (debug) { 
     192          debug("  Pixels[" + p + "] {"); 
     193          debug("    id = " + meta.getPixelsID(i, p)); 
     194        } 
     195        String order = meta.getPixelsDimensionOrder(i, p); 
     196        int effSizeC = meta.getPixelsSizeC(i, p).intValue() / samples; 
     197        int sizeT = meta.getPixelsSizeT(i, p).intValue(); 
     198        int sizeZ = meta.getPixelsSizeZ(i, p).intValue(); 
     199        int num = effSizeC * sizeT * sizeZ; 
     200 
     201        OMETiffPlane[] planes = new OMETiffPlane[num]; 
     202        for (int no=0; no<num; no++) planes[no] = new OMETiffPlane(); 
     203 
     204        int tiffDataCount = meta.getTiffDataCount(i, p); 
     205        for (int td=0; td<tiffDataCount; td++) { 
     206          if (debug) debug("    TiffData[" + td + "] {"); 
     207          // extract TiffData parameters 
     208          String filename = meta.getTiffDataFileName(i, p, td); 
     209          String uuid = meta.getTiffDataUUID(i, p, td); 
     210          Integer tdIFD = meta.getTiffDataIFD(i, p, td); 
     211          int ifd = tdIFD == null ? 0 : tdIFD.intValue(); 
     212          Integer numPlanes = meta.getTiffDataNumPlanes(i, p, td); 
     213          Integer firstC = meta.getTiffDataFirstC(i, p, td); 
     214          Integer firstT = meta.getTiffDataFirstT(i, p, td); 
     215          Integer firstZ = meta.getTiffDataFirstZ(i, p, td); 
     216          int c = firstC == null ? 0 : firstC.intValue(); 
     217          int t = firstT == null ? 0 : firstT.intValue(); 
     218          int z = firstZ == null ? 0 : firstZ.intValue(); 
     219          int index = FormatTools.getIndex(order, 
     220            sizeZ, effSizeC, sizeT, num, z, c, t); 
     221          int count = numPlanes == null ? 1 : numPlanes.intValue(); 
     222 
     223          // get reader object for this filename 
     224          if (filename == null) { 
     225            if (uuid == null) filename = id; 
     226            else filename = (String) files.get(uuid); 
     227          } 
     228          else filename = normalizeFilename(dir, filename); 
     229          IFormatReader r = (IFormatReader) readers.get(filename); 
     230          if (r == null) { 
     231            r = new TiffReader(); 
     232            readers.put(filename, r); 
     233          } 
     234 
     235          // populate plane index -> IFD mapping 
     236          for (int q=0; q<count; q++) { 
     237            int no = index + q; 
     238            planes[no].reader = r; 
     239            planes[no].id = filename; 
     240            planes[no].ifd = ifd + q; 
     241            planes[no].certain = true; 
     242            if (debug) { 
     243              debug("      Plane[" + no + "]: file=" + 
     244                planes[no].id + ", IFD=" + planes[no].ifd); 
     245            } 
     246          } 
     247          if (numPlanes == null) { 
     248            // unknown number of planes; fill down 
     249            for (int no=index+1; no<num; no++) { 
     250              if (planes[no].certain) break; 
     251              planes[no].reader = r; 
     252              planes[no].id = filename; 
     253              planes[no].ifd = planes[no - 1].ifd + 1; 
     254              if (debug) debug("      Plane[" + no + "]: FILLED"); 
     255            } 
     256          } 
     257          else { 
     258            // known number of planes; clear anything subsequently filled 
     259            for (int no=index+count; no<num; no++) { 
     260              if (planes[no].certain) break; 
     261              planes[no].reader = null; 
     262              planes[no].id = null; 
     263              planes[no].ifd = -1; 
     264              if (debug) debug("      Plane[" + no + "]: CLEARED"); 
     265            } 
     266          } 
     267          if (debug) debug("    }"); 
     268        } 
     269 
     270        // verify that all planes are available 
     271        if (debug) debug("    --------------------------------"); 
     272        for (int no=0; no<num; no++) { 
     273          if (debug) { 
     274            debug("    Plane[" + no + "]: file=" + 
     275              planes[no].id + ", IFD=" + planes[no].ifd); 
     276          } 
     277          if (planes[no].reader == null) { 
     278            throw new FormatException("Pixels ID '" + 
     279              meta.getPixelsID(i, p) + "': missing plane #" + no); 
     280          } 
     281        } 
     282        if (debug) debug("  }"); 
     283 
     284        // populate core metadata 
     285        info[s] = planes; 
     286        try { 
     287          core.sizeX[s] = meta.getPixelsSizeX(i, p).intValue(); 
     288          core.sizeY[s] = meta.getPixelsSizeY(i, p).intValue(); 
     289          core.sizeZ[s] = meta.getPixelsSizeZ(i, p).intValue(); 
     290          core.sizeC[s] = meta.getPixelsSizeC(i, p).intValue(); 
     291          core.sizeT[s] = meta.getPixelsSizeT(i, p).intValue(); 
     292          core.pixelType[s] = FormatTools.pixelTypeFromString( 
     293            meta.getPixelsPixelType(i, p)); 
     294          core.imageCount[s] = num; 
     295          core.currentOrder[s] = meta.getPixelsDimensionOrder(i, p); 
     296          core.orderCertain[s] = true; 
     297          int photo = TiffTools.getPhotometricInterpretation(firstIFD); 
     298          core.rgb[s] = samples > 1 || photo == TiffTools.RGB; 
     299          core.littleEndian[s] = !meta.getPixelsBigEndian(i, p).booleanValue(); 
     300          core.interleaved[s] = false; 
     301          core.indexed[s] = photo == TiffTools.RGB_PALETTE && 
     302            TiffTools.getIFDValue(firstIFD, TiffTools.COLOR_MAP) != null; 
     303          if (core.indexed[s]) { 
     304            core.sizeC[0] = 1; 
     305            core.rgb[0] = false; 
     306          } 
     307          core.falseColor[s] = false; 
     308          core.metadataComplete[s] = true; 
     309        } 
     310        catch (NullPointerException exc) { 
     311          throw new FormatException("Incomplete Pixels metadata", exc); 
     312        } 
     313      } 
     314      if (debug) debug("}"); 
     315    } 
     316  } 
     317 
     318  // -- Helper methods -- 
     319 
     320  private IFormatReader getReader(int no) throws FormatException, IOException { 
     321    FormatTools.checkPlaneNumber(this, no); 
     322    IFormatReader r = info[series][no].reader; 
     323    r.setId(info[series][no].id); 
     324    return r; 
     325  } 
     326 
     327  private String normalizeFilename(String dir, String name) { 
     328     return new File(dir, name).getAbsolutePath(); 
     329  } 
     330 
     331  // -- Helper classes -- 
     332 
     333  /** Structure containing details on where to find a particular image plane. */ 
     334  private class OMETiffPlane { 
     335    /** Reader to use for accessing this plane. */ 
     336    public IFormatReader reader; 
     337    /** File containing this plane. */ 
     338    public String id; 
     339    /** IFD number of this plane. */ 
     340    public int ifd = -1; 
     341    /** Certainty flag, for dealing with unspecified NumPlanes. */ 
     342    public boolean certain = false; 
    707343  } 
    708344 
Note: See TracChangeset for help on using the changeset viewer.