Source code for scanreader.core
"""
Reader for ScanImage 5 scans (including multiROI).
Example:
import scanreader
scan = scanreader.read_scan('my_scan_*.tif')
for field in scan:
#process field
"""
from tifffile import TiffFile
from glob import glob
from os import path
import numpy as np
import re
from .exceptions import ScanImageVersionError, PathnameError
from . import scans
_scans = {'5.1': scans.Scan5Point1, '5.2': scans.Scan5Point2, '5.3': scans.Scan5Point3,
'5.4': scans.Scan5Point4, '5.5': scans.Scan5Point5,
'5.6': scans.Scan5Point6, '5.7': scans.Scan5Point7,
'2016b': scans.Scan2016b,
'2017a': scans.Scan2017a, '2017b': scans.Scan2017b,
'2018a': scans.Scan2018a, '2018b': scans.Scan2018b,
'2019a': scans.Scan2019a, '2019b': scans.Scan2019b,
'2020': scans.Scan2020, '2021': scans.Scan2021}
[docs]
def read_scan(pathnames, dtype=np.int16, join_contiguous=False):
""" Reads a ScanImage scan.
Args:
pathnames: String or list of strings. Pathname(s) or pathname pattern(s) to read.
dtype: Data-type. Data type of the output array.
join_contiguous: Boolean. For multiROI scans (2016b and beyond) it will join
contiguous scanfields in the same depth. No effect in non-multiROI scans. See
help of ScanMultiROI._join_contiguous_fields for details.
Returns:
A Scan object (subclass of BaseScan) with metadata and data. See Readme for details.
"""
# Expand wildcards
filenames = expand_wildcard(pathnames)
if len(filenames) == 0:
error_msg = 'Pathname(s) {} do not match any files in disk.'.format(pathnames)
raise PathnameError(error_msg)
# Read version from one of the tiff files
with TiffFile(filenames[0]) as tiff_file:
file_info = tiff_file.pages[0].description + '\n' + tiff_file.pages[0].software
version = get_scanimage_version(file_info)
# Select the appropriate scan object
if (version in ['2016b', '2017a', '2017b', '2018a', '2018b', '2019a', '2019b', '2020', '2021'] and
is_scan_multiROI(file_info)):
scan = scans.ScanMultiROI(join_contiguous=join_contiguous)
elif version in _scans:
scan = _scans[version]()
else:
error_msg = 'Sorry, ScanImage version {} is not supported'.format(version)
raise ScanImageVersionError(error_msg)
# Read metadata and data (lazy operation)
scan.read_data(filenames, dtype=dtype)
return scan
def expand_wildcard(wildcard):
""" Expands a list of pathname patterns to form a sorted list of absolute filenames.
Args:
wildcard: String or list of strings. Pathname pattern(s) to be extended with glob.
Returns:
A list of string. Absolute filenames.
"""
if isinstance(wildcard, str):
wildcard_list = [wildcard]
elif isinstance(wildcard, (tuple, list)):
wildcard_list = wildcard
else:
error_msg = 'Expected string or list of strings, received {}'.format(wildcard)
raise TypeError(error_msg)
# Expand wildcards
rel_filenames = [glob(wildcard) for wildcard in wildcard_list]
rel_filenames = [item for sublist in rel_filenames for item in sublist] # flatten list
# Make absolute filenames
abs_filenames = [path.abspath(filename) for filename in rel_filenames]
# Sort
sorted_filenames = sorted(abs_filenames, key=path.basename)
return sorted_filenames
def get_scanimage_version(info):
""" Looks for the ScanImage version in the tiff file headers.
Args:
info: A string. All headers from tiff tags.
Returns:
A string. ScanImage version
"""
pattern = re.compile(r"SI.?\.VERSION_MAJOR = '?(?P<version>[^\s']*)'?")
match = re.search(pattern, info)
if match:
version = match.group('version')
else:
raise ScanImageVersionError('Could not find ScanImage version in the tiff header')
return version
def is_scan_multiROI(info):
"""Looks whether the scan is multiROI in the tiff file headers.
Args:
info: A string. All headers from tiff tags.
Returns:
A bool. Whether scan is multiroi or not.
"""
match = re.search(r'hRoiManager\.mroiEnable = (?P<is_multiROI>.)', info)
is_multiROI = (match.group('is_multiROI') == '1') if match else None
return is_multiROI