From ac3ca4d04b5c16c1c51bc4075c49e42d7b4aeb48 Mon Sep 17 00:00:00 2001
From: "Olivier J.N. Bertrand" <olivier.bertrand@uni-bielefeld.de>
Date: Tue, 25 Sep 2018 11:55:44 +0200
Subject: [PATCH] Update database to save frame # as well in order to avoid
 confusion in case of nans

---
 doc/source/tutorials/04-optic-flow.ipynb |  38 ++++++-
 navipy/database/__init__.py              | 132 ++++++++++-------------
 2 files changed, 89 insertions(+), 81 deletions(-)

diff --git a/doc/source/tutorials/04-optic-flow.ipynb b/doc/source/tutorials/04-optic-flow.ipynb
index 1b2bc3a..748a288 100644
--- a/doc/source/tutorials/04-optic-flow.ipynb
+++ b/doc/source/tutorials/04-optic-flow.ipynb
@@ -650,9 +650,41 @@
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "ToDO:\n",
-    "* implement obpc in optic flow\n",
-    "* convert markers2bee_sh into obpc format. Need to create viewing dir for el and az, and scene from radius.\n"
+    "## Optic flow from a database\n",
+    "\n",
+    "Database of rendered views along a trajectory can be created by using the tool `blendalongtraj`. Once the database has been created, optic flow can be obtain by using the difference in the position orientation and the distance to surrounding objects (The 'D' channel)."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import pkg_resources\n",
+    "from navipy.database import DataBase\n",
+    "mydbfile = pkg_resources.resource_filename(\n",
+    "    'navipy', 'resources/database.db')\n",
+    "mydb = DataBase(mydbfile)\n",
+    "mytraj = mydb.posorients\n",
+    "mytrajdiff = mytraj.differentiate(periods=1)\n",
+    "mytrajvel = mytraj.join(mytrajdiff)\n",
+    "mytrajvel.dropna().head()\n",
+    "\n",
+    "my_opticflow=pd.DataFrame(index=mytrajvel.index,\n",
+    "                          columns=pd.MultiIndex.from_product([points_name,['rof','hof','vof']]))\n",
+    "my_opticflow=my_opticflow.swaplevel(axis=1)\n",
+    "horizontal_of = np.zeros((mytrajvel.shape[0],*mydb.viewing_directions[...,0].shape))\n",
+    "vertical_of = horizontal_of.copy()\n",
+    "\n",
+    "for ii, (frame_i, velocity) in enumerate(mytrajvel.dropna().iterrows()):\n",
+    "    scene = mydb.scene(velocity)\n",
+    "    distance = scene[...,3,0]\n",
+    "    # Calculate the optic flow of these points\n",
+    "    _,hof,vof = optic_flow(distance,mydb.viewing_directions,velocity)\n",
+    "    # save the results in df\n",
+    "    horizontal_of[ii,...]=hof\n",
+    "    vertical_of[ii,...]=vof"
    ]
   },
   {
diff --git a/navipy/database/__init__.py b/navipy/database/__init__.py
index f7a84ab..0083324 100644
--- a/navipy/database/__init__.py
+++ b/navipy/database/__init__.py
@@ -137,6 +137,7 @@ class DataBase():
         self.tablecolumns['position_orientation']['q_1'] = 'real'
         self.tablecolumns['position_orientation']['q_2'] = 'real'
         self.tablecolumns['position_orientation']['q_3'] = 'real'
+        self.tablecolumns['position_orientation']['frame_i'] = 'real'
         self.tablecolumns['position_orientation']['rotconv_id'] = 'string'
         self.tablecolumns['image'] = dict()
         self.tablecolumns['image']['data'] = 'array'
@@ -292,6 +293,12 @@ class DataBase():
             raise Exception(msg)
         found_convention = False
         index = posorient.index
+        if isinstance(posorient.name, int):
+            msg = 'posorient.name should give the frame #'
+            self._logger.exception(msg)
+            raise Exception(msg)
+        frame_i = posorient.name
+
         if isinstance(index, pd.MultiIndex):
             convention = index.levels[0][-1]
         else:
@@ -353,79 +360,35 @@ class DataBase():
             msg = 'posorient must not contain nan\n {}'.format(posorient)
             self._logger.exception(msg)
             raise ValueError(msg)
-        cursor = self.db_cursor.execute('select * from position_orientation')
-        names = list(map(lambda x: x[0], cursor.description))
         where = ""
-        params = None
-        if 'alpha_0' in names:
-            self._logger.warning("you are loading a database with an\
-                           old convention")
-            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['location']['x'] - self.__float_tolerance,
-                posorient['location']['x'] + self.__float_tolerance,
-                posorient['location']['y'] - self.__float_tolerance,
-                posorient['location']['y'] + self.__float_tolerance,
-                posorient['location']['z'] - self.__float_tolerance,
-                posorient['location']['z'] + self.__float_tolerance,
-                posorient[convention][naming_map[0]] - self.__float_tolerance,
-                posorient[convention][naming_map[0]] + self.__float_tolerance,
-                posorient[convention][naming_map[1]] - self.__float_tolerance,
-                posorient[convention][naming_map[1]] + self.__float_tolerance,
-                posorient[convention][naming_map[2]] - self.__float_tolerance,
-                posorient[convention][naming_map[2]] + self.__float_tolerance)
-        elif convention != 'quaternion':
-            where += """x>=? and x<=?"""
-            where += """and y>=? and y<=?"""
-            where += """and z>=? and z<=?"""
-            where += """and q_0>=? and q_0<=?"""
-            where += """and q_1>=? and q_1<=?"""
-            where += """and q_2>=? and q_2<=?"""
-            where += """and rotconv_id =?"""
-            params = (
-                posorient['location']['x'] - self.__float_tolerance,
-                posorient['location']['x'] + self.__float_tolerance,
-                posorient['location']['y'] - self.__float_tolerance,
-                posorient['location']['y'] + self.__float_tolerance,
-                posorient['location']['z'] - self.__float_tolerance,
-                posorient['location']['z'] + self.__float_tolerance,
-                posorient[convention][naming_map[0]] - self.__float_tolerance,
-                posorient[convention][naming_map[0]] + self.__float_tolerance,
-                posorient[convention][naming_map[1]] - self.__float_tolerance,
-                posorient[convention][naming_map[1]] + self.__float_tolerance,
-                posorient[convention][naming_map[2]] - self.__float_tolerance,
-                posorient[convention][naming_map[2]] + self.__float_tolerance,
-                convention)
-        else:
-            where += """x>=? and x<=?"""
-            where += """and y>=? and y<=?"""
-            where += """and z>=? and z<=?"""
-            where += """and q_0>=? and q_0<=?"""
-            where += """and q_1>=? and q_1<=?"""
-            where += """and q_2>=? and q_2<=?"""
+        where += """x>=? and x<=?"""
+        where += """and y>=? and y<=?"""
+        where += """and z>=? and z<=?"""
+        where += """and q_0>=? and q_0<=?"""
+        where += """and q_1>=? and q_1<=?"""
+        where += """and q_2>=? and q_2<=?"""
+        where += """and rotconv_id =?"""
+        where += """and frame_i = ?"""
+        params = (
+            posorient['location']['x'] - self.__float_tolerance,
+            posorient['location']['x'] + self.__float_tolerance,
+            posorient['location']['y'] - self.__float_tolerance,
+            posorient['location']['y'] + self.__float_tolerance,
+            posorient['location']['z'] - self.__float_tolerance,
+            posorient['location']['z'] + self.__float_tolerance,
+            posorient[convention][naming_map[0]] - self.__float_tolerance,
+            posorient[convention][naming_map[0]] + self.__float_tolerance,
+            posorient[convention][naming_map[1]] - self.__float_tolerance,
+            posorient[convention][naming_map[1]] + self.__float_tolerance,
+            posorient[convention][naming_map[2]] - self.__float_tolerance,
+            posorient[convention][naming_map[2]] + self.__float_tolerance,
+            convention,
+            frame_i)
+        if convention == 'quaternion':
             where += """and q_3>=? and q_3<=?"""
