diff --git a/navipy/database/__init__.py b/navipy/database/__init__.py index 63b107ce6fbcf7ad82abfa195c05553d06188302..d5074ac65ef9a62669c2d5e1b9badabc24bac6b9 100644 --- a/navipy/database/__init__.py +++ b/navipy/database/__init__.py @@ -116,10 +116,8 @@ It creates three sql table on initialisation. def __init__(self, filename, channels=['R', 'G', 'B', 'D']): """Initialisation of the database """ - if not isinstance(filename, str): - raise TypeError('filename should be a string') - if not isinstance(channels, list): - raise TypeError('nb_channel should be a list') + assert isinstance(filename, str), 'filename should be a string' + assert isinstance(channels, list), 'nb_channel should be an integer' self.filename = filename self.channels = channels self.normalisation_columns = list() @@ -145,13 +143,14 @@ It creates three sql table on initialisation. if os.path.exists(filename): # Check database self.db = sqlite3.connect( - filename, detect_types=sqlite3.PARSE_DECLTYPES) + 'file:' + filename + '?cache=shared', uri=True, + detect_types=sqlite3.PARSE_DECLTYPES, + timeout=10) self.db_cursor = self.db.cursor() for tablename, _ in self.tablecolumns.items(): - if not self.table_exist(tablename): - raise Exception('{} does not contain\ - a table named {}'.format( - filename, tablename)) + assert self.table_exist(tablename),\ + '{} does not contain a table named {}'.format( + filename, tablename) elif self.create(): # Create database self.db = sqlite3.connect( @@ -176,8 +175,7 @@ It creates three sql table on initialisation. self.viewing_directions[..., 1] = ma def table_exist(self, tablename): - if not isinstance(tablename, str): - raise TypeError('tablename should be a string') + assert isinstance(tablename, str), 'tablename should be a string' self.db_cursor.execute( """ SELECT count(*) @@ -191,19 +189,22 @@ It creates three sql table on initialisation. """ SELECT count(*) FROM position_orientation - WHERE rowid=?;""", (rowid,)) + WHERE rowid=?; + """, (rowid,)) valid = bool(self.db_cursor.fetchone()[0]) self.db_cursor.execute( """ SELECT count(*) FROM normalisation - WHERE rowid=?;""", (rowid,)) + WHERE rowid=?; + """, (rowid,)) valid = valid and bool(self.db_cursor.fetchone()[0]) self.db_cursor.execute( """ SELECT count(*) FROM image - WHERE rowid=?;""", (rowid,)) + WHERE rowid=?; + """, (rowid,)) valid = valid and bool(self.db_cursor.fetchone()[0]) return valid @@ -309,8 +310,8 @@ database return posorient def read_posorient(self, posorient=None, rowid=None): - if (posorient is None) and (rowid is None): - raise ValueError('posorient and rowid can not be both None') + assert (posorient is None) or (rowid is None),\ + 'posorient and rowid can not be both None' if posorient is not None: rowid = self.get_posid(posorient) # Read images @@ -336,8 +337,8 @@ database :returns: an image :rtype: numpy.ndarray """ - if (posorient is None) and (rowid is None): - raise ValueError('posorient and rowid can not be both None') + assert (posorient is None) or (rowid is None),\ + 'posorient and rowid can not be both None' if posorient is not None: rowid = self.get_posid(posorient) # Read images @@ -357,20 +358,19 @@ database FROM {} WHERE (rowid={}) """.format(tablename, rowid), self.db) - if not cmaxminrange.shape[0] == 1: - raise Exception('Error while reading normalisation factors') + assert cmaxminrange.shape[0] == 1,\ + 'Error while reading normalisation factors' cmaxminrange = cmaxminrange.iloc[0, :] return self.denormalise_image(image, cmaxminrange) def denormalise_image(self, image, cmaxminrange): - if not len(image.shape) == 3: - raise Exception('image should be 3D array') - if not image.shape[2] == len(self.channels): - raise Exception('image does not have the\ - required number of channels {}'.format( - len(self.channels))) - if not isinstance(cmaxminrange, pd.Series): - raise TypeError('cmaxminrange should be a pandas Series') + assert len(image.shape) == 3,\ + 'image should be 3D array' + assert image.shape[2] == len(self.channels),\ + 'image does not have the required number of channels {}'.format( + len(self.channels)) + assert isinstance(cmaxminrange, pd.Series),\ + 'cmaxminrange should be a pandas Series' denormed_im = np.zeros(image.shape, dtype=np.float) maxval_nim = np.iinfo(image.dtype).max # @@ -383,9 +383,8 @@ database cimage *= crange cimage += cmin denormed_im[:, :, chan_i] = cimage - if not np.max(cimage) == cmax: - raise Exception('denormalisation failed\ - {}!={}'.format(np.max(cimage), cmax)) + assert np.max(cimage) == cmax,\ + 'denormalisation failed {}!={}'.format(np.max(cimage), cmax) return denormed_im @@ -421,10 +420,10 @@ class DataBaseSave(DataBase): self.insert_replace(tablename, params) def insert_replace(self, tablename, params): - if not isinstance(tablename, str): - raise TypeError('table are named by string') - if not isinstance(params, dict): - raise TypeError('params should be dictionary columns:val') + assert isinstance(tablename, str),\ + 'table are named by string' + assert isinstance(params, dict),\ + 'params should be dictionary columns:val' params_list = list() columns_str = '' for key, val in params.items(): diff --git a/navipy/moving/test_maths.py b/navipy/moving/test_maths.py index 9e5753028784287d4b7415777cbf4fc0b82d5247..bcc8c801fec6468a0107584b7baa8aef8c43035d 100644 --- a/navipy/moving/test_maths.py +++ b/navipy/moving/test_maths.py @@ -4,14 +4,16 @@ Test of maths import numpy as np import pandas as pd import navipy.moving.maths as navimaths - - import unittest class TestNavipyMovingMaths(unittest.TestCase): + """ A class to test some mathy function of the toolbox + """ def test_motion_vec_pandas(self): + """ Test that a TypeError is correctly raised + """ motion_vec = 'NotPandas' move_mode = 'on_cubic_grid' mode_param = dict() @@ -22,6 +24,8 @@ class TestNavipyMovingMaths(unittest.TestCase): move_mode) def test_notsupported_mofm(self): + """ Test that a TypeError is correctly raised + """ motion_vec = pd.Series(data=0, index=['x', 'y', 'z', 'dx', 'dy', 'dz']) move_mode = 'NotSupportedMode' @@ -33,6 +37,12 @@ class TestNavipyMovingMaths(unittest.TestCase): move_mode) def test_null_velocity(self): + """ Test null velocity + + When the agent has a null velocity, the next postion + should be equal to the current position. Here, we + test this by series equality. + """ # Test if stay at same position. motion_vec = pd.Series(data=0, index=['x', 'y', 'z', 'dx', 'dy', 'dz']) @@ -44,7 +54,13 @@ class TestNavipyMovingMaths(unittest.TestCase): 'At null velocity the agent should not move') def test_closest_cubic(self): - """ Test if the snaping to cubic is correct """ + """ Test if the snaping to cubic is correct + + When the agent move on the grid, its position has to be snapped + to the grid position. On a cubic grid, the instability is at + 22.5 degrees modulo 45 degrees. We therefore test the functions + close to each instability. + """ positions = pd.DataFrame({'x': [0, 0, 0, 1, 1, 1, 2, 2, 2], 'y': [0, 1, 2, 0, 1, 2, 0, 1, 2], 'z': [0, 0, 0, 0, 0, 0, 0, 0, 0]}, diff --git a/navipy/processing/pcode.py b/navipy/processing/pcode.py index b552906ae05249e684d20b0c60acda3702f9bb97..f2f8824cc15bc54d200f2c39cf6e0f28c44e8e01 100644 --- a/navipy/processing/pcode.py +++ b/navipy/processing/pcode.py @@ -1,53 +1,5 @@ """ -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 - +place code """ import numpy as np import pandas as pd @@ -58,35 +10,8 @@ from .constants import __obpc_indeces__ from .tools import is_ibpc from .tools import is_obpc from .tools import spherical_to_cartesian - - -def is_numeric_array(array): - """Checks if the dtype of the array is numeric. - - Booleans, unsigned integer, signed integer, floats and complex are - considered numeric. - - Parameters - ---------- - array : `numpy.ndarray`-like - The array to check. - - Returns - ------- - is_numeric : `bool` - True if it is a recognized numerical and False if object or - string. - """ - numerical_dtype_kinds = {'b', # boolean - 'u', # unsigned integer - 'i', # signed integer - 'f', # floats - 'c'} # complex - try: - return array.dtype.kind in numerical_dtype_kinds - except AttributeError: - # in case it's not a numpy array it will probably have no dtype. - return np.asarray(array).dtype.kind in numerical_dtype_kinds +from .tools import check_scene +from .tools import check_viewing_direction def scene(database, posorient=None, rowid=None): @@ -147,61 +72,6 @@ def scene(database, posorient=None, rowid=None): raise ValueError('either rowid or posoriend must be given') -def check_scene(scene): - if is_ibpc(scene): - # print("normal") - if not is_numeric_array(scene): - raise TypeError('scene is of non numeric type') - if not ~np.any(np.isnan(scene)): - raise ValueError('scene contains nans') - if not len(scene.shape) == 4: - raise Exception('scene has wrong shape, must have 4 dimensions') - if not (scene.shape[1] > 0): - raise Exception('scenes first dimension is empty') - if not (scene.shape[0] > 0): - raise Exception('scenes second dimension is empty') - if not (scene.shape[2] == 4): - raise Exception('3rd dimension of scene must be four') - if not (scene.shape[3] == 1): - raise Exception('4rd dimension of scene must be one') - # assert ~(np.any(np.isNone(scene))) - return True - - elif is_obpc(scene): - if not is_numeric_array(scene): - raise TypeError('scene is of non numeric type') - if not ~np.any(np.isnan(scene)): - raise ValueError('scene contains nans') - if not len(scene.shape) == 3: - raise Exception('scene has wrong shape, must have 4 dimensions') - if not ~(scene.shape[1] <= 0): - raise Exception('scenes first dimension is empty') - if not ~(scene.shape[0] <= 0): - raise Exception('scenes second dimension is empty') - if not (scene.shape[2] == 4): - raise Exception('3rd dimension of scene must be four') - if not (scene.shape[3] == 1): - raise Exception('4rd dimension of scene must be one') - # assert ~(np.any(np.isNone(scene))) - return True - - -def check_viewing_direction(viewing_direction): - if not is_numeric_array(viewing_direction): - raise TypeError('viewing direction is of non numeric type') - if not ~np.any(np.isnan(viewing_direction)): - raise ValueError('viewing direction contains nans') - if not len(viewing_direction.shape) == 3: - raise Exception('viewing direction must have 3 dimensions') - if not (viewing_direction.shape[1] > 0): - raise Exception('viewing direction has empty second dimension') - if not (viewing_direction.shape[0] > 0): - raise Exception('viewing direction has empty first dimension') - if not (viewing_direction.shape[2] == 2): - raise Exception(' 3rd dimension of viewing direction must equal 2') - return True - - def skyline(scene): """Return the average along the elevation of a scene :param scene: the scenery at a given location (a 4d numpy array) @@ -373,8 +243,3 @@ def apcv(place_code, viewing_directions): 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') diff --git a/navipy/processing/tools.py b/navipy/processing/tools.py index d61aefa2c9489b5b32dc004e856577f12b67da22..06771d6005c3144a23e73ccdf24fe04488ddebb0 100644 --- a/navipy/processing/tools.py +++ b/navipy/processing/tools.py @@ -6,6 +6,84 @@ from .constants import __eye_indeces__ import numpy as np +def check_scene(scene): + if is_ibpc(scene): + # print("normal") + if not is_numeric_array(scene): + raise TypeError('scene is of non numeric type') + if not ~np.any(np.isnan(scene)): + raise ValueError('scene contains nans') + if not len(scene.shape) == 4: + raise Exception('scene has wrong shape, must have 4 dimensions') + if not (scene.shape[1] > 0): + raise Exception('scenes first dimension is empty') + if not (scene.shape[0] > 0): + raise Exception('scenes second dimension is empty') + if not (scene.shape[2] == 4): + raise Exception('3rd dimension of scene must be four') + if not (scene.shape[3] == 1): + raise Exception('4rd dimension of scene must be one') + # assert ~(np.any(np.isNone(scene))) + return True + + elif is_obpc(scene): + if not is_numeric_array(scene): + raise TypeError('scene is of non numeric type') + if not ~np.any(np.isnan(scene)): + raise ValueError('scene contains nans') + if not len(scene.shape) == 3: + raise Exception('scene has wrong shape, must have 4 dimensions') + if not ~(scene.shape[1] <= 0): + raise Exception('scenes first dimension is empty') + if not ~(scene.shape[0] <= 0): + raise Exception('scenes second dimension is empty') + if not (scene.shape[2] == 4): + raise Exception('3rd dimension of scene must be four') + if not (scene.shape[3] == 1): + raise Exception('4rd dimension of scene must be one') + # assert ~(np.any(np.isNone(scene))) + return True + + +def check_viewing_direction(viewing_direction): + if not is_numeric_array(viewing_direction): + raise TypeError('viewing direction is of non numeric type') + if not ~np.any(np.isnan(viewing_direction)): + raise ValueError('viewing direction contains nans') + if not len(viewing_direction.shape) == 3: + raise Exception('viewing direction must have 3 dimensions') + if not (viewing_direction.shape[1] > 0): + raise Exception('viewing direction has empty second dimension') + if not (viewing_direction.shape[0] > 0): + raise Exception('viewing direction has empty first dimension') + if not (viewing_direction.shape[2] == 2): + raise Exception(' 3rd dimension of viewing direction must equal 2') + return True + + +def is_numeric_array(array): + """Checks if the dtype of the array is numeric. + + Booleans, unsigned integer, signed integer, floats and complex are + considered numeric. + + :param array : `numpy.ndarray`-like The array to check. + :returns: True if it is a recognized numerical and False \ + if object or string. + :rtype:bool + """ + numerical_dtype_kinds = {'b', # boolean + 'u', # unsigned integer + 'i', # signed integer + 'f', # floats + 'c'} # complex + try: + return array.dtype.kind in numerical_dtype_kinds + except AttributeError: + # in case it's not a numpy array it will probably have no dtype. + return np.asarray(array).dtype.kind in numerical_dtype_kinds + + def is_ibpc(place_code): """Test if a place code is image based diff --git a/navipy/rendering/bee_sampling.py b/navipy/rendering/bee_sampling.py index ff113ec58ca8d2659e16cc1fd435241c00c916f8..0d91148e273409ba0a098be6ce4ce149cf23ee97 100644 --- a/navipy/rendering/bee_sampling.py +++ b/navipy/rendering/bee_sampling.py @@ -88,7 +88,8 @@ harddrive space, as each image is composed of 4 channels of 180x360 pixels. dataloger = DataBaseSave(database_filename, channels=['R', 'G', 'B', 'D'], arr_dtype=np.uint8) - for frame_i, posorient in self.__grid_posorients.iterrows(): + for frame_i, posorient in self.__grid_posorients.iloc[::-1].iterrows(): + print(frame_i) if np.any(np.isnan(posorient)): # warnings.warn('frame_i: {} posorient nans'.format(frame_i)) continue