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?
+