diff --git a/build/lib/navipy/__init__.py b/build/lib/navipy/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/build/lib/navipy/comparing/__init__.py b/build/lib/navipy/comparing/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..703cb552f4d603eed2ebcc9feb4c6a68db04f59a --- /dev/null +++ b/build/lib/navipy/comparing/__init__.py @@ -0,0 +1,143 @@ +""" +The Place comparator list different methods to +compare a current place to a memorised place or +memorised places. +""" +import numpy as np +from navipy.processing.tools import is_ibpc, is_obpc +from navipy.processing.tools import check_scene + + +def simple_imagediff(current, memory): + """Compute the difference between +the current and memorised place code + + :param current: current place code + :param memory: memorised place code + :returns: the image difference + :rtype: float + + ..ref: Zeil, J., 2012. Visual homing: an insect perspective. + Current opinion in neurobiology + + """ + if not isinstance(current, np.ndarray): + raise TypeError('current place code should be a numpy array') + if not isinstance(memory, np.ndarray): + raise TypeError('memory place code should be a numpy array') + if not np.all(current.shape == memory.shape): + raise Exception('memory and current place code should\ + have the same shape') + check_scene(current) + check_scene(memory) + diff = current - memory + if is_ibpc(current): + return diff + elif is_obpc(current): + return diff + else: + raise TypeError('place code is neither an ibpc nor obpc') + + +def imagediff(current, memory): + """Compute the root mean square difference between +the current and memorised place code + + :param current: current place code + :param memory: memorised place code + :returns: the image difference + :rtype: float #array(1,4) for ibpc and float for obpc + + ..ref: Zeil, J., 2012. Visual homing: an insect perspective. + Current opinion in neurobiology + + """ + simple_diff = simple_imagediff(current, memory) + diff = np.power(simple_diff, 2) + if is_ibpc(current): + return np.sqrt(diff.mean(axis=0).mean(axis=0)) # 1 + elif is_obpc(current): + return np.sqrt(diff.mean(axis=0).mean(axis=0)) + else: + raise TypeError('place code is neither an ibpc nor obpc') + + +def rot_imagediff(current, memory): + """Compute the rotational image difference between +the current and memorised place code. + + :param current: current place code + :param memory: memorised place code + :returns: the rotational image difference + :rtype: (np.ndarray) + + ..ref: Zeil, J., 2012. Visual homing: an insect perspective. + Current opinion in neurobiology + ..note: assume that the image is periodic along the x axis + (the left-right axis) + """ + if not is_ibpc(current): # and not is_obpc(current): + raise TypeError('The current and memory place code\ + should be image based') + if not is_ibpc(memory): # and not is_obpc(memory): + raise TypeError('The current and memory place code\ + should be image based') + check_scene(current) + check_scene(memory) + ridf = np.zeros((current.shape[1], current.shape[2])) + for azimuth_i in range(0, current.shape[1]): + rot_im = np.roll(current, azimuth_i, axis=1) + ridf[azimuth_i, :] = np.squeeze(imagediff(rot_im, memory)) # rot_im + return ridf + + +def diff_optic_flow(current, memory): + """Computes the direction of motion from current +to memory by using the optic flow under the +constrain that the brightness is constant, (small movement), +using a taylor expansion and solving the equation: +0=I_t+\delta I*<u,v> or I_x+I_y+I_t=0 + +afterwards the aperture problem is solved by a +Matrix equation Ax=b, where x=(u,v) and +A=(I_x,I_y) and b = I_t + + :param current: current place code + :param memory: memorised place code + :returns: a directional vectors + :rtype: (np.ndarray) + + ..ref: aperture problem: + Shimojo, Shinsuke, Gerald H. Silverman, and Ken Nakayama: + "Occlusion and the solution to the aperture problem for motion." + Vision research 29.5 (1989): 619-626. + optic flow: + Horn, Berthold KP, and Brian G. Schunck.: + "Determining optical flow." + Artificial intelligence 17.1-3 (1981): 185-203. + """ + if not is_ibpc(current): # and not is_obpc(current): + raise TypeError('The current and memory place code\ + should be image based') + if not is_ibpc(memory): # and not is_obpc(memory): + raise TypeError('The current and memory place code\ + should be image based') + check_scene(current) + check_scene(memory) + currroll = np.roll(current, 1, axis=1) + dx = current - currroll + memroll = np.roll(memory, 1, axis=1) + dy = memory - memroll + dy = np.reshape(dy, (np.prod(dy.shape), 1)) + dx = np.reshape(dx, (np.prod(dx.shape), 1)) + di = current - memory + di = np.reshape(di, (np.prod(di.shape), 1)) + a_matrix = np.column_stack([dy, dx]) + a_matrix_sqr = np.dot(np.transpose(a_matrix), a_matrix) + b_vector = np.dot(np.transpose(a_matrix), di) + res = np.linalg.solve(a_matrix_sqr, b_vector) + return res + + +def gradient(current, memory): + return 0 diff --git a/build/lib/navipy/comparing/test.py b/build/lib/navipy/comparing/test.py new file mode 100644 index 0000000000000000000000000000000000000000..041eba06e66c0d9958abc0956019d9617f0f6464 --- /dev/null +++ b/build/lib/navipy/comparing/test.py @@ -0,0 +1,236 @@ +import unittest +import numpy as np +import navipy.database as database +import navipy.comparing as comparing +from navipy.processing.tools import is_numeric_array +import pkg_resources + + +class TestCase(unittest.TestCase): + def setUp(self): + self.mydb_filename = pkg_resources.resource_filename( + 'navipy', 'resources/database.db') + self.mydb = database.DataBaseLoad(self.mydb_filename) + + def test_imagediff_curr(self): + curr = self.mydb.read_image(rowid=1) + mem = self.mydb.read_image(rowid=2) + curr = np.expand_dims(curr, axis=3) + mem = np.expand_dims(mem, axis=3) + curr2 = curr.copy() + curr2[3, 5, 2, 0] = np.nan + curr3 = [[1, 2, 3], [1, 2, 3], [1, 2, 3]] + curr3 = [curr3, curr3, curr3] + curr3 = np.array(curr3) + curr4 = np.zeros((3, 4, 5, 0)) + + # put useless stuff here + with self.assertRaises(ValueError): + comparing.imagediff(curr2, mem) + with self.assertRaises(Exception): + comparing.imagediff(curr3, mem) + with self.assertRaises(Exception): + comparing.imagediff(curr4, mem) + + # should be working -> check if result is correct + for s in [curr]: + diff = comparing.imagediff(s, mem) + # self.assertTrue(diff.shape[1] == 1) + self.assertTrue(diff.shape[0] > 0) + self.assertFalse(np.any(np.isnan(diff))) + self.assertTrue(is_numeric_array(diff)) + + def test_imagediff_memory(self): + curr = self.mydb.read_image(rowid=1) + mem = self.mydb.read_image(rowid=2) + curr = np.expand_dims(curr, axis=3) + mem = np.expand_dims(mem, axis=3) + mem2 = curr.copy() + mem2[3, 5, 2] = np.nan + mem3 = [[1, 2], [1, 2], [1, 2]] + mem3 = [mem3, mem3, mem3] + mem3 = np.array(mem3) + mem4 = np.zeros((3, 4, 5)) + + with self.assertRaises(ValueError): + comparing.imagediff(curr, mem2) + with self.assertRaises(Exception): + comparing.imagediff(curr, mem3) + with self.assertRaises(Exception): + comparing.imagediff(curr, mem4) + + # should be working -> check if result is correct + for s in [mem]: + diff = comparing.imagediff(curr, s) + self.assertFalse(diff.shape[0] <= 0) + # self.assertTrue(diff.shape[1] == 1) + self.assertFalse(np.any(np.isnan(diff))) + self.assertTrue(is_numeric_array(diff)) + + def test_rot_imagediff_curr(self): + curr = self.mydb.read_image(rowid=1) + mem = self.mydb.read_image(rowid=2) + curr = np.expand_dims(curr, axis=3) + mem = np.expand_dims(mem, axis=3) + curr2 = curr.copy() + curr2[3, 5, 2] = np.nan + curr3 = [[1, 2, 3], [1, 2, 3], [1, 2, 3]] + curr3 = [curr3, curr3, curr3] + curr3 = np.array(curr3) + curr4 = np.zeros((3, 4, 5)) + + with self.assertRaises(ValueError): + comparing.rot_imagediff(curr2, mem) + with self.assertRaises(Exception): + comparing.rot_imagediff(curr3, mem) + with self.assertRaises(Exception): + comparing.rot_imagediff(curr4, mem) + + # should be working -> check if result is correct + for s in [curr]: + diff = comparing.rot_imagediff(s, mem) + self.assertFalse(diff.shape[0] <= 0) + self.assertTrue(diff.shape[1] == 4) + self.assertFalse(np.any(np.isnan(diff))) + self.assertTrue(is_numeric_array(diff)) + + def test_rotimagediff_memory(self): + curr = self.mydb.read_image(rowid=1) + mem = self.mydb.read_image(rowid=2) + curr = np.expand_dims(curr, axis=3) + mem = np.expand_dims(mem, axis=3) + mem2 = curr.copy() + mem2[3, 5, 2] = np.nan + mem3 = [[1, 2, 3], [1, 2, 3], [1, 2, 3]] + mem3 = [mem3, mem3, mem3] + mem3 = np.array(mem3) + mem4 = np.zeros((3, 4, 5)) + + with self.assertRaises(ValueError): + comparing.rot_imagediff(curr, mem2) + with self.assertRaises(Exception): + comparing.rot_imagediff(curr, mem3) + with self.assertRaises(Exception): + comparing.rot_imagediff(curr, mem4) + + # should be working -> check if result is correct + for s in [mem]: + diff = comparing.rot_imagediff(curr, s) + self.assertFalse(diff.shape[0] <= 0) + self.assertTrue(diff.shape[1] == 4) + self.assertFalse(np.any(np.isnan(diff))) + self.assertTrue(is_numeric_array(diff)) + + def test_simple_imagediff_curr(self): + curr = self.mydb.read_image(rowid=1) + mem = self.mydb.read_image(rowid=2) + curr = np.expand_dims(curr, axis=3) + mem = np.expand_dims(mem, axis=3) + curr2 = curr.copy() + curr2[3, 5, 2] = np.nan + curr3 = [[1, 2, 3], [1, 2, 3], [1, 2, 3]] + curr3 = [curr3, curr3, curr3] + curr3 = np.array(curr3) + curr4 = np.zeros((3, 4, 5)) + + with self.assertRaises(ValueError): + comparing.simple_imagediff(curr2, mem) + with self.assertRaises(Exception): + comparing.simple_imagediff(curr3, mem) + with self.assertRaises(Exception): + comparing.simple_imagediff(curr4, mem) + + # should be working -> check if result is correct + for s in [curr]: + diff = comparing.simple_imagediff(s, mem) + self.assertFalse(diff.shape[0] <= 0) + self.assertTrue(diff.shape[1] > 0) + self.assertFalse(np.any(np.isnan(diff))) + self.assertTrue(is_numeric_array(diff)) + self.assertTrue(diff.shape[2] == 4) + # self.assertTrue(diff.shape[3] == 1) + + def test_simple_imagediff_mem(self): + curr = self.mydb.read_image(rowid=1) + mem = self.mydb.read_image(rowid=2) + curr = np.expand_dims(curr, axis=3) + mem = np.expand_dims(mem, axis=3) + mem2 = curr.copy() + mem2[3, 5, 2] = np.nan + mem3 = [[1, 2, 3], [1, 2, 3], [1, 2, 3]] + mem3 = [mem3, mem3, mem3] + mem3 = np.array(mem3) + mem4 = np.zeros((3, 4, 5)) + + with self.assertRaises(ValueError): + comparing.simple_imagediff(curr, mem2) + with self.assertRaises(Exception): + comparing.simple_imagediff(curr, mem3) + with self.assertRaises(Exception): + comparing.simple_imagediff(curr, mem4) + + # should be working -> check if result is correct + for s in [mem]: + diff = comparing.simple_imagediff(curr, s) + self.assertFalse(diff.shape[0] <= 0) + self.assertTrue(diff.shape[1] > 0) + self.assertFalse(np.any(np.isnan(diff))) + self.assertTrue(is_numeric_array(diff)) + self.assertTrue(diff.shape[2] == 4) + # self.assertTrue(diff.shape[3] == 1) + + def test_diff_optic_flow_memory(self): + curr = self.mydb.read_image(rowid=1) + mem = self.mydb.read_image(rowid=2) + curr = np.expand_dims(curr, axis=3) + mem = np.expand_dims(mem, axis=3) + mem2 = curr.copy() + mem2[3, 5, 2] = np.nan + mem3 = [[1, 2, 3], [1, 2, 3], [1, 2, 3]] + mem3 = [mem3, mem3, mem3] + mem3 = np.array(mem3) + mem4 = np.zeros((3, 4, 5)) + + with self.assertRaises(ValueError): + comparing.diff_optic_flow(curr, mem2) + with self.assertRaises(Exception): + comparing.diff_optic_flow(curr, mem3) + with self.assertRaises(Exception): + comparing.diff_optic_flow(curr, mem4) + + # should be working -> check if result is correct + for s in [mem]: + vec = comparing.diff_optic_flow(curr, s) + self.assertFalse(vec.shape[1] == (1, 2)) + self.assertFalse(np.any(np.isnan(vec))) + self.assertTrue(is_numeric_array(vec)) + + def test_diff_optic_flow_curr(self): + curr = self.mydb.read_image(rowid=1) + mem = self.mydb.read_image(rowid=2) + curr = np.expand_dims(curr, axis=3) + mem = np.expand_dims(mem, axis=3) + curr2 = curr.copy() + curr2[3, 5, 2] = np.nan + curr3 = [[1, 2], [1, 2], [1, 2]] + curr3 = [curr3, curr3, curr3] + curr3 = np.array(curr3) + curr4 = np.zeros((3, 4, 5, 1)) + + with self.assertRaises(ValueError): + comparing.diff_optic_flow(curr2, mem) + with self.assertRaises(Exception): + comparing.diff_optic_flow(curr3, mem) + with self.assertRaises(Exception): + comparing.diff_optic_flow(curr4, mem) + + # should be working -> check if result is correct + for s in [mem]: + vec = comparing.diff_optic_flow(s, curr) + self.assertFalse(vec.shape[1] == (1, 2)) + self.assertFalse(np.any(np.isnan(vec))) + self.assertTrue(is_numeric_array(vec)) + + +if __name__ == '__main__': + unittest.main() diff --git a/build/lib/navipy/database/__init__.py b/build/lib/navipy/database/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..2df843460a1acdec657782cbac74091d54d05847 --- /dev/null +++ b/build/lib/navipy/database/__init__.py @@ -0,0 +1,623 @@ +""" +Database are generated by the rendering module, and contains all \ +images and there corresponding position-orientations. + +* position_orientation: containing all position and orientation of where \ +images were rendered. The position-orientation is described by \ +['x','y','z','alpha_0','alpha_1','alpha_2'] +* image: containing all images ever rendered. Each channel of each image \ +are normalised, so to use the full coding range. +* normalisation: the normalisation constantes + + +How to load a database +---------------------- + +.. code-block:: python + + from database import DataBaseLoad + mydb_filename = 'database.db' + mydb = DataBaseLoad(mydb_filename) + +How to load all position-orientation +------------------------------------ + +The database contains all position-orientation \ +at which an image as been rendered. In certain \ +situation, it may be usefull to know all \ +position-orientation in the database. More technically \ +speaking, loading the full table of position-orientaiton. + +.. code-block:: python + + posorients = mydb.get_posorients() + posorients.head() + +How to load an image +-------------------- + +The database contains images which can be processed differently \ +depending on the navigation strategy beeing used. + +Images are at given position-orientations. To load an image \ +the position-orientation can be given. The DataBaseLoader will \ +look if this position-orientation has been rendered. If it is \ +the case, the image will be returned. + +.. code-block:: python + + posorient = pd.Series(index=['x', 'y', 'z', + 'alpha_0', 'alpha_1', 'alpha_2']) + posorient.x = -0.6 + posorient.y = -7.2 + posorient.z = 2.35 + posorient.alpha_0 = np.pi / 2 + posorient.alpha_1 = 0 + posorient.alpha_2 = 0 + image = mydb.read_image(posorient=posorient) + +.. plot:: example/database/load_image_posorient.py + +However, looking in the database if an image has already been \ +rendered at a given position-orientation can cost time. To speed up \ +certain calculation, image can instead be access by row number. \ +Indeed each position-orientation can be identified by a unique row \ +number. This number is consistant through the entire database. Thus, \ +an image can be loaded by providing the row number. + +.. code-block:: python + + rowid = 1000 + image = mydb.read_image(rowid=rowid) + +.. plot:: example/database/load_image_rowid.py + + + +.. todo: channels as part of database + +""" + +import os +import numpy as np +import pandas as pd +import sqlite3 +import io +import warnings +import navipy.processing.tools as tools + + +def adapt_array(arr): + """ + http://stackoverflow.com/a/31312102/190597 (SoulNibbler) + """ + if arr is None: + raise ValueError('array must not be None') + out = io.BytesIO() + np.save(out, arr) + out.seek(0) + return sqlite3.Binary(out.read()) + + +def convert_array(text): + if text is None: + raise ValueError('text must not be None') + out = io.BytesIO(text) + out.seek(0) + return np.load(out) + + +# Converts np.array to TEXT when inserting +sqlite3.register_adapter(np.ndarray, adapt_array) +# Converts TEXT to np.array when selecting +sqlite3.register_converter("array", convert_array) + + +class DataBase(): + """DataBase is the parent class of DataBaseLoad and DataBaseSave. +It creates three sql table on initialisation. + """ + __float_tolerance = 1e-14 + + 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 or np array') + """for c in channels: + if not c in ['R','G','B','D']: + raise ValueError('channels + must be either R,G,B or D (Distance)')""" + self.filename = filename + self.channels = channels + self.normalisation_columns = list() + for chan_n in self.channels: + self.normalisation_columns.append(str(chan_n) + '_max') + self.normalisation_columns.append(str(chan_n) + '_min') + self.normalisation_columns.append(str(chan_n) + '_range') + + self.tablecolumns = dict() + self.tablecolumns['position_orientation'] = dict() + self.tablecolumns['position_orientation']['x'] = 'real' + self.tablecolumns['position_orientation']['y'] = 'real' + self.tablecolumns['position_orientation']['z'] = 'real' + self.tablecolumns['position_orientation']['alpha_0'] = 'real' + self.tablecolumns['position_orientation']['alpha_1'] = 'real' + self.tablecolumns['position_orientation']['alpha_2'] = 'real' + self.tablecolumns['image'] = dict() + self.tablecolumns['image']['data'] = 'array' + self.tablecolumns['normalisation'] = dict() + for col in self.normalisation_columns: + self.tablecolumns['normalisation'][col] = 'real' + + if os.path.exists(filename): + # Check database + self.db = sqlite3.connect( + '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)) + elif self.create: + # Create database + self.db = sqlite3.connect( + filename, detect_types=sqlite3.PARSE_DECLTYPES) + self.db_cursor = self.db.cursor() + for key, val in self.tablecolumns.items(): + columns = "(id integer primary key autoincrement" + for colname, coltype in val.items(): + columns += ' , ' + colname + ' ' + coltype + columns += ')' + self.db_cursor.execute( + "create table {} {}".format(key, columns)) + self.db.commit() + else: + raise NameError('Database {} does not exist'.format(filename)) + + azimuth = np.linspace(-180, 180, 360) + elevation = np.linspace(-90, 90, 180) + [ma, me] = np.meshgrid(azimuth, elevation) + self.viewing_directions = np.zeros((ma.shape[0], ma.shape[1], 2)) + self.viewing_directions[..., 0] = me + self.viewing_directions[..., 1] = ma + + def table_exist(self, tablename): + if not isinstance(tablename, str): + raise TypeError('tablename should be a string') + self.db_cursor.execute( + """ + SELECT count(*) + FROM sqlite_master + WHERE type='table' and name=?; + """, (tablename,)) + return bool(self.db_cursor.fetchone()) + + def check_data_validity(self, rowid): + if not isinstance(rowid, int): + raise TypeError('rowid must be an integer') + if rowid <= 0: + raise ValueError('rowid must be greater zero') + if rowid is np.nan: + raise ValueError('rowid must not be nan') + self.db_cursor.execute( + """ + SELECT count(*) + FROM position_orientation + WHERE rowid=?; + """, (rowid,)) + valid = bool(self.db_cursor.fetchone()[0]) + self.db_cursor.execute( + """ + SELECT count(*) + FROM normalisation + WHERE rowid=?; + """, (rowid,)) + valid = valid and bool(self.db_cursor.fetchone()[0]) + self.db_cursor.execute( + """ + SELECT count(*) + FROM image + WHERE rowid=?; + """, (rowid,)) + valid = valid and bool(self.db_cursor.fetchone()[0]) + return valid + + def get_posid(self, posorient): + if not isinstance(posorient, pd.Series): + raise TypeError('posorient should be a pandas Series') + if posorient is not None: + if not isinstance(posorient, pd.Series): + raise TypeError('posorient should be a pandas Series') + if posorient.empty: + raise Exception('position must not be empty') + if 'x' not in posorient.index: + raise ValueError('missing index x') + if 'y' not in posorient.index: + raise ValueError('missing index y') + if 'z' not in posorient.index: + raise ValueError('missing index z') + if 'alpha_0' not in posorient.index: + raise ValueError('missing index alpha_0') + if 'alpha_1' not in posorient.index: + raise ValueError('missing index alpha_1') + if 'alpha_2' not in posorient.index: + raise ValueError('missing index alpha_2') + if np.any(pd.isnull(posorient)): + raise ValueError('posorient must not contain nan') + where = """x>=? and x<=?""" + where += """and y>=? and y<=?""" + where += """and z>=? and z<=?""" + where += """and alpha_0>=? and alpha_0<=?""" + where += """and alpha_1>=? and alpha_1<=?""" + where += """and alpha_2>=? and alpha_2<=?""" + params = ( + posorient['x'] - self.__float_tolerance, + posorient['x'] + self.__float_tolerance, + posorient['y'] - self.__float_tolerance, + posorient['y'] + self.__float_tolerance, + posorient['z'] - self.__float_tolerance, + posorient['z'] + self.__float_tolerance, + posorient['alpha_0'] - self.__float_tolerance, + posorient['alpha_0'] + self.__float_tolerance, + posorient['alpha_1'] - self.__float_tolerance, + posorient['alpha_1'] + self.__float_tolerance, + posorient['alpha_2'] - self.__float_tolerance, + posorient['alpha_2'] + self.__float_tolerance) + self.db_cursor.execute( + """ + SELECT count(*) + FROM position_orientation + WHERE {};""".format(where), params) + exist = self.db_cursor.fetchone()[0] # [0] because of tupple + if bool(exist): + self.db_cursor.execute( + """ + SELECT rowid + FROM position_orientation + WHERE {}; + """.format(where), params) + return self.db_cursor.fetchone()[0] + elif self.create: + self.db_cursor.execute( + """ + INSERT + INTO position_orientation(x,y,z,alpha_0,alpha_1,alpha_2) + VALUES (?,?,?,?,?,?) + """, ( + posorient['x'], + posorient['y'], + posorient['z'], + posorient['alpha_0'], + posorient['alpha_1'], + posorient['alpha_2'])) + rowid = self.db_cursor.lastrowid + self.db.commit() + return rowid + else: + print(posorient) + raise ValueError('posorient not found') + + @property + def create(self): + return False + + +class DataBaseLoad(DataBase): + """A database generated by the rendering module is based on sqlite3. + """ + + def __init__(self, filename, channels=['R', 'G', 'B', 'D']): + """Initialise the DataBaseLoader""" + 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 or np array') + for c in channels: + if c not in ['R', 'G', 'B', 'D']: + raise ValueError('channels must be either\ + R,G,B or D (Distance)') + DataBase.__init__(self, filename, channels=channels) + + @property + def create(self): + """use to decide weather to alter the database or not + return False because we do not want + to write on database (Load class)""" + return False + + def iter_posorients(self): + """Iter through all position orientation in the database + """ + self.db_cursor.execute( + """ + SELECT * + FROM position_orientation + """) + + columns_names = [] + for col in self.db_cursor.description: + columns_names.append(col[0]) + for row in self.db_cursor: + toyield = pd.Series(data=row, index=columns_names) + toyield.name = toyield.id + toyield.drop('id', inplace=True) + yield toyield + + @property + def posorients(self): + """Return the position orientations of all points in the \ +database + """ + posorient = pd.read_sql_query( + "select * from position_orientation;", self.db) + posorient.set_index('id', inplace=True) + return posorient + + def read_posorient(self, posorient=None, rowid=None): + if not isinstance(posorient, pd.Series): + ('posorient should be a pandas Series') + if posorient is not None: + if not isinstance(posorient, pd.Series): + raise TypeError('posorient should be a pandas Series') + if posorient.empty: + raise Exception('position must not be empty') + if 'x' not in posorient.index: + raise ValueError('missing index x') + if 'y' not in posorient.index: + raise ValueError('missing index y') + if 'z' not in posorient.index: + raise ValueError('missing index z') + if 'alpha_0' not in posorient.index: + raise ValueError('missing index alpha_0') + if 'alpha_1' not in posorient.index: + raise ValueError('missing index alpha_1') + if 'alpha_2' not in posorient.index: + raise ValueError('missing index alpha_2') + if np.any(pd.isnull(posorient)): + raise ValueError('posorient must not contain nan') + if not isinstance(rowid, int): + raise TypeError('rowid must be an integer') + if rowid <= 0: + raise ValueError('rowid must be greater zero') + if rowid is np.nan: + raise ValueError('rowid must not be nan') + if (posorient is None) and (rowid is None): + Exception('posorient and rowid can not be both None') + if posorient is not None: + rowid = self.get_posid(posorient) + # Read images + tablename = 'position_orientation' + toreturn = pd.read_sql_query( + """ + SELECT * + FROM {} + WHERE (rowid={}) + """.format(tablename, rowid), self.db) + toreturn = toreturn.loc[0, :] + toreturn.name = toreturn.id + toreturn.drop('id') + toreturn = toreturn.astype(float) + return toreturn + + def read_image(self, posorient=None, rowid=None): + if not isinstance(posorient, pd.Series): + ('posorient should be a pandas Series') + if posorient is not None: + if not isinstance(posorient, pd.Series): + raise TypeError('posorient should be a pandas Series') + if posorient.empty: + raise Exception('position must not be empty') + if 'x' not in posorient.index: + raise ValueError('missing index x') + if 'y' not in posorient.index: + raise ValueError('missing index y') + if 'z' not in posorient.index: + raise ValueError('missing index z') + if 'alpha_0' not in posorient.index: + raise ValueError('missing index alpha_0') + if 'alpha_1' not in posorient.index: + raise ValueError('missing index alpha_1') + if 'alpha_2' not in posorient.index: + raise ValueError('missing index alpha_2') + if np.any(pd.isnull(posorient)): + raise ValueError('posorient must not contain nan') + if rowid is not None: + if not isinstance(rowid, int): + raise TypeError('rowid must be an integer') + if rowid <= 0: + raise ValueError('rowid must be greater zero') + if rowid is np.nan: + raise ValueError('rowid must not be nan') + if (posorient is None) and (rowid is None): + raise Exception('posorient and rowid can not be both None') + if posorient is not None: + rowid = self.get_posid(posorient) + """Read an image at a given position-orientation or given id of row in the \ + database. + + :param posorient: a pandas Series with index \ + ['x','y','z','alpha_0','alpha_1','alpha_2'] + :param rowid: an integer + :returns: an image + :rtype: numpy.ndarray + """ + if (posorient is None) and (rowid is None): + Exception('posorient and rowid can not be both None') + if posorient is not None: + rowid = self.get_posid(posorient) + # Read images + tablename = 'image' + self.db_cursor.execute( + """ + SELECT data + FROM {} + WHERE (rowid=?) + """.format(tablename), (rowid,)) + image = self.db_cursor.fetchone()[0] + # Read cmaxminrange + tablename = 'normalisation' + cmaxminrange = pd.read_sql_query( + """ + SELECT * + FROM {} + WHERE (rowid={}) + """.format(tablename, rowid), self.db) + if cmaxminrange.shape[0] != 1: + raise Exception('Error while reading normalisation factors') + cmaxminrange = cmaxminrange.iloc[0, :] + cmaxminrange.name = cmaxminrange.id + cmaxminrange.drop('id') + cmaxminrange = cmaxminrange.astype(float) + return self.denormalise_image(image, cmaxminrange) + + def denormalise_image(self, image, cmaxminrange): + if len(image.shape) != 3: + raise Exception('image should be 3D array') + if 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 Exception('cmaxminrange should be a pandas Series') + if cmaxminrange.empty: + raise Exception('cmaxminrange must not be empty') + for chan_n in self.channels: + if str(chan_n) + '_max' not in cmaxminrange.index: + raise ValueError('cminmax range is missing index ' + + str(chan_n) + '_max') + if str(chan_n) + '_min' not in cmaxminrange.index: + raise ValueError('cminmax range is missing index ' + + str(chan_n) + '_min') + if str(chan_n) + '_range' not in cmaxminrange.index: + raise ValueError('cminmax range is missing index ' + + str(chan_n) + '_range') + if np.any(np.isnan(cmaxminrange.loc[str(chan_n) + '_max'])): + raise ValueError('cmaxminrange contains nans') + if np.any(np.isnan(cmaxminrange.loc[str(chan_n) + '_min'])): + raise ValueError('cmaxminrange contains nans') + if np.any(np.isnan(cmaxminrange.loc[str(chan_n) + '_range'])): + raise ValueError('cmaxminrange contains nans') + denormed_im = np.zeros(image.shape, dtype=np.float) + maxval_nim = np.iinfo(image.dtype).max + # + for chan_i, chan_n in enumerate(self.channels): + cimage = image[:, :, chan_i].astype(float) + cmax = cmaxminrange.loc[str(chan_n) + '_max'] + cmin = cmaxminrange.loc[str(chan_n) + '_min'] + crange = cmaxminrange.loc[str(chan_n) + '_range'] + cimage /= maxval_nim + cimage *= crange + cimage += cmin + denormed_im[:, :, chan_i] = cimage + if np.max(cimage) != cmax: + print(cmaxminrange.name) + # raise ValueError( + # 'denormalisation failed {}!={} + # '.format(np.max(cimage), cmax)) + return denormed_im + + +class DataBaseSave(DataBase): + def __init__(self, filename, channels=['R', 'G', 'B', 'D'], + arr_dtype=np.uint8): + """ + """ + DataBase.__init__(self, filename, channels=channels) + self.arr_dtype = arr_dtype + + def create(self): + """use to decide weather to alter the database or not + return True because we will need + to write on database (Save class)""" + return True + + def write_image(self, posorient, image): + normed_im, cmaxminrange = self.normalise_image(image, self.arr_dtype) + rowid = self.get_posid(posorient) + # Write image + tablename = 'image' + params = dict() + params['rowid'] = rowid + params['data'] = normed_im + self.insert_replace(tablename, params) + # + tablename = 'normalisation' + params = dict() + params['rowid'] = rowid + for chan_n in self.normalisation_columns: + params[chan_n] = cmaxminrange.loc[chan_n] + self.insert_replace(tablename, params) + + def insert_replace(self, tablename, params): + if not isinstance(tablename, str): + raise ValueError('table are named by string') + if not isinstance(params, dict): + raise ValueError('params should be dictionary columns:val') + params_list = list() + columns_str = '' + for key, val in params.items(): + columns_str += key + ',' + params_list.append(val) + columns_str = columns_str[:-1] # remove last comma + if len(params_list) == 0: + warnings.warn('nothing to be done in {}'.format(tablename)) + return + questionsmarks = '?' + for _ in range(1, len(params_list)): + questionsmarks += ',?' + self.db_cursor.execute( + """ + INSERT OR REPLACE + INTO {} ({}) + VALUES ({}) + """.format(tablename, + columns_str, + questionsmarks), + tuple(params_list) + ) + self.db.commit() + + def normalise_image(self, image, dtype=np.uint8): + if not isinstance(image, np.ndarray): + raise TypeError('image must be np.array') + if np.any(np.isnan(image)): + raise ValueError('image must not contain nan values') + if image.shape[0] <= 0 or image.shape[1] <= 0: + raise Exception('image dimensions incorrect') + if image.shape[2] != len(self.channels): + raise Exception('image channels number differs from\ + given channel number') + if not tools.is_numeric_array(image): + raise TypeError('scene is of non numeric type') + normed_im = np.zeros(image.shape, dtype=dtype) + maxval_nim = np.iinfo(normed_im.dtype).max + # + columns = list() + for chan_n in self.channels: + columns.append(str(chan_n) + '_max') + columns.append(str(chan_n) + '_min') + columns.append(str(chan_n) + '_range') + + cmaxminrange = pd.Series(index=columns) + for chan_i, chan_n in enumerate(self.channels): + cimage = image[:, :, chan_i].astype(float) + cmax = cimage.max() + cmin = cimage.min() + crange = cmax - cmin + cimage -= cmin + if np.isclose(crange, 0): + # cimage should be equal to 0 + # so crange is irelevant we can assign it to 1 + crange = 1 + cimage /= crange + cimage *= maxval_nim + cimage = cimage.astype(normed_im.dtype) + normed_im[:, :, chan_i] = cimage + cmaxminrange.loc[str(chan_n) + '_max'] = cmax + cmaxminrange.loc[str(chan_n) + '_min'] = cmin + cmaxminrange.loc[str(chan_n) + '_range'] = crange + return normed_im, cmaxminrange diff --git a/build/lib/navipy/database/tools.py b/build/lib/navipy/database/tools.py new file mode 100644 index 0000000000000000000000000000000000000000..2233ea63a9d565fd05d2afc2c393e2b84e63a5c6 --- /dev/null +++ b/build/lib/navipy/database/tools.py @@ -0,0 +1,21 @@ +""" +Some tools to work with databases +""" +from navipy.database import DataBaseLoad, DataBaseSave + + +def copy(filename_in, filename_out): + """ Copy database until in crashed or finish + + :param filename_in: Path to the input database + :param filename_out: Path to the output database + """ + dbin = DataBaseLoad(filename_in) + dbout = DataBaseSave(filename_out) + for i, posorient in dbin.get_posorients().iterrows(): + print(posorient) + try: + image = dbin.read_image(posorient) + except ValueError: + break + dbout.write_image(posorient, image) diff --git a/build/lib/navipy/moving/__init__.py b/build/lib/navipy/moving/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/build/lib/navipy/moving/agent.py b/build/lib/navipy/moving/agent.py new file mode 100644 index 0000000000000000000000000000000000000000..ad3cd9c6c4370243c9b43b4edb629c4bd3f57340 --- /dev/null +++ b/build/lib/navipy/moving/agent.py @@ -0,0 +1,361 @@ +""" + +""" +import numpy as np +import pandas as pd +import networkx as nx +import multiprocessing +from multiprocessing import Queue, JoinableQueue, Process +import inspect +try: + from pandas.core.common import is_numeric_dtype +except ImportError: + from pandas.api.types import is_numeric_dtype +from navipy.database import DataBaseLoad +import navipy.moving.maths as navimomath + + +def defaultcallback(database, posorients): + raise NameError('No Callback') + + +class AbstractAgent(): + """ + An abtract class for agent + """ + + def __init__(self, + database_filename, + memory_friendly=False): + + self.db = DataBaseLoad(database_filename) + self.dbname = database_filename + if memory_friendly: + self.__posorients = None + else: + self.__posorients = self.db.posorients + # set mode of motion + mode_move = {'mode': 'on_cubic_grid', + 'param': {'grid_spacing': + pd.Series(data=1, + index=['dx', 'dy', 'dz'])}} + self.mode_of_motion = mode_move + + @property + def posorients(self): + toreturn = self.__posorients + if toreturn is not None: + toreturn = toreturn.copy() + return toreturn + + @property + def mode_of_motion(self): + """ + """ + toreturn = self.__mode_move + toreturn['describe'] = \ + navimomath.mode_moves_supported()[ + self.__mode_move['mode']]['describe'] + return toreturn + + @mode_of_motion.setter + def mode_of_motion(self, mode): + """ + + """ + if not isinstance(mode, dict): + raise TypeError('Mode is not a dictionary') + if 'mode' not in mode: + raise KeyError("'mode' is not a key of mode") + if 'param' not in mode: + raise KeyError("'param' is not a key of mode") + if mode['mode'] in navimomath.mode_moves_supported().keys(): + for param in navimomath.mode_moves_supported()[ + mode['mode']]['param']: + if param not in mode['param']: + raise KeyError( + "'{}' is not in mode['param']".format(param)) + self.__mode_move = mode + else: + raise ValueError('mode is not supported') + + def abstractmove(self, posorients_vel): + if isinstance(posorients_vel, pd.Series) is False: + raise TypeError('posorients_vel should be a pandas Series') + for col in ['x', 'y', 'z', 'alpha_0', 'alpha_1', 'alpha_2', + 'dx', 'dy', 'dz', 'dalpha_0', 'dalpha_1', 'dalpha_2']: + if col not in posorients_vel.index: + raise KeyError( + 'posorients_vel should have {} as index'.format(col)) + # Compute the next position + posorients_vel = navimomath.next_pos( + posorients_vel, + move_mode=self.__mode_move['mode'], + move_param=self.__mode_move['param']) + + # Compute the closest possible position + if posorients_vel is None: + tmp = navimomath.closest_pos_memory_friendly( + posorients_vel, + self.db) + posorients_vel[['x', 'y', 'z', + 'alpha_0', 'alpha_1', 'alpha_2']] = tmp + posorients_vel.name = tmp.name + else: + tmp = navimomath.closest_pos( + posorients_vel, + self.__posorients) + posorients_vel[['x', 'y', 'z', + 'alpha_0', 'alpha_1', 'alpha_2']] = tmp + posorients_vel.name = tmp.name + return posorients_vel + + +class Single(AbstractAgent, Process): + + def __init__(self, + database_filename, + initial_condition, + memory_friendly=False, + posorients_queue=None, + results_queue=None): + if (posorients_queue is not None) and (results_queue is not None): + multiprocessing.Process.__init__(self) + AbstractAgent.__init__(self, database_filename, + memory_friendly) + + self.__posorientvel = pd.Series( + data=0, + index=['x', 'y', 'z', + 'alpha_0', 'alpha_1', 'alpha_2', + 'dx', 'dy', 'dz', + 'dalpha_0', 'dalpha_1', 'dalpha_2'], + dtype=np.float) + + if isinstance(initial_condition, pd.Series): + if is_numeric_dtype(initial_condition): + common_id = list(set(initial_condition.index).intersection( + self.__posorientvel.index)) + self.__posorientvel.loc[common_id] = \ + initial_condition.loc[common_id] + else: + raise TypeError('vel should be numeric') + + else: + raise TypeError('vel should be a pandas Series') + + self.__posorients_queue = posorients_queue + self.__results_queue = results_queue + self.__callback_function = defaultcallback + + def move(self): + # Compute the next position + tmp = self.__callback_function(database=self.db, + posorient=self.__posorientvel) + common_id = list(set(tmp.index).intersection( + self.__posorientvel.index)) + self.__posorientvel.loc[common_id] = tmp.loc[common_id] + self.__posorientvel = self.abstractmove(self.__posorientvel) + + def fly(self, nsteps): + """move until either speed is null, or nsteps has been reached""" + prev_move = self.__posorientvel + for stepi in range(nsteps): + self.move() + if prev_move.equals(self.__posorientvel): + break + prev_move = self.__posorientvel + + def run(self): + """ Only supported when multiprocess""" + if self.__posorients_queue is None or self.__results_queue is None: + raise NameError('Single agent class has not be inititialised ' + + 'with multiprocessing suppport') + proc_name = self.name + print('Process {} started'.format(proc_name)) + while True: + start_posorient = self.__posorients_queue.get(timeout=1) + if start_posorient is None: + # Poison pill means shutdown) + break + common_id = list(set(start_posorient.index).intersection( + self.__posorientvel.index)) + self.__posorientvel.loc[common_id] = start_posorient.loc[common_id] + self.move() + next_posorient = self.__posorientvel + + self.__posorients_queue.task_done() + self.__results_queue.put((start_posorient, next_posorient)) + self.__posorients_queue.task_done() + print('Process {} done'.format(proc_name)) + + @property + def callback_function(self): + return inspect.getsourcelines(self.__callback_function) + + @callback_function.setter + def callback_function(self, callback_function): + self.__callback_function = callback_function + + @property + def position(self): + return self.__posorientvel.loc[['x', 'y', 'z']] + + @property + def velocity(self): + return self.__posorientvel.loc[['dx', 'dy', 'dz']] + + @property + def orientation(self): + return self.__posorientvel.loc[['alpha_0', 'alpha_1', 'alpha_2']] + + @property + def angular_velocity(self): + return self.__posorientvel.loc[['dalpha_0', 'dalpha_1', 'dalpha_2']] + + +class Multi(AbstractAgent): + + def __init__(self, database_filename): + super().__init__(database_filename, False) + # Init the graph + self.__graph = nx.DiGraph() + for row_id, posor in self.db.posorients.iterrows(): + posor.name = row_id + self.__graph.add_node(row_id, + posorient=posor) + + @property + def graph(self): + return self.__graph + + @graph.setter + def graph(self, graph): + if isinstance(graph, nx.DiGraph) is False: + raise TypeError('graph is not a nx.DiGraph') + self.__graph = graph.copy() + self.check_graph() + + def build_graph(self, callback_function, + ncpu=5, + timeout=1): + # Build a list of nodes + results_edges = [] + posorients_queue = JoinableQueue() + results_queue = Queue() + for node in self.__graph.nodes: + posorients_queue.put(self.__graph.nodes[node]['posorient']) + initpos = 0 * self.__graph.nodes[node]['posorient'] + + # Start ndatabase loader + num_agents = ncpu + agents = [Single(self.dbname, + initial_condition=initpos, + memory_friendly=False, + posorients_queue=posorients_queue, + results_queue=results_queue) + for _ in range(num_agents)] + for w in agents: + w.callback_function = callback_function + w.start() + + # Add a poison pill for each agent + for _ in range(num_agents): + posorients_queue.put(None) + + # Wait for all of the tasks to finish + # posorients_queue.join() + + for _ in range(nx.number_of_nodes(self.__graph)): + result = results_queue.get(timeout=timeout) + results_edges.append((result[0].name, + result[1].name)) + # print(results_edges[-1]) + self.__graph.add_edges_from(results_edges) + self.check_graph() + + def check_graph(self): + self.check_single_target() + + def check_single_target(self): + for node in self.__graph.nodes: + # not connected -> loop not ran + for count, _ in enumerate(self.__graph.neighbors(node)): + # count == 0 -> connected to one node + # count == 1 -> connected to two nodes + if count > 0: + raise ValueError( + 'Node {} leads to several locations'.format(node)) + + def find_attractors(self): + """Return a list of node going to each attractor in a graph + """ + attractors = list() + for attractor in nx.attracting_components(self.__graph): + att = dict() + att['attractor'] = attractor + attractors.append(att) + return attractors + + def find_attractors_sources(self, attractors=None): + """Find all sources going to each attractors + """ + if attractors is None: + attractors = self.find_attractors() + + if isinstance(attractors, list) is False: + raise TypeError('Attractors should be a list of dict') + elif len(attractors) == 0: + raise ValueError('No attractors found') + + # Check attractor + for att in attractors: + keyatt = att.keys() + if 'attractor' not in keyatt: + raise ValueError( + 'Each attractors should contain the key attractor') + + # Calculate connection + for att_i, att in enumerate(attractors): + + # [0] because one node of the attractor is enough + # all other node of the attractor are connected to this one + target = list(att['attractor'])[0] + attractors[att_i]['paths'] = \ + nx.shortest_path(self.graph, target=target) + attractors[att_i]['sources'] = \ + list(attractors[att_i]['paths'].keys()) + return attractors + + def catchment_area(self, attractors=None): + """Return the catchment area for attractors + """ + if attractors is None: + attractors = self.find_attractors_sources() + + if isinstance(attractors, list) is False: + raise TypeError('Attractors should be a list of dict') + elif len(attractors) == 0: + raise ValueError('No attractors found') + + # Check attractor + for att in attractors: + keyatt = att.keys() + if 'sources' not in keyatt: + raise ValueError( + 'Each attractors should contains a list of sources') + + return [len(att['sources']) for att in attractors] + + def reach_goals(self, goals): + """ Return all paths to the goals """ + return nx.shortest_path(self.__graph, target=goals) + + def neighboring_nodes(self, target): + """ Return the nodes going to the target """ + # Reverse graph because nx.neighbors give the end node + # and we want to find the start node going to target + # not where target goes. + tmpgraph = self.__graph.reverse(copy=True) + neighbors = tmpgraph.neighbors(target) + return neighbors diff --git a/build/lib/navipy/moving/maths.py b/build/lib/navipy/moving/maths.py new file mode 100644 index 0000000000000000000000000000000000000000..630225562d11688a7b17814237fcd3e77a4940fd --- /dev/null +++ b/build/lib/navipy/moving/maths.py @@ -0,0 +1,100 @@ +""" +Mathematical computation are done in this module. Those involve mostly +geometry, and predefined grids shapes +""" +import numpy as np +import pandas as pd + + +def mode_moves_supported(): + return { + 'on_cubic_grid': { + 'param': + ['grid_spacing'], + 'describe': + "Agent restricted to move on a grid"}, + 'free_run': { + 'param': [], + 'describe': + "Freely moving agent, pos(t+dt)=pos+speed (dt=1)"}} + + +def next_pos(motion_vec, move_mode, move_param=None): + """return the future position knowing speed and current position + + :param motion_vec: the position and speed of the agent +(pandas Series with columns ['x','y','z','dx','dy','dz']) + :param grid_spacing: the spacing between two grid points +(only relevant for regular grids) + :param grid_mode: the type of grid. + + ..todo: add literal include for supported_grid_mode + """ + if isinstance(motion_vec, pd.Series) is False: + raise TypeError('motion vector must be a pandas Series') + if move_mode not in mode_moves_supported().keys(): + raise KeyError( + 'move mode must is not supported {}'.format(move_mode)) + + speed = motion_vec.loc[['dx', 'dy', 'dz']] + if move_mode == 'on_cubic_grid': + # speed in spherical coord + epsilon = np.arctan2(speed.dz, np.sqrt(speed.dx**2 + speed.dy**2)) + phi = np.arctan2(speed.dy, speed.dx) + radius = np.sqrt(np.sum(speed**2)) + if np.isclose(radius, 0): + scaling = 0 + speed = 0 * speed + else: + deltas = pd.Series(index=['dx', 'dy', 'dz']) + deltas.dz = epsilon > (np.pi / 8) - epsilon < (np.pi / 8) + edgecases = np.linspace(-np.pi, np.pi, 9) + case_i = np.argmin(np.abs(phi - edgecases)) + if case_i == 8 or case_i == 0 or case_i == 1 or case_i == 7: + deltas.dx = -1 + elif case_i == 3 or case_i == 4 or case_i == 5: + deltas.dx = 1 + else: + deltas.dx = 0 + + if case_i == 1 or case_i == 2 or case_i == 3: + deltas.dy = -1 + elif case_i == 5 or case_i == 6 or case_i == 7: + deltas.dy = 1 + else: + deltas.dy = 0 + scaling = 1 + speed = move_param['grid_spacing'] * deltas + elif move_mode is 'free_run': + scaling = 1 # <=> dt = 1, user need to scale speed in dt units + else: + raise ValueError('grid_mode is not supported') + toreturn = motion_vec + toreturn.loc[['x', 'y', 'z']] += speed.rename({'dx': 'x', + 'dy': 'y', + 'dz': 'z'}) * scaling + return toreturn + + +def closest_pos(pos, positions): + """Return the closest position from a list of positions + + :param pos: the position to find (a pandas Series with ['x','y','z'] + :param positions: the possible closest positions + (a pandas dataframe with ['x','y','z']) + """ + euclidian_dist = np.sqrt( + (pos.x - positions.x)**2 + + (pos.y - positions.y)**2 + + (pos.z - positions.z)**2) + return positions.loc[euclidian_dist.idxmin(), :] + + +def closest_pos_memory_friendly(pos, database): + """Return the closest position from a list of positions + + :param pos: the position to find (a pandas Series with ['x','y','z'] + :param database: the possible closest positions + (a pandas dataframe with ['x','y','z']) + """ + raise NameError('Not implemated') diff --git a/build/lib/navipy/moving/test_agent.py b/build/lib/navipy/moving/test_agent.py new file mode 100644 index 0000000000000000000000000000000000000000..8775361fbcede6c9ee87feb54c9d41bd8ca496c5 --- /dev/null +++ b/build/lib/navipy/moving/test_agent.py @@ -0,0 +1,228 @@ +""" +Test of maths +""" +import numpy as np +import pandas as pd +import networkx as nx +import navipy.moving.agent as naviagent +import navipy.database as navidb +import pkg_resources + +import unittest + + +class TestNavipyMovingAgent(unittest.TestCase): + + def setUp(self): + self.mydb_filename = pkg_resources.resource_filename( + 'navipy', 'resources/database.db') + self.mydb = navidb.DataBaseLoad(self.mydb_filename) + + def test_memfriendly(self): + """posorient is loaded if memory_friendly is False """ + agent = naviagent.AbstractAgent(self.mydb_filename, + memory_friendly=False) + self.assertTrue( + isinstance(agent.posorients, pd.DataFrame), + 'posorients should be a pd.DataFrame when memfriendly is false') + agent = naviagent.AbstractAgent(self.mydb_filename, + memory_friendly=True) + self.assertTrue( + agent.posorients is None, + 'posorients should be None when memfriendly is true') + + def test_abstractmove_inputs(self): + """abstractmove should TypeError if not pandas Series """ + agent = naviagent.AbstractAgent(self.mydb_filename, + memory_friendly=False) + with self.assertRaises(TypeError): + agent.abstractmove('NotPandasSeries') + + posorient_vel = pd.Series() + for col in ['x', 'y', 'z', 'alpha_0', 'alpha_1', 'alpha_2', + 'dx', 'dy', 'dz', 'dalpha_0', 'dalpha_1', 'dalpha_2']: + with self.assertRaises(KeyError): + agent.abstractmove(posorient_vel) + posorient_vel[col] = 2 + + def test_abstractmove_null_vellocity(self): + """abstractmove should leads to same point with null vel""" + agent = naviagent.AbstractAgent(self.mydb_filename, + memory_friendly=False) + + posorient_vel = pd.Series(data=0, + index=['x', 'y', 'z', + 'alpha_0', 'alpha_1', 'alpha_2', + 'dx', 'dy', 'dz', + 'dalpha_0', 'dalpha_1', 'dalpha_2']) + pos = self.mydb.read_posorient(rowid=1) + posorient_vel.loc[['x', 'y', 'z']] = pos.loc[['x', 'y', 'z']] + + newpos = agent.abstractmove(posorient_vel) + self.assertTrue(newpos.equals(posorient_vel), + 'Agent moved although velocity is null') + + # + # Single + # + def test_init_inputs(self): + initial_condition = 'A' + with self.assertRaises(TypeError): + naviagent.Single(self.mydb_filename, + initial_condition, + memory_friendly=False) + + initial_condition = pd.Series(index=['x'], data='a') + with self.assertRaises(TypeError): + naviagent.Single(self.mydb_filename, + initial_condition, + memory_friendly=False) + + # + # Multi + # + def test_init(self): + agent = naviagent.Multi(self.mydb_filename) + self.assertEqual(sorted(agent.graph.nodes), + sorted(list(self.mydb.posorients.index)), + 'Init of graph failed. Node missmatch') + + def test_graph_setter(self): + agent = naviagent.Multi(self.mydb_filename) + graph_nodes = list(agent.graph.nodes) + graph_edges = list() + for gnode in graph_nodes[1:]: + graph_edges.append((gnode, graph_nodes[0])) + + graph = nx.DiGraph() + graph.add_nodes_from(graph_nodes) + graph.add_edges_from(graph_edges) + agent.graph = graph + + graph_edges.append((graph_nodes[2], graph_nodes[1])) + graph = nx.DiGraph() + graph.add_nodes_from(graph_nodes) + graph.add_edges_from(graph_edges) + with self.assertRaises(ValueError): + agent.graph = graph + + def test_catchment_area(self): + """ + 1 Test all node to first + 2 Test 11 nodes to first, 14 to 12th + 3 Two loops attractors + """ + # Test all node to first + agent = naviagent.Multi(self.mydb_filename) + + graph_nodes = list(agent.graph.nodes) + graph_edges = list() + for gnode in graph_nodes[1:]: + graph_edges.append((gnode, graph_nodes[0])) + + graph = nx.DiGraph() + graph.add_nodes_from(graph_nodes) + graph.add_edges_from(graph_edges) + agent.graph = graph + attractors = agent.find_attractors() + self.assertEqual(len(attractors), 1, 'Too many or too few attractors') + attractors = agent.find_attractors_sources(attractors) + catchment_area = agent.catchment_area(attractors) + self.assertEqual(catchment_area, [len(graph_nodes)], + 'Too big or too short catchment area') + + # Test 11 nodes to first, 14 to 12th + graph_edges = list() + for gnode in graph_nodes[1:11]: + graph_edges.append((gnode, graph_nodes[0])) + for gnode in graph_nodes[11:]: + graph_edges.append((gnode, graph_nodes[11])) + + graph = nx.DiGraph() + graph.add_nodes_from(graph_nodes) + graph.add_edges_from(graph_edges) + agent.graph = graph + attractors = agent.find_attractors() + self.assertEqual(len(attractors), 2, 'Too many or too few attractors') + attractors = agent.find_attractors_sources(attractors) + catchment_area = agent.catchment_area(attractors) + self.assertEqual(sorted(catchment_area), [11, 14], + 'Too big or too short catchment area') + + # Two loops attractors + graph_edges = list() + for snode, enode in zip(graph_nodes[:11], + np.roll(graph_nodes[:11], 1)): + graph_edges.append((snode, enode)) + for snode, enode in zip(graph_nodes[11:], + np.roll(graph_nodes[11:], 1)): + graph_edges.append((snode, enode)) + + graph = nx.DiGraph() + graph.add_nodes_from(graph_nodes) + graph.add_edges_from(graph_edges) + agent.graph = graph + attractors = agent.find_attractors() + self.assertEqual(len(attractors), 2, 'Too many or too few attractors') + attractors = agent.find_attractors_sources(attractors) + catchment_area = agent.catchment_area(attractors) + self.assertEqual(sorted(catchment_area), [11, 14], + 'Too big or too short catchment area') + + def test_neighboring_nodes(self): + """ Counting neighnoring nodes for 3 situations + 1. Local maxima + 2. Saddle points + 3. Local minima + """ + agent = naviagent.Multi(self.mydb_filename) + # Local maxima + graph_nodes = list(agent.graph.nodes) + graph_edges = list() + graph_edges.append((graph_nodes[0], + graph_nodes[1])) + + graph = nx.DiGraph() + graph.add_nodes_from(graph_nodes) + graph.add_edges_from(graph_edges) + agent.graph = graph + neighbors = agent.neighboring_nodes(graph_nodes[0]) + expected_nbh = [] + obtained_nbh = [a for a in neighbors] + self.assertEqual(sorted(expected_nbh), + sorted(obtained_nbh), + 'Problem neighbors maxima') + + # Saddle points + graph_edges.append((graph_nodes[1], + graph_nodes[2])) + + graph = nx.DiGraph() + graph.add_nodes_from(graph_nodes) + graph.add_edges_from(graph_edges) + agent.graph = graph + neighbors = agent.neighboring_nodes(graph_nodes[1]) + expected_nbh = [graph_nodes[0]] + obtained_nbh = [a for a in neighbors] + self.assertEqual(sorted(expected_nbh), + sorted(obtained_nbh), + 'Problem neighbors saddle') + + # Local maxima points + graph_edges.append((graph_nodes[3], + graph_nodes[2])) + + graph = nx.DiGraph() + graph.add_nodes_from(graph_nodes) + graph.add_edges_from(graph_edges) + agent.graph = graph + neighbors = agent.neighboring_nodes(graph_nodes[2]) + expected_nbh = [graph_nodes[3], graph_nodes[1]] + obtained_nbh = [a for a in neighbors] + self.assertEqual(sorted(expected_nbh), + sorted(obtained_nbh), + 'Problem neighbors minima') + + +if __name__ == '__main__': + unittest.main() diff --git a/build/lib/navipy/moving/test_maths.py b/build/lib/navipy/moving/test_maths.py new file mode 100644 index 0000000000000000000000000000000000000000..0917850568097bd3d22ee8d154a20dfc188cd7ac --- /dev/null +++ b/build/lib/navipy/moving/test_maths.py @@ -0,0 +1,108 @@ +""" +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() + mode_param['grid_spacing'] = 1 + with self.assertRaises(TypeError): + navimaths.next_pos(motion_vec, + move_mode, + 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' + mode_param = dict() + mode_param['grid_spacing'] = 1 + with self.assertRaises(KeyError): + navimaths.next_pos(motion_vec, + move_mode, + 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']) + move_mode = 'on_cubic_grid' + mode_param = dict() + mode_param['grid_spacing'] = 1 + new_pos = navimaths.next_pos(motion_vec, move_mode, mode_param) + self.assertTrue(new_pos.equals(motion_vec), + 'At null velocity the agent should not move') + + def test_closest_cubic(self): + """ 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]}, + dtype=np.float) + move_mode = 'on_cubic_grid' + move_param = dict() + move_param['grid_spacing'] = pd.Series(data=1, + index=['dx', 'dy', 'dz']) + expected_dict = dict() + expected_dict[-22] = 7 # [2,1] + expected_dict[22] = 7 + expected_dict[24] = 8 # [2,2] + expected_dict[67] = 8 + expected_dict[68] = 5 # [1,2] + expected_dict[112] = 5 + expected_dict[113] = 2 # [0,2] + expected_dict[157] = 2 + expected_dict[158] = 1 # [0, 1] + expected_dict[202] = 1 + expected_dict[204] = 0 # [0, 0] + expected_dict[247] = 0 + expected_dict[248] = 3 # [1, 0] + expected_dict[292] = 3 + expected_dict[293] = 6 # [2, 0] + expected_dict[337] = 6 + expected_dict[338] = 7 # equivalent to -22 + + for angle, exp_i in expected_dict.items(): + alpha = np.deg2rad(angle) + motion_vec = pd.Series( + data=[1, 1, 0, + np.cos(alpha), np.sin(alpha), 0], + index=['x', 'y', 'z', + 'dx', 'dy', 'dz'], + dtype=np.float) + newpos = navimaths.next_pos(motion_vec, + move_mode, + move_param) + snappos = navimaths.closest_pos(newpos, positions) + self.assertEqual(snappos.name, exp_i, + 'closest pos is not correctly snapped') + + +if __name__ == '__main__': + unittest.main() diff --git a/build/lib/navipy/processing/__init__.py b/build/lib/navipy/processing/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..180241d76893c51ab29b067d4bf020009eb01fa4 --- /dev/null +++ b/build/lib/navipy/processing/__init__.py @@ -0,0 +1,46 @@ +""" +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. +""" diff --git a/build/lib/navipy/processing/constants.py b/build/lib/navipy/processing/constants.py new file mode 100644 index 0000000000000000000000000000000000000000..b2570be93b5c89e30db43b797d4f15476d8a8625 --- /dev/null +++ b/build/lib/navipy/processing/constants.py @@ -0,0 +1,22 @@ +""" +Define some constant +""" + +__spherical_indeces__ = {'elevation': 0, + 'azimuth': 1, + 'radius': 2} +__cartesian_indeces__ = {'x': 0, + 'y': 1, + 'z': 2} +__ibpc_indeces__ = {'elevation': 0, + 'azimuth': 1, + 'channel': 2, + 'component': 3} +__obpc_indeces__ = {'ommatidia': 0, + 'channel': 1, + 'component': 2} +__eye_indeces__ = {'elevation': 0, + 'azimuth': 1, + 'component': 2} +__ommadia_indeces__ = {'ommatidia': 0, + 'component': 1} diff --git a/build/lib/navipy/processing/mcode.py b/build/lib/navipy/processing/mcode.py new file mode 100644 index 0000000000000000000000000000000000000000..7eb14619b1754d4ea70ffe5c934e16c034252329 --- /dev/null +++ b/build/lib/navipy/processing/mcode.py @@ -0,0 +1,15 @@ +""" +Motion code +""" +from .tools import check_scene + + +def optic_flow(scene, viewing_directions, + velocity, distance_channel=3): + """ optic flow """ + check_scene(scene) + if distance_channel not in range(4): + raise ValueError('distance channel out of range') + distance = scene[..., distance_channel, 0] + distance += distance + raise NameError('Not Implemented') diff --git a/build/lib/navipy/processing/pcode.py b/build/lib/navipy/processing/pcode.py new file mode 100644 index 0000000000000000000000000000000000000000..24bea9c826059f1edfbc21ef6728588c324f87bb --- /dev/null +++ b/build/lib/navipy/processing/pcode.py @@ -0,0 +1,193 @@ +""" +place code derived from scene +""" +import numpy as np +from scipy.ndimage import maximum_filter, minimum_filter +from .constants import __spherical_indeces__ +from .constants import __ibpc_indeces__ +from .constants import __obpc_indeces__ +from .tools import is_ibpc +from .tools import is_obpc +from .tools import spherical_to_cartesian +from .tools import check_scene +from .tools import check_viewing_direction + + +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 + + """ + if not is_ibpc(scene): + raise TypeError('scene should be image based to compute a skyline') + check_scene(scene) + skyline = scene.mean(axis=__ibpc_indeces__['elevation']) + return skyline[np.newaxis, :] + + +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 + + """ + check_scene(scene) + if not is_ibpc(scene): + raise TypeError('scene should be image based\ + to compute the michelson constrast') + if not isinstance(size, int): + raise TypeError('size must be integer') + if (size < 2 or size > 5): + raise ValueError('size must be between 2 and 5') + contrast = np.zeros_like(scene) + for channel in range(scene.shape[__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') + divider = i_max + i_min + nonzero = divider != 0 + eqzero = divider == 0 + i_min = i_min[nonzero] + i_max = i_max[nonzero] + divider = divider[nonzero] + contrast[nonzero, channel, 0] = (i_max - i_min) / divider + contrast[eqzero, channel, 0] = 0 + 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: 13-15 + + .. plot:: example/processing/contrast_weighted_nearness.py + + """ + check_scene(scene) + if not isinstance(contrast_size, int): + raise TypeError('constrast size must be of type integer') + if not isinstance(distance_channel, int): + raise TypeError('distance channel must be of type integer') + if contrast_size not in range(2, 6): + raise ValueError('contrast size out of range') + if distance_channel not in range(4): + raise ValueError('distance channel out of range') + if not is_ibpc(scene): + raise TypeError('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: 13-15 + + .. plot:: example/processing/pcv.py + + """ + # print("place code shape",place_code.shape) + if is_ibpc(place_code): + component_dim = __ibpc_indeces__['component'] + channel_dim = __ibpc_indeces__['channel'] + elif is_obpc(place_code): + component_dim = __obpc_indeces__['component'] + channel_dim = __obpc_indeces__['channel'] + else: + raise TypeError('place code should be either an ibpc or obpc') + + check_scene(place_code) + check_viewing_direction(viewing_directions) + if not place_code.shape[0] == viewing_directions.shape[0]: + raise Exception('dimensions of place code and viewing\ + direction do not match') + if not place_code.shape[1] == viewing_directions.shape[1]: + raise Exception('dimensions of place code and viewing\ + direction do not match') + if not isinstance(viewing_directions, np.ndarray): + raise TypeError('viewing_directions should be a numpy array') + if not place_code.shape[component_dim] == 1: + raise Exception('the last dimension ({}) of the place-code\ + should be 1'.format(place_code.shape[component_dim])) + elevation = viewing_directions[..., __spherical_indeces__['elevation']] + azimuth = viewing_directions[..., __spherical_indeces__['azimuth']] + unscaled_lv = 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: 13-15 + + .. plot:: example/processing/apcv.py + + """ + check_scene(place_code) + check_viewing_direction(viewing_directions) + if not place_code.shape[0] == viewing_directions.shape[0]: + raise Exception('dimensions of place code and viewing\ + direction do not match') + if not place_code.shape[1] == viewing_directions.shape[1]: + raise Exception('dimensions of place code and viewing\ + direction do not match') + scaled_lv = pcv(place_code, viewing_directions) + if is_ibpc(place_code): + return (scaled_lv.sum(axis=0).sum(axis=0))[np.newaxis, np.newaxis, ...] + elif is_obpc(place_code): + return (scaled_lv.sum(axis=0))[np.newaxis, ...] + else: + raise TypeError('place code is neither an ibpc nor obpc') diff --git a/build/lib/navipy/processing/test.py b/build/lib/navipy/processing/test.py new file mode 100644 index 0000000000000000000000000000000000000000..daa12edf988a007de54ab8007f8f01289554c8e4 --- /dev/null +++ b/build/lib/navipy/processing/test.py @@ -0,0 +1,368 @@ +import unittest +import sqlite3 +import numpy as np +import pandas as pd +import navipy.database as database +import navipy.processing.pcode as pcode +from navipy.processing.tools import is_numeric_array +import pkg_resources + + +class TestCase(unittest.TestCase): + def setUp(self): + self.mydb_filename = pkg_resources.resource_filename( + 'navipy', 'resources/database.db') + self.mydb = database.DataBaseLoad(self.mydb_filename) + + def test_scene_posorient(self): + conn = sqlite3.connect(self.mydb_filename) + c = conn.cursor() + c.execute(""" SELECT * FROM position_orientation WHERE (rowid=1) """) + rows = c.fetchall()[0] + # working case + posorient = pd.Series(index=['x', 'y', 'z', + 'alpha_0', 'alpha_1', 'alpha_2']) + posorient.x = rows[5] + posorient.y = rows[6] + posorient.z = rows[1] + posorient.alpha_0 = rows[3] + posorient.alpha_1 = rows[2] + posorient.alpha_2 = rows[4] + image = self.mydb.read_image(posorient=posorient) + image = np.expand_dims(image, axis=3) + self.assertIsNotNone(image) + self.assertFalse(sum(image.shape) == 0) + # print("shape",image.shape) + self.assertTrue(len(image.shape) == 4) + self.assertTrue(image.shape[3] == 1) + + # incorrect case missing column + posorient2 = pd.Series(index=['y', 'z', + 'alpha_0', 'alpha_1', 'alpha_2']) + posorient2.y = posorient.y + posorient2.z = posorient.z + posorient2.alpha_0 = posorient.alpha_0 + posorient2.alpha_1 = posorient.alpha_1 + posorient2.alpha_2 = posorient.alpha_2 + with self.assertRaises(Exception): + image = self.mydb.read_image(posorient=posorient2) + + # incorrect case None + posorient2 = pd.Series(index=['x', 'y', 'z', + 'alpha_0', 'alpha_1', 'alpha_2']) + posorient2.x = None + posorient2.y = posorient.y + posorient2.z = posorient.z + posorient2.alpha_0 = posorient.alpha_0 + posorient2.alpha_1 = posorient.alpha_1 + posorient2.alpha_2 = posorient.alpha_2 + with self.assertRaises(ValueError): + image = self.mydb.read_image(posorient=posorient2) + + # incorrect case nan + posorient2 = pd.Series(index=['x', 'y', 'z', + 'alpha_0', 'alpha_1', 'alpha_2']) + posorient2.x = np.nan + posorient2.y = posorient.y + posorient2.z = posorient.z + posorient2.alpha_0 = posorient.alpha_0 + posorient2.alpha_1 = posorient.alpha_1 + posorient2.alpha_2 = posorient.alpha_2 + with self.assertRaises(ValueError): + image = self.mydb.read_image(posorient=posorient2) + + # incorrect case no pandas series but dict + posorient2 = {} + posorient2['x'] = posorient.x + posorient2['y'] = posorient.y + posorient2['z'] = posorient.z + posorient2['alpha_0'] = posorient.alpha_0 + posorient2['alpha_1'] = posorient.alpha_1 + posorient2['alpha_2'] = posorient.alpha_2 + with self.assertRaises(TypeError): + image = self.mydb.read_image(posorient=posorient2) + + # not working case empty + posorient2 = pd.Series(index=['x', 'y', 'z', + 'alpha_0', 'alpha_1', 'alpha_2']) + + with self.assertRaises(Exception): + image = self.mydb.read_image(posorient=posorient2) + + def test_skyline_scene(self): + scene = self.mydb.read_image(rowid=1) + scene2 = scene.copy() + scene = np.expand_dims(scene, axis=3) + scene2 = np.expand_dims(scene2, axis=3) + scene2[3, 5, 2, 0] = np.nan + scene3 = [[1, 2, 3], [1, 2, 3], [1, 2, 3]] + scene3 = [scene3, scene3, scene3] + scene3 = np.array(scene3) + scene4 = np.zeros((3, 4, 5, 0)) + + # put useless stuff here + with self.assertRaises(ValueError): + pcode.skyline(scene2) + with self.assertRaises(TypeError): + pcode.skyline(scene3) + with self.assertRaises(Exception): + pcode.skyline(scene4) + + # should be working -> check if result(skyline) is correct + for s in [scene]: + skyline = pcode.skyline(s) + self.assertFalse(skyline.shape[1] <= 0) + self.assertTrue(skyline.shape[2] == 4) + self.assertFalse(np.any(np.isnan(skyline))) + # self.assertFalse(np.any(np.isNone(skyline))) + self.assertTrue(is_numeric_array(skyline)) + self.assertTrue(skyline.shape[3] == 1) + self.assertTrue(skyline.shape[0] > 0) + self.assertTrue(skyline.shape[1] > 0) + + def test_id(self): + for rowid in [0, -2]: + with self.assertRaises(ValueError): + # print("rowid",rowid) + self.mydb.read_image(rowid=rowid) + with self.assertRaises(TypeError): + self.mydb.read_image(rowid='T') + with self.assertRaises(TypeError): + self.mydb.read_image(rowid=None) + with self.assertRaises(TypeError): + self.mydb.read_image(rowid=np.nan) + with self.assertRaises(TypeError): + self.mydb.read_image(rowid=4.5) + + for rowid in [1, 2, 3, 4, 5]: + image = self.mydb.read_image(rowid=rowid) + image = np.expand_dims(image, axis=3) + # image=np.array(image) + self.assertIsNotNone(image) + self.assertFalse(sum(image.shape) == 0) + self.assertTrue(len(image.shape) == 4) + self.assertFalse(np.any(np.isnan(image))) + self.assertTrue(image.shape[3] == 1) + self.assertTrue(image.shape[2] == 4) + self.assertTrue(image.shape[0] > 0) + self.assertTrue(image.shape[1] > 0) + + def test_distance_channel(self): + scene = self.mydb.read_image(rowid=1) + scene = np.expand_dims(scene, axis=3) + # should not be working + for d in ['g', None, np.nan, 8.4]: + with self.assertRaises(TypeError): + pcode.contrast_weighted_nearness(scene, + distance_channel=d) + with self.assertRaises(ValueError): + pcode.contrast_weighted_nearness(scene, + distance_channel=-1) + + # should work + d = 3 + weighted_scene = \ + pcode.contrast_weighted_nearness(scene, + distance_channel=d) + # print("last channel",d) + self.assertTrue(is_numeric_array(weighted_scene)) + self.assertTrue(~np.any(np.isnan(weighted_scene))) + self.assertEqual(weighted_scene.shape, scene.shape) + + def test_contr_weight_scene(self): + scene = self.mydb.read_image(rowid=1) + scene = np.expand_dims(scene, axis=3) + # working cases + contrast = pcode.contrast_weighted_nearness(scene) + self.assertIsNotNone(contrast) + self.assertFalse(sum(contrast.shape) == 0) + self.assertTrue(len(contrast.shape) == 4) + self.assertFalse(np.any(np.isnan(contrast))) + self.assertTrue(contrast.shape[3] == 1) + self.assertTrue(contrast.shape[2] == 4) + self.assertTrue(contrast.shape[0] > 0) + self.assertTrue(contrast.shape[1] > 0) + + # not working case + scene2 = scene.copy() + scene2[3, 2, 1, 0] = np.nan + scene3 = [[1, 2, 3], [1, 2, 3], [1, 2, 3]] + scene3 = [scene3, scene3, scene3] + scene3 = np.array(scene3) + scene4 = np.zeros((3, 4, 5, 0)) + with self.assertRaises(ValueError): + contrast = pcode.contrast_weighted_nearness(scene2) + with self.assertRaises(Exception): + contrast = pcode.contrast_weighted_nearness(scene3) + with self.assertRaises(Exception): + contrast = pcode.contrast_weighted_nearness(scene4) + + def test_contr_weight_contrast(self): + scene = self.mydb.read_image(rowid=1) + scene = np.expand_dims(scene, axis=3) + for size in [9.4, 'g', None, np.nan]: + with self.assertRaises(TypeError): + contrast = pcode.contrast_weighted_nearness( + scene, contrast_size=size) + for size in [8, 1, 0, -4]: + with self.assertRaises(ValueError): + contrast = \ + pcode.contrast_weighted_nearness( + scene, contrast_size=size) + + # working cases + for size in [2, 3, 4, 5]: + contrast = pcode.contrast_weighted_nearness(scene, + contrast_size=size) + self.assertIsNotNone(contrast) + self.assertFalse(sum(contrast.shape) == 0) + self.assertTrue(len(contrast.shape) == 4) + self.assertFalse(np.any(np.isnan(contrast))) + self.assertEqual(contrast.shape[3], 1) + self.assertEqual(contrast.shape[2], scene.shape[2]) + self.assertEqual(contrast.shape[0], scene.shape[0]) + self.assertEqual(contrast.shape[1], scene.shape[1]) + + def test_pcv(self): + # working case + rowid = 1 + my_scene = self.mydb.read_image(rowid=rowid) + my_scene = np.expand_dims(my_scene, axis=3) + directions = self.mydb.viewing_directions + my_pcv = pcode.pcv(my_scene, directions) + self.assertIsNotNone(my_pcv) + self.assertFalse(sum(my_pcv.shape) == 0) + self.assertTrue(len(my_pcv.shape) == 4) + self.assertFalse(np.any(np.isnan(my_pcv))) + self.assertTrue(my_pcv.shape[3] == 3) + self.assertTrue(my_pcv.shape[2] == 4) + self.assertTrue(my_pcv.shape[0] > 0) + self.assertTrue(my_pcv.shape[1] > 0) + + # not working cases doesnt match with shape of place code + testdirection = np.zeros((2, 4, 2)) + with self.assertRaises(Exception): + my_pcv = pcode.pcv(my_scene, testdirection) + + # not working cases wrong last dimension + testdirection = np.zeros((180, 360, 1)) + with self.assertRaises(Exception): + my_pcv = pcode.pcv(my_scene, testdirection) + + # not working cases too many dimensions + testdirection = np.zeros((180, 360, 2, 4)) + with self.assertRaises(Exception): + my_pcv = pcode.pcv(my_scene, testdirection) + + # not working cases empty + testdirection = np.zeros(()) + with self.assertRaises(Exception): + my_pcv = pcode.pcv(my_scene, testdirection) + + # not working cases nans + testdirection = np.zeros((180, 360, 2, 4)) + testdirection[2, 3, 0] = np.nan + with self.assertRaises(ValueError): + my_pcv = pcode.pcv(my_scene, testdirection) + + def test_apcv(self): + # working case + rowid = 1 + my_scene = self.mydb.read_image(rowid=rowid) + my_scene = np.expand_dims(my_scene, axis=3) + # print("scene shape",my_scene.shape) + directions = self.mydb.viewing_directions + print("directions", directions.shape) + my_pcv = pcode.apcv(my_scene, directions) + + self.assertIsNotNone(my_pcv) + self.assertFalse(sum(my_pcv.shape) == 0) + self.assertTrue(len(my_pcv.shape) == 4) + self.assertFalse(np.any(np.isnan(my_pcv))) + self.assertTrue(my_pcv.shape[3] == 3) + self.assertTrue(my_pcv.shape[2] == 4) + self.assertTrue(my_pcv.shape[0] == 1) + self.assertTrue(my_pcv.shape[1] == 1) + + # not working cases doesnt match with shape of place code + testdirection = np.zeros((2, 4, 2)) + with self.assertRaises(Exception): + my_pcv = pcode.apcv(my_scene, testdirection) + + # not working cases wrong last dimension + testdirection = np.zeros((180, 360, 1)) + with self.assertRaises(Exception): + my_pcv = pcode.apcv(my_scene, testdirection) + + # not working cases too many dimensions + testdirection = np.zeros((180, 360, 2, 4)) + with self.assertRaises(Exception): + my_pcv = pcode.apcv(my_scene, testdirection) + + # not working cases empty + testdirection = np.zeros(()) + with self.assertRaises(Exception): + my_pcv = pcode.apcv(my_scene, testdirection) + + # not working cases nans + testdirection = np.zeros((180, 360, 2, 4)) + testdirection[2, 3, 0] = np.nan + with self.assertRaises(ValueError): + my_pcv = pcode.apcv(my_scene, testdirection) + + def test_size(self): + # not working cases: + scene = self.mydb.read_image(rowid=1) + scene = np.expand_dims(scene, axis=3) + for size in [8, 1, 0, -4]: + with self.assertRaises(ValueError): + contrast = pcode.michelson_contrast( + scene, size=size) + for size in [9.4, 'g', None, np.nan]: + with self.assertRaises(TypeError): + contrast = pcode.michelson_contrast( + scene, size=size) + + # working cases + for size in [2, 3, 4, 5]: + contrast = pcode.michelson_contrast(scene, size=size) + self.assertIsNotNone(contrast) + self.assertFalse(sum(contrast.shape) == 0) + self.assertTrue(len(contrast.shape) == 4) + self.assertFalse(np.any(np.isnan(contrast))) + self.assertTrue(contrast.shape[3] == 1) + self.assertTrue(contrast.shape[2] == 4) + self.assertTrue(contrast.shape[0] > 0) + self.assertTrue(contrast.shape[1] > 0) + + def test_michelsoncontrast_scene(self): + + scene = self.mydb.read_image(rowid=1) + scene = np.expand_dims(scene, axis=3) + + # working cases + contrast = pcode.michelson_contrast(scene) + self.assertIsNotNone(contrast) + self.assertFalse(sum(contrast.shape) == 0) + self.assertTrue(len(contrast.shape) == 4) + self.assertFalse(np.any(np.isnan(contrast))) + self.assertTrue(contrast.shape[3] == 1) + self.assertTrue(contrast.shape[2] == 4) + self.assertTrue(contrast.shape[0] > 0) + self.assertTrue(contrast.shape[1] > 0) + + # not working case + scene2 = scene.copy() + scene2[3, 2, 1, 0] = np.nan + scene3 = [[1, 2, 3], [1, 2, 3], [1, 2, 3]] + scene3 = [scene3, scene3, scene3] + scene3 = np.array(scene3) + scene4 = np.zeros((3, 4, 5, 0)) + for s in [scene2, scene3, scene4]: + with self.assertRaises(Exception) as cm: + contrast = pcode.michelson_contrast(s,) + print("wanted exception occured", cm.exception) + + +if __name__ == '__main__': + unittest.main() diff --git a/build/lib/navipy/processing/tools.py b/build/lib/navipy/processing/tools.py new file mode 100644 index 0000000000000000000000000000000000000000..42e0cc8de33ee128ce9353d095412b1f32fbc87a --- /dev/null +++ b/build/lib/navipy/processing/tools.py @@ -0,0 +1,165 @@ +from .constants import __ibpc_indeces__ +from .constants import __spherical_indeces__ +from .constants import __cartesian_indeces__ +from .constants import __obpc_indeces__ +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') + # 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 len(viewing_direction.shape) < 3: + raise Exception('viewing direction must have at least 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[-1] == 2): + raise Exception(' last 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 + + :param place_code: a place-code + :returns: True if image based place-code + :rtype: bool + + """ + toreturn = isinstance(place_code, np.ndarray) + toreturn = toreturn and (len(place_code.shape) == + len(__ibpc_indeces__)) + return toreturn + + +def is_obpc(place_code): + """Test if a place code is ommatidia based + + :param place_code: a place-code + :returns: True if ommatidia based place-code + :rtype: bool + + """ + toreturn = isinstance(place_code, np.ndarray) + toreturn = toreturn and (len(place_code.shape) == + len(__obpc_indeces__)) + return toreturn + + +def ibs_to_obs(scene, eye_map): + """Convert an image based scene to an ommatidium based scene. + + :param scene: The scene to be converted + :param eye_map: The eye_map to use + :returns: (obs_scene,ommatidia_map) + :rtype: (np.ndarray,np.ndarray) + """ + assert is_ibpc(scene),\ + 'scene should be an ibs scene' + assert isinstance(eye_map, np.ndarray), 'eye_map should be a numpy array' + assert len(eye_map.shape) == len(__eye_indeces__),\ + 'eye_map should have {} dimensions to be an ibs scene'.format( + len(__eye_indeces__)) + for index_name in ['elevation', 'azimuth']: + index = __ibpc_indeces__[index_name] + assert eye_map.shape[index] == scene.shape[index],\ + 'eye_map and scene should have the same number of {}'.format( + index_name) + obs_size = (scene.shape[__ibpc_indeces__['elevation']] * + scene.shape[__ibpc_indeces__['azimuth']], + scene.shape[__ibpc_indeces__['channel']], + scene.shape[__ibpc_indeces__['component']]) + obs_scene = scene.reshape(obs_size) + omm_size = (eye_map.shape[__ibpc_indeces__['elevation']] * + eye_map.shape[__ibpc_indeces__['azimuth']], + eye_map.shape[__ibpc_indeces__['component']]) + ommatidia_map = eye_map.reshape(omm_size) + return (obs_scene, ommatidia_map) + + +def cartesian_to_spherical(x, y, z): + radius = np.sqrt(x**2 + y**2 + z**2) + elevation = np.arctan2(z, np.sqrt(x**2 + y**2)) + azimuth = np.arctan2(y, x) + spherical = np.zeros_like(x) + spherical = np.tile(spherical[..., np.newaxis], (3,)) + spherical[..., __spherical_indeces__['elevation']] = elevation + spherical[..., __spherical_indeces__['azimuth']] = azimuth + spherical[..., __spherical_indeces__['radius']] = radius + return spherical + + +def spherical_to_cartesian(elevation, azimuth, radius=1): + cartesian = np.zeros_like(elevation) + cartesian = np.tile(cartesian[..., np.newaxis], (3,)) + cartesian[..., __cartesian_indeces__['x']] = np.cos( + elevation) * np.cos(azimuth) + cartesian[..., __cartesian_indeces__['y']] = np.cos( + elevation) * np.sin(azimuth) + cartesian[..., __cartesian_indeces__['z']] = np.sin(elevation) + cartesian = radius * cartesian + return cartesian diff --git a/build/lib/navipy/python_test.py b/build/lib/navipy/python_test.py new file mode 100644 index 0000000000000000000000000000000000000000..9ee1cb9f6b284340ad3570cdfb2a6a679877ce15 --- /dev/null +++ b/build/lib/navipy/python_test.py @@ -0,0 +1,5 @@ +""" + +""" +x = 0 +print('blabla') diff --git a/build/lib/navipy/rendering/__init__.py b/build/lib/navipy/rendering/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/build/lib/navipy/rendering/bee_sampling.py b/build/lib/navipy/rendering/bee_sampling.py new file mode 100644 index 0000000000000000000000000000000000000000..817ae03ec11cb75bbb7208238190feb7800023d7 --- /dev/null +++ b/build/lib/navipy/rendering/bee_sampling.py @@ -0,0 +1,167 @@ +""" +The beesampling class + +.. tothinkof: conditional bpy import to build doc from comments +""" +import bpy +import os +import numpy as np +import pandas as pd +import warnings +from navipy.rendering.cyber_bee import Cyberbee +from navipy.database import DataBaseSave + + +class BeeSampling(Cyberbee): + """ + BeeSampling is a class deriving from Cyberbee. + The BeeSampling can be used to generate a database of +images taken on a rectangular regular grid. For the database, +the BeeSampling rely on DataBase + It worth noting that the generated database can take a large +harddrive space, as each image is composed of 4 channels of 180x360 pixels. + """ + + def __init__(self): + """Initialise the BeeSampling""" + Cyberbee.__init__(self) + self.blenddirname = os.path.dirname(bpy.data.filepath) + self.blendfilename = os.path.basename(bpy.data.filepath) + self.__grid_posorients = None + self.__grid_size = None + self.world_dim = np.inf + + def create_sampling_grid(self, x, y, z, alpha1, alpha2, alpha3): + """Create a cubic grid from all the sampling points + + :param x: the positions along the x-axis + :param y: the positions along the y-axis + :param z: the positions along the z-axis + :param alpha1: the first euler angles + :param alpha2: the 2nd euler angles + :param alpha3: the 3rd euler angles + """ + if not (isinstance(x, np.ndarray) or isinstance(x, list)): + raise TypeError('x must be list or np.array') + if not (isinstance(y, np.ndarray) or isinstance(y, list)): + raise TypeError('y must be list or np.array') + if not (isinstance(z, np.ndarray) or isinstance(z, list)): + raise TypeError('z must be list or np.array') + if not (isinstance(alpha1, np.ndarray) or + isinstance(alpha1, list)): + raise TypeError('alpha1 must be list or np.array') + if not (isinstance(alpha2, np.ndarray) or + isinstance(alpha2, list)): + raise TypeError('alpha2 must be list or np.array') + if not (isinstance(alpha3, np.ndarray) or + isinstance(alpha3, list)): + raise TypeError('alpha3 must be list or np.array') + [mx, my, mz, ma1, ma2, ma3] = np.meshgrid(x, + y, + z, + alpha1, + alpha2, + alpha3) + self.grid_size = mx.shape + mx = mx.flatten() + my = my.flatten() + mz = mz.flatten() + ma1 = ma1.flatten() + ma2 = ma2.flatten() + ma3 = ma3.flatten() + self.__grid_posorients = pd.DataFrame(index=range(mx.shape[0]), + columns=['x', 'y', 'z', + 'alpha_0', + 'alpha_1', + 'alpha_2']) + self.__grid_posorients.loc[:, 'x'] = mx + self.__grid_posorients.loc[:, 'y'] = my + self.__grid_posorients.loc[:, 'z'] = mz + self.__grid_posorients.loc[:, 'alpha_0'] = ma1 + self.__grid_posorients.loc[:, 'alpha_1'] = ma2 + self.__grid_posorients.loc[:, 'alpha_2'] = ma3 + self.__grid_posorients.index.name = 'grid_index' + self.__grid_posorients.name = 'grid_position_orientation' + + @property + def grid_posorients(self): + """return a copy of the posorientation matrix + + :returns: position-orientations of the grid + :rtype: pandas array + + .. todo: use @property + def grid_posorients(self) + + .. todo: create @property.setter + def grid_posorients(self,posorients) + (need a type check, and col check, and copy of df) + """ + return self.__grid_posorients.copy() + + def set_gridindeces2nan(self, indeces): + """Set certain grid point to nan, so they will be ignore in the rendering + + :param indeces: a list of indeces to be set to nan + + .. todo: use @property.setter + def blacklist_indeces(self,indeces) + """ + if not isinstance(indeces, list): + raise TypeError('indeces must be a list') + if any(np.isnan(indeces)): + raise ValueError('indeces must not contain nans') + self.__grid_posorients.loc[indeces, :] = np.nan + + def render(self, database_filename): + if not isinstance(database_filename, str): + raise TypeError('filename must be a string') + database_folder = os.path.dirname(database_filename) + if not os.path.exists(database_folder): + os.makedirs(database_folder) + dataloger = DataBaseSave(database_filename, + channels=['R', 'G', 'B', 'D'], + arr_dtype=np.uint8) + 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 + rowid = dataloger.posid(posorient) + if dataloger.check_data_validity(rowid): + warnings.warn( + 'frame_i: {} data is valid rowid {}'.format(frame_i, + rowid)) + continue + # The position-orientatios is valid (none nan) + # and the cmaxminrange has not already been assigned + # so the image need to be rendered + self.update(posorient) + distance = self.distance + distance[distance > self.world_dim] = self.world_dim + image = self.image + image[:, :, 3] = distance + dataloger.write_image(posorient, image) + print('rendering completed') + + +if __name__ == "__main__": + import tempfile + bee_samp = BeeSampling() + # Create mesh + world_dim = 15.0 + x = np.linspace(-7.5, 7.5, 5) + y = np.linspace(-7.5, 7.5, 5) + z = np.arange(1, 8, 2) + alpha_1 = np.array([0]) + np.pi / 2 + alpha_2 = np.array([0]) + alpha_3 = np.array([0]) + bee_samp.create_sampling_grid( + x, y, z, alpha1=alpha_1, alpha2=alpha_2, alpha3=alpha_3) + bee_samp.world_dim = world_dim + grid_pos = bee_samp.grid_posorients + condition = (grid_pos.x**2 + grid_pos.y**2) < ((bee_samp.world_dim / 2)**2) + bee_samp.set_gridindeces2nan(condition[condition == 0].index) + bee_samp.cycle_samples(samples=5) + with tempfile.TemporaryDirectory() as folder: + bee_samp.render(folder + '/database.db') diff --git a/build/lib/navipy/rendering/cyber_bee.py b/build/lib/navipy/rendering/cyber_bee.py new file mode 100644 index 0000000000000000000000000000000000000000..9c203e55a30fae9d6fdc3189894cf868aa336e1f --- /dev/null +++ b/build/lib/navipy/rendering/cyber_bee.py @@ -0,0 +1,394 @@ +""" + How to test the script: + ----------------------- + >>> blender test.blend --background --python Cyberbee.py + + :Author: Olivier Bertrand (olivier.bertrand@uni-bielefeld.de) + :Parent module: Scene_rendering + + ..tothinkof for the doc bpy will raise an issue. + conditional import of bpy +""" +import bpy +import numpy as np +import tempfile +import os +import pandas as pd + + +class Cyberbee(): + """ + Cyberbee is a small class binding python with blender. + With Cyberbee one can move the bee to a position, and render what + the bee see at this position. + + The Bee eye is a panoramic camera with equirectangular projection + The light rays attaining the eyes are filtered with a gaussian. + """ + + def __init__(self): + """Initialise the Cyberbee + ..todo check that TemporaryDirectory is writtable and readable + """ + # Rendering engine needs to be Cycles to support panoramic + # equirectangular camera + bpy.context.scene.render.engine = 'CYCLES' + bpy.context.scene.render.layers["RenderLayer"].use_pass_z = True + # Look for object camera + camera_found = False + for obj in bpy.context.scene.objects: + if obj.type == 'CAMERA': + self.camera = obj + camera_found = True + break + assert camera_found, 'The blender file does not contain a camera' + # The bee eye is panoramic, and with equirectangular projection + self.camera.data.type = 'PANO' + self.camera.data.cycles.panorama_type = 'EQUIRECTANGULAR' + # Filtering props + bpy.context.scene.cycles.filter_type = 'GAUSSIAN' + # Call all set function with default values + self.camera_rotation_mode = 'XYZ' + self.camera_fov = [[-90, 90], [-180, 180]] + self.camera_gaussian_width = 1.5 + self.camera_resolution = [360, 180] + self.cycle_samples = 30 + # switch on nodes + # Create render link to OutputFile with Image and Z buffer + bpy.context.scene.use_nodes = True + scene = bpy.context.scene + nodes = scene.node_tree.nodes + + render_layers = nodes['Render Layers'] + output_file = nodes.new("CompositorNodeOutputFile") + output_file.format.file_format = "OPEN_EXR" + output_file.file_slots.remove(output_file.inputs[0]) + tmp_fileoutput = dict() + tmp_fileoutput['Image'] = 'Image' + tmp_fileoutput['Depth'] = 'Depth' + tmp_fileoutput['Folder'] = tempfile.TemporaryDirectory().name + tmp_fileoutput['ext'] = '.exr' + output_file.file_slots.new(tmp_fileoutput['Image']) + output_file.file_slots.new(tmp_fileoutput['Depth']) + output_file.base_path = tmp_fileoutput['Folder'] + scene.node_tree.links.new( + render_layers.outputs['Image'], + output_file.inputs['Image'] + ) + scene.node_tree.links.new( + render_layers.outputs['Z'], + output_file.inputs['Depth'] + ) + self.tmp_fileoutput = tmp_fileoutput + + @property + def camera_rotation_mode(self): + """get the current camera rotation mode + + :returns: the mode of rotation used by the camera + :rtype: string + + ..todo: Use @property + def camera_rotation_mode(self) + """ + return bpy.data.scenes["Scene"].camera.rotation_mode + + @camera_rotation_mode.setter + def camera_rotation_mode(self, mode='XYZ'): + """change the camera rotation mode + + + + :param mode: the mode of rotation for the camera see blender doc + + (default: 'XYZ'). + + :type mode: a string + + .. seealso: blender bpy.data.scenes["Scene"].camera.rotation_mode + + + + ..todo: Use @property.setter + + def camera_rotation_mode(self, mode='XYZ') + + """ + if not isinstance(mode, str): + + raise TypeError('mode must be a string') + bpy.data.scenes["Scene"].camera.rotation_mode = mode + + @property + def cycle_samples(self): + """get the samples for rendering with cycle + + :returns: the number of samples used for the rendering + :rtype: int + + ..todo use @property + def cycle_samples(self) + """ + return bpy.context.scene.cycles.samples + + @cycle_samples.setter + def cycle_samples(self, samples=30): + """change the samples for rendering with cycle + + + :param samples: the number of samples to use when rendering images + + :type samples: int + + + ..todo: Use @property.setter + + def cycle_samples(self, samples=30) + """ + if not isinstance(samples, int): + raise TypeError('samples must be an integer') + bpy.context.scene.cycles.samples = samples + + @property + def camera_fov(self): + """get fov of camera + + + + :returns: the field of view of the camera as min/max,longitude/latitude + + in degrees + + :rtype: dict + + + + ..todo use @property + + def camera_fov() + + + + ..todo Change assert to if -> raise TypeError/KeyError + + """ + assert self.camera.data.type == 'PANO', 'Camera is not panoramic' + assert self.camera.cycles.panorama_type == 'EQUIRECTANGULAR',\ + 'Camera is not equirectangular' + fov = dict() + fov['latitude_min'] = np.rad2ged(self.camera.data.cycles.latitude_min) + fov['latitude_max'] = np.rad2ged(self.camera.data.cycles.latitude_max) + fov['longitude_min'] = np.rad2ged( + self.camera.data.cycles.longitude_min) + fov['longitude_max'] = np.rad2ged( + self.camera.data.cycles.longitude_max) + return fov + + @camera_fov.setter + def camera_fov(self, resolution=[[-90, 90], [-180, 180]]): + """change the field of view of the panoramic camera + + :param resolution: [[minimum latitude, maximum latitude], + [minimum longitude, maximum longitude]] + (in deg) + :type latmin: 2x2 float array or list + + ..todo use @property.setter + def camera_fov(self, latlongrange) + here latlongrange is a a 2x2 list or array: + [[latmin,latmax],[longmin,longmax]] + + ..todo Change assert to if -> raise TypeError()/KeyError() + """ + if not (isinstance(resolution, tuple) or + isinstance(resolution, list) or + isinstance(resolution, np.ndarray)): + raise TypeError('resolution must be list, array, or tuple') + if not self.camera.data.type == 'PANO': + raise Exception('Camera is not panoramic') + if not self.camera.data.cycles.panorama_type == 'EQUIRECTANGULAR': + raise Exception('Camera is not equirectangular') + self.camera.data.cycles.latitude_min = np.deg2rad( + resolution[0][0]) + self.camera.data.cycles.latitude_max = np.deg2rad( + resolution[0][1]) + self.camera.data.cycles.longitude_min = np.deg2rad( + resolution[1][0]) + self.camera.data.cycles.longitude_max = np.deg2rad( + resolution[1][1]) + + @property + def camera_gaussian_width(self, gauss_w=1.5): + """get width of the gaussian spatial filter + + :returns: the width of the gaussian filter + :rtype: float + + ..todo use @property + def camera_gaussian_width(self) + """ + if not (isinstance(gauss_w, int) or isinstance(gauss_w, float)): + raise TypeError('gauss window must be integer or float') + return bpy.context.scene.cycles.filter_width + + @camera_gaussian_width.setter + def camera_gaussian_width(self, gauss_w=1.5): + """change width of the gaussian spatial filter + + :param gauss_w: width of the gaussian filter + + :type gauss_w: float + + ..todo use @property.setter + def camera_gaussian_width(self) + + + ..todo check that input argument is of correct type, + if not raise TypeError() + + """ + if not (isinstance(gauss_w, int) or isinstance(gauss_w, float)): + raise TypeError('gauss window must be integer or float') + bpy.context.scene.cycles.filter_width = gauss_w + + @property + def camera_resolution(self): + """return camera resolution (x,y) + + :returns: the resolution of the camera along (x-axis,y-axis) + :rtype: (int,int) + + ..todo use @property + def camera_resolution(self) + """ + resolution_x = bpy.context.scene.render.resolution_x + resolution_y = bpy.context.scene.render.resolution_y + return resolution_x, resolution_y + + @camera_resolution.setter + def camera_resolution(self, resolution=[360, 180]): + """change the camera resolution (nb of pixels) + + + :param resolution_x: number of pixels along the x-axis of the camera + :type resolution_x: int + + :param resolution_y: number of pixels along the y-axis of the camera + + :type resolution_y: int + + + ..todo use @property.setter + + def camera_resolution(self,resolution) + + here resolution is [res_x,res_y] + + + + ..todo check type and raise TypeError + + """ + if not (isinstance(resolution, list) or + isinstance(resolution, np.ndarray)): + raise TypeError('resolution list or array') + bpy.context.scene.render.resolution_x = resolution[0] + bpy.context.scene.render.resolution_y = resolution[1] + bpy.context.scene.render.resolution_percentage = 100 + + def update(self, posorient): + """assign the position and the orientation of the camera. + + :param posorient: is a 1x6 vector containing: + x,y,z, angle_1, angle_2, angle_3, + here the angles are euler rotation around the axis + specified by scene.camera.rotation_mode + :type posorient: 1x6 double array + """ + print(posorient) + if (isinstance(posorient, np.ndarray) or + isinstance(posorient, list)): + + if len(posorient) != 6: + raise Exception('posorient should be a 1x6 double array') + self.camera.location = posorient[:3] + self.camera.rotation_euler = posorient[3:] + elif isinstance(posorient, pd.Series): + self.camera.location = posorient.loc[['x', 'y', 'z']].values + self.camera.rotation_euler = \ + posorient.loc[['alpha_0', 'alpha_1', 'alpha_2']].values + else: + raise TypeError( + 'posorient must be of type array, list, or pandas Series') + # Render + bpy.ops.render.render() + + @property + def image(self): + """return the last rendered image as a numpy array + + :returns: the image (height,width,4) + :rtype: a double numpy array + + .. note: A temporary file will be written on the harddrive, + due to API blender limitation + + .. todo: use @property + def image(self) + """ + # save image as a temporary file, and then loaded + # sadly the rendered image pixels can not directly be access + filename = os.path.join(self.tmp_fileoutput['Folder'], + self.tmp_fileoutput['Image'] + '0001' + + self.tmp_fileoutput['ext']) + im_width, im_height = self.camera_resolution + im = bpy.data.images.load(filename) + pixels = np.array(im.pixels) + # im=PIL.Image.open(filename) + # pixels=np.asarray(im) + pixels = pixels.reshape([im_height, im_width, 4]) + return pixels + + @property + def distance(self): + """return the last rendered distance map as a numpy array + + :returns: the distance map (height, width) + :rtype: a double numpy array + + .. note: A temporary file will be written on the harddrive, + due to API blender limitation + + .. todo: use @property + def distance(self) + """ + # save image as a temporary file, and then loaded + # sadly the rendered image pixels can not directly be access + filename = os.path.join(self.tmp_fileoutput['Folder'], + self.tmp_fileoutput['Depth'] + '0001' + + self.tmp_fileoutput['ext']) + im_width, im_height = self.camera_resolution + im = bpy.data.images.load(filename) + distance = np.array(im.pixels) + # im=PIL.Image.open(filename) + # distance=np.asarray(im) + distance = distance.reshape([im_height, im_width, 4]) + distance = distance[:, :, 0] + return distance + + +if __name__ == "__main__": + # Initiate the Cyberbee + mybee = Cyberbee() + frames_per_revolution = 5.0 + step_size = 2 * np.pi / frames_per_revolution + posorients = np.zeros((frames_per_revolution, 6)) + posorients[:, 0] = np.sin(np.arange(frames_per_revolution) * step_size) * 5 + posorients[:, 1] = np.cos(np.arange(frames_per_revolution) * step_size) * 5 + for frame_i, posorient in enumerate(posorients): + mybee.update(posorient) + # Test image + image = mybee.image + # Test distance + distance = mybee.distance + print('Cyberbee OK') diff --git a/build/lib/navipy/resources/database.db b/build/lib/navipy/resources/database.db new file mode 100644 index 0000000000000000000000000000000000000000..450fe54728c12e42263541e1541b71d2e9e2a815 Binary files /dev/null and b/build/lib/navipy/resources/database.db differ diff --git a/dist/navipy-0.1-py3.5.egg b/dist/navipy-0.1-py3.5.egg new file mode 100644 index 0000000000000000000000000000000000000000..a3b260f5a72083304e1e67d1cfb94960293f27ce Binary files /dev/null and b/dist/navipy-0.1-py3.5.egg differ diff --git a/navipy.egg-info/PKG-INFO b/navipy.egg-info/PKG-INFO new file mode 100644 index 0000000000000000000000000000000000000000..cb2d0a173a0a145c009c7bab788a28cf48697381 --- /dev/null +++ b/navipy.egg-info/PKG-INFO @@ -0,0 +1,14 @@ +Metadata-Version: 1.1 +Name: navipy +Version: 0.1 +Summary: Insect Navigation Toolbox +Home-page: UNKNOWN +Author: Olivier J.N. Bertrand +Author-email: olivier.bertrand@uni-bielefeld.de +License: UNKNOWN +Description: UNKNOWN +Platform: UNKNOWN +Requires: numpy +Requires: pandas +Requires: matplotlib +Requires: scipy diff --git a/navipy.egg-info/SOURCES.txt b/navipy.egg-info/SOURCES.txt new file mode 100644 index 0000000000000000000000000000000000000000..f0f931e235dddb99eb6045a59229abd5918b4d7e --- /dev/null +++ b/navipy.egg-info/SOURCES.txt @@ -0,0 +1,26 @@ +setup.py +navipy/__init__.py +navipy/python_test.py +navipy.egg-info/PKG-INFO +navipy.egg-info/SOURCES.txt +navipy.egg-info/dependency_links.txt +navipy.egg-info/requires.txt +navipy.egg-info/top_level.txt +navipy/comparing/__init__.py +navipy/comparing/test.py +navipy/database/__init__.py +navipy/database/tools.py +navipy/moving/__init__.py +navipy/moving/agent.py +navipy/moving/maths.py +navipy/moving/test_agent.py +navipy/moving/test_maths.py +navipy/processing/__init__.py +navipy/processing/constants.py +navipy/processing/mcode.py +navipy/processing/pcode.py +navipy/processing/test.py +navipy/processing/tools.py +navipy/rendering/__init__.py +navipy/rendering/bee_sampling.py +navipy/rendering/cyber_bee.py \ No newline at end of file diff --git a/navipy.egg-info/dependency_links.txt b/navipy.egg-info/dependency_links.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/navipy.egg-info/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/navipy.egg-info/requires.txt b/navipy.egg-info/requires.txt new file mode 100644 index 0000000000000000000000000000000000000000..6e27488c3669fe064238d31c089c1a764ffe3239 --- /dev/null +++ b/navipy.egg-info/requires.txt @@ -0,0 +1,4 @@ +numpy +pandas +matplotlib +scipy diff --git a/navipy.egg-info/top_level.txt b/navipy.egg-info/top_level.txt new file mode 100644 index 0000000000000000000000000000000000000000..c5f564d1a1a80b6f4fdf3a52d9be71e527d727f9 --- /dev/null +++ b/navipy.egg-info/top_level.txt @@ -0,0 +1 @@ +navipy diff --git a/navipy/__pycache__/__init__.cpython-35.pyc b/navipy/__pycache__/__init__.cpython-35.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d77138ae6ab15a1ae937595049e7b6a9a1c3fc02 Binary files /dev/null and b/navipy/__pycache__/__init__.cpython-35.pyc differ diff --git a/navipy/comparing/__init__.py b/navipy/comparing/__init__.py index 2c6c3e88709254755550551763587bb252cefe86..703cb552f4d603eed2ebcc9feb4c6a68db04f59a 100644 --- a/navipy/comparing/__init__.py +++ b/navipy/comparing/__init__.py @@ -76,10 +76,10 @@ the current and memorised place code. ..note: assume that the image is periodic along the x axis (the left-right axis) """ - if not is_ibpc(current): + if not is_ibpc(current): # and not is_obpc(current): raise TypeError('The current and memory place code\ should be image based') - if not is_ibpc(memory): + if not is_ibpc(memory): # and not is_obpc(memory): raise TypeError('The current and memory place code\ should be image based') check_scene(current) @@ -116,10 +116,10 @@ A=(I_x,I_y) and b = I_t "Determining optical flow." Artificial intelligence 17.1-3 (1981): 185-203. """ - if not is_ibpc(current): + if not is_ibpc(current): # and not is_obpc(current): raise TypeError('The current and memory place code\ should be image based') - if not is_ibpc(memory): + if not is_ibpc(memory): # and not is_obpc(memory): raise TypeError('The current and memory place code\ should be image based') check_scene(current) diff --git a/navipy/comparing/__init__.py~ b/navipy/comparing/__init__.py~ new file mode 100644 index 0000000000000000000000000000000000000000..dcc7e6f4e8ba083943af3a140fbd390542c8611e --- /dev/null +++ b/navipy/comparing/__init__.py~ @@ -0,0 +1,143 @@ +""" +The Place comparator list different methods to +compare a current place to a memorised place or +memorised places. +""" +import numpy as np +from navipy.processing.tools import is_ibpc, is_obpc +from navipy.processing.tools import check_scene + + +def simple_imagediff(current, memory): + """Compute the difference between +the current and memorised place code + + :param current: current place code + :param memory: memorised place code + :returns: the image difference + :rtype: float + + ..ref: Zeil, J., 2012. Visual homing: an insect perspective. + Current opinion in neurobiology + + """ + if not isinstance(current, np.ndarray): + raise TypeError('current place code should be a numpy array') + if not isinstance(memory, np.ndarray): + raise TypeError('memory place code should be a numpy array') + if not np.all(current.shape == memory.shape): + raise Exception('memory and current place code should\ + have the same shape') + check_scene(current) + check_scene(memory) + diff = current - memory + if is_ibpc(current): + return diff + elif is_obpc(current): + return diff + else: + raise TypeError('place code is neither an ibpc nor obpc') + + +def imagediff(current, memory): + """Compute the root mean square difference between +the current and memorised place code + + :param current: current place code + :param memory: memorised place code + :returns: the image difference + :rtype: float #array(1,4) for ibpc and float for obpc + + ..ref: Zeil, J., 2012. Visual homing: an insect perspective. + Current opinion in neurobiology + + """ + simple_diff = simple_imagediff(current, memory) + diff = np.power(simple_diff, 2) + if is_ibpc(current): + return np.sqrt(diff.mean(axis=0).mean(axis=0)) # 1 + elif is_obpc(current): + return np.sqrt(diff.mean(axis=0).mean(axis=0)) + else: + raise TypeError('place code is neither an ibpc nor obpc') + + +def rot_imagediff(current, memory): + """Compute the rotational image difference between +the current and memorised place code. + + :param current: current place code + :param memory: memorised place code + :returns: the rotational image difference + :rtype: (np.ndarray) + + ..ref: Zeil, J., 2012. Visual homing: an insect perspective. + Current opinion in neurobiology + ..note: assume that the image is periodic along the x axis + (the left-right axis) + """ + if not is_ibpc(current): # and not is_obpc(current): + raise TypeError('The current and memory place code\ + should be image based') + if not is_ibpc(memory): # and not is_obpc(memory): + raise TypeError('The current and memory place code\ + should be image based') + check_scene(current) + check_scene(memory) + ridf = np.zeros((current.shape[1], current.shape[2])) + for azimuth_i in range(0, current.shape[1]): + rot_im = np.roll(current, azimuth_i, axis=1) + ridf[azimuth_i, :] = np.squeeze(imagediff(rot_im, memory)) # rot_im + return ridf + + +def diff_optic_flow(current, memory): + """Computes the direction of motion from current +to memory by using the optic flow under the +constrain that the brightness is constant, (small movement), +using a taylor expansion and solving the equation: +0=I_t+\delta I*<u,v> or I_x+I_y+I_t=0 + +afterwards the aperture problem is solved by a +Matrix equation Ax=b, where x=(u,v) and +A=(I_x,I_y) and b = I_t + + :param current: current place code + :param memory: memorised place code + :returns: a directional vectors + :rtype: (np.ndarray) + + ..ref: aperture problem: + Shimojo, Shinsuke, Gerald H. Silverman, and Ken Nakayama: + "Occlusion and the solution to the aperture problem for motion." + Vision research 29.5 (1989): 619-626. + optic flow: + Horn, Berthold KP, and Brian G. Schunck.: + "Determining optical flow." + Artificial intelligence 17.1-3 (1981): 185-203. + """ + if not is_ibpc(current): # and not is_obpc(current): + raise TypeError('The current and memory place code\ + should be image based') + if not is_ibpc(memory): # and not is_obpc(memory): + raise TypeError('The current and memory place code\ + should be image based') + check_scene(current) + check_scene(memory) + currroll = np.roll(current, 1, axis=1) + dx = current - currroll + memroll = np.roll(memory, 1, axis=1) + dy = memory - memroll + dy = np.reshape(dy, (np.prod(dy.shape), 1)) + dx = np.reshape(dx, (np.prod(dx.shape), 1)) + di = current - memory + di = np.reshape(di, (np.prod(di.shape), 1)) + a_matrix = np.column_stack([dy, dx]) + a_matrix_sqr = np.dot(np.transpose(a_matrix), a_matrix) + b_vector = np.dot(np.transpose(a_matrix), di) + res = np.linalg.solve(a_matrix_sqr, b_vector) + return res + + +def gradient(current, memory): + return 0 diff --git a/navipy/comparing/__pycache__/__init__.cpython-35.pyc b/navipy/comparing/__pycache__/__init__.cpython-35.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bfeb1d2527d0e393eb86a95df0bfb32e98f70153 Binary files /dev/null and b/navipy/comparing/__pycache__/__init__.cpython-35.pyc differ diff --git a/navipy/comparing/__pycache__/test.cpython-35.pyc b/navipy/comparing/__pycache__/test.cpython-35.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9107a89e8de76c262b55ba81868acec9bb1438bf Binary files /dev/null and b/navipy/comparing/__pycache__/test.cpython-35.pyc differ diff --git a/navipy/comparing/test.py b/navipy/comparing/test.py index fabdf2a9dde534c09881f43249f24928112dd609..041eba06e66c0d9958abc0956019d9617f0f6464 100644 --- a/navipy/comparing/test.py +++ b/navipy/comparing/test.py @@ -13,8 +13,10 @@ class TestCase(unittest.TestCase): self.mydb = database.DataBaseLoad(self.mydb_filename) def test_imagediff_curr(self): - curr = self.mydb.scene(rowid=1) - mem = self.mydb.scene(rowid=2) + curr = self.mydb.read_image(rowid=1) + mem = self.mydb.read_image(rowid=2) + curr = np.expand_dims(curr, axis=3) + mem = np.expand_dims(mem, axis=3) curr2 = curr.copy() curr2[3, 5, 2, 0] = np.nan curr3 = [[1, 2, 3], [1, 2, 3], [1, 2, 3]] @@ -33,20 +35,22 @@ class TestCase(unittest.TestCase): # should be working -> check if result is correct for s in [curr]: diff = comparing.imagediff(s, mem) - self.assertTrue(diff.shape[1] == 1) + # self.assertTrue(diff.shape[1] == 1) self.assertTrue(diff.shape[0] > 0) self.assertFalse(np.any(np.isnan(diff))) self.assertTrue(is_numeric_array(diff)) def test_imagediff_memory(self): - curr = self.mydb.scene(rowid=1) - mem = self.mydb.scene(rowid=2) + curr = self.mydb.read_image(rowid=1) + mem = self.mydb.read_image(rowid=2) + curr = np.expand_dims(curr, axis=3) + mem = np.expand_dims(mem, axis=3) mem2 = curr.copy() - mem2[3, 5, 2, 0] = np.nan - mem3 = [[1, 2, 3], [1, 2, 3], [1, 2, 3]] + mem2[3, 5, 2] = np.nan + mem3 = [[1, 2], [1, 2], [1, 2]] mem3 = [mem3, mem3, mem3] mem3 = np.array(mem3) - mem4 = np.zeros((3, 4, 5, 0)) + mem4 = np.zeros((3, 4, 5)) with self.assertRaises(ValueError): comparing.imagediff(curr, mem2) @@ -59,19 +63,21 @@ class TestCase(unittest.TestCase): for s in [mem]: diff = comparing.imagediff(curr, s) self.assertFalse(diff.shape[0] <= 0) - self.assertTrue(diff.shape[1] == 1) + # self.assertTrue(diff.shape[1] == 1) self.assertFalse(np.any(np.isnan(diff))) self.assertTrue(is_numeric_array(diff)) def test_rot_imagediff_curr(self): - curr = self.mydb.scene(rowid=1) - mem = self.mydb.scene(rowid=2) + curr = self.mydb.read_image(rowid=1) + mem = self.mydb.read_image(rowid=2) + curr = np.expand_dims(curr, axis=3) + mem = np.expand_dims(mem, axis=3) curr2 = curr.copy() - curr2[3, 5, 2, 0] = np.nan + curr2[3, 5, 2] = np.nan curr3 = [[1, 2, 3], [1, 2, 3], [1, 2, 3]] curr3 = [curr3, curr3, curr3] curr3 = np.array(curr3) - curr4 = np.zeros((3, 4, 5, 0)) + curr4 = np.zeros((3, 4, 5)) with self.assertRaises(ValueError): comparing.rot_imagediff(curr2, mem) @@ -89,14 +95,16 @@ class TestCase(unittest.TestCase): self.assertTrue(is_numeric_array(diff)) def test_rotimagediff_memory(self): - curr = self.mydb.scene(rowid=1) - mem = self.mydb.scene(rowid=2) + curr = self.mydb.read_image(rowid=1) + mem = self.mydb.read_image(rowid=2) + curr = np.expand_dims(curr, axis=3) + mem = np.expand_dims(mem, axis=3) mem2 = curr.copy() - mem2[3, 5, 2, 0] = np.nan + mem2[3, 5, 2] = np.nan mem3 = [[1, 2, 3], [1, 2, 3], [1, 2, 3]] mem3 = [mem3, mem3, mem3] mem3 = np.array(mem3) - mem4 = np.zeros((3, 4, 5, 0)) + mem4 = np.zeros((3, 4, 5)) with self.assertRaises(ValueError): comparing.rot_imagediff(curr, mem2) @@ -114,14 +122,16 @@ class TestCase(unittest.TestCase): self.assertTrue(is_numeric_array(diff)) def test_simple_imagediff_curr(self): - curr = self.mydb.scene(rowid=1) - mem = self.mydb.scene(rowid=2) + curr = self.mydb.read_image(rowid=1) + mem = self.mydb.read_image(rowid=2) + curr = np.expand_dims(curr, axis=3) + mem = np.expand_dims(mem, axis=3) curr2 = curr.copy() - curr2[3, 5, 2, 0] = np.nan + curr2[3, 5, 2] = np.nan curr3 = [[1, 2, 3], [1, 2, 3], [1, 2, 3]] curr3 = [curr3, curr3, curr3] curr3 = np.array(curr3) - curr4 = np.zeros((3, 4, 5, 0)) + curr4 = np.zeros((3, 4, 5)) with self.assertRaises(ValueError): comparing.simple_imagediff(curr2, mem) @@ -138,17 +148,19 @@ class TestCase(unittest.TestCase): self.assertFalse(np.any(np.isnan(diff))) self.assertTrue(is_numeric_array(diff)) self.assertTrue(diff.shape[2] == 4) - self.assertTrue(diff.shape[3] == 1) + # self.assertTrue(diff.shape[3] == 1) def test_simple_imagediff_mem(self): - curr = self.mydb.scene(rowid=1) - mem = self.mydb.scene(rowid=2) + curr = self.mydb.read_image(rowid=1) + mem = self.mydb.read_image(rowid=2) + curr = np.expand_dims(curr, axis=3) + mem = np.expand_dims(mem, axis=3) mem2 = curr.copy() - mem2[3, 5, 2, 0] = np.nan + mem2[3, 5, 2] = np.nan mem3 = [[1, 2, 3], [1, 2, 3], [1, 2, 3]] mem3 = [mem3, mem3, mem3] mem3 = np.array(mem3) - mem4 = np.zeros((3, 4, 5, 0)) + mem4 = np.zeros((3, 4, 5)) with self.assertRaises(ValueError): comparing.simple_imagediff(curr, mem2) @@ -165,17 +177,19 @@ class TestCase(unittest.TestCase): self.assertFalse(np.any(np.isnan(diff))) self.assertTrue(is_numeric_array(diff)) self.assertTrue(diff.shape[2] == 4) - self.assertTrue(diff.shape[3] == 1) + # self.assertTrue(diff.shape[3] == 1) def test_diff_optic_flow_memory(self): - curr = self.mydb.scene(rowid=1) - mem = self.mydb.scene(rowid=2) + curr = self.mydb.read_image(rowid=1) + mem = self.mydb.read_image(rowid=2) + curr = np.expand_dims(curr, axis=3) + mem = np.expand_dims(mem, axis=3) mem2 = curr.copy() - mem2[3, 5, 2, 0] = np.nan + mem2[3, 5, 2] = np.nan mem3 = [[1, 2, 3], [1, 2, 3], [1, 2, 3]] mem3 = [mem3, mem3, mem3] mem3 = np.array(mem3) - mem4 = np.zeros((3, 4, 5, 0)) + mem4 = np.zeros((3, 4, 5)) with self.assertRaises(ValueError): comparing.diff_optic_flow(curr, mem2) @@ -192,14 +206,16 @@ class TestCase(unittest.TestCase): self.assertTrue(is_numeric_array(vec)) def test_diff_optic_flow_curr(self): - curr = self.mydb.scene(rowid=1) - mem = self.mydb.scene(rowid=2) + curr = self.mydb.read_image(rowid=1) + mem = self.mydb.read_image(rowid=2) + curr = np.expand_dims(curr, axis=3) + mem = np.expand_dims(mem, axis=3) curr2 = curr.copy() - curr2[3, 5, 2, 0] = np.nan - curr3 = [[1, 2, 3], [1, 2, 3], [1, 2, 3]] + curr2[3, 5, 2] = np.nan + curr3 = [[1, 2], [1, 2], [1, 2]] curr3 = [curr3, curr3, curr3] curr3 = np.array(curr3) - curr4 = np.zeros((3, 4, 5, 0)) + curr4 = np.zeros((3, 4, 5, 1)) with self.assertRaises(ValueError): comparing.diff_optic_flow(curr2, mem) diff --git a/navipy/comparing/test.py~ b/navipy/comparing/test.py~ new file mode 100644 index 0000000000000000000000000000000000000000..041eba06e66c0d9958abc0956019d9617f0f6464 --- /dev/null +++ b/navipy/comparing/test.py~ @@ -0,0 +1,236 @@ +import unittest +import numpy as np +import navipy.database as database +import navipy.comparing as comparing +from navipy.processing.tools import is_numeric_array +import pkg_resources + + +class TestCase(unittest.TestCase): + def setUp(self): + self.mydb_filename = pkg_resources.resource_filename( + 'navipy', 'resources/database.db') + self.mydb = database.DataBaseLoad(self.mydb_filename) + + def test_imagediff_curr(self): + curr = self.mydb.read_image(rowid=1) + mem = self.mydb.read_image(rowid=2) + curr = np.expand_dims(curr, axis=3) + mem = np.expand_dims(mem, axis=3) + curr2 = curr.copy() + curr2[3, 5, 2, 0] = np.nan + curr3 = [[1, 2, 3], [1, 2, 3], [1, 2, 3]] + curr3 = [curr3, curr3, curr3] + curr3 = np.array(curr3) + curr4 = np.zeros((3, 4, 5, 0)) + + # put useless stuff here + with self.assertRaises(ValueError): + comparing.imagediff(curr2, mem) + with self.assertRaises(Exception): + comparing.imagediff(curr3, mem) + with self.assertRaises(Exception): + comparing.imagediff(curr4, mem) + + # should be working -> check if result is correct + for s in [curr]: + diff = comparing.imagediff(s, mem) + # self.assertTrue(diff.shape[1] == 1) + self.assertTrue(diff.shape[0] > 0) + self.assertFalse(np.any(np.isnan(diff))) + self.assertTrue(is_numeric_array(diff)) + + def test_imagediff_memory(self): + curr = self.mydb.read_image(rowid=1) + mem = self.mydb.read_image(rowid=2) + curr = np.expand_dims(curr, axis=3) + mem = np.expand_dims(mem, axis=3) + mem2 = curr.copy() + mem2[3, 5, 2] = np.nan + mem3 = [[1, 2], [1, 2], [1, 2]] + mem3 = [mem3, mem3, mem3] + mem3 = np.array(mem3) + mem4 = np.zeros((3, 4, 5)) + + with self.assertRaises(ValueError): + comparing.imagediff(curr, mem2) + with self.assertRaises(Exception): + comparing.imagediff(curr, mem3) + with self.assertRaises(Exception): + comparing.imagediff(curr, mem4) + + # should be working -> check if result is correct + for s in [mem]: + diff = comparing.imagediff(curr, s) + self.assertFalse(diff.shape[0] <= 0) + # self.assertTrue(diff.shape[1] == 1) + self.assertFalse(np.any(np.isnan(diff))) + self.assertTrue(is_numeric_array(diff)) + + def test_rot_imagediff_curr(self): + curr = self.mydb.read_image(rowid=1) + mem = self.mydb.read_image(rowid=2) + curr = np.expand_dims(curr, axis=3) + mem = np.expand_dims(mem, axis=3) + curr2 = curr.copy() + curr2[3, 5, 2] = np.nan + curr3 = [[1, 2, 3], [1, 2, 3], [1, 2, 3]] + curr3 = [curr3, curr3, curr3] + curr3 = np.array(curr3) + curr4 = np.zeros((3, 4, 5)) + + with self.assertRaises(ValueError): + comparing.rot_imagediff(curr2, mem) + with self.assertRaises(Exception): + comparing.rot_imagediff(curr3, mem) + with self.assertRaises(Exception): + comparing.rot_imagediff(curr4, mem) + + # should be working -> check if result is correct + for s in [curr]: + diff = comparing.rot_imagediff(s, mem) + self.assertFalse(diff.shape[0] <= 0) + self.assertTrue(diff.shape[1] == 4) + self.assertFalse(np.any(np.isnan(diff))) + self.assertTrue(is_numeric_array(diff)) + + def test_rotimagediff_memory(self): + curr = self.mydb.read_image(rowid=1) + mem = self.mydb.read_image(rowid=2) + curr = np.expand_dims(curr, axis=3) + mem = np.expand_dims(mem, axis=3) + mem2 = curr.copy() + mem2[3, 5, 2] = np.nan + mem3 = [[1, 2, 3], [1, 2, 3], [1, 2, 3]] + mem3 = [mem3, mem3, mem3] + mem3 = np.array(mem3) + mem4 = np.zeros((3, 4, 5)) + + with self.assertRaises(ValueError): + comparing.rot_imagediff(curr, mem2) + with self.assertRaises(Exception): + comparing.rot_imagediff(curr, mem3) + with self.assertRaises(Exception): + comparing.rot_imagediff(curr, mem4) + + # should be working -> check if result is correct + for s in [mem]: + diff = comparing.rot_imagediff(curr, s) + self.assertFalse(diff.shape[0] <= 0) + self.assertTrue(diff.shape[1] == 4) + self.assertFalse(np.any(np.isnan(diff))) + self.assertTrue(is_numeric_array(diff)) + + def test_simple_imagediff_curr(self): + curr = self.mydb.read_image(rowid=1) + mem = self.mydb.read_image(rowid=2) + curr = np.expand_dims(curr, axis=3) + mem = np.expand_dims(mem, axis=3) + curr2 = curr.copy() + curr2[3, 5, 2] = np.nan + curr3 = [[1, 2, 3], [1, 2, 3], [1, 2, 3]] + curr3 = [curr3, curr3, curr3] + curr3 = np.array(curr3) + curr4 = np.zeros((3, 4, 5)) + + with self.assertRaises(ValueError): + comparing.simple_imagediff(curr2, mem) + with self.assertRaises(Exception): + comparing.simple_imagediff(curr3, mem) + with self.assertRaises(Exception): + comparing.simple_imagediff(curr4, mem) + + # should be working -> check if result is correct + for s in [curr]: + diff = comparing.simple_imagediff(s, mem) + self.assertFalse(diff.shape[0] <= 0) + self.assertTrue(diff.shape[1] > 0) + self.assertFalse(np.any(np.isnan(diff))) + self.assertTrue(is_numeric_array(diff)) + self.assertTrue(diff.shape[2] == 4) + # self.assertTrue(diff.shape[3] == 1) + + def test_simple_imagediff_mem(self): + curr = self.mydb.read_image(rowid=1) + mem = self.mydb.read_image(rowid=2) + curr = np.expand_dims(curr, axis=3) + mem = np.expand_dims(mem, axis=3) + mem2 = curr.copy() + mem2[3, 5, 2] = np.nan + mem3 = [[1, 2, 3], [1, 2, 3], [1, 2, 3]] + mem3 = [mem3, mem3, mem3] + mem3 = np.array(mem3) + mem4 = np.zeros((3, 4, 5)) + + with self.assertRaises(ValueError): + comparing.simple_imagediff(curr, mem2) + with self.assertRaises(Exception): + comparing.simple_imagediff(curr, mem3) + with self.assertRaises(Exception): + comparing.simple_imagediff(curr, mem4) + + # should be working -> check if result is correct + for s in [mem]: + diff = comparing.simple_imagediff(curr, s) + self.assertFalse(diff.shape[0] <= 0) + self.assertTrue(diff.shape[1] > 0) + self.assertFalse(np.any(np.isnan(diff))) + self.assertTrue(is_numeric_array(diff)) + self.assertTrue(diff.shape[2] == 4) + # self.assertTrue(diff.shape[3] == 1) + + def test_diff_optic_flow_memory(self): + curr = self.mydb.read_image(rowid=1) + mem = self.mydb.read_image(rowid=2) + curr = np.expand_dims(curr, axis=3) + mem = np.expand_dims(mem, axis=3) + mem2 = curr.copy() + mem2[3, 5, 2] = np.nan + mem3 = [[1, 2, 3], [1, 2, 3], [1, 2, 3]] + mem3 = [mem3, mem3, mem3] + mem3 = np.array(mem3) + mem4 = np.zeros((3, 4, 5)) + + with self.assertRaises(ValueError): + comparing.diff_optic_flow(curr, mem2) + with self.assertRaises(Exception): + comparing.diff_optic_flow(curr, mem3) + with self.assertRaises(Exception): + comparing.diff_optic_flow(curr, mem4) + + # should be working -> check if result is correct + for s in [mem]: + vec = comparing.diff_optic_flow(curr, s) + self.assertFalse(vec.shape[1] == (1, 2)) + self.assertFalse(np.any(np.isnan(vec))) + self.assertTrue(is_numeric_array(vec)) + + def test_diff_optic_flow_curr(self): + curr = self.mydb.read_image(rowid=1) + mem = self.mydb.read_image(rowid=2) + curr = np.expand_dims(curr, axis=3) + mem = np.expand_dims(mem, axis=3) + curr2 = curr.copy() + curr2[3, 5, 2] = np.nan + curr3 = [[1, 2], [1, 2], [1, 2]] + curr3 = [curr3, curr3, curr3] + curr3 = np.array(curr3) + curr4 = np.zeros((3, 4, 5, 1)) + + with self.assertRaises(ValueError): + comparing.diff_optic_flow(curr2, mem) + with self.assertRaises(Exception): + comparing.diff_optic_flow(curr3, mem) + with self.assertRaises(Exception): + comparing.diff_optic_flow(curr4, mem) + + # should be working -> check if result is correct + for s in [mem]: + vec = comparing.diff_optic_flow(s, curr) + self.assertFalse(vec.shape[1] == (1, 2)) + self.assertFalse(np.any(np.isnan(vec))) + self.assertTrue(is_numeric_array(vec)) + + +if __name__ == '__main__': + unittest.main() diff --git a/navipy/database/__init__.py b/navipy/database/__init__.py index e111ad5f7956a0fa12261cc551f262ca3e40890a..2df843460a1acdec657782cbac74091d54d05847 100644 --- a/navipy/database/__init__.py +++ b/navipy/database/__init__.py @@ -123,8 +123,7 @@ It creates three sql table on initialisation. """Initialisation of the database """ if not isinstance(filename, str): raise TypeError('filename should be a string') - if (not isinstance(channels, list) or - not isinstance(channels, np.array)): + if not isinstance(channels, list): raise TypeError('nb_channel should be a list or np array') """for c in channels: if not c in ['R','G','B','D']: @@ -247,7 +246,7 @@ It creates three sql table on initialisation. raise ValueError('missing index alpha_1') if 'alpha_2' not in posorient.index: raise ValueError('missing index alpha_2') - if not ~np.any(pd.isnull(posorient)): + if np.any(pd.isnull(posorient)): raise ValueError('posorient must not contain nan') where = """x>=? and x<=?""" where += """and y>=? and y<=?""" @@ -315,8 +314,7 @@ class DataBaseLoad(DataBase): """Initialise the DataBaseLoader""" if not isinstance(filename, str): raise TypeError('filename should be a string') - if (not isinstance(channels, list) or - not isinstance(channels, np.array)): + if not isinstance(channels, list): raise TypeError('nb_channel should be a list or np array') for c in channels: if c not in ['R', 'G', 'B', 'D']: @@ -427,16 +425,17 @@ database raise ValueError('missing index alpha_2') if np.any(pd.isnull(posorient)): raise ValueError('posorient must not contain nan') - if not isinstance(rowid, int): - raise TypeError('rowid must be an integer') - if rowid <= 0: - raise ValueError('rowid must be greater zero') - if rowid is np.nan: - raise ValueError('rowid must not be nan') - if (posorient is None) and (rowid is None): - raise Exception('posorient and rowid can not be both None') - if posorient is not None: - rowid = self.get_posid(posorient) + if rowid is not None: + if not isinstance(rowid, int): + raise TypeError('rowid must be an integer') + if rowid <= 0: + raise ValueError('rowid must be greater zero') + if rowid is np.nan: + raise ValueError('rowid must not be nan') + if (posorient is None) and (rowid is None): + raise Exception('posorient and rowid can not be both None') + if posorient is not None: + rowid = self.get_posid(posorient) """Read an image at a given position-orientation or given id of row in the \ database. @@ -496,11 +495,11 @@ database if str(chan_n) + '_range' not in cmaxminrange.index: raise ValueError('cminmax range is missing index ' + str(chan_n) + '_range') - if any(np.isnan(cmaxminrange.loc[str(chan_n) + '_max'])): + if np.any(np.isnan(cmaxminrange.loc[str(chan_n) + '_max'])): raise ValueError('cmaxminrange contains nans') - if any(np.isnan(cmaxminrange.loc[str(chan_n) + '_min'])): + if np.any(np.isnan(cmaxminrange.loc[str(chan_n) + '_min'])): raise ValueError('cmaxminrange contains nans') - if any(np.isnan(cmaxminrange.loc[str(chan_n) + '_range'])): + if np.any(np.isnan(cmaxminrange.loc[str(chan_n) + '_range'])): raise ValueError('cmaxminrange contains nans') denormed_im = np.zeros(image.shape, dtype=np.float) maxval_nim = np.iinfo(image.dtype).max @@ -585,7 +584,7 @@ class DataBaseSave(DataBase): def normalise_image(self, image, dtype=np.uint8): if not isinstance(image, np.ndarray): raise TypeError('image must be np.array') - if any(np.isnan(image)): + if np.any(np.isnan(image)): raise ValueError('image must not contain nan values') if image.shape[0] <= 0 or image.shape[1] <= 0: raise Exception('image dimensions incorrect') diff --git a/navipy/database/__init__.py~ b/navipy/database/__init__.py~ index 2e0ee2bdaefcafd9fa3ed0fd56460020946432d3..2df843460a1acdec657782cbac74091d54d05847 100644 --- a/navipy/database/__init__.py~ +++ b/navipy/database/__init__.py~ @@ -91,7 +91,7 @@ def adapt_array(arr): """ http://stackoverflow.com/a/31312102/190597 (SoulNibbler) """ - if array is None: + if arr is None: raise ValueError('array must not be None') out = io.BytesIO() np.save(out, arr) @@ -123,8 +123,7 @@ It creates three sql table on initialisation. """Initialisation of the database """ if not isinstance(filename, str): raise TypeError('filename should be a string') - if (not isinstance(channels, list) or - not isinstance(channels, np.array)): + if not isinstance(channels, list): raise TypeError('nb_channel should be a list or np array') """for c in channels: if not c in ['R','G','B','D']: @@ -247,7 +246,7 @@ It creates three sql table on initialisation. raise ValueError('missing index alpha_1') if 'alpha_2' not in posorient.index: raise ValueError('missing index alpha_2') - if not ~np.any(pd.isnull(posorient)): + if np.any(pd.isnull(posorient)): raise ValueError('posorient must not contain nan') where = """x>=? and x<=?""" where += """and y>=? and y<=?""" @@ -315,8 +314,7 @@ class DataBaseLoad(DataBase): """Initialise the DataBaseLoader""" if not isinstance(filename, str): raise TypeError('filename should be a string') - if (not isinstance(channels, list) or - not isinstance(channels, np.array)): + if not isinstance(channels, list): raise TypeError('nb_channel should be a list or np array') for c in channels: if c not in ['R', 'G', 'B', 'D']: @@ -427,16 +425,17 @@ database raise ValueError('missing index alpha_2') if np.any(pd.isnull(posorient)): raise ValueError('posorient must not contain nan') - if not isinstance(rowid, int): - raise TypeError('rowid must be an integer') - if rowid <= 0: - raise ValueError('rowid must be greater zero') - if rowid is np.nan: - raise ValueError('rowid must not be nan') - if (posorient is None) and (rowid is None): - raise Exception('posorient and rowid can not be both None') - if posorient is not None: - rowid = self.get_posid(posorient) + if rowid is not None: + if not isinstance(rowid, int): + raise TypeError('rowid must be an integer') + if rowid <= 0: + raise ValueError('rowid must be greater zero') + if rowid is np.nan: + raise ValueError('rowid must not be nan') + if (posorient is None) and (rowid is None): + raise Exception('posorient and rowid can not be both None') + if posorient is not None: + rowid = self.get_posid(posorient) """Read an image at a given position-orientation or given id of row in the \ database. @@ -496,11 +495,11 @@ database if str(chan_n) + '_range' not in cmaxminrange.index: raise ValueError('cminmax range is missing index ' + str(chan_n) + '_range') - if any(np.isnan(cmaxminrange.loc[str(chan_n) + '_max'])): + if np.any(np.isnan(cmaxminrange.loc[str(chan_n) + '_max'])): raise ValueError('cmaxminrange contains nans') - if any(np.isnan(cmaxminrange.loc[str(chan_n) + '_min'])): + if np.any(np.isnan(cmaxminrange.loc[str(chan_n) + '_min'])): raise ValueError('cmaxminrange contains nans') - if any(np.isnan(cmaxminrange.loc[str(chan_n) + '_range'])): + if np.any(np.isnan(cmaxminrange.loc[str(chan_n) + '_range'])): raise ValueError('cmaxminrange contains nans') denormed_im = np.zeros(image.shape, dtype=np.float) maxval_nim = np.iinfo(image.dtype).max @@ -585,7 +584,7 @@ class DataBaseSave(DataBase): def normalise_image(self, image, dtype=np.uint8): if not isinstance(image, np.ndarray): raise TypeError('image must be np.array') - if any(np.isnan(image)): + if np.any(np.isnan(image)): raise ValueError('image must not contain nan values') if image.shape[0] <= 0 or image.shape[1] <= 0: raise Exception('image dimensions incorrect') diff --git a/navipy/database/__pycache__/__init__.cpython-35.pyc b/navipy/database/__pycache__/__init__.cpython-35.pyc index 62e15c4cf883db00790382af43cc53a92b35ca52..3bfd065f5b9480e6ed59d81e03ef5c21a0c2e838 100644 Binary files a/navipy/database/__pycache__/__init__.cpython-35.pyc and b/navipy/database/__pycache__/__init__.cpython-35.pyc differ diff --git a/navipy/moving/__pycache__/__init__.cpython-35.pyc b/navipy/moving/__pycache__/__init__.cpython-35.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9fba1b965c52102713da30cfa1546b76f4973c0c Binary files /dev/null and b/navipy/moving/__pycache__/__init__.cpython-35.pyc differ diff --git a/navipy/moving/__pycache__/agent.cpython-35.pyc b/navipy/moving/__pycache__/agent.cpython-35.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3501c939970e1f0f1e0577baf5253c7be2df08b7 Binary files /dev/null and b/navipy/moving/__pycache__/agent.cpython-35.pyc differ diff --git a/navipy/moving/__pycache__/maths.cpython-35.pyc b/navipy/moving/__pycache__/maths.cpython-35.pyc new file mode 100644 index 0000000000000000000000000000000000000000..54a569a60906dc1a11576ddf359469ec20246340 Binary files /dev/null and b/navipy/moving/__pycache__/maths.cpython-35.pyc differ diff --git a/navipy/moving/__pycache__/test_agent.cpython-35.pyc b/navipy/moving/__pycache__/test_agent.cpython-35.pyc new file mode 100644 index 0000000000000000000000000000000000000000..aa4058d63e97924dd19098b73d86c8fe532523f7 Binary files /dev/null and b/navipy/moving/__pycache__/test_agent.cpython-35.pyc differ diff --git a/navipy/moving/__pycache__/test_maths.cpython-35.pyc b/navipy/moving/__pycache__/test_maths.cpython-35.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c9562c6a7391a164142221e8d1638f6fb06a0aaa Binary files /dev/null and b/navipy/moving/__pycache__/test_maths.cpython-35.pyc differ diff --git a/navipy/moving/agent.py b/navipy/moving/agent.py index d34306b4f362676dcda263db84fa6b4e0f5b4426..ad3cd9c6c4370243c9b43b4edb629c4bd3f57340 100644 --- a/navipy/moving/agent.py +++ b/navipy/moving/agent.py @@ -7,7 +7,10 @@ import networkx as nx import multiprocessing from multiprocessing import Queue, JoinableQueue, Process import inspect -from pandas.api.types import is_numeric_dtype +try: + from pandas.core.common import is_numeric_dtype +except ImportError: + from pandas.api.types import is_numeric_dtype from navipy.database import DataBaseLoad import navipy.moving.maths as navimomath diff --git a/navipy/moving/agent.py~ b/navipy/moving/agent.py~ index b8aeee59364cf80b0203b8eeb3d4cd078dc686ba..c6b42101f4c7fb8494fe630906b5c34411b5b45f 100644 --- a/navipy/moving/agent.py~ +++ b/navipy/moving/agent.py~ @@ -3,4 +3,359 @@ """ import numpy as np import pandas as pd -import .maths +import networkx as nx +import multiprocessing +from multiprocessing import Queue, JoinableQueue, Process +import inspect +try: + from pandas.api.types import is_numeric_dtype +except ImportError: + from pandas.core.common import is_numeric_dtype +from navipy.database import DataBaseLoad +import navipy.moving.maths as navimomath + + +def defaultcallback(database, posorients): + raise NameError('No Callback') + + +class AbstractAgent(): + """ + An abtract class for agent + """ + + def __init__(self, + database_filename, + memory_friendly=False): + + self.db = DataBaseLoad(database_filename) + self.dbname = database_filename + if memory_friendly: + self.__posorients = None + else: + self.__posorients = self.db.posorients + # set mode of motion + mode_move = {'mode': 'on_cubic_grid', + 'param': {'grid_spacing': + pd.Series(data=1, + index=['dx', 'dy', 'dz'])}} + self.mode_of_motion = mode_move + + @property + def posorients(self): + toreturn = self.__posorients + if toreturn is not None: + toreturn = toreturn.copy() + return toreturn + + @property + def mode_of_motion(self): + """ + """ + toreturn = self.__mode_move + toreturn['describe'] = \ + navimomath.mode_moves_supported()[ + self.__mode_move['mode']]['describe'] + return toreturn + + @mode_of_motion.setter + def mode_of_motion(self, mode): + """ + + """ + if not isinstance(mode, dict): + raise TypeError('Mode is not a dictionary') + if 'mode' not in mode: + raise KeyError("'mode' is not a key of mode") + if 'param' not in mode: + raise KeyError("'param' is not a key of mode") + if mode['mode'] in navimomath.mode_moves_supported().keys(): + for param in navimomath.mode_moves_supported()[ + mode['mode']]['param']: + if param not in mode['param']: + raise KeyError( + "'{}' is not in mode['param']".format(param)) + self.__mode_move = mode + else: + raise ValueError('mode is not supported') + + def abstractmove(self, posorients_vel): + if isinstance(posorients_vel, pd.Series) is False: + raise TypeError('posorients_vel should be a pandas Series') + for col in ['x', 'y', 'z', 'alpha_0', 'alpha_1', 'alpha_2', + 'dx', 'dy', 'dz', 'dalpha_0', 'dalpha_1', 'dalpha_2']: + if col not in posorients_vel.index: + raise KeyError( + 'posorients_vel should have {} as index'.format(col)) + # Compute the next position + posorients_vel = navimomath.next_pos( + posorients_vel, + move_mode=self.__mode_move['mode'], + move_param=self.__mode_move['param']) + + # Compute the closest possible position + if posorients_vel is None: + tmp = navimomath.closest_pos_memory_friendly( + posorients_vel, + self.db) + posorients_vel[['x', 'y', 'z', + 'alpha_0', 'alpha_1', 'alpha_2']] = tmp + posorients_vel.name = tmp.name + else: + tmp = navimomath.closest_pos( + posorients_vel, + self.__posorients) + posorients_vel[['x', 'y', 'z', + 'alpha_0', 'alpha_1', 'alpha_2']] = tmp + posorients_vel.name = tmp.name + return posorients_vel + + +class Single(AbstractAgent, Process): + + def __init__(self, + database_filename, + initial_condition, + memory_friendly=False, + posorients_queue=None, + results_queue=None): + if (posorients_queue is not None) and (results_queue is not None): + multiprocessing.Process.__init__(self) + AbstractAgent.__init__(self, database_filename, + memory_friendly) + + self.__posorientvel = pd.Series( + data=0, + index=['x', 'y', 'z', + 'alpha_0', 'alpha_1', 'alpha_2', + 'dx', 'dy', 'dz', + 'dalpha_0', 'dalpha_1', 'dalpha_2'], + dtype=np.float) + + if isinstance(initial_condition, pd.Series): + if is_numeric_dtype(initial_condition): + common_id = list(set(initial_condition.index).intersection( + self.__posorientvel.index)) + self.__posorientvel.loc[common_id] = \ + initial_condition.loc[common_id] + else: + raise TypeError('vel should be numeric') + + else: + raise TypeError('vel should be a pandas Series') + + self.__posorients_queue = posorients_queue + self.__results_queue = results_queue + self.__callback_function = defaultcallback + + def move(self): + # Compute the next position + tmp = self.__callback_function(database=self.db, + posorient=self.__posorientvel) + common_id = list(set(tmp.index).intersection( + self.__posorientvel.index)) + self.__posorientvel.loc[common_id] = tmp.loc[common_id] + self.__posorientvel = self.abstractmove(self.__posorientvel) + + def fly(self, nsteps): + """move until either speed is null, or nsteps has been reached""" + prev_move = self.__posorientvel + for stepi in range(nsteps): + self.move() + if prev_move.equals(self.__posorientvel): + break + prev_move = self.__posorientvel + + def run(self): + """ Only supported when multiprocess""" + if self.__posorients_queue is None or self.__results_queue is None: + raise NameError('Single agent class has not be inititialised ' + + 'with multiprocessing suppport') + proc_name = self.name + print('Process {} started'.format(proc_name)) + while True: + start_posorient = self.__posorients_queue.get(timeout=1) + if start_posorient is None: + # Poison pill means shutdown) + break + common_id = list(set(start_posorient.index).intersection( + self.__posorientvel.index)) + self.__posorientvel.loc[common_id] = start_posorient.loc[common_id] + self.move() + next_posorient = self.__posorientvel + + self.__posorients_queue.task_done() + self.__results_queue.put((start_posorient, next_posorient)) + self.__posorients_queue.task_done() + print('Process {} done'.format(proc_name)) + + @property + def callback_function(self): + return inspect.getsourcelines(self.__callback_function) + + @callback_function.setter + def callback_function(self, callback_function): + self.__callback_function = callback_function + + @property + def position(self): + return self.__posorientvel.loc[['x', 'y', 'z']] + + @property + def velocity(self): + return self.__posorientvel.loc[['dx', 'dy', 'dz']] + + @property + def orientation(self): + return self.__posorientvel.loc[['alpha_0', 'alpha_1', 'alpha_2']] + + @property + def angular_velocity(self): + return self.__posorientvel.loc[['dalpha_0', 'dalpha_1', 'dalpha_2']] + + +class Multi(AbstractAgent): + + def __init__(self, database_filename): + super().__init__(database_filename, False) + # Init the graph + self.__graph = nx.DiGraph() + for row_id, posor in self.db.posorients.iterrows(): + posor.name = row_id + self.__graph.add_node(row_id, + posorient=posor) + + @property + def graph(self): + return self.__graph + + @graph.setter + def graph(self, graph): + if isinstance(graph, nx.DiGraph) is False: + raise TypeError('graph is not a nx.DiGraph') + self.__graph = graph.copy() + self.check_graph() + + def build_graph(self, callback_function, + ncpu=5, + timeout=1): + # Build a list of nodes + results_edges = [] + posorients_queue = JoinableQueue() + results_queue = Queue() + for node in self.__graph.nodes: + posorients_queue.put(self.__graph.nodes[node]['posorient']) + initpos = 0 * self.__graph.nodes[node]['posorient'] + + # Start ndatabase loader + num_agents = ncpu + agents = [Single(self.dbname, + initial_condition=initpos, + memory_friendly=False, + posorients_queue=posorients_queue, + results_queue=results_queue) + for _ in range(num_agents)] + for w in agents: + w.callback_function = callback_function + w.start() + + # Add a poison pill for each agent + for _ in range(num_agents): + posorients_queue.put(None) + + # Wait for all of the tasks to finish + # posorients_queue.join() + + for _ in range(nx.number_of_nodes(self.__graph)): + result = results_queue.get(timeout=timeout) + results_edges.append((result[0].name, + result[1].name)) + # print(results_edges[-1]) + self.__graph.add_edges_from(results_edges) + self.check_graph() + + def check_graph(self): + self.check_single_target() + + def check_single_target(self): + for node in self.__graph.nodes: + # not connected -> loop not ran + for count, _ in enumerate(self.__graph.neighbors(node)): + # count == 0 -> connected to one node + # count == 1 -> connected to two nodes + if count > 0: + raise ValueError( + 'Node {} leads to several locations'.format(node)) + + def find_attractors(self): + """Return a list of node going to each attractor in a graph + """ + attractors = list() + for attractor in nx.attracting_components(self.__graph): + att = dict() + att['attractor'] = attractor + attractors.append(att) + return attractors + + def find_attractors_sources(self, attractors=None): + """Find all sources going to each attractors + """ + if attractors is None: + attractors = self.find_attractors() + + if isinstance(attractors, list) is False: + raise TypeError('Attractors should be a list of dict') + elif len(attractors) == 0: + raise ValueError('No attractors found') + + # Check attractor + for att in attractors: + keyatt = att.keys() + if 'attractor' not in keyatt: + raise ValueError( + 'Each attractors should contain the key attractor') + + # Calculate connection + for att_i, att in enumerate(attractors): + + # [0] because one node of the attractor is enough + # all other node of the attractor are connected to this one + target = list(att['attractor'])[0] + attractors[att_i]['paths'] = \ + nx.shortest_path(self.graph, target=target) + attractors[att_i]['sources'] = \ + list(attractors[att_i]['paths'].keys()) + return attractors + + def catchment_area(self, attractors=None): + """Return the catchment area for attractors + """ + if attractors is None: + attractors = self.find_attractors_sources() + + if isinstance(attractors, list) is False: + raise TypeError('Attractors should be a list of dict') + elif len(attractors) == 0: + raise ValueError('No attractors found') + + # Check attractor + for att in attractors: + keyatt = att.keys() + if 'sources' not in keyatt: + raise ValueError( + 'Each attractors should contains a list of sources') + + return [len(att['sources']) for att in attractors] + + def reach_goals(self, goals): + """ Return all paths to the goals """ + return nx.shortest_path(self.__graph, target=goals) + + def neighboring_nodes(self, target): + """ Return the nodes going to the target """ + # Reverse graph because nx.neighbors give the end node + # and we want to find the start node going to target + # not where target goes. + tmpgraph = self.__graph.reverse(copy=True) + neighbors = tmpgraph.neighbors(target) + return neighbors diff --git a/navipy/processing/__pycache__/__init__.cpython-35.pyc b/navipy/processing/__pycache__/__init__.cpython-35.pyc index 6368410ef085656e5d7efcd12bf8d0b248cf38c9..dcc89be0a4833923e1595f5f3f18d97c1f3e153e 100644 Binary files a/navipy/processing/__pycache__/__init__.cpython-35.pyc and b/navipy/processing/__pycache__/__init__.cpython-35.pyc differ diff --git a/navipy/processing/__pycache__/constants.cpython-35.pyc b/navipy/processing/__pycache__/constants.cpython-35.pyc index d29e33e3804083de741252705e872a200e2e6f26..8de0152796b7d7ec0ddbe8ced40f12d156c84a0e 100644 Binary files a/navipy/processing/__pycache__/constants.cpython-35.pyc and b/navipy/processing/__pycache__/constants.cpython-35.pyc differ diff --git a/navipy/processing/__pycache__/pcode.cpython-35.pyc b/navipy/processing/__pycache__/pcode.cpython-35.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b72a525ba7f6eba6b6841a27d11412b61ff87267 Binary files /dev/null and b/navipy/processing/__pycache__/pcode.cpython-35.pyc differ diff --git a/navipy/processing/__pycache__/test.cpython-35.pyc b/navipy/processing/__pycache__/test.cpython-35.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5215b9e25c7fc208e484f01cff0f3a4f069ef965 Binary files /dev/null and b/navipy/processing/__pycache__/test.cpython-35.pyc differ diff --git a/navipy/processing/__pycache__/tools.cpython-35.pyc b/navipy/processing/__pycache__/tools.cpython-35.pyc index 6e1d9ee8db14f2abecf63d93f94331abc5b0c18a..4b13d82876b9b00e7c5067fa526792351985f8b9 100644 Binary files a/navipy/processing/__pycache__/tools.cpython-35.pyc and b/navipy/processing/__pycache__/tools.cpython-35.pyc differ diff --git a/navipy/processing/constants.py~ b/navipy/processing/constants.py~ new file mode 100644 index 0000000000000000000000000000000000000000..1839e292fe194dbfa0c3f2bd952c6958bc3a0ba5 --- /dev/null +++ b/navipy/processing/constants.py~ @@ -0,0 +1,22 @@ +""" +Define some constant +""" + +__spherical_indeces__ = {'elevation': 0, + 'azimuth': 1, + 'radius': 2} +__cartesian_indeces__ = {'x': 0, + 'y': 1, + 'z': 2} +__ibpc_indeces__ = {'elevation': 0, + 'azimuth': 1, + 'channel': 2, + 'component': 3} +__obpc_indeces__ = {'ommatidia': 0, + 'channel': 1, + 'component': 2} +__eye_indeces__ = {'elevation': 0, + 'azimuth': 1, + 'component': 2} +__ommadia_indeces__ = {'ommatidia': 0, + 'component': 1} diff --git a/navipy/processing/test.py b/navipy/processing/test.py index 39b75415a3eb68f4996ba9419ebbb451c24ccc60..daa12edf988a007de54ab8007f8f01289554c8e4 100644 --- a/navipy/processing/test.py +++ b/navipy/processing/test.py @@ -28,7 +28,8 @@ class TestCase(unittest.TestCase): posorient.alpha_0 = rows[3] posorient.alpha_1 = rows[2] posorient.alpha_2 = rows[4] - image = self.mydb.scene(posorient=posorient) + image = self.mydb.read_image(posorient=posorient) + image = np.expand_dims(image, axis=3) self.assertIsNotNone(image) self.assertFalse(sum(image.shape) == 0) # print("shape",image.shape) @@ -44,7 +45,7 @@ class TestCase(unittest.TestCase): posorient2.alpha_1 = posorient.alpha_1 posorient2.alpha_2 = posorient.alpha_2 with self.assertRaises(Exception): - image = self.mydb.scene(posorient=posorient2) + image = self.mydb.read_image(posorient=posorient2) # incorrect case None posorient2 = pd.Series(index=['x', 'y', 'z', @@ -56,7 +57,7 @@ class TestCase(unittest.TestCase): posorient2.alpha_1 = posorient.alpha_1 posorient2.alpha_2 = posorient.alpha_2 with self.assertRaises(ValueError): - image = self.mydb.scene(posorient=posorient2) + image = self.mydb.read_image(posorient=posorient2) # incorrect case nan posorient2 = pd.Series(index=['x', 'y', 'z', @@ -68,7 +69,7 @@ class TestCase(unittest.TestCase): posorient2.alpha_1 = posorient.alpha_1 posorient2.alpha_2 = posorient.alpha_2 with self.assertRaises(ValueError): - image = self.mydb.scene(posorient=posorient2) + image = self.mydb.read_image(posorient=posorient2) # incorrect case no pandas series but dict posorient2 = {} @@ -79,18 +80,20 @@ class TestCase(unittest.TestCase): posorient2['alpha_1'] = posorient.alpha_1 posorient2['alpha_2'] = posorient.alpha_2 with self.assertRaises(TypeError): - image = self.mydb.scene(posorient=posorient2) + image = self.mydb.read_image(posorient=posorient2) # not working case empty posorient2 = pd.Series(index=['x', 'y', 'z', 'alpha_0', 'alpha_1', 'alpha_2']) with self.assertRaises(Exception): - image = self.mydb.scene(posorient=posorient2) + image = self.mydb.read_image(posorient=posorient2) def test_skyline_scene(self): - scene = self.mydb.scene(rowid=1) + scene = self.mydb.read_image(rowid=1) scene2 = scene.copy() + scene = np.expand_dims(scene, axis=3) + scene2 = np.expand_dims(scene2, axis=3) scene2[3, 5, 2, 0] = np.nan scene3 = [[1, 2, 3], [1, 2, 3], [1, 2, 3]] scene3 = [scene3, scene3, scene3] @@ -121,18 +124,19 @@ class TestCase(unittest.TestCase): for rowid in [0, -2]: with self.assertRaises(ValueError): # print("rowid",rowid) - self.mydb.scene(rowid=rowid) + self.mydb.read_image(rowid=rowid) with self.assertRaises(TypeError): - self.mydb.scene(rowid='T') - with self.assertRaises(ValueError): - self.mydb.scene(rowid=None) + self.mydb.read_image(rowid='T') + with self.assertRaises(TypeError): + self.mydb.read_image(rowid=None) with self.assertRaises(TypeError): - self.mydb.scene(rowid=np.nan) + self.mydb.read_image(rowid=np.nan) with self.assertRaises(TypeError): - self.mydb.scene(rowid=4.5) + self.mydb.read_image(rowid=4.5) for rowid in [1, 2, 3, 4, 5]: - image = self.mydb.scene(rowid=rowid) + image = self.mydb.read_image(rowid=rowid) + image = np.expand_dims(image, axis=3) # image=np.array(image) self.assertIsNotNone(image) self.assertFalse(sum(image.shape) == 0) @@ -144,7 +148,8 @@ class TestCase(unittest.TestCase): self.assertTrue(image.shape[1] > 0) def test_distance_channel(self): - scene = self.mydb.scene(rowid=1) + scene = self.mydb.read_image(rowid=1) + scene = np.expand_dims(scene, axis=3) # should not be working for d in ['g', None, np.nan, 8.4]: with self.assertRaises(TypeError): @@ -165,8 +170,8 @@ class TestCase(unittest.TestCase): self.assertEqual(weighted_scene.shape, scene.shape) def test_contr_weight_scene(self): - scene = self.mydb.scene(rowid=1) - + scene = self.mydb.read_image(rowid=1) + scene = np.expand_dims(scene, axis=3) # working cases contrast = pcode.contrast_weighted_nearness(scene) self.assertIsNotNone(contrast) @@ -193,7 +198,8 @@ class TestCase(unittest.TestCase): contrast = pcode.contrast_weighted_nearness(scene4) def test_contr_weight_contrast(self): - scene = self.mydb.scene(rowid=1) + scene = self.mydb.read_image(rowid=1) + scene = np.expand_dims(scene, axis=3) for size in [9.4, 'g', None, np.nan]: with self.assertRaises(TypeError): contrast = pcode.contrast_weighted_nearness( @@ -220,7 +226,8 @@ class TestCase(unittest.TestCase): def test_pcv(self): # working case rowid = 1 - my_scene = self.mydb.scene(rowid=rowid) + my_scene = self.mydb.read_image(rowid=rowid) + my_scene = np.expand_dims(my_scene, axis=3) directions = self.mydb.viewing_directions my_pcv = pcode.pcv(my_scene, directions) self.assertIsNotNone(my_pcv) @@ -261,10 +268,11 @@ class TestCase(unittest.TestCase): def test_apcv(self): # working case rowid = 1 - my_scene = self.mydb.scene(rowid=rowid) + my_scene = self.mydb.read_image(rowid=rowid) + my_scene = np.expand_dims(my_scene, axis=3) # print("scene shape",my_scene.shape) directions = self.mydb.viewing_directions - # print("directions",directions.shape) + print("directions", directions.shape) my_pcv = pcode.apcv(my_scene, directions) self.assertIsNotNone(my_pcv) @@ -304,7 +312,8 @@ class TestCase(unittest.TestCase): def test_size(self): # not working cases: - scene = self.mydb.scene(rowid=1) + scene = self.mydb.read_image(rowid=1) + scene = np.expand_dims(scene, axis=3) for size in [8, 1, 0, -4]: with self.assertRaises(ValueError): contrast = pcode.michelson_contrast( @@ -328,7 +337,8 @@ class TestCase(unittest.TestCase): def test_michelsoncontrast_scene(self): - scene = self.mydb.scene(rowid=1) + scene = self.mydb.read_image(rowid=1) + scene = np.expand_dims(scene, axis=3) # working cases contrast = pcode.michelson_contrast(scene) diff --git a/navipy/processing/test.py~ b/navipy/processing/test.py~ new file mode 100644 index 0000000000000000000000000000000000000000..daa12edf988a007de54ab8007f8f01289554c8e4 --- /dev/null +++ b/navipy/processing/test.py~ @@ -0,0 +1,368 @@ +import unittest +import sqlite3 +import numpy as np +import pandas as pd +import navipy.database as database +import navipy.processing.pcode as pcode +from navipy.processing.tools import is_numeric_array +import pkg_resources + + +class TestCase(unittest.TestCase): + def setUp(self): + self.mydb_filename = pkg_resources.resource_filename( + 'navipy', 'resources/database.db') + self.mydb = database.DataBaseLoad(self.mydb_filename) + + def test_scene_posorient(self): + conn = sqlite3.connect(self.mydb_filename) + c = conn.cursor() + c.execute(""" SELECT * FROM position_orientation WHERE (rowid=1) """) + rows = c.fetchall()[0] + # working case + posorient = pd.Series(index=['x', 'y', 'z', + 'alpha_0', 'alpha_1', 'alpha_2']) + posorient.x = rows[5] + posorient.y = rows[6] + posorient.z = rows[1] + posorient.alpha_0 = rows[3] + posorient.alpha_1 = rows[2] + posorient.alpha_2 = rows[4] + image = self.mydb.read_image(posorient=posorient) + image = np.expand_dims(image, axis=3) + self.assertIsNotNone(image) + self.assertFalse(sum(image.shape) == 0) + # print("shape",image.shape) + self.assertTrue(len(image.shape) == 4) + self.assertTrue(image.shape[3] == 1) + + # incorrect case missing column + posorient2 = pd.Series(index=['y', 'z', + 'alpha_0', 'alpha_1', 'alpha_2']) + posorient2.y = posorient.y + posorient2.z = posorient.z + posorient2.alpha_0 = posorient.alpha_0 + posorient2.alpha_1 = posorient.alpha_1 + posorient2.alpha_2 = posorient.alpha_2 + with self.assertRaises(Exception): + image = self.mydb.read_image(posorient=posorient2) + + # incorrect case None + posorient2 = pd.Series(index=['x', 'y', 'z', + 'alpha_0', 'alpha_1', 'alpha_2']) + posorient2.x = None + posorient2.y = posorient.y + posorient2.z = posorient.z + posorient2.alpha_0 = posorient.alpha_0 + posorient2.alpha_1 = posorient.alpha_1 + posorient2.alpha_2 = posorient.alpha_2 + with self.assertRaises(ValueError): + image = self.mydb.read_image(posorient=posorient2) + + # incorrect case nan + posorient2 = pd.Series(index=['x', 'y', 'z', + 'alpha_0', 'alpha_1', 'alpha_2']) + posorient2.x = np.nan + posorient2.y = posorient.y + posorient2.z = posorient.z + posorient2.alpha_0 = posorient.alpha_0 + posorient2.alpha_1 = posorient.alpha_1 + posorient2.alpha_2 = posorient.alpha_2 + with self.assertRaises(ValueError): + image = self.mydb.read_image(posorient=posorient2) + + # incorrect case no pandas series but dict + posorient2 = {} + posorient2['x'] = posorient.x + posorient2['y'] = posorient.y + posorient2['z'] = posorient.z + posorient2['alpha_0'] = posorient.alpha_0 + posorient2['alpha_1'] = posorient.alpha_1 + posorient2['alpha_2'] = posorient.alpha_2 + with self.assertRaises(TypeError): + image = self.mydb.read_image(posorient=posorient2) + + # not working case empty + posorient2 = pd.Series(index=['x', 'y', 'z', + 'alpha_0', 'alpha_1', 'alpha_2']) + + with self.assertRaises(Exception): + image = self.mydb.read_image(posorient=posorient2) + + def test_skyline_scene(self): + scene = self.mydb.read_image(rowid=1) + scene2 = scene.copy() + scene = np.expand_dims(scene, axis=3) + scene2 = np.expand_dims(scene2, axis=3) + scene2[3, 5, 2, 0] = np.nan + scene3 = [[1, 2, 3], [1, 2, 3], [1, 2, 3]] + scene3 = [scene3, scene3, scene3] + scene3 = np.array(scene3) + scene4 = np.zeros((3, 4, 5, 0)) + + # put useless stuff here + with self.assertRaises(ValueError): + pcode.skyline(scene2) + with self.assertRaises(TypeError): + pcode.skyline(scene3) + with self.assertRaises(Exception): + pcode.skyline(scene4) + + # should be working -> check if result(skyline) is correct + for s in [scene]: + skyline = pcode.skyline(s) + self.assertFalse(skyline.shape[1] <= 0) + self.assertTrue(skyline.shape[2] == 4) + self.assertFalse(np.any(np.isnan(skyline))) + # self.assertFalse(np.any(np.isNone(skyline))) + self.assertTrue(is_numeric_array(skyline)) + self.assertTrue(skyline.shape[3] == 1) + self.assertTrue(skyline.shape[0] > 0) + self.assertTrue(skyline.shape[1] > 0) + + def test_id(self): + for rowid in [0, -2]: + with self.assertRaises(ValueError): + # print("rowid",rowid) + self.mydb.read_image(rowid=rowid) + with self.assertRaises(TypeError): + self.mydb.read_image(rowid='T') + with self.assertRaises(TypeError): + self.mydb.read_image(rowid=None) + with self.assertRaises(TypeError): + self.mydb.read_image(rowid=np.nan) + with self.assertRaises(TypeError): + self.mydb.read_image(rowid=4.5) + + for rowid in [1, 2, 3, 4, 5]: + image = self.mydb.read_image(rowid=rowid) + image = np.expand_dims(image, axis=3) + # image=np.array(image) + self.assertIsNotNone(image) + self.assertFalse(sum(image.shape) == 0) + self.assertTrue(len(image.shape) == 4) + self.assertFalse(np.any(np.isnan(image))) + self.assertTrue(image.shape[3] == 1) + self.assertTrue(image.shape[2] == 4) + self.assertTrue(image.shape[0] > 0) + self.assertTrue(image.shape[1] > 0) + + def test_distance_channel(self): + scene = self.mydb.read_image(rowid=1) + scene = np.expand_dims(scene, axis=3) + # should not be working + for d in ['g', None, np.nan, 8.4]: + with self.assertRaises(TypeError): + pcode.contrast_weighted_nearness(scene, + distance_channel=d) + with self.assertRaises(ValueError): + pcode.contrast_weighted_nearness(scene, + distance_channel=-1) + + # should work + d = 3 + weighted_scene = \ + pcode.contrast_weighted_nearness(scene, + distance_channel=d) + # print("last channel",d) + self.assertTrue(is_numeric_array(weighted_scene)) + self.assertTrue(~np.any(np.isnan(weighted_scene))) + self.assertEqual(weighted_scene.shape, scene.shape) + + def test_contr_weight_scene(self): + scene = self.mydb.read_image(rowid=1) + scene = np.expand_dims(scene, axis=3) + # working cases + contrast = pcode.contrast_weighted_nearness(scene) + self.assertIsNotNone(contrast) + self.assertFalse(sum(contrast.shape) == 0) + self.assertTrue(len(contrast.shape) == 4) + self.assertFalse(np.any(np.isnan(contrast))) + self.assertTrue(contrast.shape[3] == 1) + self.assertTrue(contrast.shape[2] == 4) + self.assertTrue(contrast.shape[0] > 0) + self.assertTrue(contrast.shape[1] > 0) + + # not working case + scene2 = scene.copy() + scene2[3, 2, 1, 0] = np.nan + scene3 = [[1, 2, 3], [1, 2, 3], [1, 2, 3]] + scene3 = [scene3, scene3, scene3] + scene3 = np.array(scene3) + scene4 = np.zeros((3, 4, 5, 0)) + with self.assertRaises(ValueError): + contrast = pcode.contrast_weighted_nearness(scene2) + with self.assertRaises(Exception): + contrast = pcode.contrast_weighted_nearness(scene3) + with self.assertRaises(Exception): + contrast = pcode.contrast_weighted_nearness(scene4) + + def test_contr_weight_contrast(self): + scene = self.mydb.read_image(rowid=1) + scene = np.expand_dims(scene, axis=3) + for size in [9.4, 'g', None, np.nan]: + with self.assertRaises(TypeError): + contrast = pcode.contrast_weighted_nearness( + scene, contrast_size=size) + for size in [8, 1, 0, -4]: + with self.assertRaises(ValueError): + contrast = \ + pcode.contrast_weighted_nearness( + scene, contrast_size=size) + + # working cases + for size in [2, 3, 4, 5]: + contrast = pcode.contrast_weighted_nearness(scene, + contrast_size=size) + self.assertIsNotNone(contrast) + self.assertFalse(sum(contrast.shape) == 0) + self.assertTrue(len(contrast.shape) == 4) + self.assertFalse(np.any(np.isnan(contrast))) + self.assertEqual(contrast.shape[3], 1) + self.assertEqual(contrast.shape[2], scene.shape[2]) + self.assertEqual(contrast.shape[0], scene.shape[0]) + self.assertEqual(contrast.shape[1], scene.shape[1]) + + def test_pcv(self): + # working case + rowid = 1 + my_scene = self.mydb.read_image(rowid=rowid) + my_scene = np.expand_dims(my_scene, axis=3) + directions = self.mydb.viewing_directions + my_pcv = pcode.pcv(my_scene, directions) + self.assertIsNotNone(my_pcv) + self.assertFalse(sum(my_pcv.shape) == 0) + self.assertTrue(len(my_pcv.shape) == 4) + self.assertFalse(np.any(np.isnan(my_pcv))) + self.assertTrue(my_pcv.shape[3] == 3) + self.assertTrue(my_pcv.shape[2] == 4) + self.assertTrue(my_pcv.shape[0] > 0) + self.assertTrue(my_pcv.shape[1] > 0) + + # not working cases doesnt match with shape of place code + testdirection = np.zeros((2, 4, 2)) + with self.assertRaises(Exception): + my_pcv = pcode.pcv(my_scene, testdirection) + + # not working cases wrong last dimension + testdirection = np.zeros((180, 360, 1)) + with self.assertRaises(Exception): + my_pcv = pcode.pcv(my_scene, testdirection) + + # not working cases too many dimensions + testdirection = np.zeros((180, 360, 2, 4)) + with self.assertRaises(Exception): + my_pcv = pcode.pcv(my_scene, testdirection) + + # not working cases empty + testdirection = np.zeros(()) + with self.assertRaises(Exception): + my_pcv = pcode.pcv(my_scene, testdirection) + + # not working cases nans + testdirection = np.zeros((180, 360, 2, 4)) + testdirection[2, 3, 0] = np.nan + with self.assertRaises(ValueError): + my_pcv = pcode.pcv(my_scene, testdirection) + + def test_apcv(self): + # working case + rowid = 1 + my_scene = self.mydb.read_image(rowid=rowid) + my_scene = np.expand_dims(my_scene, axis=3) + # print("scene shape",my_scene.shape) + directions = self.mydb.viewing_directions + print("directions", directions.shape) + my_pcv = pcode.apcv(my_scene, directions) + + self.assertIsNotNone(my_pcv) + self.assertFalse(sum(my_pcv.shape) == 0) + self.assertTrue(len(my_pcv.shape) == 4) + self.assertFalse(np.any(np.isnan(my_pcv))) + self.assertTrue(my_pcv.shape[3] == 3) + self.assertTrue(my_pcv.shape[2] == 4) + self.assertTrue(my_pcv.shape[0] == 1) + self.assertTrue(my_pcv.shape[1] == 1) + + # not working cases doesnt match with shape of place code + testdirection = np.zeros((2, 4, 2)) + with self.assertRaises(Exception): + my_pcv = pcode.apcv(my_scene, testdirection) + + # not working cases wrong last dimension + testdirection = np.zeros((180, 360, 1)) + with self.assertRaises(Exception): + my_pcv = pcode.apcv(my_scene, testdirection) + + # not working cases too many dimensions + testdirection = np.zeros((180, 360, 2, 4)) + with self.assertRaises(Exception): + my_pcv = pcode.apcv(my_scene, testdirection) + + # not working cases empty + testdirection = np.zeros(()) + with self.assertRaises(Exception): + my_pcv = pcode.apcv(my_scene, testdirection) + + # not working cases nans + testdirection = np.zeros((180, 360, 2, 4)) + testdirection[2, 3, 0] = np.nan + with self.assertRaises(ValueError): + my_pcv = pcode.apcv(my_scene, testdirection) + + def test_size(self): + # not working cases: + scene = self.mydb.read_image(rowid=1) + scene = np.expand_dims(scene, axis=3) + for size in [8, 1, 0, -4]: + with self.assertRaises(ValueError): + contrast = pcode.michelson_contrast( + scene, size=size) + for size in [9.4, 'g', None, np.nan]: + with self.assertRaises(TypeError): + contrast = pcode.michelson_contrast( + scene, size=size) + + # working cases + for size in [2, 3, 4, 5]: + contrast = pcode.michelson_contrast(scene, size=size) + self.assertIsNotNone(contrast) + self.assertFalse(sum(contrast.shape) == 0) + self.assertTrue(len(contrast.shape) == 4) + self.assertFalse(np.any(np.isnan(contrast))) + self.assertTrue(contrast.shape[3] == 1) + self.assertTrue(contrast.shape[2] == 4) + self.assertTrue(contrast.shape[0] > 0) + self.assertTrue(contrast.shape[1] > 0) + + def test_michelsoncontrast_scene(self): + + scene = self.mydb.read_image(rowid=1) + scene = np.expand_dims(scene, axis=3) + + # working cases + contrast = pcode.michelson_contrast(scene) + self.assertIsNotNone(contrast) + self.assertFalse(sum(contrast.shape) == 0) + self.assertTrue(len(contrast.shape) == 4) + self.assertFalse(np.any(np.isnan(contrast))) + self.assertTrue(contrast.shape[3] == 1) + self.assertTrue(contrast.shape[2] == 4) + self.assertTrue(contrast.shape[0] > 0) + self.assertTrue(contrast.shape[1] > 0) + + # not working case + scene2 = scene.copy() + scene2[3, 2, 1, 0] = np.nan + scene3 = [[1, 2, 3], [1, 2, 3], [1, 2, 3]] + scene3 = [scene3, scene3, scene3] + scene3 = np.array(scene3) + scene4 = np.zeros((3, 4, 5, 0)) + for s in [scene2, scene3, scene4]: + with self.assertRaises(Exception) as cm: + contrast = pcode.michelson_contrast(s,) + print("wanted exception occured", cm.exception) + + +if __name__ == '__main__': + unittest.main() diff --git a/navipy/processing/tools.py b/navipy/processing/tools.py index 34a54ba8556de4cb069eb2a1de034c2295630b99..42e0cc8de33ee128ce9353d095412b1f32fbc87a 100644 --- a/navipy/processing/tools.py +++ b/navipy/processing/tools.py @@ -39,8 +39,6 @@ def check_scene(scene): 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 diff --git a/navipy/processing/tools.py~ b/navipy/processing/tools.py~ new file mode 100644 index 0000000000000000000000000000000000000000..a1c3e0fb9db8ec00ba86423dd5cce4e2ca3140f5 --- /dev/null +++ b/navipy/processing/tools.py~ @@ -0,0 +1,165 @@ +from .constants import __ibpc_indeces__ +from .constants import __spherical_indeces__ +from .constants import __cartesian_indeces__ +from .constants import __obpc_indeces__ +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') + # 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 len(viewing_direction.shape) < 3: + raise Exception('viewing direction must have at least 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[-1] == 2): + raise Exception(' last 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 + + :param place_code: a place-code + :returns: True if image based place-code + :rtype: bool + + """ + toreturn = isinstance(place_code, np.ndarray) + toreturn = toreturn and (len(place_code.shape) == + len(__ibpc_indeces__)) + return toreturn + + +def is_obpc(place_code): + """Test if a place code is ommatidia based + + :param place_code: a place-code + :returns: True if ommatidia based place-code + :rtype: bool + + """ + toreturn = isinstance(place_code, np.ndarray) + toreturn = toreturn and (len(place_code.shape) == + len(__obpc_indeces__)) + return toreturn + + +def ibs_to_obs(scene, eye_map): + """Convert an image based scene to an ommatidium based scene. + + :param scene: The scene to be converted + :param eye_map: The eye_map to use + :returns: (obs_scene,ommatidia_map) + :rtype: (np.ndarray,np.ndarray) + """ + assert is_ibpc(scene),\ + 'scene should be an ibs scene' + assert isinstance(eye_map, np.ndarray), 'eye_map should be a numpy array' + assert len(eye_map.shape) == len(__eye_indeces__),\ + 'eye_map should have {} dimensions to be an ibs scene'.format( + len(__eye_indeces__)) + for index_name in ['elevation', 'azimuth']: + index = __ibpc_indeces__[index_name] + assert eye_map.shape[index] == scene.shape[index],\ + 'eye_map and scene should have the same number of {}'.format( + index_name) + obs_size = (scene.shape[__ibpc_indeces__['elevation']] * + scene.shape[__ibpc_indeces__['azimuth']], + scene.shape[__ibpc_indeces__['channel']], + scene.shape[__ibpc_indeces__['component']]) + obs_scene = scene.reshape(obs_size) + omm_size = (eye_map.shape[__ibpc_indeces__['elevation']] * + eye_map.shape[__ibpc_indeces__['azimuth']], + eye_map.shape[__ibpc_indeces__['component']]) + ommatidia_map = eye_map.reshape(omm_size) + return (obs_scene, ommatidia_map) + + +def cartesian_to_spherical(x, y, z): + radius = np.sqrt(x**2 + y**2 + z**2) + elevation = np.arctan2(z, np.sqrt(x**2 + y**2)) + azimuth = np.arctan2(y, x) + spherical = np.zeros_like(x) + spherical = np.tile(spherical[..., np.newaxis], (3,)) + spherical[..., __spherical_indeces__['elevation']] = elevation + spherical[..., __spherical_indeces__['azimuth']] = azimuth + spherical[..., __spherical_indeces__['radius']] = radius + return spherical + + +def spherical_to_cartesian(elevation, azimuth, radius=1): + cartesian = np.zeros_like(elevation) + cartesian = np.tile(cartesian[..., np.newaxis], (3,)) + cartesian[..., __cartesian_indeces__['x']] = np.cos( + elevation) * np.cos(azimuth) + cartesian[..., __cartesian_indeces__['y']] = np.cos( + elevation) * np.sin(azimuth) + cartesian[..., __cartesian_indeces__['z']] = np.sin(elevation) + cartesian = radius * cartesian + return cartesian diff --git a/navipy/rendering/__pycache__/__init__.cpython-35.pyc b/navipy/rendering/__pycache__/__init__.cpython-35.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a2503f97bf18d28410e60c9e97814282d10886d8 Binary files /dev/null and b/navipy/rendering/__pycache__/__init__.cpython-35.pyc differ diff --git a/todo b/todo index e1a0fa9d49479cc05553767fb207b2b804b2df31..4371356b537bda96b8389ed19f06df7dbfa2c162 100644 --- a/todo +++ b/todo @@ -37,3 +37,7 @@ Need to propagate the changes through all the code (see rendering / processing / 0007: Improve cyber_bee so that every getter and setter are properties 0008: Improve bee_sampling so that every getter and setter are properties +-------------------------------------------------------------------- +0009: can the last dimension (4) of the place-code be greater 1? (should be 1) +0010: what is the image in the database? must it be transformed to a a scene? +