Source code for pyfibre.addons.shg_pl_trans.shg_reader

import json
import logging
import os

import numpy as np
from skimage.util import img_as_float
from skimage.external.tifffile import TiffFile

from pyfibre.core.base_multi_image_reader import (
    BaseMultiImageReader)

from .shg_image import SHGImage
from .shg_pl_trans_parser import SHGPLTransFileSet

logger = logging.getLogger(__name__)


[docs]def lookup_page(tiff_page): """Obtain relevant information from a TiffPage object""" xy_dim = (tiff_page.image_width, tiff_page.image_length) description = tiff_page.image_description.decode('utf-8') return xy_dim, description
[docs]def get_fluoview_param(description, xy_dim, shape): desc_list = description.split('\n') channel_lines = [ line.strip() for line in desc_list if 'Gamma' in line] n_modes = len(channel_lines) # If number of modes is not in image shape (typically # because the image only contains one mode) if shape.count(n_modes) == 0: if n_modes == 1 and len(shape) == 2: minor_axis = None elif n_modes == 1 and len(shape) == 3: minor_axis = np.argmin(shape) else: raise IndexError( f"Image shape {shape} not supported") return minor_axis, n_modes, xy_dim # If there is an exact match in the image shape, identify # this as the axis containing each mode if shape.count(n_modes) == 1: major_axis = shape.index(n_modes) # If multiple image dimensions share the same number of # elements as number of modes, identify which corresponds # to each mode else: if shape[0] == n_modes: major_axis = 0 else: raise IndexError( f"Image shape {shape} not supported") # Work out the minor axis (stack to average over) from the # remaining image dimensions minor_axes = [ index for index, value in enumerate(shape) if value not in xy_dim and index != major_axis] if len(minor_axes) == 0: minor_axis = None elif len(minor_axes) == 1: minor_axis = minor_axes[0] else: raise IndexError( f"Image shape {shape} not supported") return minor_axis, n_modes, xy_dim
[docs]def get_imagej_param(description, xy_dim, shape): desc_list = description.split('\n') slices = [ line.strip() for line in desc_list if 'slices' in line] if not slices: raise IndexError( f"Image shape {shape} not supported") else: n_slices = int(slices[0].split('=')[-1]) minor_axis = shape.index(n_slices) # Work out the number of modes from the # remaining image dimensions n_modes = [ index for index, value in enumerate(shape) if value not in xy_dim and index != minor_axis ] if len(n_modes) == 0: n_modes = 1 elif len(n_modes) == 1: n_modes = shape[n_modes[0]] else: raise IndexError( f"Image shape {shape} not supported") return minor_axis, n_modes, xy_dim
[docs]def get_tiff_param(tiff_file): """Obtain relevant parameters of TiffFile object""" xy_dim, description = lookup_page(tiff_file.pages[0]) shape = tiff_file.asarray().shape if tiff_file.is_fluoview: return get_fluoview_param(description, xy_dim, shape) elif tiff_file.is_imagej: return get_imagej_param(description, xy_dim, shape) else: # We are using test data desc_dict = json.loads(description) minor_axis = desc_dict['minor_axis'] n_modes = desc_dict['n_modes'] xy_dim = tuple(desc_dict['xy_dim']) return minor_axis, n_modes, xy_dim
[docs]def get_accumulation_number(file_name): """ Extrct accumulation from file name if present. Return default value of 1 if not present. Parameters ---------- file_name: str File name of Tiff image Returns ------- acc_number: int Accumulation number for image Notes ----- Expects the following file formatting: <prefix>-acc<number>.ext """ path, ext = os.path.splitext(file_name) if 'acc' in path.lower(): _, number = path.split('acc') return int(number) return 1
[docs]class SHGReader(BaseMultiImageReader): """Reader class for a combined SHG file"""
[docs] def get_multi_image_class(self): return SHGImage
[docs] def get_supported_file_sets(self): """Returns class of IFileSets that will be supported.""" return [SHGPLTransFileSet]
[docs] def get_filenames(self, file_set): yield file_set.registry['SHG']
def _format_image(self, image, minor_axis=None, acc_number=1): """Transform image to normalised float array and average over stack + accumulation number """ # Average over minor axis if needed if minor_axis is not None: image = np.mean(image, axis=minor_axis) return img_as_float(image) / acc_number
[docs] def load_image(self, filename): with TiffFile(filename) as tiff_file: image = tiff_file.asarray() minor_axis, n_modes, xy_dim = get_tiff_param(tiff_file) logger.debug(f"Number of image modes = {n_modes}") logger.debug(f"Size of image = {xy_dim}") if minor_axis is not None: n_stacks = image.shape[minor_axis] logger.debug(f"Number of stacks = {n_stacks}") acc_number = get_accumulation_number(filename) logger.debug(f"Using accumulation number = {acc_number}") image = self._format_image( image, minor_axis=minor_axis, acc_number=acc_number ) return image
[docs] def can_load(self, filename): """Perform check to see whether file is formatted correctly to be loaded""" try: with TiffFile(filename) as tiff_file: # Check is this is Olympus FluoView formatted if tiff_file.is_fluoview: return True # Check is this is ImageJ formatted if tiff_file.is_imagej: return True # Check if this is test data _, description = lookup_page(tiff_file.pages[0]) desc_dict = json.loads(description) for key in ['minor_axis', 'n_modes', 'xy_dim']: _ = desc_dict[key] except Exception as e: logger.info( f'File type not supported: {e}') return False return True