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
@@ -761,6 +813,23 @@ function diagnostic(oStack)
761813
762814 end
763815
816+ %% -- Overloaded load method
817+
818+ methods (Static )
819+
820+ % loadobj - Load method
821+ function oStack = loadobj(sSavedVar )
822+ % - Create a new TIFFStack
823+ oStack = TIFFStack(sSavedVar .strFilename , sSavedVar .bInvert , [], ...
824+ sSavedVar .bForceTiffread );
825+
826+ % - Adjust dimensions to look like saved stack
827+ oStack.vnApparentSize = sSavedVar .vnApparentSize ;
828+ oStack.vnDimensionOrder = sSavedVar .vnDimensionOrder ;
829+ end
830+
831+ end
832+
764833%% -- Overloaded load method
765834
766835 methods (Static )
@@ -984,6 +1053,74 @@ function diagnostic(oStack)
9841053 end
9851054end
9861055
1056+
1057+ % TS_read_data_MappedTensor - FUNCTION Read the requested pixels from an ImageJ fake binary TIFF file (using MappedTensor)
1058+ %
1059+ % Usage: [tfData] = TS_read_data_MappedTensor(oStack, cIndices)
1060+ %
1061+ % 'oStack' is a TIFFStack. 'cIndices' are the indices passed in from subsref.
1062+ % Colon indexing will be converted to full range indexing. cIndices is a cell
1063+ % array with the format {rows, cols, frames, slices}. Slices are RGB or CMYK
1064+ % or so on.
1065+
1066+ function [tfData ] = TS_read_data_MappedTensor(oStack , cIndices , bLinearIndexing )
1067+
1068+ % - Fix up subsample detection for unitary dimensions
1069+ vbIsColon = cellfun(@iscolon , cIndices );
1070+ vbIsOne = cellfun(@(c )isequal(c , 1 ), cIndices(~vbIsColon ));
1071+ vbIsColon(~vbIsColon ) = vbIsOne & (oStack .vnDataSize(~vbIsColon ) == 1 );
1072+
1073+ % - Check ranges
1074+ vnMinRange = cellfun(@(c )(min(c(: ))), cIndices(~vbIsColon ));
1075+ vnMaxRange = cellfun(@(c )(max(c(: ))), cIndices(~vbIsColon ));
1076+
1077+ if (any(vnMinRange < 1 ) || any(vnMaxRange > oStack .vnDataSize(~vbIsColon )))
1078+ error(' TIFFStack:badsubscript' , ...
1079+ ' *** TIFFStack: Index exceeds stack dimensions.' );
1080+ end
1081+
1082+ % - Handle linear or subscript indexing
1083+ if (~bLinearIndexing )
1084+
1085+ try
1086+ % - Just read using MappedTensor
1087+ tfData = oStack .TIF(cIndices{1 : end });
1088+
1089+ catch mErr
1090+ % - Record error state
1091+ base_ME = MException(' TIFFStack:ReadError' , ...
1092+ ' *** TIFFStack: Could not read data from image file.' );
1093+ new_ME = addCause(base_ME , mErr );
1094+ throw(new_ME );
1095+ end
1096+
1097+ else
1098+ % -- Linear indexing
1099+
1100+ % - Convert frame indices back to stack-linear
1101+ vnStackLinearIndices = sub2ind(oStack .vnDataSize , cIndices{: });
1102+
1103+ % - Loop over images in stack and extract required frames
1104+ try
1105+ % - Just read using MappedTensor
1106+ tfData = oStack .TIF(vnStackLinearIndices );
1107+
1108+ catch mErr
1109+ % - Record error state
1110+ base_ME = MException(' TIFFStack:ReadError' , ...
1111+ ' *** TIFFStack: Could not read data from image file.' );
1112+ new_ME = addCause(base_ME , mErr );
1113+ throw(new_ME );
1114+ end
1115+ end
1116+
1117+ % - Invert data if requested
1118+ if (oStack .bInvert )
1119+ tfData = oStack .sImageInfo(1 ).MaxSampleValue(1 ) - (tfData - oStack .sImageInfo(1 ).MinSampleValue(1 ));
1120+ end
1121+ end
1122+
1123+
9871124% GetLinearIndicesForRefs - FUNCTION Convert a set of multi-dimensional indices directly into linear indices
9881125function [vnLinearIndices , vnDimRefSizes ] = GetLinearIndicesForRefs(cRefs , vnLims , hRepSumFunc )
9891126
@@ -1299,6 +1436,7 @@ function TS_set_directory_pre2014(tlStack, nDirectory) %#ok<DEFNU>
12991436 try
13001437 fid = fopen(strFile );
13011438 strFile = fopen(fid );
1439+ fclose(fid );
13021440
13031441 [strDir , strName , strExt ] = fileparts(strFile );
13041442
@@ -1311,6 +1449,11 @@ function TS_set_directory_pre2014(tlStack, nDirectory) %#ok<DEFNU>
13111449 end
13121450
13131451 catch mErr
1452+ % - Close file id, if necessary
1453+ if (ismember(fid , fopen(' all' )))
1454+ fclose(fid );
1455+ end
1456+
13141457 % - Record error state
13151458 base_ME = MException(' TIFFStack:ReadError' , ...
13161459 ' *** TIFFStack: Could not open file [%s ].' , strFile );
@@ -1324,8 +1467,6 @@ function TS_set_directory_pre2014(tlStack, nDirectory) %#ok<DEFNU>
13241467 bIsColon = ischar(ref ) && isequal(ref , ' :' );
13251468end
13261469
1327- % --- END of TIFFStack.m ---
1328-
13291470%% -- MEX-handling functions
13301471
13311472function [hRepSumFunc ] = GetMexFunctionHandles
@@ -1357,3 +1498,75 @@ function TS_set_directory_pre2014(tlStack, nDirectory) %#ok<DEFNU>
13571498 hRepSumFunc = @mapped_tensor_repsum_nomex ;
13581499 end
13591500end
1501+
1502+ %% -- ImageJ helper functions
1503+
1504+ function [bIsImageJBigStack , bIsImageJHyperStack , vnStackDims , vnInterleavedFrameDims ] = IsImageJBigStack(sInfo )
1505+
1506+ % - Set up default return arguments
1507+ bIsImageJBigStack = false ;
1508+ bIsImageJHyperStack = false ;
1509+ vnStackDims = [];
1510+ vnInterleavedFrameDims = [];
1511+
1512+ % - Check for ImageDescription field
1513+ if (~isfield(sInfo , ' ImageDescription' ))
1514+ return ;
1515+ end
1516+
1517+ % - Get image description
1518+ strImageDesc = sInfo(1 ).ImageDescription;
1519+
1520+ % - Look for ImageJ version information
1521+ strImageJVer = sscanf(strImageDesc(strfind(strImageDesc , ' ImageJ=' ): end ), ' ImageJ=%s ' );
1522+
1523+ % - Look for stack size information
1524+ if (~isempty(strImageJVer ))
1525+ nNumImages = sscanf(strImageDesc(strfind(strImageDesc , ' images=' ): end ), ' images=%d ' );
1526+
1527+ % - Does ImageJ report a greater number of images than sInfo?
1528+ if (~isempty(nNumImages ) && (numel(sInfo ) ~= nNumImages ))
1529+ bIsImageJBigStack = true ;
1530+ end
1531+
1532+ % - Is this a hyperstack?
1533+ strHyperStack = sscanf(strImageDesc(strfind(strImageDesc , ' hyperstack=' ): end ), ' hyperstack=%s ' );
1534+
1535+ if (strcmpi(strHyperStack , ' true' ))
1536+ bIsImageJHyperStack = true ;
1537+
1538+ % - Extract information about the stack size for a hyperstack
1539+ nNumChannels = sscanf(strImageDesc(strfind(strImageDesc , ' channels=' ): end ), ' channels=%d ' );
1540+ nNumSlices = sscanf(strImageDesc(strfind(strImageDesc , ' slices=' ): end ), ' slices=%d ' );
1541+ nNumFrames = sscanf(strImageDesc(strfind(strImageDesc , ' frames=' ): end ), ' frames=%d ' );
1542+
1543+ % - Deinterleave stack
1544+ vnStackDims = [sInfo(1 ).Height sInfo(1 ).Width nNumFrames * nNumSlices * nNumChannels 1 ];
1545+ vnInterleavedFrameDims = [nNumChannels nNumSlices nNumFrames ];
1546+
1547+ else
1548+ % - Extract information about the stack size for a fake big stack
1549+ vnStackDims = [sInfo(1 ).Height sInfo(1 ).Width nNumImages sInfo(1 ).SamplesPerPixel];
1550+ vnInterleavedFrameDims = [];
1551+ end
1552+ end
1553+ end
1554+
1555+ function [mtHandle , bUseMappedTensor , strDataClass ] = OpenImageJBigStack(oStack , vnStackDims )
1556+ bUseMappedTensor = (exist(' MappedTensor' , ' class' ) == 8 );
1557+ mtHandle = [];
1558+ strDataClass = [];
1559+
1560+ if (bUseMappedTensor )
1561+ % - Use tiffread to get file information
1562+ [TIF , HEADER ] = tiffread31_header(oStack .strFilename );
1563+
1564+ % - Use MappedTensor to open the file
1565+ strDataClass = TIF .classname ;
1566+ mtHandle = MappedTensor(oStack .strFilename , vnStackDims , ...
1567+ ' MachineFormat' , TIF .ByteOrder , ' Class' , strDataClass , ' HeaderBytes' , HEADER .StripOffsets );
1568+ end
1569+ end
1570+
1571+ % --- END of TIFFStack.m ---
1572+
0 commit comments