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:
Guillaume Raffy 2020-04-14 18:58:03 +02:00
parent 463cd9d0d9
commit 97dc74f08c
22 changed files with 425 additions and 172 deletions

View File

@ -0,0 +1,6 @@
The catalog module
------------------
.. automodule:: lipase.catalog
:members:

View File

@ -0,0 +1,5 @@
The circsymdetector module
--------------------------
.. automodule:: lipase.circsymdetector
:members:

View File

@ -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

View File

@ -0,0 +1,7 @@
The hdf5 module
-----------------
.. automodule:: lipase.hdf5.hdf5_data
:members:

View File

@ -0,0 +1,5 @@
The imageengine module
----------------------
.. automodule:: lipase.imageengine
:members:

View File

@ -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

View File

@ -0,0 +1,6 @@
The lipase module
-----------------
.. automodule:: lipase.lipase
:members:

View File

@ -0,0 +1,5 @@
The maxima_finder module
---------------------------
.. automodule:: lipase.maxima_finder
:members:

View File

@ -0,0 +1,6 @@
The telemos module
------------------
.. automodule:: lipase.telemos
:members:

View File

@ -0,0 +1,6 @@
The template_matcher module
---------------------------
.. automodule:: lipase.template_matcher
:members:

View File

@ -0,0 +1,6 @@
The traps_detector module
-------------------------
.. automodule:: lipase.traps_detector
:members:

View File

@ -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 ]

View File

@ -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):

View File

@ -1 +1 @@
from hdf5_data import Group, DataSet, ElementType
from .hdf5_data import Group, DataSet, ElementType

View File

@ -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):

View File

@ -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):
"""
@ -73,27 +75,29 @@ class IImage(ABC):
def set_subimage(self, subimage, position):
"""
:param IImage subimage: the image to copy into self
:param (int, int) position: where to insert the subimage in self (upper left pixel)
:param (int,int) position: where to insert the subimage in self (upper left pixel)
"""
@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
"""

View File

@ -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

View File

@ -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()

View File

@ -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):
"""

View File

@ -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):
"""
Method that estimates the white from a sequence of images
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
This method expects the sequence of images to represen sparse particles moving over a white background
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:
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
@ -114,6 +96,30 @@ class WhiteEstimator(object):
% 27 mai 2019 : offset dark
% 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:
@ -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,95 +243,98 @@ 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
% this function is specific of Telemos images (DISCO line SOLEIL)
% acquired using micromanager software linked to imageJ
%% input
% nothing : interactive function
Based on original matlab code::
%% output
% zmax : focal plane
% this function is specific of Telemos images (DISCO line SOLEIL)
% acquired using micromanager software linked to imageJ
%% principe
% images were acquired for a given roi which position is found in
% metadata.txt file
% white and dark images were recorded for the full field of view of the DISCO
% TELEMOS camera
%
% correlation between a estimated white fluorescence image estalished from
% actual acquisitions of a given sample and all z plane acquired for the reference TELEMOS white
% image (Matthieu slide or any fluorescent homogeneous image) :
%% input
% nothing : interactive function
% The estimated white fluorescence image is generallly obtained bt using function whiteFluoImageTelemosestimation
% This is not compulsary as any homogenous sample image hat can roughly show the shape of illumination can be used to find
% the white reference image
%
%
% the z plane for which the maximum correlation is observed between estimated white and reference white images is retained.
% the white image is then offsetted (its corresponding Dark is subtracted) and copied in the subfolder <WhiteReference> of the sample
% rootfolder to show that it has been specifically selected for the considered experiment
%The matlab corrcoeff function is used
%
% correlation coefficients are saved in a file called
% 'corr.txt' in the subfolder 'WhiteReference'
%
%
% expected input folder hierarchy:
%
% >sampleFolder
% > PosFolders or RoiFolders
% > WhiteEstimate
% >darkFolder
% >darkFolder.smooth
% >whiteFolder
% > darkFolderforWhite
% > darkFolderforWhite.smooth
% >whiteFolder.smooth
%
%
% expected output folder hierarchy:
%
% >sampleFolder
% > PosFolders or RoiFolders
% > WhiteEstimate
% > WhiteReference
% > white after offset
% >darkFolder
% >darkFolder.smooth
% >whiteFolder
% > darkFolderforWhite
% > darkFolderforWhite.smooth
% >whiteFolder.smooth
%% output
% zmax : focal plane
%% principe
% images were acquired for a given roi which position is found in
% metadata.txt file
% white and dark images were recorded for the full field of view of the DISCO
% TELEMOS camera
%
% correlation between a estimated white fluorescence image estalished from
% actual acquisitions of a given sample and all z plane acquired for the reference TELEMOS white
% image (Matthieu slide or any fluorescent homogeneous image) :
% The estimated white fluorescence image is generallly obtained bt using function whiteFluoImageTelemosestimation
% This is not compulsary as any homogenous sample image hat can roughly show the shape of illumination can be used to find
% the white reference image
%
%
% the z plane for which the maximum correlation is observed between estimated white and reference white images is retained.
% the white image is then offsetted (its corresponding Dark is subtracted) and copied in the subfolder <WhiteReference> of the sample
% rootfolder to show that it has been specifically selected for the considered experiment
%The matlab corrcoeff function is used
%
% correlation coefficients are saved in a file called
% 'corr.txt' in the subfolder 'WhiteReference'
%
%
% expected input folder hierarchy:
%
% >sampleFolder
% > PosFolders or RoiFolders
% > WhiteEstimate
% >darkFolder
% >darkFolder.smooth
% >whiteFolder
% > darkFolderforWhite
% > darkFolderforWhite.smooth
% >whiteFolder.smooth
%
%
% expected output folder hierarchy:
%
% >sampleFolder
% > PosFolders or RoiFolders
% > WhiteEstimate
% > WhiteReference
% > white after offset
% >darkFolder
% >darkFolder.smooth
% >whiteFolder
% > darkFolderforWhite
% > darkFolderforWhite.smooth
% >whiteFolder.smooth
%% use
% [zmax]=findWhiteReferenceImageTelemos
%% use
% [zmax]=findWhiteReferenceImageTelemos
%% Comments
% written for proposal 20161050
% adapted for proposal 20170043
%% Comments
% written for proposal 20161050
% adapted for proposal 20170043
%% Author
% MF Devaux
% INRA BIA
% PVPP
%% Author
% MF Devaux
% INRA BIA
% PVPP
%% date
% 5 octobre 2017:
% 15 decembre 2017 : adapted to take into account the roi known from
% metadata
% 27 fevrier 2018 : comments details
% 4 septembre 2018: comments and check and naming of folders
% 12 mars 2019 : save white reference with the same size as white estimate
% 16 avril 2019 : include diagonals to check the relevance of white
% reference
% 20 mai 2019 : track folder
% 27 mai 2019 : offset dark
% 4 juin 2019 : replace exist by isfolder or isfile
%% date
% 5 octobre 2017:
% 15 decembre 2017 : adapted to take into account the roi known from
% metadata
% 27 fevrier 2018 : comments details
% 4 septembre 2018: comments and check and naming of folders
% 12 mars 2019 : save white reference with the same size as white estimate
% 16 avril 2019 : include diagonals to check the relevance of white
% reference
% 20 mai 2019 : track folder
% 27 mai 2019 : offset dark
% 4 juin 2019 : replace exist by isfolder or isfile
"""
raise NotImplementedError()

View File

@ -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

View File

@ -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):