2626package loci .formats .in ;
2727
2828import java .io .IOException ;
29+ import java .util .ArrayList ;
30+ import java .util .Arrays ;
31+ import java .util .List ;
2932import javax .xml .parsers .ParserConfigurationException ;
3033
3134import org .slf4j .Logger ;
3235import org .slf4j .LoggerFactory ;
3336
3437import loci .common .DataTools ;
38+ import loci .common .Location ;
3539import loci .common .RandomAccessInputStream ;
3640import loci .common .xml .XMLTools ;
3741import loci .formats .CoreMetadata ;
4246import loci .formats .tiff .IFD ;
4347import loci .formats .tiff .PhotoInterp ;
4448import loci .formats .tiff .TiffParser ;
49+ import loci .formats .tiff .TiffRational ;
4550import ome .xml .model .primitives .Color ;
4651import ome .xml .model .primitives .NonNegativeInteger ;
4752
@@ -68,10 +73,17 @@ public class VectraReader extends BaseTiffReader {
6873 /** TIFF image description prefix for PerkinElmer Vectra/QPTIFF files. */
6974 private static final String SOFTWARE_CHECK = "PerkinElmer-QPI" ;
7075
76+ private static final String ANNOTATION_SUFFIX = "_annotations.xml" ;
77+ private static final List <String > EXTRA_FILES = Arrays .asList (
78+ "CoverslipMask.tif" , "FocusMap.tif" , "Label.tif" ,
79+ "OverviewBF.tif" , "OverviewFL.tif" , "SampleMask.tif"
80+ );
81+
7182 // -- Fields --
7283
7384 private int pyramidDepth = 1 ;
7485 private String profileXML ;
86+ private List <String > allFiles = new ArrayList <String >();
7587
7688 // -- Constructor --
7789
@@ -82,6 +94,7 @@ public VectraReader() {
8294 noSubresolutions = true ;
8395 suffixSufficient = false ;
8496 canSeparateSeries = false ;
97+ hasCompanionFiles = true ;
8598 }
8699
87100 // -- IFormatReader API methods --
@@ -114,6 +127,19 @@ public boolean isThisType(String name, boolean open) {
114127 }
115128 }
116129
130+ /* @see loci.formats.IFormatReader#getSeriesUsedFiles(boolean) */
131+ @ Override
132+ public String [] getSeriesUsedFiles (boolean noPixels ) {
133+ if (allFiles .size () == 1 ) {
134+ return super .getSeriesUsedFiles (noPixels );
135+ }
136+ if (noPixels ) {
137+ return allFiles .subList (
138+ 1 , allFiles .size ()).toArray (new String [allFiles .size () - 1 ]);
139+ }
140+ return allFiles .toArray (new String [allFiles .size ()]);
141+ }
142+
117143 /**
118144 * @see loci.formats.IFormatReader#openBytes(int, byte[], int, int, int, int)
119145 */
@@ -150,6 +176,9 @@ public void close(boolean fileOnly) throws IOException {
150176 if (!fileOnly ) {
151177 pyramidDepth = 1 ;
152178 profileXML = null ;
179+ if (allFiles != null ) {
180+ allFiles .clear ();
181+ }
153182 }
154183 }
155184
@@ -188,6 +217,21 @@ public int getOptimalTileHeight() {
188217 protected void initStandardMetadata () throws FormatException , IOException {
189218 super .initStandardMetadata ();
190219
220+ // look for companion files
221+
222+ Location currentFile = new Location (currentId ).getAbsoluteFile ();
223+ allFiles .add (currentFile .getAbsolutePath ());
224+ Location parent = currentFile .getParentFile ();
225+ String [] list = parent .list (true );
226+ Arrays .sort (list );
227+ for (String f : list ) {
228+ if (f .endsWith (ANNOTATION_SUFFIX ) || EXTRA_FILES .contains (f )) {
229+ allFiles .add (new Location (parent , f ).getAbsolutePath ());
230+ }
231+ }
232+
233+ // set up IFDs
234+
191235 ifds = tiffParser .getMainIFDs ();
192236 thumbnailIFDs = null ;
193237
@@ -276,6 +320,7 @@ protected void initMetadataStore() throws FormatException {
276320 MetadataStore store = makeFilterMetadata ();
277321
278322 for (int i =0 ; i <getSeriesCount (); i ++) {
323+ setSeries (i );
279324 int coreIndex = seriesToCoreIndex (i );
280325 store .setImageName (getImageName (coreIndex ), i );
281326 store .setImageDescription ("" , i );
@@ -287,11 +332,31 @@ protected void initMetadataStore() throws FormatException {
287332
288333 store .setPixelsPhysicalSizeX (FormatTools .getPhysicalSizeX (x ), i );
289334 store .setPixelsPhysicalSizeY (FormatTools .getPhysicalSizeY (y ), i );
335+
336+ TiffRational xPos = ifd .getIFDRationalValue (IFD .X_POSITION );
337+ TiffRational yPos = ifd .getIFDRationalValue (IFD .Y_POSITION );
338+ int unitMultiplier = ifd .getResolutionMultiplier ();
339+
340+ for (int c =0 ; c <getEffectiveSizeC (); c ++) {
341+ store .setPlaneTheZ (new NonNegativeInteger (0 ), i , c );
342+ store .setPlaneTheT (new NonNegativeInteger (0 ), i , c );
343+ store .setPlaneTheC (new NonNegativeInteger (c ), i , c );
344+
345+ if (xPos != null ) {
346+ double position = xPos .doubleValue () * unitMultiplier ;
347+ store .setPlanePositionX (FormatTools .getPhysicalSizeX (position ), i , c );
348+ }
349+ if (yPos != null ) {
350+ double position = yPos .doubleValue () * unitMultiplier ;
351+ store .setPlanePositionY (FormatTools .getPhysicalSizeY (position ), i , c );
352+ }
353+ }
290354 }
355+ setSeries (0 );
291356
292357 // each high-resolution IFD has an XML description that needs to be parsed
293358
294- for (int c =0 ; c <getSizeC (); c ++) {
359+ for (int c =0 ; c <getEffectiveSizeC (); c ++) {
295360 String xml = getIFDComment (c );
296361 try {
297362 Element root = XMLTools .parseDOM (xml ).getDocumentElement ();
@@ -375,9 +440,6 @@ else if (name.equals("Objective") && c == 0) {
375440 else if (name .equals ("ExposureTime" )) {
376441 Time exposure = new Time (DataTools .parseDouble (value ), UNITS .MICROSECOND );
377442 store .setPlaneExposureTime (exposure , 0 , c );
378- store .setPlaneTheZ (new NonNegativeInteger (0 ), 0 , c );
379- store .setPlaneTheT (new NonNegativeInteger (0 ), 0 , c );
380- store .setPlaneTheC (new NonNegativeInteger (c ), 0 , c );
381443 }
382444 }
383445 }
0 commit comments