-            where += """and rotconv_id =?"""
-            params = (
-                posorient['location']['x'] - self.__float_tolerance,
-                posorient['location']['x'] + self.__float_tolerance,
-                posorient['location']['y'] - self.__float_tolerance,
-                posorient['location']['y'] + self.__float_tolerance,
-                posorient['location']['z'] - self.__float_tolerance,
-                posorient['location']['z'] + self.__float_tolerance,
-                posorient[convention][naming_map[0]] - self.__float_tolerance,
-                posorient[convention][naming_map[0]] + self.__float_tolerance,
-                posorient[convention][naming_map[1]] - self.__float_tolerance,
-                posorient[convention][naming_map[1]] + self.__float_tolerance,
-                posorient[convention][naming_map[2]] - self.__float_tolerance,
-                posorient[convention][naming_map[2]] + self.__float_tolerance,
+            params = params + (
                 posorient[convention][naming_map[3]] - self.__float_tolerance,
-                posorient[convention][naming_map[3]] + self.__float_tolerance,
-                convention)
+                posorient[convention][naming_map[3]] + self.__float_tolerance)
         self.db_cursor.execute(
             """
             SELECT count(*)
@@ -445,8 +408,8 @@ class DataBase():
                 self.db_cursor.execute(
                     """
                     INSERT
-                    INTO position_orientation(x,y,z,q_0,q_1,q_2,q_3,rotconv_id)
-                    VALUES (?,?,?,?,?,?,?,?)
+                    INTO position_orientation(x,y,z,q_0,q_1,q_2,q_3,rotconv_id,frame_i)
+                    VALUES (?,?,?,?,?,?,?,?,?)
                     """, (
                         posorient['location']['x'],
                         posorient['location']['y'],
@@ -455,13 +418,13 @@ class DataBase():
                         posorient[convention]['alpha_1'],
                         posorient[convention]['alpha_2'],
                         np.nan,
-                        convention))
+                        convention, frame_i))
             else:
                 self.db_cursor.execute(
                     """
                     INSERT
-                    INTO position_orientation(x,y,z,q_0,q_1,q_2,rotconv_id)
-                    VALUES (?,?,?,?,?,?,?,?)
+                    INTO position_orientation(x,y,z,q_0,q_1,q_2,rotconv_id,frame_i)
+                    VALUES (?,?,?,?,?,?,?,?,?)
                     """, (
                         posorient['location']['x'],
                         posorient['location']['y'],
@@ -470,7 +433,7 @@ class DataBase():
                         posorient[convention]['q_1'],
                         posorient[convention]['q_2'],
                         posorient[convention]['q_3'],
-                        convention))
+                        convention, frame_i))
             rowid = self.db_cursor.lastrowid
             self.db.commit()
             return rowid
@@ -532,15 +495,20 @@ class DataBase():
         return self.__convention
 
     @property
-    def posorients(self):
+    def posorients(self, indexby='frame_i'):
         """Return the position orientations of all points in the \
         database
+        :params indexby: index posorients by 'frame_i' (default) or 'id'
         :returns: all position orientations
         :rtype: list of pd.Series
         """
         posorient = pd.read_sql_query(
             "select * from position_orientation;", self.db)
-        posorient.set_index('id', inplace=True)
+        if indexby in posorient.columns:
+            posorient.set_index('frame_i', inplace=True)
+        else:  # Handle older db version
+            print('Could not index by {}'.format(indexby))
+            posorient.set_index('id', inplace=True)
         if self.rotation_convention is not None:
             posorients = Trajectory()
             posorients.from_dataframe(posorient, rotconv=self.__convention)
@@ -562,6 +530,14 @@ class DataBase():
     # Read from database
     #
     def read_posorient(self, posorient=None, rowid=None):
+        """Read posorient with a given posorient or rowid
+
+        :param posorient: pd.Series with MuliIndex
+        :param rowid: integer, rowid of the database
+        :returns: return posorient
+        :rtype: pd.Series
+
+        """
         if rowid is not None:
             if not isinstance(rowid, int):
                 msg = 'rowid must be an integer'
-- 
GitLab