Source code for timagetk.components.multi_angle

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# ------------------------------------------------------------------------------
#  Copyright (c) 2022 Univ. Lyon, ENS de Lyon, UCB Lyon 1, CNRS, INRAe, Inria
#  All rights reserved.
#  This file is part of the TimageTK library, and is released under the "GPLv3"
#  license. Please see the LICENSE.md file that should have been included as
#  part of this package.
# ------------------------------------------------------------------------------

"""Multi-angle image class.

Since they can be made of a great number of images, and that these can be
too big to save them all in memory we use disk file access instead of loading
everything in memory.

"""

from os.path import join

from timagetk.components.spatial_image import SpatialImage
from timagetk.components.trsf import Trsf
from timagetk.io import imread
from timagetk.io import imsave
from timagetk.io.util import assert_all_files
from timagetk.bin.logger import get_logger

log = get_logger(__name__)

#: Naming convention for mean image files.
MEAN_IMG_FNAME = "{}-mean_image-iter{}.{}"  # name,  iter_id, extension
#: Naming convention for transformation files.
TRSF_FNAME = "{}t{}_on_t{}-{}-{}.trsf"  # name, float_angle, ref_angle, iter_id, trsf_type
#: Default extension for image files.
DEF_IMG_EXT = 'mha'
#: Default transformations dictionary.
DEF_DICT_TRSF = {'rigid': {}, 'affine': {}, 'vectorfield': {}}


