Something went wrong on our end
-
Olivier Bertrand authoredOlivier Bertrand authored
__init__.py 7.88 KiB
"""
The scene processing part of the toolbox defines methodes
to transform an image into a place code in the sense
of Basten and Mallot (2010).
The scene is either
* a 4d numpy array, used when the image is a equirectangular \
projection, i.e. a panoramic image.
* a 3d numpy array, used when the viewing direction can not \
be mapped/projected on a regular image (e.g. insect eye).
We thus define the following for a scene:
image based scene (IBS)
A classical image. Each pixel is viewed in a direction
(elevation,azimuth) in a regular manner.
In that case the scene is a 4d numpy array
[elevation-index,azimuth-index,channel-index,1].
Omatidium based scene (OBS)
In an ommatidia based scene, the viewing direction
do not need to be regularally spaced.
In that case the scene is a 3d numpy array
[ommatidia-index, channel-index,1].
By extension a place-code is either image based or ommatidium based.
The number of dimension of an ib-place-code is always 4, and of an
ob-place-code always 3.
image based place-code (IBPC)
A place code derived from IBS. Each pixel is viewed in a direction
(elevation,azimuth) in a regular manner.
In that case the scene is a 4d numpy array
[elevation-index,azimuth-index,channel-index,component-index].
Omatidium based place-code (OBPC)
A place code derived from OBS, the viewing direction
do not need to be regularally spaced.
In that case the scene is a 3d numpy array
[ommatidia-index, channel-index,component-index].
Abusing the terminology of a place-code, a scene can be a place-code.
Therefore ibs and obs have 4 and 3 dimension, respectively.
.. todo:
* implement optic flow vector
"""
import numpy as np
import pandas as pd
from scipy.ndimage import maximum_filter, minimum_filter
import processing.constants as prc
import processing.tools as prt
def scene(database, posorient=None, rowid=None):
""" Return a scene at a position orientation or given rowid
in a given database.
:param database: a DataBaseLoad class \
:param posorient: a pandas Series with index: \
['x','y','z','alpha_0,'alpha_1,'alpha_2'] (default None, i.e. not used)
:param rowid: a row identification integer for directly reading \
in the database (default None, i.e. not used).
:returns: a scene [elevation, azimuth, channel, 1] or \
[ommatidia,channel,1].
:rtype: np.ndarray
.. literalinclude:: example/processing/scene.py
:lines: 13-14
.. plot:: example/processing/scene.py
"""
if posorient is not None:
assert isinstance(posorient, pd.Series),\
'posorient should be a pandas Series'
scene = database.read_image(posorient=posorient,
rowid=rowid)
scene = scene[..., np.newaxis]
return scene
def skyline(scene):
"""Return the average along the elevation of a scene
:param scene: the scenery at a given location (a 4d numpy array)
:returns: the skyline [1,azimuth,channel,1]
:rtype: np.ndarray
.. literalinclude:: example/processing/skyline.py
:lines: 12-14
.. plot:: example/processing/skyline.py
"""
assert prt.is_ibpc(scene),\
'scene should be image based to compute a skyline'
skyline = scene.mean(axis=prc.__ibpc_indeces__['elevation'])
return skyline[np.newaxis, :]
def mztest():
return 1
def michelson_contrast(scene, size=3):
"""Return the michelson constrast
.. math::
\\frac{I_\\text{max}-I_\\text{min}}{I_\\text{max}+I_\\text{min}}
with :math:`I_\\text{max}` and :math:`I_\\text{min}` representing the \
highest and lowest luminance in an image region around each pixel.
:param scene: an image based scene
:param size: the size of the region to calculate the maximum \
and minimum of the local image intensity
:returns: the michelson-contrast
:rtype: np.ndarray
.. literalinclude:: example/processing/michelson_contrast.py
:lines: 12-14
.. plot:: example/processing/michelson_contrast.py
"""
assert prt.is_ibpc(scene), \
'scene should be image based to compute the michelson constrast'
contrast = np.zeros_like(scene)
for channel in range(scene.shape[prc.__ibpc_indeces__['channel']]):
i_max = maximum_filter(scene[..., channel, 0],
size=size, mode='wrap')
i_min = minimum_filter(scene[..., channel, 0],
size=size, mode='wrap')
contrast[..., channel, 0] = (i_max - i_min) / (i_max + i_min)
return contrast
def contrast_weighted_nearness(scene, contrast_size=3, distance_channel=3):
"""Return the michelson contrast wheighted nearness
:param scene: an image based scene
:param contrast_size: the size of the region to calculate the maximum \
and minimum of the local image intensity in the michelson-contrast.
:param distance_channel: the index of the distance-channel.
.. literalinclude:: example/processing/contrast_weighted_nearness.py
:lines: 12-14
.. plot:: example/processing/contrast_weighted_nearness.py
"""
assert prt.is_ibpc(scene), \
'scene should be image based to compute the contrast weighted nearness'
contrast = michelson_contrast(scene, size=contrast_size)
distance = scene[..., distance_channel, 0]
distance = distance[..., np.newaxis, np.newaxis]
distance = np.tile(distance, (1, 1, scene.shape[-2], 1))
return contrast / distance
def pcv(place_code, viewing_directions):
"""Place code vectors
:param place_code: the place code at a given location (e.g. an ibs scene)
:param viewing_directions: viewing direction of each pixel
:returns: the place code vectors in cartesian coordinates
:rtype: (np.ndarray)
.. literalinclude:: example/processing/pcv.py
:lines: 12-14
.. plot:: example/processing/pcv.py
"""
if prt.is_ibpc(place_code):
component_dim = prc.__ibpc_indeces__['component']
channel_dim = prc.__ibpc_indeces__['channel']
elif prt.is_obpc(place_code):
component_dim = prc.__obpc_indeces__['component']
channel_dim = prc.__obpc_indeces__['channel']
else:
raise TypeError('place code should be either an ibpc or obpc')
assert isinstance(viewing_directions, np.ndarray), \
'viewing_directions should be a numpy array'
assert place_code.shape[component_dim] == 1, \
'the last dimension ({}) of the place-code should be 1'.format(
place_code.shape[component_dim])
elevation = viewing_directions[..., prc.__spherical_indeces__['elevation']]
azimuth = viewing_directions[..., prc.__spherical_indeces__['azimuth']]
unscaled_lv = prt.spherical_to_cartesian(elevation, azimuth, radius=1)
scaled_lv = np.zeros_like(place_code)
# (3,) -> (1,1,3) or (1,1,1,3) see numpy.tile
scaled_lv = np.tile(scaled_lv, (unscaled_lv.shape[-1],))
for channel_index in range(0, scaled_lv.shape[channel_dim]):
radius = np.tile(place_code[..., channel_index, 0]
[..., np.newaxis], (scaled_lv.shape[-1],))
scaled_lv[..., channel_index, :] = unscaled_lv * radius
return scaled_lv
def apcv(place_code, viewing_directions):
"""Calculate the average scene vector
:param place_code: the place code at a given location (e.g. an ibs scene)
:param viewing_directions: viewing direction of each pixel
:returns: the average place-code vector
:rtype: (np.ndarray)
.. literalinclude:: example/processing/apcv.py
:lines: 12-14
.. plot:: example/processing/apcv.py
"""
scaled_lv = pcv(place_code, viewing_directions)
if prt.is_ibpc(place_code):
return (scaled_lv.sum(axis=0).sum(axis=0))[np.newaxis, np.newaxis, ...]
elif prt.is_obpc(place_code):
return (scaled_lv.sum(axis=0))[np.newaxis, ...]
else:
raise TypeError('place code is neither an ibpc nor obpc')
def optic_flow(place_code, viewing_directions, velocity):
"""NOT IMPLEMENTED"""
raise NameError('Not Implemented')