Skip to content
Snippets Groups Projects
Commit 7238fcda authored by Florian Vollrath's avatar Florian Vollrath
Browse files

added some not finalized support for LIF file meta data

parent b2faf845
No related branches found
No related tags found
No related merge requests found
......@@ -90,21 +90,25 @@ try
catch
obj.refractiveMedium = 'Unknown';
end
try
obj.NA = double(ome.getObjectiveLensNA(0,0));
catch
obj.NA = NaN;
end
try
obj.objectiveName = char(ome.getObjectiveModel(0,0));
catch
obj.objectiveName = 'Unknown';
end
try
obj.refractiveIndex = double(ome.getObjectiveSettingsRefractiveIndex(0));
catch
obj.refractiveIndex = NaN;
end
try
obj.objectiveMagnification = double(ome.getObjectiveNominalMagnification(0,0));
catch
......@@ -117,6 +121,7 @@ try
catch
obj.zoom = NaN;
end
try
obj.gain = double(ome.getDetectorGain(0, 0));
catch
......
......
......@@ -73,7 +73,6 @@ classdef (Abstract = true) ImageIO < handle
objectiveMagnification = nan;
objectiveName = '';
objectiveNA = nan;
originalMetadata; % as presented originally in the file. Each
% file format has a specific type of metadata,
% so no assumption is made on the type of
......
......
classdef LIFReader < imageIO.ImageIO
%BIOREADER Wrapper for Matlab bioFormat package
% This class acts as a wrapper for the BioFormat package.
% This class uses the bioformat library to open a file and it then tries to
% read all the metadata and store them in the properties defined in the
% ImageIO class.
% Author: Florian.Vollrath@brain.mpg.de
%
% Revision history:
% 25.08.2016 Stefano: testdebug and test with files provided by Stephan
properties
bfPtr; % BIOFORMAT reader pointer
end
methods
function obj = LIFReader(filename)
%BIOREADER Constructs the BioReader object
% The constructor calls the superclass constructor and then tries to
% extract as many metadata as possible
% Must call explicitly because we pass one argument
obj = obj@imageIO.ImageIO(filename);
obj.bfPtr = bfGetReader(obj.fileFullPath);
obj = obj.readMetadata();
end
function data = read(obj, varargin)
%READ extracts image data
% This function reads data from the bioformat file. If no parameters
% are specified for a specific dimension, all the data will be
% extracted.
% INPUT
% obj: class instance
% varargin: Name-Value arguments. Allowed parameters are 'Cols', 'Rows',
% 'C', 'Z', 'T', 'TileRows', 'TileCols'
% OUTPUT
% data: image data, up to 5 dimension (in this order: XYCZT). If only one
% channel is extracted (or the input is single channel), the singleton
% dimension relative to channel is squeezed.
% EXAMPLES
% myBR = imageIO.BioReader('testfile.lsm');
% data = myBR.getData(); %Reads all the data
% data = myBR.getData('Cols', 1:10) %Reads only the first then rows
% data = myBR.getData('Cols', 1:2:end) %Reads only the odd rows
% data = myBR.getData('C', 1, 'Z', 4:8) %Reads stacks 4 to 8, only 1st channel
% data = myBR.getData('TileRows', 1:6, 'TileCols', 2:4) %Reads first six rows of
% tiles, and column tiles from 2 to 4
if isempty(varargin) % Read all the data
data = obj.getAllData();
elseif 1 == obj.tile
data = obj.getDataNoTiles(varargin{:});
else
data = obj.getTiledData(varargin{:});
end
data = squeeze(data);
end
function delete(obj)
%DELETE Close object instances.
%Close performs the cleanup and release of the instantiated object
obj.bfPtr.close();
end
end
methods (Access = protected)
obj = readMetadata(obj); % IMPLEMENTED IN SEPARATE FILE
function obj = setTileProperties(obj, ome)
%SETTILEPROPERTIES Set all properties related to tiles
if 1 == obj.tile
obj.rowTilePos = 1;
obj.colTilePos = 1;
obj.numTilesRow = 1;
obj.numTilesCol = 1;
obj.tileOverlap = 0;
obj.pixPerTileRow = ome.getPixelsSizeY(0).getValue();
obj.pixPerTileCol = ome.getPixelsSizeX(0).getValue();
else
obj.rowTilePos = nan(1, obj.tile);
obj.colTilePos = nan(1, obj.tile);
try
for k = 1:obj.tile
obj.rowTilePos(k) = double(ome.getPlanePositionY(k-1,0).value());
obj.colTilePos(k) = double(ome.getPlanePositionX(k-1,0).value());
end
obj.numTilesRow = length(unique(obj.rowTilePos));
obj.numTilesCol = length(unique(obj.colTilePos));
catch
obj.rowTilePos = nan(1, obj.tile);
obj.colTilePos = nan(1, obj.tile);
obj.numTilesRow = nan;
obj.numTilesCol = nan;
end
try
obj.pixPerTileRow = double(ome.getPixelsSizeY(0).getValue());
obj.pixPerTileCol = double(ome.getPixelsSizeX(0).getValue());
catch
obj.pixPerTileRow = NaN;
obj.pixPerTileCol = NaN;
end
try
if obj.numTilesRow > 1
%get the minimum value above zero
rowDiffs = diff(obj.rowTilePos);
rowDiffs(rowDiffs <= 0) = Inf;
adjacentDiff = min(rowDiffs);
obj.tileOverlap = 1 - adjacentDiff / (obj.pixPerTileRow * obj.scaleSize(2));
elseif obj.numTilesCol > 1
colDiffs = diff(obj.colTilePos);
colDiffs(colDiffs == 0) = Inf;
adjacentDiff = min(colDiffs);
obj.tileOverlap = 1 - adjacentDiff / (obj.pixPerTileCol * obj.scaleSize(1));
else
obj.tileOverlap = 0;
end
catch
obj.tileOverlap = 0;
end
end
end
end
end
\ No newline at end of file
function [ data ] = getAllData( obj )
%GETALLDATA Get all the image data
% This method extracts all the image data from a BioReader object
data = zeros(obj.height, obj.width, obj.channels, obj.stacks, obj.time, obj.datatype);
progBar = TextProgressBar('BioReader --> Extracting data: ', 30);
if 1 == obj.numTilesRow && 1 == obj.numTilesCol
maxNum = obj.stacks * obj.channels * obj.time;
for s = 1:obj.stacks
for ch = 1:obj.channels
for t = 1:obj.time
%update progress bar
currNum = t + (ch-1)*obj.time + (s-1)*obj.channels*obj.time;
progBar.update(currNum/maxNum * 100);
%set index
tileIdx = obj.bfPtr.getIndex(s-1, ch-1, t-1) + 1;
tmp = bfGetPlane(obj.bfPtr, tileIdx);
assert(size(tmp, 1) == obj.pixPerTileRow && size(tmp, 2) == obj.pixPerTileCol);
data(:, :, ch, s, t) = tmp;
end
end
end
else
maxNum = obj.stacks * obj.channels * obj.time * obj.numTilesRow * obj.numTilesCol;
for row = 1:obj.numTilesRow
for col = 1:obj.numTilesCol
%set series
obj.bfPtr.setSeries((row-1) * obj.numTilesCol + col - 1);
for s = 1:obj.stacks
for ch = 1:obj.channels
for t = 1:obj.time
%update progress bar
currNum = t + (ch-1)*obj.time + (s-1)*obj.channels*obj.time + ...
(col-1)*obj.stacks*obj.channels*obj.time + ...
(row-1)*obj.numTilesCol*obj.stacks*obj.channels*obj.time;
progBar.update(currNum/maxNum * 100);
%set index
tileIdx = obj.bfPtr.getIndex(s-1, ch-1, t-1) + 1;
tmp = bfGetPlane(obj.bfPtr, tileIdx);
assert(size(tmp, 1) == obj.pixPerTileRow && size(tmp, 2) == obj.pixPerTileCol);
if 1 ~= row
ovDiffRow = round(obj.tileOverlap * obj.pixPerTileRow);
else
ovDiffRow = 0;
end
if 1 ~= col
ovDiffCol = round(obj.tileOverlap * obj.pixPerTileCol);
else
ovDiffCol = 0;
end
startR = 1 + (row - 1) * obj.pixPerTileRow - ovDiffRow;
startC = 1 + (col - 1) * obj.pixPerTileCol - ovDiffCol;
endR = startR + obj.pixPerTileRow - 1;
endC = startC + obj.pixPerTileCol - 1;
data(startR:endR, startC:endC, ch, s, t) = tmp;
end
end
end
end
end
end
end
function [ data ] = getDataNoTiles( obj, varargin )
%GETDATANOTILES Retrieves image data when the input is not tiled
% This method retrieves the image data (or a subset of it) in the case of
% images that do not contain multiple tiles. The user can specify subset
% of the images by specifying the dimension and the interval of interest
% as a Name-Value pair. If no arguments are given, all the data is
% extracted.
% INPUT:
% obj: the BioReader 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: image data, up to 5 dimension (in this order: XYCZT). If only one
% channel is extracted (or the input is single channel), the singleton
% dimension relative to channel is squeezed.
% EXAMPLES:
% data = obj.getDataNoTiles(); %extract all data
% data = obj.getDataNoTiles('C', 1:2); %extract data only from the first
% 2 channels
% data = obj.getDataNoTiles('X', 1:2:obj.width, 'Y', 1:2:obj.height); %
% extract data subsampled by a factor 2 in rows and cols
%parse input
p = inputParser();
p.KeepUnmatched = true;
p.addParameter('Cols', 1:obj.width, @(x) isvector(x) && all(x > 0) && max(x) <= obj.width);
p.addParameter('Rows', 1:obj.height, @(x) isvector(x) && all(x > 0) && max(x) <= obj.height);
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;
data = zeros(length(rows), length(cols), length(channels), length(stacks), ...
length(timeseries), obj.datatype);
% get numelements in each dimension
nS = numel(stacks);
nCh = numel(channels);
nT = numel(timeseries);
maxNum = nS * nCh * nT;
% define progress bar
progBar = TextProgressBar('BioReader --> Extracting data: ', 30);
%set series
obj.bfPtr.setSeries(0);
idxS = 1;
for s = stacks
idxCh = 1;
for ch = channels
idxT = 1;
for t = timeseries
% update progress bar
currNum = idxT + (idxCh-1)*nT + (idxS-1)*nCh*nT;
progBar.update(currNum/maxNum * 100);
%set index
tileIdx = obj.bfPtr.getIndex(s-1, ch-1, t-1) + 1;
%get plane
tmp = bfGetPlane(obj.bfPtr, tileIdx);
data(:, :, idxCh, idxS, idxT) = tmp(rows, cols);
idxT = idxT + 1;
end
idxCh = idxCh + 1;
end
idxS = idxS + 1;
end
%squeeze data, to remove singleton dimensions
data = squeeze(data);
end
function [ data ] = getTiledData( obj, varargin )
%GETTILEDDATA Retrieves image data when the input is tiled
% This method retrieves the image data (or a subset of it) in the case of
% images that contain multiple tiles. The user can specify subset
% of the images by specifying the dimension and the interval of interest
% as a Name-Value pair. If no arguments are given, all the data is
% extracted. For the Cols and Rows argument, the interval is intented
% per-tile. For example, if the user wants to keep only the top left tile,
% he won't specify any subset for Rows and Cols (that is, take them all),
% but will specify the subset TileRow = 1 and TileCol = 1. On the other
% hand, if the user wants to extract an image subsampled of a factor 2
% compared to the original, he will specify a subset Rows = 1:2:obj.pixPerTileRow
% and Cols = 1:2:obj.pixPerTileCol, and no subset for the tiles (i.e. use
% all tiles).
% INPUT:
% obj: the BioReader 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
% 'TileRows': Specify which row tiles to read.
% 'TileCols': Specify which col tiles to read.
% OUTPUT:
% data: image data, up to 5 dimension (in this order: XYCZT). If only one
% channel is extracted (or the input is single channel), the singleton
% dimension relative to channel is squeezed.
% EXAMPLES:
% data = obj.getTiledData(); %extract all data
% data = obj.getTiledData('C', 1:2); %extract data only from the first
% 2 channels
% data = obj.getTiledData('Rows', 1:2:obj.pixPerTileRow, 'Cols', 1:2:obj.pixPerTileCol); %
% extract data subsampled by a factor 2 in rows and cols
% data = obj.getTiledData('TileRows', 1:6, 'TileCols, 2:4) %Reads first six rows of
% tiles, and column tiles from 2 to 4
%parse input
p = inputParser();
p.KeepUnmatched = true;
p.addParameter('Cols', 1:obj.pixPerTileCol, @(x) isvector(x) && all(x > 0) && max(x) <= obj.pixPerTileCol);
p.addParameter('Rows', 1:obj.pixPerTileRow, @(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.addParameter('TileCols', 1:obj.numTilesCol, @(x) isvector(x) && all(x > 0) && max(x) <= obj.numTilesCol);
p.addParameter('TileRows', 1:obj.numTilesRow, @(x) isvector(x) && all(x > 0) && max(x) <= obj.numTilesRow);
p.parse(varargin{:});
rows = p.Results.Rows;
cols = p.Results.Cols;
channels = p.Results.C;
stacks = p.Results.Z;
timeseries = p.Results.T;
tileCol = p.Results.TileCols;
tileRow = p.Results.TileRows;
sizeRows = round(length(rows) * (1 + (max(tileRow) - 1) * (1 - obj.tileOverlap)));
sizeCols = round(length(cols) * (1 + (max(tileCol) - 1) * (1 - obj.tileOverlap)));
data = zeros(sizeRows, sizeCols, length(channels), length(stacks), ...
length(timeseries), obj.datatype);
%get index of start of each new tile
pixelStartTileRow = 1 + round((0:max(tileRow)-1) * (1 - obj.tileOverlap) * length(rows));
pixelStartTileCol = 1 + round((0:max(tileCol)-1) * (1 - obj.tileOverlap) * length(cols));
% get numelements in each dimension
nS = numel(stacks);
nCh = numel(channels);
nT = numel(timeseries);
nR = numel(tileRow);
nC = numel(tileCol);
maxNum = nS * nCh * nT * nR * nC;
% define progress bar
progBar = TextProgressBar('BioReader --> Extracting data: ', 30);
% For every combination of Time, Z, Channel
idxS = 1;
for s = stacks
idxCh = 1;
for ch = channels
idxT = 1;
for t = timeseries
%Create the whole 2D image
for row = tileRow
for col = tileCol
% update progress bar
currNum = idxT + (idxCh-1)*nT + (idxS-1)*nCh*nT + ...
(col-1)*nS*nCh*nT + (row-1)*nC*nS*nCh*nT;
progBar.update(currNum/maxNum * 100);
%set series
obj.bfPtr.setSeries((row-1) * obj.numTilesCol + col - 1);
%set index
tileIdx = obj.bfPtr.getIndex(s-1, ch-1, t-1) + 1;
%get plane
tmpTile = bfGetPlane(obj.bfPtr, tileIdx);
[rr, cc] = size(tmpTile(rows, cols));
data(pixelStartTileRow(row) : pixelStartTileRow(row) + rr - 1, ...
pixelStartTileCol(col) : pixelStartTileCol(col) + cc - 1, ...
idxCh, idxS, idxT) = tmpTile(rows, cols);
end
end
idxT = idxT + 1;
end
idxCh = idxCh + 1;
end
idxS = idxS + 1;
end
%squeeze data, to remove singleton dimensions
data = squeeze(data);
%remove zero rows and cols
if ismatrix(data)
data(1:pixelStartTileRow(tileRow(1)) - 1, :) = [];
data(:, 1:pixelStartTileCol(tileCol(1)) - 1) = [];
elseif 3 == ndims(data)
data(1:pixelStartTileRow(tileRow(1)) - 1, :, :) = [];
data(:, 1:pixelStartTileCol(tileCol(1)) - 1, :) = [];
elseif 4 == ndims(data)
data(1:pixelStartTileRow(tileRow(1)) - 1, :, :, :) = [];
data(:, 1:pixelStartTileCol(tileCol(1)) - 1, :, :) = [];
else % 5 == ndims(data)
data(1:pixelStartTileRow(tileRow(1)) - 1, :, :, :, :) = [];
data(:, 1:pixelStartTileCol(tileCol(1)) - 1, :, :, :) = [];
end
end
function obj = readMetadata(obj)
%READMETADATA Access the OME metadata and sets the object properties
%The function accesses the OME metadata. The method takes as input the
%metadata stored in a Java Hashtable (as they are returned by the
%BioFormat package) object and from there accesses all the required
%metadata. If the metadata is not available a default value is set
% get OME metadata object
ome = obj.bfPtr.getMetadataStore();
r = bfGetReader(obj.fileFullPath);
seriesMetadata = r.getSeriesMetadata();
Info = strsplit(string(seriesMetadata),', ');
for i=1:size(Info,2)
s=strsplit(Info(1,i),'=');
metadata(i,1) = s(1);
metadata(i,2) = s(2);
end
% get Pixels Physical Size
obj.scaleSize = zeros(1, 3);
try
obj.scaleSize(1) = double(ome.getPixelsPhysicalSizeX(0).value());
catch
obj.scaleSize(1) = 1;
end
try
obj.scaleSize(2) = double(ome.getPixelsPhysicalSizeY(0).value());
catch
obj.scaleSize(2) = 1;
end
try
obj.scaleSize(3) = double(ome.getPixelsPhysicalSizeZ(0).value());
catch
obj.scaleSize(3) = 1;
end
%for tiled data: get total number of tiles and accessory info
try
obj.tile = ome.getImageCount();
catch
obj.tile = 1;
end
obj = obj.setTileProperties(ome);
%dimensions
try
obj.height = round(obj.pixPerTileRow * (1 + (obj.numTilesRow - 1) * (1 - obj.tileOverlap)));
catch
obj.height = NaN;
end
try
obj.width = round(obj.pixPerTileCol * (1 + (obj.numTilesCol - 1) * (1 - obj.tileOverlap)));
catch
obj.width = NaN;
end
try
obj.stacks = double(ome.getPixelsSizeZ(0).getValue());
catch
obj.stacks = NaN;
end
try
obj.time = double(ome.getPixelsSizeT(0).getValue());
catch
obj.time = NaN;
end
try
obj.channels = double(ome.getPixelsSizeC(0).getValue());
catch
obj.channels = NaN;
end
try
obj.datatype = char(ome.getPixelsType(0));
catch
obj.datatype = 'uint16';
end
%scaling units
obj.scaleUnits = cell(1, 3);
try
obj.scaleUnits{1} = char(ome.getPixelsPhysicalSizeY(0).unit().getSymbol());
catch
obj.scaleUnits{1} = 'Unknown';
end
try
obj.scaleUnits{2} = char(ome.getPixelsPhysicalSizeX(0).unit().getSymbol());
catch
obj.scaleUnits{2} = 'Unknown';
end
try
obj.scaleUnits{3} = char(ome.getPixelsPhysicalSizeZ(0).unit().getSymbol());
catch
obj.scaleUnits{3} = 'Unknown';
end
%objective properties
try
pos = contains(metadata, 'Image|ATLCameraSettingDefinition|Immersion');
pos = find(pos(:,1));
obj.refractiveMedium = char(metadata(pos,2));
catch
obj.refractiveMedium = 'Unknown';
end
try
pos = contains(metadata, 'Image|ATLCameraSettingDefinition|NumericalAperture');
pos = find(pos(:,1));
obj.NA = double(metadata(pos,2));
catch
obj.NA = NaN;
end
try
pos = contains(metadata, 'Image|ATLCameraSettingDefinition|WideFieldChannelConfigurator|ObjectiveName');
pos = find(pos(:,1));
obj.objectiveName = char(metadata(pos,2));
catch
obj.objectiveName = 'Unknown';
end
try
pos = contains(metadata, 'Image|ATLCameraSettingDefinition|RefractionIndex');
pos = find(pos(:,1));
obj.refractiveIndex = double(metadata(pos,2));
catch
obj.refractiveIndex = NaN;
end
try
pos = contains(metadata, 'Image|ATLCameraSettingDefinition|TotalVideoMag');
pos = find(pos(:,1));
obj.objectiveMagnification = double(metadata(pos,2));
catch
obj.objectiveMagnification = NaN;
end
%
try
pos = contains(metadata, 'Image|ATLCameraSettingDefinition|MicroscopeModel');
pos = find(pos(:,1));
obj.microscopeName = char(metadata(pos,2));
catch
obj.microscopeName = NaN;
end
%acquisition properties
try
obj.zoom = double(ome.getDetectorZoom(0, 0));
catch
obj.zoom = NaN;
end
try
obj.gain = double(ome.getDetectorGain(0, 0));
catch
obj.gain = NaN;
end
try
pos = contains(metadata, 'Image|ATLCameraSettingDefinition|WideFieldChannelConfigurator|WideFieldChannelInfo|ExposureTime');
pos = find(pos(:,1));
obj.timeFrame = double(metadata(pos,2));
catch
obj.timeFrame = NaN;
end
%laser properties
%exc and emission wavelengths
if obj.channels > 1
for ch = 0:obj.channels-1
try
obj.wavelengthExc(ch+1) = double(ome.getChannelExcitationWavelength(0,ch).value());
catch
obj.wavelengthExc(ch+1) = NaN;
end
try
pos = contains(metadata, 'Image|ATLCameraSettingDefinition|WideFieldChannelConfigurator|WideFieldChannelInfo|EmissionWavelength');
pos = find(pos(:,1));
obj.wavelengthEm(ch+1) = double(metadata(pos,2));
catch
obj.wavelengthEm(ch+1) = NaN;
end
end
else
try
obj.wavelengthExc = double(ome.getChannelExcitationWavelength(0,0).value());
catch
obj.wavelengthExc = NaN;
end
try
obj.wavelengthEm = double(ome.getChannelEmissionWavelength(0,0).value());
catch
obj.wavelengthEm = NaN;
end
end
end
......@@ -87,6 +87,8 @@ else
end
case '.lsm'
imgPtr = imageIO.LSMReader(file);
case '.lif'
imgPtr = imageIO.LIFReader(file);
otherwise %assume it could be opened using the BioFormatReader
imgPtr = imageIO.BioReader(file);
end
......
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please to comment