[docs] class MultiAngleImage(list): """Multi-angles image data structure. This data structure rely on filenames to save some memory during the registration process, especially for large images. Attributes ---------- trsfs : dict Dictionary of transformation files location structured as {'<trsf_type>': {n_iter : {(flo_angle, ref_angle): '<path_to_Trsf>'}}}, with: * ``'<trsf_type>'`` the type of transformation * ``n_iter`` the iteration id * ``flo_angle`` & ``ref_angle`` the floating and reference angle-id, use 'mean_<n_iter>' when using mean image as reference * ``'<path_to_Trsf>'`` the corresponding transformation file location mean_image : list List of mean image, ordered by iterations, if any. .. todo:: Use saved 'acquisition_time' to check images are indeed multiple angle of the same object! Examples -------- >>> from timagetk.components.multi_angle import MultiAngleImage >>> from timagetk.io.util import shared_data >>> from timagetk.io import imread >>> list_fnames = [shared_data(f'p58-t0-a{idx}.lsm', "p58") for idx in range(3)] >>> # Step 1: Create the MultiAngle object >>> ma = MultiAngleImage(list_fnames) >>> # Manual creation of a rigid trsf to generate an artificial multi-angles image: >>> from timagetk.algorithms.trsf import create_trsf >>> from timagetk.algorithms.trsf import apply_trsf >>> trsf = create_trsf('random', trsf_type='rigid', angle_range=[0.2, 0.25], translation_range=[0.2, 1.2]) >>> trsf.print() >>> float_img = apply_trsf(ref_img, trsf) >>> # Step 1: Create the MultiAngle object >>> ma = MultiAngleImage([ref_img, float_img]) """
[docs] def __init__(self, list_img, prefix="", **kwargs): """MultiAngleImage object constructor. Parameters ---------- list_img : list of SpatialImages or list of str List of images representing multiple angles of the same object. prefix : str, optional If set, filename prefix used to save the intermediary images at each iteration. Else only the last one is saved. Other Parameters ---------------- name : str Name of the multi-angles image. Raises ------ IOError If not all image files can be found. """ # - Initialize EMPTY attributes: self.trsfs = DEF_DICT_TRSF # -- Mean images computed for each iteration of fusion:- {n_iter : location}}, with # * ``n_iter`` (int) the iteration id as key # * ``filename`` (str) the corresponding 'mean image' file location self.mean_image = {} # -- Reference images:- {n_iter : location}}, with # * ``n_iter`` the iteration id # * ``location`` the corresponding mean image file location self.ref_image = {} # dictionary of reference images with 'iteration id' as key and 'filename' (str) as value assert_all_files(list_img) super().__init__(list_img) # - Defines attributes: # -- Update the number of angles: self.nb_angles = len(self) # -- Prefix or name self.prefix = prefix # - Set some attributes from kwargs: self._ext = kwargs.get("ext", DEF_IMG_EXT) # -- Output path: self._out_path = ""
def _get_trsf_fname(self, float_a, ref_a, iter_id, trsf_type): """Formatter for transformation file names. Parameters ---------- float_a : int Floating (registered) angle-id ref_a : int Reference angle-id iter_id : int Iteration id trsf_type : str Type of transformation used Returns ------- str Formatted filename for a transformation """ name = self.prefix if self.prefix != "": name += "-" fname = TRSF_FNAME.format(name, float_a, ref_a, iter_id, trsf_type) return join(self._out_path, fname) def _get_mean_img_fname(self, iter_id): """Formatter for mean image file names. Parameters ---------- iter_id : int Iteration id Returns ------- str Formatted filename for a mean image """ name = self.prefix if self.prefix != "": name += "-" fname = MEAN_IMG_FNAME.format(name, iter_id, self._ext) return join(self._out_path, fname) # def _handle_trsf(self, trsf, float_a, ref_a, iter_id): # """ # Generic function to know what to do when you get a Trsf. # # Save the matrix if the Trsf is linear, else write a file and save its # location in ``self.trsf``. # # Parameters # ---------- # trsf : Trsf # transformation to handle # method : str # used method # float_a : int # floating image angle index # ref_a : int # reference image angle index # iter_id : int # iteration id # # """ # if isinstance(trsf, list): # return [self._handle_trsf(t, float_a, ref_a, iter_id) for t # in trsf] # else: # # - Add iter_id key and empty trsf dict # if not iter_id in self.rigid_trsf: # self.rigid_trsf.update({iter_id: DEF_DICT_TRSF}) # if trsf.is_linear(): # # - In case of linear transformation, save the matrix # self.rigid_trsf[iter_id].update({(float_a, ref_a): trsf}) # else: # # - In case of a non-linear transformation, write the file and save its location # fname = self._get_trsf_fname(float_a, ref_a, iter_id, # 'vectorfield') # trsf.write(fname) # self.rigid_trsf[iter_id].update({(float_a, ref_a): fname}) def _handle_mean_img(self, image, iter_id): """Generic function to know what to do when you get a mean image. Write a file with its 'iteration id' in its name and save its location in ``self.mean_image[iter_id]``. Parameters ---------- image : timagetk.SpatialImage Image to handle iter_id : int Iteration id """ if isinstance(image, list): return [self._handle_mean_img(t, iter_id) for t in image] else: fname = self._get_mean_img_fname(iter_id) imsave(fname, image) self.mean_image.update({iter_id: fname}) return None
[docs] def get_angle_image(self, angle_id): """Return SpatialImage for given angle-id. Parameters ---------- angle_id : int Angle-id of the image, *i.e.* same order as given list. Returns ------- SpatialImage Image of given angle-id """ if isinstance(angle_id, int): return imread(self[angle_id]) elif isinstance(angle_id, str): return imread(self.mean_image[angle_id]) else: raise ValueError("Unknown angle id '{}'!".format(angle_id))
[docs] def get_images(self): """Return all SpatialImage as a list. Returns ------- list of SpatialImage List with all SpatialImages """ # FIXME: return [self.get_angle_image(angle_id) for angle_id in range(self.nb_angles)]
[docs] def get_average_image(self, iter_id): """Return the average image corresponding to the iteration. Parameters ---------- iter_id : int Iteration step to get the image from. Returns ------- SpatialImage The corresponding averaged intensity image. """ return imread(self.mean_image[iter_id])
[docs] def get_trsf(self, trsf_type, iter_id, angle_id): """Return the average image corresponding to the iteration. Parameters ---------- trsf_type : {'rigid', 'affine', 'vectorfield'} Type of transformation to get. iter_id : int Iteration step to get the transformation from. angle_id : int Angle image id to get the transformation from. Returns ------- Trsf The loaded transformation object. """ ref_img = self.ref_image[iter_id] return Trsf(self.trsfs[trsf_type][iter_id][(angle_id, ref_img)])
[docs] def save(self, filename): """Save the data structure to a file. Parameters ---------- filename : str File path & name to use. """ raise NotImplementedError("YET...")