#!/usr/bin/env python
# file profiles.py
# author Florent Guiotte <florent.guiotte@irisa.fr>
# version 0.0
# date 13 déc. 2019
"""
Profiles
========
This submodule contains the attribute profiles related classes.
Example
-------
>>> import sap
>>> import numpy as np
>>> image = np.arange(5*5).reshape(5, 5)
Create the attribute profiles (AP) of `image` based on area attribute
and three thresholds.
>>> aps = sap.attribute_profiles(image, {'area': [10, 100, 1000]})
>>> aps.vectorize()
[[...]]
Create the extended AP of `image` based on area compactness and volume
attributes.
>>> attributes = {'compactness': [.1, .5, .7], 'volume': [10, 100]}
>>> eaps = sap.attribute_profiles(image, attributes)
>>> eaps.vectorize()
[[...]]
Concatenation of profiles to create complex extended profiles.
>>> profiles = sap.attribute_profiles(image, {'area': [10, 100]}) \\
... + sap.feature_profiles(image, {'compactness': [.3, .7]}) \\
... + sap.self_dual_attribute_profiles(image, {'height': [5, 15]})
Profiles[{'attribute': 'area',
'filtering rule': 'direct',
'image': -7518820387991786804,
'name': 'attribute profiles',
'out feature': 'altitude',
'profiles': [{'operation': 'thinning', 'threshold': 100},
{'operation': 'thinning', 'threshold': 10},
{'operation': 'copy feature altitude'},
{'operation': 'thickening', 'threshold': 10},
{'operation': 'thickening', 'threshold': 100}]},
{'attribute': 'compactness',
'filtering rule': 'direct',
'image': -7518820387991786804,
'name': 'feature profiles',
'out feature': 'compactness',
'profiles': [{'operation': 'thinning', 'threshold': 0.7},
{'operation': 'thinning', 'threshold': 0.3},
{'operation': 'copy feature compactness'},
{'operation': 'thickening', 'threshold': 0.3},
{'operation': 'thickening', 'threshold': 0.7}]},
{'attribute': 'height',
'filtering rule': 'direct',
'image': -7518820387991786804,
'name': 'self dual attribute profiles',
'out feature': 'altitude',
'profiles': [{'operation': 'copy feature altitude'},
{'operation': 'sd filtering', 'threshold': 5},
{'operation': 'sd filtering', 'threshold': 15}]}]
"""
import numpy as np
from tqdm.auto import tqdm
from pprint import pformat
from matplotlib import pyplot as plt
from pathlib import Path
from . import trees
from .utils import *
[docs]class Profiles:
"""
Base class for profiles.
Parameters
----------
data : list of ndarray
List of ndarray representing profiles grouped by image or attribute
filtering.
description : list of dict
List of dictionary containing the metadata of the profiles.
"""
def __init__(self, data, description):
if len(data) != len(description):
raise AttributeError('Data and description missmatch.')
self.data = data if len(data) > 1 else data[0]
self.description = description if len(data) > 1 else description[0]
self.len = len(data)
def __str__(self):
return self.__repr__()
def __repr__(self):
return self.__class__.__name__ + pformat(self.description)
def __iter__(self):
if self.len == 1:
yield self
return
for data, description in zip(self.data, self.description):
yield Profiles([data], [description])
def __len__(self):
return self.len
def __getitem__(self, key):
if self.len == 1 and key == 0:
return self
return Profiles([self.data[key]], [self.description[key]])
def __add__(self, other):
return concatenate((self, other))
[docs] def diff(self):
"""Compute the differential of profiles.
Refer to :func:`differential` for full documentation.
Returns
-------
differential : Profiles
The processed differential profiles.
"""
return differential(self)
[docs] def lf(self, local_feature=(np.mean, np.std), patch_size=7):
"""lf(self, local_feature=(np.mean, np.std), patch_size=7)
Compute the local features of profiles
Refer to :func:`local_features` for full documentation.
local_feature : function or tuple of functions
The function(s) to describe the local patches.
patch_size : int
The size of the patches.
Returns
-------
local_features : Profiles
The local features of ``profiles``.
"""
return local_features(self, local_feature, patch_size)
[docs] def vectorize(self):
"""Return the vectors of the profiles.
Refer to :func:`vectorize` for full documentation.
Returns
-------
vectors : numpy.ndarray
The vectors of the profiles.
See Also
--------
vectorize : equivalent function.
"""
return vectorize(self)
[docs] def strip(self, condition):
"""strip(lambda x: x['operation'] != 'thinning')
Remove profiles according to condition. Iteration is done on
profiles description.
Refer to :func:`strip_profiles` for full documentation.
Parameters
----------
condition : function
The function (or lambda function) to use on profiles description
to filter the profiles.
Returns
-------
new_profiles : Profiles
Filtered profiles.
See Also
--------
strip_profiles : equivalent function
"""
return strip_profiles(condition, self)
[docs] def strip_copy(self):
"""Remove all the copied images in profiles.
Refer to :func:`strip_profiles_copy` for full documentation.
Parameters
----------
profiles : Profiles
The profiles to strip on the copied images.
Returns
-------
new_profiles : Profiles
Copy of profiles without copied image.
See Also
--------
strip_profiles_copy : equivalent function
"""
return strip_profiles_copy(self)
[docs]def create_profiles(tree, attribute, out_feature='altitude',
filtering_rule='direct', profiles_name='unknow'):
"""
Compute the profiles of an images. Generic function.
Parameters
----------
image : ndarray
The image to be profiled.
attribute : dict
Dictionary of attribute (as key, str) with according thresholds
(as values, iterable of thresholds).
tree_type : sap.trees.Tree, serie of sap.trees.Tree
Tree or pair of tree for non dual filtering (e.g. min-tree and
max-tree for attribute profiles).
adjacency : int, optional
Adjacency used for the tree construction. Default is 4.
image_name : str, optional
The name of the image Useful to track filtering process and
display. If not set, the name is replaced by the hash of the
image.
out_feature : str or list, optional
Out feature of the profiles. Can be 'altitude' (default), 'same'
or a list of feature. If 'same' then out feature of the profiles
match the filtering attribute. Refer to :func:`feature_profiles`
and :func:`self_dual_feature_profiles` for more details.
filtering_rule : str, optional
The filtering rule to use. It can be 'direct', 'min', 'max' or
'subtractive'. Default is 'direct'.
profiles_name : str, optional
Name of the profiles (e.g. `'attribute profiles'`).
Todo
----
out_feature takes a list of features.
Example
-------
>>> image = np.arange(5*5).reshape(5, 5)
>>> sap.create_profiles(image, {'area': [5, 10]},
... (sap.MinTree, sap.MaxTree))
Profiles{'attribute': 'area',
'filtering rule': 'direct',
'image': -7518820387991786804,
'name': 'unknow',
'out feature': 'altitude',
'profiles': [{'operation': 'thinning', 'threshold': 10},
{'operation': 'thinning', 'threshold': 5},
{'operation': 'copy feature altitude'},
{'operation': 'thickening', 'threshold': 5},
{'operation': 'thickening', 'threshold': 10}]}
"""
data = []
description = []
# Create Trees
try:
if isinstance(tree, trees.Tree):
# Dual tree
ndual = False
thinning_tree = None
thickening_tree = tree
else:
# Non dual trees
ndual = True
thinning_tree = tree[0]
thickening_tree = tree[1]
except:
raise TypeError('Parameter tree_type must be a tuple or a single' \
' instance of Tree, not {}'.format(tree))
out_features = (out_feature, ) if isinstance(out_feature, str) else out_feature
iter_count = (sum(len(x) for x in attribute.values()) * (1 + ndual) + \
len(attribute)) * len(out_features)
ttq = tqdm(desc='Total', total=iter_count)
for att, thresholds in attribute.items():
tq = tqdm(total=(len(thresholds) * (1 + ndual) + 1) * len(out_features), desc=att)
for out_feature in out_features:
profiles = []; profiles_description = []
of = att if out_feature == 'same' else out_feature
if ndual:
# thinning
prof, desc = _compute_profiles(thinning_tree, att,
thresholds[::-1], (ttq, tq), of, filtering_rule)
profiles += prof
profiles_description += desc
# Origin
tq.update(); ttq.update()
profiles += [thickening_tree.reconstruct(feature=of)]
profiles_description += [{'operation': 'copy feature {}'.format(of)}]
# thickening
prof, desc = _compute_profiles(thickening_tree, att, thresholds,
(ttq, tq), of, filtering_rule)
profiles += prof
profiles_description += desc
data += [np.stack(profiles)]
description += [{'tree': thickening_tree.get_params(),
'name': profiles_name,
'attribute': att,
'profiles': profiles_description,
'filtering rule': filtering_rule,
'out feature': of}]
tq.close()
ttq.close()
return Profiles(data, description)
def _compute_profiles(tree, attribute, thresholds, tqs,
feature='altitude', rule='direct'):
data = []
desc = []
for t in thresholds:
for tq in tqs: tq.update()
desc += [{'operation': tree.operation_name, 'threshold': t}]
deleted_nodes = tree.get_attribute(attribute) < t
data += [tree.reconstruct(deleted_nodes, feature, rule)]
return data, desc
[docs]def attribute_profiles(image, attribute, adjacency=4, image_name=None,
filtering_rule='direct'):
"""
Compute the attribute profiles of an image.
Parameters
----------
image : ndarray
The image
attribute : dict
Dictionary of attribute (as key, str) with according thresholds
(as values, number).
adjacency : int
Adjacency used for the tree construction. Default is 4.
image_name : str
The name of the image (optional). Useful to track filtering
process and display. If not set, the name is replaced by the
hash of the image.
filtering_rule: str, optional
The filtering rule to use. It can be 'direct', 'min', 'max' or
'subtractive'. Default is 'direct'.
Examples
--------
>>> image = np.arange(5*5).reshape(5,5)
>>> sap.attribute_profiles(image, {'area': [10, 100]})
Profiles{'attribute': 'area',
'filtering rule': 'direct',
'image': -7518820387991786804,
'name': 'attribute profiles',
'out feature': 'altitude',
'profiles': [{'operation': 'thinning', 'threshold': 100},
{'operation': 'thinning', 'threshold': 10},
{'operation': 'copy feature altitude'},
{'operation': 'thickening', 'threshold': 10},
{'operation': 'thickening', 'threshold': 100}]}
See Also
--------
sap.trees.available_attributes : List available attributes.
"""
maxt = trees.MaxTree(image, adjacency, image_name)
mint = trees.MinTree(image, adjacency, image_name)
return create_profiles((mint, maxt), attribute, 'altitude',
filtering_rule, 'attribute profiles')
[docs]def self_dual_attribute_profiles(image, attribute, adjacency=4,
image_name=None, filtering_rule='direct'):
"""
Compute the self dual attribute profiles of an image.
Parameters
----------
image : ndarray
The image
attribute : dict
Dictionary of attribute (as key, str) with according thresholds
(as values, number).
adjacency : int
Adjacency used for the tree construction. Default is 4.
image_name : str
The name of the image (optional). Useful to track filtering
process and display. If not set, the name is replaced by the
hash of the image.
filtering_rule: str, optional
The filtering rule to use. It can be 'direct', 'min', 'max' or
'subtractive'. Default is 'direct'.
Examples
--------
>>> image = np.arange(5*5).reshape(5,5)
>>> sap.self_dual_attribute_profiles(image, {'area': [10, 100]})
Profiles{'attribute': 'area',
'filtering rule': 'direct',
'image': -7518820387991786804,
'name': 'self dual attribute profiles',
'out feature': 'altitude',
'profiles': [{'operation': 'copy feature altitude'},
{'operation': 'sd filtering', 'threshold': 10},
{'operation': 'sd filtering', 'threshold': 100}]}
See Also
--------
sap.trees.available_attributes : List available attributes.
attribute_profiles : other profiles.
"""
tost = trees.TosTree(image, adjacency, image_name)
return create_profiles(tost, attribute, 'altitude',
filtering_rule, 'self dual attribute profiles')
[docs]def self_dual_feature_profiles(image, attribute, adjacency=4, image_name=None,
out_feature='same', filtering_rule='direct'):
"""
Compute the self dual features profiles of an image.
Parameters
----------
image : ndarray
The image
attribute : dict
Dictionary of attribute (as key, str) with according thresholds
(as values, number).
adjacency : int
Adjacency used for the tree construction. Default is 4.
image_name : str
The name of the image (optional). Useful to track filtering
process and display. If not set, the name is replaced by the
hash of the image.
out_feature : str or list, optional
Out feature of the profiles. Can be 'altitude' (default), 'same'
or a list of feature. If 'same' then out feature of the profiles
match the filtering attribute.
filtering_rule: str, optional
The filtering rule to use. It can be 'direct', 'min', 'max' or
'subtractive'. Default is 'direct'.
Examples
--------
>>> image = np.arange(5*5).reshape(5,5)
>>> sap.self_dual_feature_profiles(image, {'area': [10, 100]})
Profiles{'attribute': 'area',
'filtering rule': 'direct',
'image': -7518820387991786804,
'name': 'self dual feature profiles',
'out feature': 'area',
'profiles': [{'operation': 'copy feature area'},
{'operation': 'sd filtering', 'threshold': 10},
{'operation': 'sd filtering', 'threshold': 100}]}
See Also
--------
sap.trees.available_attributes : List available attributes.
attribute_profiles : other profiles.
"""
tost = trees.TosTree(image, adjacency, image_name)
return create_profiles(tost, attribute, out_feature, filtering_rule,
'self dual feature profiles')
[docs]def feature_profiles(image, attribute, adjacency=4, image_name=None,
out_feature='same', filtering_rule='direct'):
"""
Compute the feature profiles of an image.
Parameters
----------
image : ndarray
The image
attribute : dict
Dictionary of attribute (as key, str) with according thresholds
(as values, number).
adjacency : int
Adjacency used for the tree construction. Default is 4.
image_name : str
The name of the image (optional). Useful to track filtering
process and display. If not set, the name is replaced by the
hash of the image.
out_feature : str or list, optional
Out feature of the profiles. Can be 'altitude' (default), 'same'
or a list of feature. If 'same' then out feature of the profiles
match the filtering attribute.
filtering_rule: str, optional
The filtering rule to use. It can be 'direct', 'min', 'max' or
'subtractive'. Default is 'direct'.
Examples
--------
>>> image = np.arange(5*5).reshape(5,5)
>>> sap.feature_profiles(image, {'area': [5, 10]})
Profiles{'attribute': 'area',
'filtering rule': 'direct',
'image': -7518820387991786804,
'name': 'feature profiles',
'out feature': 'area',
'profiles': [{'operation': 'thinning', 'threshold': 10},
{'operation': 'thinning', 'threshold': 5},
{'operation': 'copy feature area'},
{'operation': 'thickening', 'threshold': 5},
{'operation': 'thickening', 'threshold': 10}]}
See Also
--------
sap.trees.available_attributes : List available attributes.
attribute_profiles : other profiles.
"""
maxt = trees.MaxTree(image, adjacency, image_name)
mint = trees.MinTree(image, adjacency, image_name)
return create_profiles((mint, maxt), attribute,
out_feature, filtering_rule, 'feature profiles')
[docs]def alpha_profiles(image, attribute, adjacency=4,
image_name=None, filtering_rule='direct'):
"""
Compute the alpha profiles of an image.
Parameters
----------
image : ndarray
The image
attribute : dict
Dictionary of attribute (as key, str) with according thresholds
(as values, number).
adjacency : int
Adjacency used for the tree construction. Default is 4.
image_name : str
The name of the image (optional). Useful to track filtering
process and display. If not set, the name is replaced by the
hash of the image.
filtering_rule: str, optional
The filtering rule to use. It can be 'direct', 'min', 'max' or
'subtractive'. Default is 'direct'.
Examples
--------
>>> image = np.arange(5 * 5).reshape(5, 5)
>>> sap.alpha_profiles(image, {'area': [10, 100]})
Profiles{'attribute': 'area',
'filtering rule': 'direct',
'name': 'alpha profiles',
'out feature': 'altitude',
'profiles': [{'operation': 'copy feature altitude'},
{'operation': 'alpha filtering', 'threshold': 10},
{'operation': 'alpha filtering', 'threshold': 100}],
'tree': {'adjacency': 4, 'image_hash': '44f17c0f', 'image_name': None}}
See Also
--------
sap.trees.available_attributes : List available attributes.
"""
atree = trees.AlphaTree(image, adjacency, image_name)
return create_profiles(atree, attribute, 'altitude', filtering_rule, 'alpha profiles')
[docs]def omega_profiles(image, attribute, adjacency=4,
image_name=None, filtering_rule='direct'):
"""
Compute the omega profiles of an image.
Parameters
----------
image : ndarray
The image
attribute : dict
Dictionary of attribute (as key, str) with according thresholds
(as values, number).
adjacency : int
Adjacency used for the tree construction. Default is 4.
image_name : str
The name of the image (optional). Useful to track filtering
process and display. If not set, the name is replaced by the
hash of the image.
filtering_rule: str, optional
The filtering rule to use. It can be 'direct', 'min', 'max' or
'subtractive'. Default is 'direct'.
Examples
--------
>>> image = np.arange(5 * 5).reshape(5, 5)
>>> sap.omega_profiles(image, {'area': [10, 100]})
Profiles{'attribute': 'area',
'filtering rule': 'direct',
'name': 'omega profiles',
'out feature': 'altitude',
'profiles': [{'operation': 'copy feature altitude'},
{'operation': '(ω) filtering', 'threshold': 10},
{'operation': '(ω) filtering', 'threshold': 100}],
'tree': {'adjacency': 4, 'image_hash': '44f17c0f', 'image_name': None}}
See Also
--------
sap.trees.available_attributes : List available attributes.
"""
otree = trees.OmegaTree(image, adjacency, image_name)
return create_profiles(otree, attribute, 'altitude', filtering_rule, 'omega profiles')
[docs]def watershed_profiles(image, attribute, markers=None, adjacency=4, image_name=None, filtering_rule='direct', weight_function = 'L1', watershed_attribute='area'):
"""
Compute the watershed profiles of an image.
Parameters
----------
image : ndarray
The image
attribute : dict
Dictionary of attribute (as key, str) with according thresholds
(as values, number).
markers : 2D ndarray of same dimension as 'image'
Prior-knowledge to be combined to the image gradient before the
construction of the hierarchical watershed. If ``None``, an
ndarray of ones is used, the result will be equivalent of not
using markers at all.
adjacency : int
Adjacency used for the tree construction. Default is 4.
image_name : str
The name of the image (optional). Useful to track filtering
process and display. If not set, the name is replaced by the
hash of the image.
filtering_rule: str, optional
The filtering rule to use. It can be 'direct', 'min', 'max' or
'subtractive'. Default is 'direct'.
weight_function : str
The function used to compute dissimilarity between neighbour
pixels. Default is 'L1' (absolute different between pixel
values).
watershed_attribute : str
The criteria used to guide the contruction of the hierarchical
watershed. The allowed criteria are : 'area', 'volume',
'dynamics' and 'parents'.
Examples
--------
>>> image = np.arange(5 * 5).reshape(5, 5)
>>> markers = np.ones((5,5))
>>> sap.watershed_profiles(image, markers, {'area': [10, 100]})
Profiles{'attribute': 'area',
'filtering rule': 'direct',
'name': 'watershed profiles',
'out feature': 'altitude',
'profiles': [{'operation': 'copy feature altitude'},
{'operation': 'watershed filtering', 'threshold': 10},
{'operation': 'watershed filtering', 'threshold': 100}],
'tree': {'adjacency': 4, 'image_hash': '44f17c0f', 'image_name': None}}
See Also
--------
sap.trees.available_attributes : List available attributes.
"""
atree = trees.WatershedTree(image, markers, adjacency, image_name, weight_function, watershed_attribute)
return create_profiles(atree, attribute, 'altitude', filtering_rule, 'watershed profiles')
def _show_profiles(profiles, height=None, fname=None, **kwargs):
assert len(profiles) == 1, 'Show profile only for one attribute at a time.'
# Set vmin and vmax if not set
if not 'vmin' in kwargs:
kwargs['vmin'] = profiles.data.min()
if not 'vmax' in kwargs:
kwargs['vmax'] = profiles.data.max()
if height is not None:
plt.figure(figsize=_figsize(profiles, height))
suptitle = '{} - {}'.format(profiles.description['tree']['image_name'], profiles.description['attribute'])
for i, (im, profile) in enumerate(zip(profiles.data, profiles.description['profiles'])):
plt.subplot(1, len(profiles.data), i+1)
plt.imshow(im, **kwargs)
plt.title(_title(profile))
plt.tight_layout()
plt.suptitle(suptitle)
if fname:
fname = Path(fname)
fn = fname.parent / Path(fname.stem + '_{}'.format(profiles.description['attribute']) + fname.suffix)
plt.savefig(fn)
[docs]def show_all_profiles(profiles, attribute=None, image=None, height=None, fname=None, **kwargs):
"""Display profiles with matplotlib.
Parameters
----------
profiles : sap.profiles.Profiles
The profiles to display.
attribute : sring, optional
Name of attribute to display. By default display all the
attributes contained in profiles.
image : string, optional
Name of the image to display. By default display the profiles of
all images.
height : scalar, optional, default: None
Height of the figure in inches. Automatically adjust the size of
the figure to display correctly the profiles and the title with
matplot.
fname : str or PathLike, optional
If set, the file path to save the figure. The attribute name is
automatically inserted in the file name.
See Also
--------
show_profiles : Display a profiles stack.
Notes
-----
This is a utility function to call recursively `show_profiles`.
Attribute and image filters are available to filter the profiles to
display.
"""
# Filter profiles according to attribute if attribute is set
if attribute:
profiles = filter(lambda x: x.description['attribute'] == attribute, profiles)
# Same for image
if image:
profiles = filter(lambda x: x.description['tree']['image_name'] == image, profiles)
for p in profiles:
show_profiles(p, height, fname, **kwargs)
[docs]def strip_profiles_copy(profiles):
"""Remove all the copied images in profiles.
Copy are the original images where profiles are computed on.
Parameters
----------
profiles : Profiles
The profiles to strip on the copied images.
Returns
-------
new_profiles : Profiles
Copy of profiles without copied image.
See Also
--------
sap.strip_profiles : Filter profiles according to condition.
"""
return strip_profiles(lambda x: x['operation'] == 'copy', profiles)
[docs]def strip_profiles(condition, profiles):
"""strip_profiles(lambda x: x['operation'] != 'thinning', profiles)
Remove profiles according to condition. Iteration is done on
profiles description (see Notes).
Parameters
----------
condition : function
The function (or lambda function) to use on profiles description
to filter the profiles.
profiles : Profiles
The profiles to filter.
Returns
-------
new_profiles : Profiles
Filtered profiles.
Notes
-----
The condition is tested on the description of each profiles.
Considering this stack:
>>> aps
Profiles{'attribute': 'area',
'image': -8884649894275650052,
'profiles': [{'operation': 'thinning', 'threshold': 1000},
{'operation': 'thinning', 'threshold': 100},
{'operation': 'thinning', 'threshold': 10},
{'operation': 'copy'},
{'operation': 'thickening', 'threshold': 10},
{'operation': 'thickening', 'threshold': 100},
{'operation': 'thickening', 'threshold': 1000}]}
The condition function is tested on each item of the list
``'profiles'``.
See Also
--------
Profiles.strip : Remove profiles based on condition.
Examples
--------
Strip profiles depending on thresholds level:
>>> image = np.random.random((100, 100))
>>> aps = sap.attribute_profiles(image, {'area': [10, 100, 1000]})
>>> sap.strip_profiles(lambda x: 'threshold' in x and x['threshold'] > 20, aps)
Profiles{'attribute': 'area',
'image': 2376333419322655105,
'profiles': [{'operation': 'thinning', 'threshold': 10},
{'operation': 'copy'},
{'operation': 'thickening', 'threshold': 10}]}
Strip profiles depending on operation:
>>> sap.strip_profiles(lambda x: x['operation'] == 'thinning', aps)
Profiles{'attribute': 'area',
'image': 2376333419322655105,
'profiles': [{'operation': 'copy'},
{'operation': 'thickening', 'threshold': 10},
{'operation': 'thickening', 'threshold': 100},
{'operation': 'thickening', 'threshold': 1000}]}
"""
new_profiles = []
for ap in profiles:
# Process the profile filter
prof_filter = [not condition(x) for x in ap.description['profiles']]
# Create filtered description
new_desc = ap.description.copy()
new_desc['profiles'] = [p for p, f in zip(ap.description['profiles'], prof_filter) if f]
# Filter the new data
new_data = ap.data[prof_filter]
new_profiles += [Profiles([new_data], [new_desc])]
return concatenate(new_profiles)
[docs]def differential(profiles):
"""Compute the differential of profiles.
Parameters
----------
profiles : Profiles
Attribute profiles or other profiles to process the
differential on.
Returns
-------
differential : Profiles
The processed differential profiles.
"""
new_data = []
new_desc = []
for p in profiles:
new_data += [p.data[:-1] - p.data[1:]]
new_desc += [p.description.copy()]
d = new_desc[-1]
d['profiles'] = [{'operation': 'differential',
'profiles': [x, y]} for x, y in zip(d['profiles'][:-1],
d['profiles'][1:])]
return Profiles(new_data, new_desc)
[docs]def local_features(profiles, local_feature=(np.mean, np.std), patch_size=7):
"""local_features(profiles, local_feature=(np.mean, np.std), patch_size=7)
Compute the local features of profiles
Parameters
----------
profiles : Profiles
Input Profiles.
local_feature : function or tuple of functions
The function(s) to describe the local patches.
patch_size : int
The size of the patches.
Returns
-------
local_feature : Profiles
The local features of ``profiles``.
"""
try:
iter(local_feature)
except TypeError:
local_feature = (local_feature,)
new_data = []
new_desc = []
for p in profiles:
for f in local_feature:
nd = [local_patch_f(d, patch_size, f) for d in p.data]
new_data += [np.array(nd)]
new_desc += [p.description.copy()]
d = new_desc[-1]
d['profiles'] = [{'operation': 'local feature',
'function': f.__name__,
'patch size': patch_size,
'profile': x} for x in d['profiles']]
return Profiles(new_data, new_desc)
[docs]def show_profiles(profiles, height=None, fname=None, **kwargs):
"""Display a profiles stack with matplotlib.
Parameters
----------
profiles : Profiles
The profiles to display. Can be only of length 1.
height : scalar, optional, default: None
Height of the figure in inches. Automatically adjust the size of
the figure to display correctly the profiles and the title with
matplot.
fname : str or PathLike, optional
If set, the file path to save the figure. The attribute name is
automatically inserted in the file name.
See Also
--------
show_profiles_all : Display several profiles at once.
"""
_show_profiles(profiles, height, fname, **kwargs)
def _figsize(profiles, height):
"""Compute size of fig given height."""
shape = profiles.data.shape[1:]
count = profiles.data.shape[0]
hw_ratio = shape[1] / shape[0]
width = height * hw_ratio * count
return (width, 1.1 * height)
def _title(profile):
"""Process a title of a fig."""
if profile['operation'] == 'differential':
p1, p2 = profile['profiles']
return 'differential ({}, {})'.format(_title(p1), _title(p2))
elif profile['operation'] == 'local feature':
p = profile['profile']
return 'local feature {} ({})'.format(profile['function'], _title(p))
else:
return ' '.join([str(x) for x in profile.values()])
[docs]def concatenate(sequence):
"""concatenate((profiles_1, profiles_2, ...))
Concatenate a sequence of profiles.
Parameters
----------
sequence : sequence of Profiles
The sequence of profiles to concatenate.
Returns
-------
profiles : Profiles
The concatenated profiles.
Examples
--------
>>> aps_a = sap.attribute_profiles(image, {'area': [10, 100]})
>>> aps_b = sap.attribute_profiles(image, {'compactness': [.1, .5]})
>>> aps = sap.concatenate((aps_a, aps_b))
>>> len(aps) == len(aps_a) + len(aps_b)
True
"""
return Profiles([x.data for y in sequence for x in y],
[x.description for y in sequence for x in y])
[docs]def vectorize(profiles):
"""Return the classification vectors of the profiles.
Parameters
----------
profiles : Profiles
Profiles on which process the vectors.
Returns
-------
vectors : numpy.ndarray
The vectors of the profiles.
See Also
--------
Profiles.vectorize : get the vectors of profiles.
Example
-------
>>> image = np.random.random((100, 100))
>>> aps = sap.attribute_profiles(image, {'area': [10, 100]})
>>> vectors = sap.vectorize(aps)
>>> vectors.shape
(5, 100, 100)
"""
return np.concatenate([p.data for p in profiles])