This repository has been archived by the owner. It is now read-only.
Permalink
Cannot retrieve contributors at this time
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
ImageIO/+imageIO/@TiffReader/TiffReader.m
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
272 lines (223 sloc)
9.68 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
classdef TiffReader < imageIO.ImageIO | |
%TIFFREADER Wrapper for Tiff interface, based on Matlab TIFF class | |
% This class is just a simple wrapper around Matlab Tiff class, adapted | |
% to conform to the structure of the imageIO library. Unlike | |
% TiffWriter, this class does not interface directly with the Tiff | |
% library, since no speed-ups are expected compared to the Matlab | |
% version. | |
% Author: Stefano.Masneri@brain.mpg.de | |
% Date: 29.07.2016 | |
% SEE ALSO: imageIO.TiffWriter, Tiff | |
properties | |
tiffPtr; % pointer to a Matlab Tiff class | |
filePtr; % pointer to a file. Used in some special cases | |
% Other properties. Assuming that for multistack images they remain | |
% constant over the stack | |
XResolution; % resolution on horizontal axis | |
YResolution; % resolution on vertical haxis | |
resolutionUnit; % unit of measurement for resolution (none, inch, centimeter) | |
bps; % bits per sample used | |
colormap; % colormap used. Empty array if none | |
compression; % compression scheme used | |
tagNames; % Cell of available tags. Useful if the user wants to access | |
% additonal metadata | |
isImageJFmt = false; % true if the Tiff is non-standard and was created via imageJ | |
isSutterMOM1 = false; % true if the Tiff is non-standard and from SutterMOM v1 | |
isSutterMOM2 = false; % true if the Tiff is non-standard and from SutterMOM v2 | |
endianness='l'; % specify if data is stored as little-endian or big-endian | |
% used only if 'isImageJFmt' is true. Can be either 'l' | |
% or 'b' | |
offsetToImg; % offset to first image in the stack. Used only if isImageJFmt is true | |
stripByteCounts; % number of bytes to reads per strip. Used only if isImageJFmt is true | |
end | |
methods | |
function obj = TiffReader(filename) | |
%TIFFREADER Constructor of the class | |
%The constructor calls the constructor of the superclass, and then | |
%tries to parse the Tiff tags to extract as much information as | |
%possible from the file. No actual data is read in the constructor | |
%SEE ALSO imageIO.ImageIO.ImageIO | |
% Must call explicitly because we pass one argument | |
obj = obj@imageIO.ImageIO(filename); | |
% Use Matlab interface | |
obj.tiffPtr = Tiff(obj.fileFullPath, 'r'); | |
% Set as many properties from the superclass as possible | |
obj = obj.readMetadata(); | |
if obj.isImageJFmt % handle file differently | |
obj.filePtr = fopen(obj.fileFullPath); | |
endianness = fread(obj.filePtr, 2, '*char')'; | |
if strcmpi(endianness, 'MM') | |
obj.endianness = 'b'; | |
else %'II' | |
obj.endianness = 'l'; | |
end | |
%fseek(obj.filePtr, obj.offsetToImg, 'bof'); | |
end | |
end | |
function data = read(obj, varargin) | |
%READ read all the image data | |
%This function reads all the planes of the image. If the file has | |
%only one plane just returns that. | |
% INPUT | |
% obj: the TiffReader instance | |
% NAME-VALUE ARGUMENTS | |
% 'Cols': Specify which columns to extract | |
% 'Rows': Specify which rows to extract | |
% 'C': Specify which channels to extract | |
% 'Z': Specify which planes to extract | |
% 'T': Specify which timeseries to extract | |
% OUTPUT | |
% data: the whole image content | |
p = inputParser(); | |
p.KeepUnmatched = true; | |
p.addParameter('Cols', 1:obj.width, @(x) isvector(x) && all(x > 0) && max(x) <= obj.pixPerTileCol); | |
p.addParameter('Rows', 1:obj.height, @(x) isvector(x) && all(x > 0) && max(x) <= obj.pixPerTileRow); | |
p.addParameter('C', 1:obj.channels, @(x) isvector(x) && all(x > 0) && max(x) <= obj.channels); | |
p.addParameter('Z', 1:obj.stacks, @(x) isvector(x) && all(x > 0) && max(x) <= obj.stacks); | |
p.addParameter('T', 1:obj.time, @(x) isvector(x) && all(x > 0) && max(x) <= obj.time); | |
p.parse(varargin{:}); | |
rows = p.Results.Rows; | |
cols = p.Results.Cols; | |
channels = p.Results.C; | |
stacks = p.Results.Z; | |
timeseries = p.Results.T; | |
if obj.isImageJFmt | |
data = readTifImageJ(obj, cols, rows, channels, stacks); | |
elseif obj.isSutterMOM1 || obj.isSutterMOM2 | |
data = readSutter(obj, cols, rows, channels, stacks, timeseries ); | |
else % normal tif | |
data = zeros(length(rows), length(cols), length(channels), ... | |
length(stacks), obj.datatype); | |
idx = 1; | |
progBar = TextProgressBar('TiffReader --> Extracting data: ', 30); | |
for k = stacks | |
progBar.update(idx/(length(stacks)) * 100); | |
img = obj.readImage(k); | |
data(:, :, :, idx) = img(rows, cols, channels); | |
idx = idx + 1; | |
end | |
end | |
data = squeeze(data); | |
end | |
function img = readImage( obj, n ) | |
%READIMAGE read one image plane | |
%This function reads one single plane of the image. If the file has | |
%only one plane just returns that. | |
% INPUT | |
% n the directory (aka the plane) to read. If bigger than the number | |
% of stacks, issue a warning and return an empty array. If not | |
% specified, return the image in the current directory | |
% OUTPUT | |
% img the image just read | |
if obj.isImageJFmt | |
imageSize = obj.height * obj.width * obj.channels; | |
precision = [ obj.datatype '=>' obj.datatype ]; | |
if n > obj.stacks | |
warning('TiffReader.readImage: Cannot read image. n is bigger than the number of stacks') | |
img = []; | |
else | |
if nargin > 1 % n specified | |
fseek(obj.filePtr, obj.offsetToImg + (n-1)*imageSize*obj.bps/8, 'bof'); | |
end | |
img = fread(obj.filePtr, imageSize, precision); | |
img = reshape(img, [obj.width, obj.height, obj.channels]); | |
img = img'; | |
end | |
else | |
if 1 == nargin % n not specified | |
img = obj.tiffPtr.read(); | |
elseif ~obj.isSutterMOM1 && ~obj.isSutterMOM2 && n > obj.stacks | |
warning('TiffReader.readImage: Cannot read image. n is bigger than the number of stacks') | |
img = []; | |
else % valid n | |
obj.tiffPtr.setDirectory(n); | |
img = obj.tiffPtr.read(); | |
end | |
end | |
end | |
function delete(obj) | |
%DELETE Close object instances. | |
%Close performs the cleanup and release of the instantiated object | |
obj.tiffPtr.close(); | |
if obj.filePtr > 0 | |
fclose(obj.filePtr); | |
end | |
end | |
end | |
methods (Access = protected) | |
function obj = readMetadata(obj) | |
%First get usual info with imfinfo | |
try | |
imgInfo = imfinfo(obj.fileFullPath); | |
% Dimensions | |
obj.width = imgInfo(1).Width; | |
obj.height = imgInfo(1).Height; | |
obj.channels = imgInfo(1).SamplesPerPixel; | |
iconIndex = cat(1, imgInfo.NewSubFileType); | |
obj.stacks = sum(~iconIndex); | |
obj.time = 1; | |
obj.bps = imgInfo(1).BitsPerSample(1); | |
obj.resolutionUnit = imgInfo(1).ResolutionUnit; | |
obj.compression = imgInfo(1).Compression; | |
stripOffset = cat(1,imgInfo(iconIndex == 0).StripOffsets); | |
obj.offsetToImg = stripOffset(1); | |
% Standard TIFF does not have multitiled images | |
obj.tile = 1; | |
obj.numTilesRow = 1; | |
obj.numTilesCol = 1; | |
obj.rowTilePos = 0; | |
obj.colTilePos = 0; | |
obj.pixPerTileRow = obj.height; | |
obj.pixPerTileCol = obj.width; | |
obj.tileOverlap = 0; | |
% Other info available in imfinfo | |
obj.XResolution = imgInfo(1).XResolution; | |
obj.YResolution = imgInfo(1).YResolution; | |
obj.colormap = imgInfo(1).Colormap; | |
catch ME | |
error('TiffReader.TiffReader: Cannot read metadata. %s', ME.message) | |
end | |
% now use the Tiff pointer | |
obj.tagNames = obj.tiffPtr.getTagNames; | |
% retrieve datatype | |
sampleFormat = obj.tiffPtr.getTag('SampleFormat'); | |
switch sampleFormat | |
case 1 % UInt | |
obj.datatype = ['uint' num2str(obj.bps)]; | |
case 2 % Int | |
obj.datatype = ['int' num2str(obj.bps)]; | |
case 3 % IEEEFP | |
if 64 == obj.bps | |
obj.datatype = 'double'; | |
elseif 32 == obj.bps | |
obj.datatype = 'float'; | |
else | |
warning('TiffReader.readMetadata: unrecognized BitsPerSample value') | |
end | |
otherwise % Void or complex types are unsupported | |
warning('TiffReader.readMetadata: unsupported sample format') | |
end | |
% check custom multitiff formats -_-' | |
try | |
imageDescription = obj.tiffPtr.getTag('ImageDescription'); | |
% check between formats | |
if strncmpi('ImageJ', imageDescription, 6) | |
obj.isImageJFmt = true; | |
obj.parseMetadataImageJ(imageDescription); | |
elseif any(strfind('MOMconfig', imageDescription)) | |
obj.isSutterMOM1 = true; | |
obj.pareMetadataSutter(imageDescription); | |
elseif any(strfind('scanimage.SI.', imageDescription)) | |
obj.isSutterMOM2 = true; | |
obj.parseMetadataSutter(imageDescription); | |
end | |
catch | |
% could not retrieve ImageDescription tag | |
warning('TiffReader::readMetadata:: Tiff.Tag.ImageDescription is missing!'); | |
end | |
end | |
%% decleare external methods | |
obj = parseMetadataImageJ(obj, imageDescription); | |
obj = parseMetadataSutter(obj, imageDescription); | |
end | |
end | |