configured sphinx to generate decent api documentation
This was primarily done to detect bad restructuredtext syntax in python code. This allowed me to detect and fix quite a few errors. In order to fix the orrors in the output document, I also added quite a lot of docstrings, as sphinx autodoc ignores classes and functions that don't have docstrings. This was my first experience with sphinx but I'm not quite happy with it, as it requires too much verbose and time consuming helps and hinting to produce something decent. Moreobver, it suffers from a lack of examples for non basic usage, such as : - how to expose to the user the __item__ methods - how to document multiple return values - etc.
This commit is contained in:
parent
463cd9d0d9
commit
97dc74f08c
|
@ -0,0 +1,6 @@
|
|||
|
||||
The catalog module
|
||||
------------------
|
||||
|
||||
.. automodule:: lipase.catalog
|
||||
:members:
|
|
@ -0,0 +1,5 @@
|
|||
The circsymdetector module
|
||||
--------------------------
|
||||
|
||||
.. automodule:: lipase.circsymdetector
|
||||
:members:
|
|
@ -17,11 +17,29 @@
|
|||
# add these directories to sys.path here. If the directory is relative to the
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
#
|
||||
# import os
|
||||
# import sys
|
||||
# sys.path.insert(0, os.path.abspath('.'))
|
||||
import os
|
||||
import sys
|
||||
sys.path.insert(0, os.path.abspath('../../../src'))
|
||||
|
||||
|
||||
# use mock mechanism for modules that sphinx cannot find (eg ij, which is only available in jython)
|
||||
#import mock # sudo apt install python3-mock
|
||||
|
||||
#MOCK_MODULES = ['numpy', 'matplotlib', 'matplotlib.pyplot']
|
||||
MOCK_MODULES = ['ij', 'ij.process', 'ij.plugin', 'ncsa', 'jarray']
|
||||
autodoc_mock_imports = MOCK_MODULES
|
||||
#for mod_name in MOCK_MODULES:
|
||||
# sys.modules[mod_name] = mock.Mock()
|
||||
|
||||
|
||||
def skip(app, what, name, obj, would_skip, options):
|
||||
if name == "__init__":
|
||||
return False
|
||||
return would_skip
|
||||
|
||||
def setup(app):
|
||||
app.connect("autodoc-skip-member", skip)
|
||||
|
||||
# -- General configuration ------------------------------------------------
|
||||
|
||||
# If your documentation needs a minimal Sphinx version, state it here.
|
||||
|
@ -32,6 +50,7 @@
|
|||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||
# ones.
|
||||
extensions = ['sphinx.ext.autodoc',
|
||||
'sphinx.ext.napoleon',
|
||||
'sphinx.ext.doctest',
|
||||
'sphinx.ext.todo',
|
||||
'sphinx.ext.coverage',
|
||||
|
@ -40,6 +59,12 @@ extensions = ['sphinx.ext.autodoc',
|
|||
'sphinx.ext.viewcode',
|
||||
'sphinx.ext.githubpages']
|
||||
|
||||
|
||||
# enable automatic translation of google style comments to rest comments as google style comments are less wordy (see https://brendanhasz.github.io/2019/01/05/sphinx.html#napoleon-extension)
|
||||
napoleon_google_docstring = True
|
||||
napoleon_numpy_docstring = True
|
||||
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
|
||||
|
@ -90,7 +115,7 @@ todo_include_todos = True
|
|||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
#
|
||||
html_theme = 'alabaster'
|
||||
html_theme = 'classic'
|
||||
|
||||
# Theme options are theme-specific and customize the look and feel of a theme
|
||||
# further. For a list of options available for each theme, see the
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
|
||||
The hdf5 module
|
||||
-----------------
|
||||
|
||||
.. automodule:: lipase.hdf5.hdf5_data
|
||||
:members:
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
The imageengine module
|
||||
----------------------
|
||||
|
||||
.. automodule:: lipase.imageengine
|
||||
:members:
|
|
@ -6,10 +6,20 @@
|
|||
Welcome to lipase's documentation!
|
||||
==================================
|
||||
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:caption: Contents:
|
||||
|
||||
imageengine
|
||||
catalog
|
||||
lipase
|
||||
circsymdetector
|
||||
traps_detector
|
||||
template_matcher
|
||||
maxima_finder
|
||||
hdf5
|
||||
telemos
|
||||
|
||||
|
||||
Indices and tables
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
|
||||
The lipase module
|
||||
-----------------
|
||||
|
||||
.. automodule:: lipase.lipase
|
||||
:members:
|
|
@ -0,0 +1,5 @@
|
|||
The maxima_finder module
|
||||
---------------------------
|
||||
|
||||
.. automodule:: lipase.maxima_finder
|
||||
:members:
|
|
@ -0,0 +1,6 @@
|
|||
|
||||
The telemos module
|
||||
------------------
|
||||
|
||||
.. automodule:: lipase.telemos
|
||||
:members:
|
|
@ -0,0 +1,6 @@
|
|||
|
||||
The template_matcher module
|
||||
---------------------------
|
||||
|
||||
.. automodule:: lipase.template_matcher
|
||||
:members:
|
|
@ -0,0 +1,6 @@
|
|||
|
||||
The traps_detector module
|
||||
-------------------------
|
||||
|
||||
.. automodule:: lipase.traps_detector
|
||||
:members:
|
|
@ -1,17 +1,20 @@
|
|||
|
||||
import os
|
||||
import json
|
||||
from imageengine import IHyperStack, IImageEngine, PixelType
|
||||
from .imageengine import IHyperStack, IImageEngine, PixelType
|
||||
|
||||
|
||||
class DacMetadata(object):
|
||||
"""Represents a display_and_comments.txt metadata file."""
|
||||
"""Represents a display_and_comments.txt metadata file.
|
||||
|
||||
display_and_comments.txt is a metadata file stored by micro manager
|
||||
"""
|
||||
|
||||
def __init__(self, dac_id, dac_file_path):
|
||||
"""Contructor.
|
||||
|
||||
:param str dac_id: eg "res_soleil2018/GGH/GGH_2018_cin2_phiG_I_327_vis_-40_1"
|
||||
:param str dac_file_path: eg "/Users/graffy/ownCloud/ipr/lipase/raw-images/res_soleil2018/GGH/GGH_2018_cin2_phiG_I_327_vis_-40_1/display_and_comments.txt"
|
||||
:param str dac_id: eg ``res_soleil2018/GGH/GGH_2018_cin2_phiG_I_327_vis_-40_1``
|
||||
:param str dac_file_path: eg ``/Users/graffy/ownCloud/ipr/lipase/raw-images/res_soleil2018/GGH/GGH_2018_cin2_phiG_I_327_vis_-40_1/display_and_comments.txt``
|
||||
"""
|
||||
self.dac_id = dac_id
|
||||
self.dac_file_path = dac_file_path
|
||||
|
@ -19,16 +22,20 @@ class DacMetadata(object):
|
|||
self.dac = json.load(dac_file, encoding='latin-1') # note : the micromanager metadata files are encoded in latin-1, not utf8 (see accents in comments)
|
||||
|
||||
def get_channel_name(self, channel_index):
|
||||
"""Returns the name of the given channel index
|
||||
"""
|
||||
channels = self.dac['Channels']
|
||||
return channels[channel_index]["Name"]
|
||||
|
||||
|
||||
class Sequence(object):
|
||||
"""A sequence of images stored in micro manager format
|
||||
"""
|
||||
def __init__(self, catalog, sequence_id, micro_manager_metadata_file_path):
|
||||
"""
|
||||
:param Catalog catalog:
|
||||
:param str sequence_id: eg 'res_soleil2018/GGH/GGH_2018_cin2_phiG_I_327_vis_-40_1/Pos0'
|
||||
:param str micro_manager_metadata_file_path: eg '/Users/graffy/ownCloud/ipr/lipase/raw-images/res_soleil2018/GGH/GGH_2018_cin2_phiG_I_327_vis_-40_1/Pos0/metadata.txt'
|
||||
:param ImageCatalog catalog:
|
||||
:param str sequence_id: eg ``res_soleil2018/GGH/GGH_2018_cin2_phiG_I_327_vis_-40_1/Pos0``
|
||||
:param str micro_manager_metadata_file_path: eg ``/Users/graffy/ownCloud/ipr/lipase/raw-images/res_soleil2018/GGH/GGH_2018_cin2_phiG_I_327_vis_-40_1/Pos0/metadata.txt``
|
||||
"""
|
||||
self.catalog = catalog
|
||||
self.sequence_id = sequence_id
|
||||
|
@ -52,26 +59,36 @@ class Sequence(object):
|
|||
|
||||
@property
|
||||
def num_frames(self):
|
||||
"""Gets the number of frames in the sequence
|
||||
"""
|
||||
summary = self.mmm['Summary']
|
||||
return int(summary['Frames'])
|
||||
|
||||
@property
|
||||
def width(self):
|
||||
"""Gets the width of images in the sequence
|
||||
"""
|
||||
summary = self.mmm['Summary']
|
||||
return int(summary['Width'])
|
||||
|
||||
@property
|
||||
def height(self):
|
||||
"""Gets the height of images in the sequence
|
||||
"""
|
||||
summary = self.mmm['Summary']
|
||||
return int(summary['Height'])
|
||||
|
||||
@property
|
||||
def num_channels(self):
|
||||
"""Gets the number of channels in the sequence
|
||||
"""
|
||||
summary = self.mmm['Summary']
|
||||
return int(summary['Channels'])
|
||||
|
||||
@property
|
||||
def num_slices(self):
|
||||
"""Gets the number of slices in the sequence
|
||||
"""
|
||||
summary = self.mmm['Summary']
|
||||
return int(summary['Slices'])
|
||||
|
||||
|
@ -81,11 +98,13 @@ class Sequence(object):
|
|||
return int(summary['BitDepth'])
|
||||
|
||||
def get_root_path(self):
|
||||
"""Returns the root location of th is sequence
|
||||
"""
|
||||
(dir_name, file_name) = os.path.split(self.micro_manager_metadata_file_path) # pylint: disable=unused-variable
|
||||
return dir_name
|
||||
|
||||
def get_image_file_path(self, channel_index, frame_index, slice_index=0):
|
||||
'''
|
||||
'''Returnsthe file location of the given image in the sequence
|
||||
:param int channel_index:
|
||||
:param int frame_index:
|
||||
:param int slice_index:
|
||||
|
@ -98,7 +117,7 @@ class Sequence(object):
|
|||
|
||||
def get_channel_index(self, channel_id):
|
||||
'''
|
||||
:param str channel_id:
|
||||
:param str channel_id: the identifier of the channel (eg ``'vis'``)
|
||||
'''
|
||||
summary = self.mmm['Summary']
|
||||
channel_index = summary['ChNames'].index(channel_id)
|
||||
|
@ -115,7 +134,7 @@ class Sequence(object):
|
|||
def get_black(self):
|
||||
''' returns the black sequence related to the the sequence self
|
||||
|
||||
:return Sequence:
|
||||
:rtype: Sequence
|
||||
'''
|
||||
seqid_to_black = {
|
||||
'res_soleil2018/GGH/GGH_2018_cin2_phiG_I_327_vis_-40_1/Pos0': 'res_soleil2018/DARK/DARK_40X_60min_1 im pae min_1/Pos0',
|
||||
|
@ -127,7 +146,7 @@ class Sequence(object):
|
|||
def get_white(self):
|
||||
''' returns the white sequence related to the the sequence self
|
||||
|
||||
:return Sequence:
|
||||
:rtype: Sequence
|
||||
'''
|
||||
# assert fixme : res_soleil2018/white/white_24112018_2/Pos0 is visible
|
||||
seqid_to_white = {
|
||||
|
@ -143,7 +162,8 @@ class Sequence(object):
|
|||
:param list(str) selected_channel_ids:
|
||||
:param list(int) selected_frames:
|
||||
:param list(int) selected_slices:
|
||||
:return IHyperStack: the resulting hyperstack
|
||||
:return: the resulting hyperstack
|
||||
:rtype: IHyperStack
|
||||
"""
|
||||
if selected_frames is None:
|
||||
selected_frames = range(self.num_frames)
|
||||
|
@ -170,6 +190,8 @@ def find_dirs_containing_file(file_name, root_dir):
|
|||
"""
|
||||
:param str file_name: the name of the searched files (eg 'metadata.txt')
|
||||
:param str root_dir: the root directory where we search
|
||||
:rtype: list(str)
|
||||
:return: the list of directories that contain the given file
|
||||
"""
|
||||
result = []
|
||||
for root, dirs, files in os.walk(root_dir): # pylint: disable=unused-variable
|
||||
|
@ -178,9 +200,20 @@ def find_dirs_containing_file(file_name, root_dir):
|
|||
return result
|
||||
|
||||
class ImageCatalog(object):
|
||||
"""A catalog of micromanager sequences
|
||||
"""
|
||||
def __init__(self, raw_images_root):
|
||||
"""Creates a new image catalog and populates it by finding the image sequences in the given root directory
|
||||
|
||||
Args:
|
||||
raw_images_root (str): the root directory that is expected to contain the micromanager formatted sequences.
|
||||
|
||||
"""
|
||||
self.raw_images_root = raw_images_root
|
||||
self.sequences = {}
|
||||
"""
|
||||
the dictionary which stores the catalog sequences (dict(str, :py:class:`Sequence`)). The sequences are indexed with the sequence identifiers.
|
||||
"""
|
||||
|
||||
sequence_paths = find_dirs_containing_file('metadata.txt', self.raw_images_root)
|
||||
sequence_ids = [ os.path.relpath(sequence_path, raw_images_root) for sequence_path in sequence_paths ]
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
an image processing technique to produce a 1D signal for each pixel : this 1D signal is obtained by projecting the neighborhood of this pixel on a set of bases (each base is used as a convilution kernel)
|
||||
https://subversion.ipr.univ-rennes1.fr/repos/main/projects/antipode/src/python/antipode/texori.py
|
||||
"""
|
||||
from imageengine import IImageEngine, PixelType, StackImageFeeder
|
||||
from .imageengine import IImageEngine, PixelType, StackImageFeeder
|
||||
import math
|
||||
|
||||
class IProjectorBase(object):
|
||||
|
@ -22,8 +22,11 @@ class IProjectorBase(object):
|
|||
pass
|
||||
|
||||
def create_circle_image(image_size, circle_radius, circle_pos, circle_thickness, background_value=0.0, circle_value=1.0):
|
||||
"""
|
||||
:param dict image_size:
|
||||
"""Creates an image containing the a circle
|
||||
|
||||
:param dict image_size: The width and height of the image to create
|
||||
:rtype: IImage
|
||||
|
||||
"""
|
||||
ie = IImageEngine.get_instance()
|
||||
width = image_size['width']
|
||||
|
@ -152,6 +155,8 @@ class CircularSymmetryProjectorBase(object):
|
|||
|
||||
|
||||
class CircularSymmetryDetector:
|
||||
"""Method to detect objects exhibiting a circular symmetry
|
||||
"""
|
||||
def __init__(self, max_radius, num_angular_sectors, num_radial_sectors=None):
|
||||
"""
|
||||
:param float max_radius: in pixels
|
||||
|
@ -168,15 +173,16 @@ class CircularSymmetryDetector:
|
|||
""" Computes for each pixel the radial profile (with this pixel as center)
|
||||
|
||||
:param IImage src_image:
|
||||
:rtype IHyperstack, Image :
|
||||
:returns:
|
||||
- radial_profiles (:py:class:`IHyperstack`) - each radial profile is stored in the channel coordinate of the hyperstack
|
||||
- radial_profiles (:py:class:`~lipase.imageengine.IHyperStack`) - each pixel stores the radial profile for this pixel. The radial profile is a 1D signal stored along the channel axis of the hyperstack, each channel being related to a radius range.
|
||||
- angular_variance_profiles (:py:class:`IHyperStack`) - each pixel stores the angular variance profile for this pixel. The angular variance profile is a 1D signal stored along the channel axis of the hyperstack, each channel being related to a radius range.
|
||||
- angular_variance_avg (:py:class:`IImage`) - each pixel stores the average variance of signal along the circles of different diameters in the neighborhood
|
||||
"""
|
||||
# https://stackoverflow.com/questions/39759503/how-to-document-multiple-return-values-using-restructuredtext-in-python-2
|
||||
ie = IImageEngine.get_instance()
|
||||
ie.debugger.on_image(src_image, 'src_image')
|
||||
projector_base = CircularSymmetryProjectorBase(self.max_radius, num_angular_sectors=self.num_angular_sectors, num_radial_sectors=self.num_radial_sectors, oversampling_scale=1)
|
||||
|
||||
radial_profile_image = ie.create_hyperstack(width=src_image.get_width(), height=src_image.get_height(), num_channels=projector_base.num_radial_sectors, num_slices=1, num_frames=1, pixel_type=PixelType.F32)
|
||||
angular_variances_image = ie.create_hyperstack(width=src_image.get_width(), height=src_image.get_height(), num_channels=projector_base.num_radial_sectors, num_slices=1, num_frames=1, pixel_type=PixelType.F32)
|
||||
for radius_index in range(projector_base.num_radial_sectors):
|
||||
|
|
|
@ -1 +1 @@
|
|||
from hdf5_data import Group, DataSet, ElementType
|
||||
from .hdf5_data import Group, DataSet, ElementType
|
||||
|
|
|
@ -6,12 +6,20 @@ note: these structures are intentionally made independent of h5py library as thi
|
|||
"""
|
||||
|
||||
class ElementType(object):
|
||||
"""The types that can be used for elements of arrays
|
||||
"""
|
||||
U1=0
|
||||
"""1 bit boolean"""
|
||||
U8=1
|
||||
"""8 bits unsigned integer"""
|
||||
U32=2
|
||||
"""32 bits unsigned integer"""
|
||||
S32=3
|
||||
"""32 bits signed integer"""
|
||||
F32=4
|
||||
"""32 bits floating precision number"""
|
||||
F64=5
|
||||
"""64 bits floating precision number"""
|
||||
|
||||
class DataSet(object):
|
||||
""" an hdf5 dataset
|
||||
|
@ -20,34 +28,64 @@ class DataSet(object):
|
|||
"""
|
||||
:param str name: the name of the dataset
|
||||
:param tuple(int) size: the size of each dimension of the dataset
|
||||
:param ElementType element_type: the type of elements stored in this dataset
|
||||
"""
|
||||
self.name = name
|
||||
"""The name of the dataset (str)
|
||||
"""
|
||||
self.size = size
|
||||
"""The size of the dataset (tuple of integers)
|
||||
"""
|
||||
self._element_type = element_type
|
||||
"""The type of elements in this dataset (ElementType)
|
||||
"""
|
||||
self._elements = []
|
||||
for i in range(self.num_elements): # pylint: disable=unused-variable
|
||||
self._elements.append(0)
|
||||
|
||||
@property
|
||||
def element_type(self):
|
||||
"""Gets the element type of this array
|
||||
|
||||
:rtype: ElementType
|
||||
"""
|
||||
return self._element_type
|
||||
|
||||
@property
|
||||
def dimension(self):
|
||||
"""Gets the dimension of the array contained in this dataset
|
||||
|
||||
Would return 3 for a 3D dataset.
|
||||
|
||||
:rtype: int
|
||||
"""
|
||||
return len(self.size)
|
||||
|
||||
@property
|
||||
def elements(self):
|
||||
"""Gets the elements value in the form of a list
|
||||
|
||||
:rtype: list
|
||||
"""
|
||||
return self._elements
|
||||
|
||||
@property
|
||||
def num_elements(self):
|
||||
"""Gets the number of elements in this dataset
|
||||
|
||||
:rtype: int
|
||||
"""
|
||||
num_elements = 1
|
||||
for i in range(self.dimension):
|
||||
num_elements *= self.size[i]
|
||||
return num_elements
|
||||
|
||||
def get_element(self, pos):
|
||||
"""Gets the value of the element indexed by pos
|
||||
|
||||
:param tuple(int) pos: the index of the element to retreive. This index is expected to be a tuple of integers. The pos argument is expected to have the same length as the number of dimensions of this dataset.
|
||||
|
||||
"""
|
||||
assert len(pos) == len(self.size)
|
||||
return self._elements[self._pos_to_index(pos)]
|
||||
|
||||
|
@ -56,10 +94,20 @@ class DataSet(object):
|
|||
return self._elements[self._pos_to_index(pos)]
|
||||
|
||||
def set_element(self, pos, value):
|
||||
"""Sets the value of the element indexed by pos
|
||||
|
||||
:param tuple(int) pos: the index of the element to set. This index is expected to be a tuple of integers. The pos argument is expected to have the same length as the number of dimensions of this dataset.
|
||||
:param value: the type is expected to be compatible (convertible to) with this dataset's element type
|
||||
"""
|
||||
assert len(pos) == len(self.size)
|
||||
self._elements[self._pos_to_index(pos)] = value
|
||||
|
||||
def __setitem__(self, pos, value):
|
||||
"""Sets the value of the element indexed by pos
|
||||
|
||||
:param tuple(int) pos: the index of the element to set. This index is expected to be a tuple of integers. The pos argument is expected to have the same length as the number of dimensions of this dataset.
|
||||
:param value: the type is expected to be compatible (convertible to) with this dataset's element type
|
||||
"""
|
||||
assert len(pos) == len(self.size)
|
||||
self._elements[self._pos_to_index(pos)] = value
|
||||
|
||||
|
@ -73,7 +121,10 @@ class DataSet(object):
|
|||
|
||||
|
||||
class Group(object):
|
||||
"""An hdf5 group
|
||||
|
||||
A group can contain datasets and other groups
|
||||
"""
|
||||
def __init__(self, name):
|
||||
"""
|
||||
:param str name: the name of the group
|
||||
|
@ -83,19 +134,35 @@ class Group(object):
|
|||
self._groups = {}
|
||||
|
||||
def add_dataset(self, dataset):
|
||||
"""Adds the given dataset to the datasets stored in this group
|
||||
|
||||
:param DataSet dataset:
|
||||
"""
|
||||
assert isinstance(dataset, DataSet)
|
||||
self._datasets[dataset.name] = dataset
|
||||
|
||||
def add_group(self, group):
|
||||
"""Adds the given group to the groups stored in this group
|
||||
|
||||
:param Group group:
|
||||
"""
|
||||
assert isinstance(group, Group)
|
||||
self._datasets[group.name] = group
|
||||
|
||||
@property
|
||||
def datasets(self):
|
||||
"""
|
||||
|
||||
:rtype: dict(str, :py:class:`DataSet`)
|
||||
"""
|
||||
return self._datasets
|
||||
|
||||
@property
|
||||
def groups(self):
|
||||
"""
|
||||
|
||||
:rtype: dict(str, :py:class:`Group`)
|
||||
"""
|
||||
return self._groups
|
||||
|
||||
def __getitem__(self, dataset_name):
|
||||
|
|
|
@ -41,7 +41,9 @@ class PixelType:
|
|||
return PixelType.PIXEL_TYPE_TO_NUM_BITS[ pixel_type ]
|
||||
|
||||
class IImage(ABC):
|
||||
"""An abstact class representing an image (2D matrix of pixels)
|
||||
|
||||
"""
|
||||
@abc.abstractmethod
|
||||
def clone(self, clone_pixel_type=None):
|
||||
"""
|
||||
|
@ -79,21 +81,23 @@ class IImage(ABC):
|
|||
@abc.abstractmethod
|
||||
def get_pixel_type(self):
|
||||
"""
|
||||
:rtype PixelType:
|
||||
:rtype: PixelType
|
||||
"""
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_value_range(self):
|
||||
"""
|
||||
:rtype (float, float): the min and the max value
|
||||
:rtype: (float, float)
|
||||
:returns: the min and the max value
|
||||
"""
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_mean_value(self):
|
||||
"""
|
||||
:rtype float: the mean pixel value in the image
|
||||
:rtype float:
|
||||
:return: the mean pixel value in the image
|
||||
"""
|
||||
pass
|
||||
|
||||
|
@ -106,6 +110,11 @@ class IImage(ABC):
|
|||
"""
|
||||
|
||||
class IHyperStack(ABC):
|
||||
"""An abstact class representing an hyperstack (5D matrix of pixels)
|
||||
|
||||
The name hyperstachk comes from imagej's terminology.
|
||||
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_width(self):
|
||||
|
@ -158,17 +167,22 @@ class IImageFeeder(ABC):
|
|||
def next(self):
|
||||
"""returns the next image in the collection
|
||||
for iterator
|
||||
|
||||
:rtype: IImage
|
||||
"""
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_num_images(self):
|
||||
"""Gets the number of images in this collection
|
||||
"""
|
||||
pass
|
||||
|
||||
def create_hyperstack(self):
|
||||
"""
|
||||
creates an hyperstack from this image feeder
|
||||
:rtype IHyperStack
|
||||
|
||||
:rtype: IHyperStack
|
||||
"""
|
||||
it = iter(self)
|
||||
|
||||
|
@ -214,7 +228,7 @@ class StackImageFeeder(IImageFeeder):
|
|||
|
||||
def __init__(self, hyperstack):
|
||||
"""
|
||||
:param IHyperStack hyperstack
|
||||
:param IHyperStack hyperstack:
|
||||
"""
|
||||
self.hyperstack = hyperstack
|
||||
|
||||
|
@ -270,19 +284,29 @@ class StackImageFeeder(IImageFeeder):
|
|||
|
||||
|
||||
class IImageProcessingDebugger(ABC):
|
||||
"""
|
||||
An abstract image processing debugger.
|
||||
|
||||
Its role is to do a specific action on some events related to image processing
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def on_image(self, image, image_id):
|
||||
'''
|
||||
'''This method is called by image processing methods when they have an intermediate image they want to send to the debugger
|
||||
|
||||
:param IImage image:
|
||||
:param str image_id:
|
||||
'''
|
||||
|
||||
class NullDebugger(IImageProcessingDebugger):
|
||||
"""
|
||||
This debugger is simply ignoring anything it receives.
|
||||
|
||||
Therefore this debugger is to be chosen when we want to disable the debugger.
|
||||
"""
|
||||
def __init__(self):
|
||||
IImageProcessingDebugger.__init__(self)
|
||||
|
||||
|
@ -335,7 +359,7 @@ class IImageEngine(ABC):
|
|||
@staticmethod
|
||||
def get_instance():
|
||||
"""Static access method.
|
||||
:rtype IImageEngine:
|
||||
:rtype: IImageEngine
|
||||
"""
|
||||
|
||||
assert IImageEngine.__instance is not None
|
||||
|
@ -350,13 +374,13 @@ class IImageEngine(ABC):
|
|||
@abc.abstractmethod
|
||||
def create_hyperstack(self, width, height, num_channels, num_slices, num_frames, pixel_type):
|
||||
"""
|
||||
:rtype IHyperStack:
|
||||
:rtype: IHyperStack
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def create_image(self, width, height, pixel_type):
|
||||
"""
|
||||
:rtype IImage:
|
||||
:rtype: IImage
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
|
@ -380,7 +404,8 @@ class IImageEngine(ABC):
|
|||
|
||||
:param IImage image1:
|
||||
:param IImage image2:
|
||||
:rtype IImage: image1-image2
|
||||
:rtype: IImage
|
||||
:return: image1-image2
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
|
@ -390,7 +415,8 @@ class IImageEngine(ABC):
|
|||
|
||||
:param IImage image1:
|
||||
:param IImage image2:
|
||||
:rtype IImage: image1*image2
|
||||
:rtype: IImage
|
||||
:return: image1*image2
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
|
@ -400,7 +426,8 @@ class IImageEngine(ABC):
|
|||
|
||||
:param IImage numerator_image:
|
||||
:param IImage denominator_image:
|
||||
:rtype IImage: numerator_image/denominator_image
|
||||
:rtype: IImage
|
||||
:return: numerator_image/denominator_image
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
|
@ -409,7 +436,8 @@ class IImageEngine(ABC):
|
|||
computes the absolute value of each pixel in the input image
|
||||
|
||||
:param IImage image:
|
||||
:rtype IImage: an image containing the absolute values of the input image
|
||||
:rtype: IImage
|
||||
:return: an image containing the absolute values of the input image
|
||||
"""
|
||||
|
||||
|
||||
|
@ -419,7 +447,7 @@ class IImageEngine(ABC):
|
|||
computes the edge transform using a sobel filter
|
||||
|
||||
:param IImage image:
|
||||
:rtype IImage:
|
||||
:rtype: IImage
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
|
@ -427,7 +455,7 @@ class IImageEngine(ABC):
|
|||
"""Compute for each pixel position the maximum at this position in all input images.
|
||||
|
||||
:param IImageFeeder image_feeder:
|
||||
:rtype IImage:
|
||||
:rtype: IImage
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
|
@ -435,7 +463,7 @@ class IImageEngine(ABC):
|
|||
"""Compute for each pixel position the median value at this position in all input images.
|
||||
|
||||
:param IImageFeeder image_feeder:
|
||||
:rtype IImage:
|
||||
:rtype: IImage
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
|
@ -443,7 +471,7 @@ class IImageEngine(ABC):
|
|||
"""Compute for each pixel position the mean value at this position in all input images.
|
||||
|
||||
:param IImageFeeder image_feeder:
|
||||
:rtype IImage:
|
||||
:rtype: IImage
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
|
@ -462,7 +490,7 @@ class IImageEngine(ABC):
|
|||
:param PixelType or None dst_type: pixel type of the destination image (if None, the pixel type is the same as the source image type)
|
||||
:param IImage kernel: convolution kernel (or rather a correlation kernel), a single-channel floating point matrix; if you want to apply different kernels to different channels, split the image into separate color planes using split() and process them individually.
|
||||
:param tuple(int, int) angor: anchor of the kernel that indicates the relative position of a filtered point within the kernel; the anchor should lie within the kernel; default value (-1,-1) means that the anchor is at the kernel center.
|
||||
:rtype IImage:
|
||||
:rtype: IImage
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
|
@ -480,9 +508,11 @@ class IImageEngine(ABC):
|
|||
|
||||
:param IImage image:
|
||||
:param int band_width: width of the outer band, in pixels
|
||||
:rtype IImage:
|
||||
:rtype: IImage
|
||||
|
||||
adaptation for the following code from matlab telemosToolbx's estimatedwhiteFluoImageTelemos function
|
||||
adaptation for the following code from matlab telemosToolbx's::
|
||||
|
||||
estimatedwhiteFluoImageTelemos function
|
||||
outer = white_estimate;
|
||||
white_estimate = white_estimate(4:(end-3),4:(end-3));
|
||||
outer(1:3,4:(end-3))=repmat(white_estimate(1,:),3,1); # top
|
||||
|
@ -498,33 +528,37 @@ class IImageEngine(ABC):
|
|||
"""
|
||||
:param IImage src_image:
|
||||
:param IImage template_image:
|
||||
:return IImage: similarity image
|
||||
:rtype: IImage
|
||||
:return: similarity image
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def find_maxima(self, src_image, threshold, tolerance):
|
||||
"""
|
||||
to understand the meaning of threshold and maxima, I had to look at the source code in https://github.com/imagej/imagej1/blob/master/ij/plugin/filter/MaximumFinder.java#L636
|
||||
|
||||
1. The algorithm first builds the list of maxima candidates. Only local maxima above this threshold are considered as valid maxima candidates
|
||||
2. Then each maximum candidate is processed to find its neighborhood area, starting from the highest. For ecach candidate, a propagation is performed on neighbours as long as the neighbour value is within the tolerance of the maximum candidate. If another candidate is encountered as a neighbour, then this candidate is removed as it('s been absorbed by the current maximum candidate)
|
||||
|
||||
:param IImage src_image:
|
||||
:param float threshold: local maxima below this value are ignored
|
||||
:param float tolerance: ignore local maxima if they are in the neighborhood of another maximum. The neighborhood of a maximum is defined by the area that propagates until the the difference between the pixel value and the value of the considered local maximum exceeds tolerance. The higher the tolerance, the more local maxima are removed....
|
||||
:return list(Match): maxima
|
||||
:return: maxima
|
||||
:rtype: list(Match)
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def threshold(self, src_image, threshold_value):
|
||||
"""
|
||||
:param float threshold_value:
|
||||
:rtype IImage:
|
||||
:rtype: IImage
|
||||
:return: the resulting image
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def auto_threshold(self, src_image):
|
||||
"""
|
||||
:rtype IImage:
|
||||
:rtype: IImage
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
|
@ -534,7 +568,8 @@ class IImageEngine(ABC):
|
|||
|
||||
:param IImage src_binary_image:
|
||||
:param (int, int) flood_start_pos: start position of the flood fill
|
||||
:rtype IImage: binary image
|
||||
:rtype: IImage
|
||||
:return: binary image
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
|
@ -543,7 +578,8 @@ class IImageEngine(ABC):
|
|||
fills the holes in the given binary image
|
||||
|
||||
:param IImage src_binary_image:
|
||||
:rtype IImage: binary image
|
||||
:rtype: IImage
|
||||
:return: binary image
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
|
@ -552,6 +588,7 @@ class IImageEngine(ABC):
|
|||
inverts the given image pixel values (black becomes white and white becomes black)
|
||||
|
||||
:param IImage src_binary_image:
|
||||
:rtype IImage: binary image
|
||||
:rtype: IImage
|
||||
:return: binary image
|
||||
"""
|
||||
|
||||
|
|
|
@ -34,7 +34,7 @@ def clone_processor(src_processor, clone_pixel_type):
|
|||
"""
|
||||
:param Processor processor:
|
||||
:param PixelType clone_pixel_type:
|
||||
:rtype Processor:
|
||||
:rtype: Processor
|
||||
"""
|
||||
clone_processor = {
|
||||
PixelType.U8: src_processor.convertToByteProcessor(),
|
||||
|
@ -315,7 +315,7 @@ class IJImageEngine(IImageEngine):
|
|||
"""Computes for each pixel position the maximum at this position in all input images.
|
||||
|
||||
:param IImageFeeder image_feeder:
|
||||
:rtype IJImage:
|
||||
:rtype: IJImage
|
||||
"""
|
||||
it = iter(image_feeder)
|
||||
|
||||
|
@ -336,7 +336,7 @@ class IJImageEngine(IImageEngine):
|
|||
"""Computes for each pixel position the median value at this position in all input images.
|
||||
|
||||
:param IImageFeeder image_feeder:
|
||||
:rtype IJmage:
|
||||
:rtype: IJmage
|
||||
"""
|
||||
hyperstack = image_feeder.create_hyperstack()
|
||||
# https://imagej.nih.gov/ij/developer/api/ij/plugin/ZProjector.html
|
||||
|
|
|
@ -11,12 +11,12 @@ from ij.process import ImageStatistics # pylint: disable=import-error
|
|||
from ij.plugin import ImageCalculator # pylint: disable=import-error
|
||||
from ij.plugin import ZProjector # pylint: disable=import-error
|
||||
from ij import WindowManager # pylint: disable=import-error
|
||||
from catalog import Sequence, ImageCatalog
|
||||
import telemos
|
||||
from imageengine import IImageEngine, IImageProcessingDebugger, NullDebugger, StackImageFeeder, IHyperStack
|
||||
from .catalog import Sequence, ImageCatalog
|
||||
from . import telemos
|
||||
from .imageengine import IImageEngine, IImageProcessingDebugger, NullDebugger, StackImageFeeder, IHyperStack
|
||||
# greeting = "Hello, " + name + "!"
|
||||
from hdf5.hdf5_data import Group, DataSet, ElementType
|
||||
from imagej.hdf5serializer import save_hdf5_file
|
||||
from .hdf5.hdf5_data import Group, DataSet, ElementType
|
||||
from .imagej.hdf5serializer import save_hdf5_file
|
||||
|
||||
import abc
|
||||
|
||||
|
@ -144,7 +144,7 @@ class IBackgroundEstimator(ABC):
|
|||
def estimate_background(self, visible_traps_sequence):
|
||||
"""
|
||||
:param IHyperStack visible_traps_sequence:
|
||||
:rtype IImage: the estimated background image
|
||||
:rtype: IImage the estimated background image
|
||||
"""
|
||||
pass
|
||||
|
||||
|
@ -189,7 +189,7 @@ class GlobulesAreaEstimator(object):
|
|||
def detect_particles(self, visible_traps_sequence):
|
||||
"""
|
||||
:param IHyperStack visible_traps_sequence:
|
||||
:rtype hdf5_data.Group:
|
||||
:rtype: hdf5_data.Group
|
||||
"""
|
||||
background_image = self.background_estimator.estimate_background(visible_traps_sequence)
|
||||
ie = IImageEngine.get_instance()
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import abc
|
||||
from imageengine import IImageEngine
|
||||
from .imageengine import IImageEngine
|
||||
|
||||
ABC = abc.ABCMeta('ABC', (object,), {})
|
||||
|
||||
|
@ -14,15 +14,22 @@ class Match(object):
|
|||
return "(%d, %d) - %f" % (self.x, self.y, self.correlation)
|
||||
|
||||
class IMaximaFinder(ABC):
|
||||
"""Abstract class for methods aiming at detecting the maxima of an image
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def find_maxima(self, image):
|
||||
"""
|
||||
:param IImage image:
|
||||
:rtype list(Match):
|
||||
:rtype: list(Match)
|
||||
"""
|
||||
|
||||
class MaximaFinder(IMaximaFinder):
|
||||
"""
|
||||
A maxima finder based on imagej's MaximumFinder
|
||||
|
||||
Uses the technique implemented in https://github.com/imagej/imagej1/blob/master/ij/plugin/filter/MaximumFinder.java
|
||||
"""
|
||||
|
||||
def __init__(self, threshold, tolerance):
|
||||
"""
|
||||
|
|
|
@ -1,33 +1,15 @@
|
|||
"""preprocessing of synchrotron images based on telemosToolbox."""
|
||||
|
||||
from catalog import ImageCatalog, Sequence
|
||||
from imageengine import IImageEngine, PixelType, FileImageFeeder
|
||||
from .catalog import ImageCatalog, Sequence
|
||||
from .imageengine import IImageEngine, PixelType, FileImageFeeder
|
||||
|
||||
class WhiteEstimator(object):
|
||||
|
||||
def __init__(self, open_size, close_size, average_size):
|
||||
"""
|
||||
:param int open_size: the diameter of the structuring element used to perform mathematical morphology's opening operator (in pixels)
|
||||
:param int close_size: the diameter of the structuring element used to perform mathematical morphology's closing operator (in pixels)
|
||||
"""
|
||||
self.open_size = open_size
|
||||
self.close_size = close_size
|
||||
self.average_size = average_size
|
||||
Method that estimates the white from a sequence of images
|
||||
|
||||
def _remove_particles(self, white_estimate):
|
||||
IImageEngine.get_instance().perform_gray_morphology(white_estimate, operator='open', structuring_element_shape='square', structuring_element_radius=(self.open_size + 1) / 2)
|
||||
IImageEngine.get_instance().perform_gray_morphology(white_estimate, operator='close', structuring_element_shape='square', structuring_element_radius=(self.close_size + 1) / 2)
|
||||
This method expects the sequence of images to represen sparse particles moving over a white background
|
||||
|
||||
IImageEngine.get_instance().mean_filter(white_estimate, radius=(self.average_size + 1) / 2)
|
||||
|
||||
def estimate_white(self, sequences, channel_ids, dark=None):
|
||||
"""Estimation of the white fluorescence image shape of synchrotron light from experimental images of Telemos microscope.
|
||||
|
||||
:param list(Sequence) sequences: the sequences to consider
|
||||
:param list(str) channel_ids: the channels to consider, eg ['DM300_327-353_fluo']
|
||||
:param ImagePlus or None dark:
|
||||
:rtype ImagePlus:
|
||||
Code adapted from matlab telemosToolbx's estimatewhiteFluoImageTelemos function
|
||||
Code adapted from matlab telemosToolbx's estimatewhiteFluoImageTelemos function::
|
||||
|
||||
% this function is specific of Telemos images (DISCO line SOLEIL)
|
||||
% acquired using micromanager software linked to imageJ
|
||||
|
@ -115,6 +97,30 @@ class WhiteEstimator(object):
|
|||
% 4 juin 2019 : replace exist by isfolder or isfile
|
||||
% 14 juin : close figure at the end
|
||||
"""
|
||||
|
||||
def __init__(self, open_size, close_size, average_size):
|
||||
"""
|
||||
:param int open_size: the diameter of the structuring element used to perform mathematical morphology's opening operator (in pixels)
|
||||
:param int close_size: the diameter of the structuring element used to perform mathematical morphology's closing operator (in pixels)
|
||||
"""
|
||||
self.open_size = open_size
|
||||
self.close_size = close_size
|
||||
self.average_size = average_size
|
||||
|
||||
def _remove_particles(self, white_estimate):
|
||||
IImageEngine.get_instance().perform_gray_morphology(white_estimate, operator='open', structuring_element_shape='square', structuring_element_radius=(self.open_size + 1) / 2)
|
||||
IImageEngine.get_instance().perform_gray_morphology(white_estimate, operator='close', structuring_element_shape='square', structuring_element_radius=(self.close_size + 1) / 2)
|
||||
|
||||
IImageEngine.get_instance().mean_filter(white_estimate, radius=(self.average_size + 1) / 2)
|
||||
|
||||
def estimate_white(self, sequences, channel_ids, dark=None):
|
||||
"""Estimation of the white fluorescence image shape of synchrotron light from experimental images of Telemos microscope.
|
||||
|
||||
:param list(Sequence) sequences: the sequences to consider
|
||||
:param list(str) channel_ids: the channels to consider, eg ['DM300_327-353_fluo']
|
||||
:param ImagePlus or None dark:
|
||||
:rtype: ImagePlus
|
||||
"""
|
||||
image_set = FileImageFeeder()
|
||||
for sequence in sequences:
|
||||
for channel_id in channel_ids:
|
||||
|
@ -170,10 +176,12 @@ class InteractiveWhiteEstimator(WhiteEstimator):
|
|||
|
||||
def correct_non_uniform_lighting(non_uniform_sequence, channel_id, white_estimator=WhiteEstimator(open_size=75, close_size=75, average_size=75)):
|
||||
""" correction of non uniform lighting
|
||||
|
||||
:param Sequence non_uniform_sequence: the input sequence, which is expected to have a non uniform lighting
|
||||
:param str channel_id:
|
||||
:param WhiteEstimator white_estimator: the method used to estimate the light image
|
||||
:return IHyperStack: the hyperstack of images after correction
|
||||
:rtype: IHyperStack
|
||||
:return: the hyperstack of images after correction
|
||||
"""
|
||||
ie = IImageEngine.get_instance()
|
||||
|
||||
|
@ -235,6 +243,9 @@ def find_white_reference_image(white_estimate, white_z_stack):
|
|||
:param IImage white_estimate:
|
||||
:param Sequence white_z_stack:
|
||||
:param IImageEngine image_engine: the image processor to use
|
||||
|
||||
Based on original matlab code::
|
||||
|
||||
% this function is specific of Telemos images (DISCO line SOLEIL)
|
||||
% acquired using micromanager software linked to imageJ
|
||||
|
||||
|
|
|
@ -1,11 +1,16 @@
|
|||
from imageengine import IImageEngine
|
||||
from maxima_finder import IMaximaFinder
|
||||
from .imageengine import IImageEngine
|
||||
from .maxima_finder import IMaximaFinder
|
||||
|
||||
class TemplateMatcher(object):
|
||||
"""
|
||||
Method to detect a pattern in an image.
|
||||
|
||||
This method finds the locations of the given pattern in the image, provided the pattern is replicated without other geometric distortions than translations.
|
||||
"""
|
||||
|
||||
def __init__(self, maxima_finder):
|
||||
"""
|
||||
:param IMaximaFinder: maxima_finder
|
||||
:param IMaximaFinder maxima_finder:
|
||||
"""
|
||||
self.maxima_finder = maxima_finder
|
||||
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
|
||||
from catalog import ImageCatalog, Sequence
|
||||
from .catalog import ImageCatalog, Sequence
|
||||
# from imageengine import Toto
|
||||
from imageengine import IImageEngine, Aabb, StackImageFeeder
|
||||
from imageengine import PixelType
|
||||
from telemos import WhiteEstimator, correct_non_uniform_lighting
|
||||
from maxima_finder import Match
|
||||
from template_matcher import TemplateMatcher
|
||||
from .imageengine import IImageEngine, Aabb, StackImageFeeder
|
||||
from .imageengine import PixelType
|
||||
from .telemos import WhiteEstimator, correct_non_uniform_lighting
|
||||
from .maxima_finder import Match
|
||||
from .template_matcher import TemplateMatcher
|
||||
|
||||
class TrapBinarizer(object):
|
||||
|
||||
|
|
Loading…
Reference in New Issue