123123%
124124% -------------------------------------------------------------------------
125125%
126+ % ImageJ stacks
127+ %
128+ % ImageJ HyperStacks are automatically deinterleaved, if encountered. By
129+ % default, the stacks will be presented as [Y X T Z C]. They can of course
130+ % be permuted. If desired, the interleaving can be overridden by providing
131+ % an explicit 'vnInterleavedFrameDims'.
132+ %
133+ % ImageJ writes "fake" big stacks as raw binary data, with a TIF file shim
134+ % as a header. These appear to Matlab as a TIF file containing a single
135+ % frame. TIFFStack loads these files using MappedTensor, when available. If
136+ % MappedTensor is not available, a warning will be issued.
137+ %
126138% References:
127139% [1] Francois Nedelec, Thomas Surrey and A.C. Maggs. Physical Review Letters
128140% 86: 3192-3195; 2001. DOI: 10.1103/PhysRevLett.86.3192
147159 bForceTiffread % - Force the use of tiffread, rather than trying to use TiffLib
148160 vnDataSize ; % - Cached size of the TIFF stack
149161 vnApparentSize ; % - Apparent size of the TIFF stack
150- TIF ; % \_ Cached header info for tiffread29 speedups
162+ TIF ; % \_ Cached header info for tiffread31 speedups
151163 HEADER ; % /
152164 bUseTiffLib ; % - Flag indicating whether TiffLib is being used
165+ bMTStack ; % - Flag indicating MappedTensor is being used
153166 fhReadFun ; % - When using Tiff class, function for reading data
154167 fhSetDirFun ; % - When using Tiff class, function for setting the directory
155168 vnDimensionOrder ; % - Internal dimensions order to support permutation
221234 sInfo = imfinfo(strFilename );
222235 oStack.sImageInfo = sInfo ;
223236
224- if (oStack .bUseTiffLib )
237+ % - Detect a ImageJ fake BigTIFF stack
238+ [bIsImageJBigStack , bIsImageJHyperStack , vnStackDims , vnInterleavedIJFrameDims ] = IsImageJBigStack(sInfo );
239+
240+ % - Handle ImageJ big stacks with MappedTensor
241+ if (bIsImageJBigStack )
242+ [oStack .TIF , oStack .bMTStack , oStack .strDataClass ] = OpenImageJBigStack(oStack , vnStackDims );
243+
244+ % - Could we use a MappedTensor?
245+ if (~oStack .bMTStack )
246+ % - No, so just access the first frame
247+ bIsImageJBigStack = false ; % #ok<NASGU>
248+ bIsImageJHyperStack = false ;
249+
250+ warning(' TIFFStack:ImageJBigStackUnsupported' , ...
251+ ' --- TIFFStack: This is an ImageJ "fake" TIF file. MappedTensor must be available to read this file.' );
252+ end
253+ end
254+
255+ % - Deinterleave hyperstacks automatically
256+ if (bIsImageJHyperStack && isempty(vnInterleavedFrameDims ))
257+ vnInterleavedFrameDims = vnInterleavedIJFrameDims ;
258+ end
259+
260+ % - Initialise object, depending on underlying access method
261+ if (oStack .bMTStack )
262+ % - Fix up stack size
263+ sInfo = repmat(sInfo , vnStackDims(3 ), 1 );
264+
265+ elseif (oStack .bUseTiffLib )
225266 % - Create a Tiff object
226267 oStack.TIF = tifflib(' open' , strFilename , ' r' );
227268
341382 end
342383
343384 else
344- % - Read TIFF header for tiffread29
385+ % - Read TIFF header for tiffread31
345386 [oStack .TIF , oStack .HEADER ] = tiffread31_header(strFilename );
346387
347388 % - Use tiffread29 to get the data class for this tiff
382423
383424 % - Initialise dimension order
384425 oStack.vnDimensionOrder = 1 : numel(oStack .vnApparentSize );
426+
427+ % - Fix up dimensions order for ImageJ HyperStack
428+ if (bIsImageJHyperStack )
429+ oStack = permute(oStack , [1 2 5 4 3 ]);
430+ end
385431
386432 catch mErr
387433 base_ME = MException(' TIFFStack:InvalidFile' , ...
@@ -411,10 +457,14 @@ function delete(oStack)
411457 function diagnostic(oStack )
412458 disp(oStack );
413459 fprintf(' <strong>Private properties:</strong>\n ' );
460+ fprintf(' vnDataSize: [' ); fprintf(' %d ' , oStack .vnDataSize ); fprintf(' ]\n ' );
461+ fprintf(' vnApparentSize: [' ); fprintf(' %d ' , oStack .vnApparentSize ); fprintf(' ]\n ' );
462+ fprintf(' vnDimensionOrder: [' ); fprintf(' %d ' , oStack .vnDimensionOrder ); fprintf(' ]\n ' );
414463 fprintf(' bUseTiffLib: %d\n ' , oStack .bUseTiffLib );
415464 fprintf(' fhReadFun: %s\n ' , func2str(oStack .fhReadFun ));
416- fprintf(' vnDimensionOrder: [ ' ); fprintf( ' %d ' , oStack .vnDimensionOrder ); fprintf( ' ] \n ' );
465+ fprintf(' fhSetDirFun: %s\n ' , func2str( oStack .fhSetDirFun ) );
417466 fprintf(' fhRepSum: %s\n ' , func2str(oStack .fhRepSum ));
467+ fprintf(' fhCastFun: %s\n ' , func2str(oStack .fhCastFun ));
418468 end
419469
420470%% --- Overloaded subsref
@@ -500,9 +550,9 @@ function diagnostic(oStack)
500550 vnRetDataSize(vbIsColon ) = vnReferencedTensorSize(vbIsColon );
501551
502552 % - Permute index order
503- vnInvOrder(oStack .vnDimensionOrder(1 : nNumNZStackDims )) = 1 : nNumNZStackDims ;
504- S.subs = S .subs(vnInvOrder(vnInvOrder ~= 0 ));
505553 S .subs(nNumNZStackDims + 1 : nNumTotalStackDims ) = {1 };
554+ vnInvOrder(oStack .vnDimensionOrder(1 : nNumTotalStackDims )) = 1 : nNumTotalStackDims ;
555+ S.subs = S .subs(vnInvOrder(vnInvOrder ~= 0 ));
506556
507557 else % (nNumRefDims > nNumNZStackDims)
508558 % - Check for non-colon references
@@ -572,12 +622,14 @@ function diagnostic(oStack)
572622 end
573623
574624 % - Construct referencing subscripts for raw stack
575- S.subs = [S .subs(1 : 2 ) {vnFrameIndices( : )} S .subs(end )];
625+ S.subs = [S .subs(1 : 2 ) reshape( vnFrameIndices , 1 , []) S .subs(end )];
576626 end
577627 end
578628
579- % - Access stack (tifflib or tiffread)
580- if (oStack .bUseTiffLib )
629+ % - Access stack (MappedTensor or tifflib or tiffread)
630+ if (oStack .bMTStack )
631+ tfData = TS_read_data_MappedTensor(oStack , S .subs , bLinearIndexing );
632+ elseif (oStack .bUseTiffLib )
581633 tfData = TS_read_data_Tiff(oStack , S .subs , bLinearIndexing );
582634 else
583635 tfData = TS_read_data_tiffread(oStack , S .subs , bLinearIndexing );
@@ -641,7 +693,7 @@ function diagnostic(oStack)
641693 % - Trim trailing unitary dimensions
642694 vbIsUnitary = vnSize == 1 ;
643695 if (vbIsUnitary(end ))
644- nLastNonUnitary = find(vnSize == 1 , 1 , ' last' ) - 1 ;
696+ nLastNonUnitary = numel( vnSize ) - find(fliplr( vnSize ) == 1 , 1 , ' last' );
645697 if (nLastNonUnitary < numel(vnSize ))
646698 vnSize = vnSize(1 : nLastNonUnitary );
647699 end
@@ -984,6 +1036,74 @@ function diagnostic(oStack)
9841036 end
9851037end
9861038
1039+
1040+ % TS_read_data_MappedTensor - FUNCTION Read the requested pixels from an ImageJ fake binary TIFF file (using MappedTensor)
1041+ %
1042+ % Usage: [tfData] = TS_read_data_MappedTensor(oStack, cIndices)
1043+ %
1044+ % 'oStack' is a TIFFStack. 'cIndices' are the indices passed in from subsref.
1045+ % Colon indexing will be converted to full range indexing. cIndices is a cell
1046+ % array with the format {rows, cols, frames, slices}. Slices are RGB or CMYK
1047+ % or so on.
1048+
1049+ function [tfData ] = TS_read_data_MappedTensor(oStack , cIndices , bLinearIndexing )
1050+
1051+ % - Fix up subsample detection for unitary dimensions
1052+ vbIsColon = cellfun(@iscolon , cIndices );
1053+ vbIsOne = cellfun(@(c )isequal(c , 1 ), cIndices(~vbIsColon ));
1054+ vbIsColon(~vbIsColon ) = vbIsOne & (oStack .vnDataSize(~vbIsColon ) == 1 );
1055+
1056+ % - Check ranges
1057+ vnMinRange = cellfun(@(c )(min(c(: ))), cIndices(~vbIsColon ));
1058+ vnMaxRange = cellfun(@(c )(max(c(: ))), cIndices(~vbIsColon ));
1059+
1060+ if (any(vnMinRange < 1 ) || any(vnMaxRange > oStack .vnDataSize(~vbIsColon )))
1061+ error(' TIFFStack:badsubscript' , ...
1062+ ' *** TIFFStack: Index exceeds stack dimensions.' );
1063+ end
1064+
1065+ % - Handle linear or subscript indexing
1066+ if (~bLinearIndexing )
1067+
1068+ try
1069+ % - Just read using MappedTensor
1070+ tfData = oStack .TIF(cIndices{1 : end });
1071+
1072+ catch mErr
1073+ % - Record error state
1074+ base_ME = MException(' TIFFStack:ReadError' , ...
1075+ ' *** TIFFStack: Could not read data from image file.' );
1076+ new_ME = addCause(base_ME , mErr );
1077+ throw(new_ME );
1078+ end
1079+
1080+ else
1081+ % -- Linear indexing
1082+
1083+ % - Convert frame indices back to stack-linear
1084+ vnStackLinearIndices = sub2ind(oStack .vnDataSize , cIndices{: });
1085+
1086+ % - Loop over images in stack and extract required frames
1087+ try
1088+ % - Just read using MappedTensor
1089+ tfData = oStack .TIF(vnStackLinearIndices );
1090+
1091+ catch mErr
1092+ % - Record error state
1093+ base_ME = MException(' TIFFStack:ReadError' , ...
1094+ ' *** TIFFStack: Could not read data from image file.' );
1095+ new_ME = addCause(base_ME , mErr );
1096+ throw(new_ME );
1097+ end
1098+ end
1099+
1100+ % - Invert data if requested
1101+ if (oStack .bInvert )
1102+ tfData = oStack .sImageInfo(1 ).MaxSampleValue(1 ) - (tfData - oStack .sImageInfo(1 ).MinSampleValue(1 ));
1103+ end
1104+ end
1105+
1106+
9871107% GetLinearIndicesForRefs - FUNCTION Convert a set of multi-dimensional indices directly into linear indices
9881108function [vnLinearIndices , vnDimRefSizes ] = GetLinearIndicesForRefs(cRefs , vnLims , hRepSumFunc )
9891109
@@ -1299,6 +1419,7 @@ function TS_set_directory_pre2014(tlStack, nDirectory) %#ok<DEFNU>
12991419 try
13001420 fid = fopen(strFile );
13011421 strFile = fopen(fid );
1422+ fclose(fid );
13021423
13031424 [strDir , strName , strExt ] = fileparts(strFile );
13041425
@@ -1311,6 +1432,11 @@ function TS_set_directory_pre2014(tlStack, nDirectory) %#ok<DEFNU>
13111432 end
13121433
13131434 catch mErr
1435+ % - Close file id, if necessary
1436+ if (ismember(fid , fopen(' all' )))
1437+ fclose(fid );
1438+ end
1439+
13141440 % - Record error state
13151441 base_ME = MException(' TIFFStack:ReadError' , ...
13161442 ' *** TIFFStack: Could not open file [%s ].' , strFile );
@@ -1324,8 +1450,6 @@ function TS_set_directory_pre2014(tlStack, nDirectory) %#ok<DEFNU>
13241450 bIsColon = ischar(ref ) && isequal(ref , ' :' );
13251451end
13261452
1327- % --- END of TIFFStack.m ---
1328-
13291453%% -- MEX-handling functions
13301454
13311455function [hRepSumFunc ] = GetMexFunctionHandles
@@ -1357,3 +1481,69 @@ function TS_set_directory_pre2014(tlStack, nDirectory) %#ok<DEFNU>
13571481 hRepSumFunc = @mapped_tensor_repsum_nomex ;
13581482 end
13591483end
1484+
1485+ %% -- ImageJ helper functions
1486+
1487+ function [bIsImageJBigStack , bIsImageJHyperStack , vnStackDims , vnInterleavedFrameDims ] = IsImageJBigStack(sInfo )
1488+ bIsImageJBigStack = false ;
1489+ bIsImageJHyperStack = false ;
1490+ strImageDesc = sInfo(1 ).ImageDescription;
1491+
1492+ % - Look for ImageJ version information
1493+ strImageJVer = sscanf(strImageDesc(strfind(strImageDesc , ' ImageJ=' ): end ), ' ImageJ=%s ' );
1494+
1495+ % - Look for stack size information
1496+ if (~isempty(strImageJVer ))
1497+ nNumImages = sscanf(strImageDesc(strfind(strImageDesc , ' images=' ): end ), ' images=%d ' );
1498+
1499+ % - Does ImageJ report a greater number of images than sInfo?
1500+ if (~isempty(nNumImages ) && (numel(sInfo ) ~= nNumImages ))
1501+ bIsImageJBigStack = true ;
1502+ end
1503+
1504+ % - Is this a hyperstack?
1505+ strHyperStack = sscanf(strImageDesc(strfind(strImageDesc , ' hyperstack=' ): end ), ' hyperstack=%s ' );
1506+
1507+ if (strcmpi(strHyperStack , ' true' ))
1508+ bIsImageJHyperStack = true ;
1509+
1510+ % - Extract information about the stack size for a hyperstack
1511+ nNumChannels = sscanf(strImageDesc(strfind(strImageDesc , ' channels=' ): end ), ' channels=%d ' );
1512+ nNumSlices = sscanf(strImageDesc(strfind(strImageDesc , ' slices=' ): end ), ' slices=%d ' );
1513+ nNumFrames = sscanf(strImageDesc(strfind(strImageDesc , ' frames=' ): end ), ' frames=%d ' );
1514+
1515+ % - Deinterleave stack
1516+ vnStackDims = [sInfo(1 ).Height sInfo(1 ).Width nNumFrames * nNumSlices * nNumChannels 1 ];
1517+ vnInterleavedFrameDims = [nNumChannels nNumSlices nNumFrames ];
1518+
1519+ else
1520+ % - Extract information about the stack size for a fake big stack
1521+ vnStackDims = [sInfo(1 ).Height sInfo(1 ).Width nNumImages sInfo(1 ).SamplesPerPixel];
1522+ vnInterleavedFrameDims = [];
1523+ end
1524+
1525+ else
1526+ % - Just use the proper TIFF header
1527+ vnStackDims = [];
1528+ vnInterleavedFrameDims = [];
1529+ end
1530+ end
1531+
1532+ function [mtHandle , bUseMappedTensor , strDataClass ] = OpenImageJBigStack(oStack , vnStackDims )
1533+ bUseMappedTensor = (exist(' MappedTensor' , ' class' ) == 8 );
1534+ mtHandle = [];
1535+ strDataClass = [];
1536+
1537+ if (bUseMappedTensor )
1538+ % - Use tiffread to get file information
1539+ [TIF , HEADER ] = tiffread31_header(oStack .strFilename );
1540+
1541+ % - Use MappedTensor to open the file
1542+ strDataClass = TIF .classname ;
1543+ mtHandle = MappedTensor(oStack .strFilename , vnStackDims , ...
1544+ ' MachineFormat' , TIF .ByteOrder , ' Class' , strDataClass , ' HeaderBytes' , HEADER .StripOffsets );
1545+ end
1546+ end
1547+
1548+ % --- END of TIFFStack.m ---
1549+
0 commit comments