From 94d9131c46c959929e744f1483e49d6d63a2764e Mon Sep 17 00:00:00 2001
From: "Olivier J.N. Bertrand" <olivier.bertrand@uni-bielefeld.de>
Date: Sun, 14 Jan 2018 12:12:12 +0100
Subject: [PATCH] Update rendering doc

---
 .../rendering/blenddemo_beesampling.py        |   4 +-
 .../example/rendering/blenddemo_cyberbee.py   |  14 +-
 navipy/rendering/bee_sampling.py              |  75 ++++----
 navipy/rendering/cyber_bee.py                 | 168 ++++++------------
 4 files changed, 106 insertions(+), 155 deletions(-)

diff --git a/doc/source/example/rendering/blenddemo_beesampling.py b/doc/source/example/rendering/blenddemo_beesampling.py
index 1ea027c..b882510 100644
--- a/doc/source/example/rendering/blenddemo_beesampling.py
+++ b/doc/source/example/rendering/blenddemo_beesampling.py
@@ -2,8 +2,8 @@
 Example on how to use the rendering module
 """
 import tempfile
-import numpy as np  # noqa: E402
-from navipy.rendering.bee_sampling import BeeSampling  # noqa: E402
+import numpy as np
+from navipy.rendering.bee_sampling import BeeSampling
 
 # create a bee sampling
 bee_samp = BeeSampling()
diff --git a/doc/source/example/rendering/blenddemo_cyberbee.py b/doc/source/example/rendering/blenddemo_cyberbee.py
index badfa9f..64f222f 100644
--- a/doc/source/example/rendering/blenddemo_cyberbee.py
+++ b/doc/source/example/rendering/blenddemo_cyberbee.py
@@ -1,4 +1,5 @@
 import numpy as np
+import pandas as pd
 from matplotlib.colors import hsv_to_rgb, rgb_to_hsv
 import matplotlib.pyplot as plt
 from navipy.rendering.cyber_bee import Cyberbee
@@ -6,7 +7,18 @@ from navipy.rendering.cyber_bee import Cyberbee
 # with tempfile.TemporaryDirectory() as folder:
 cyberbee = Cyberbee()
 cyberbee.cycle_samples = 50
-posorient = [0, 0, 1.8, np.pi / 2, 0, 0]
+cyberbee.camera_rotation_mode = 'XYZ'
+cyberbee.camera_fov = [[-90, 90], [-180, 180]]
+cyberbee.gaussian_width = 1.5
+cyberbee.camera_resolution = [360, 180]
+posorient = pd.Series(index=['x', 'y', 'z',
+                             'alpha_0', 'alpha_1', 'alpha_2'])
+posorient.x = 0
+posorient.y = 0
+posorient.z = 1.8
+posorient.alpha_0 = np.pi / 2
+posorient.alpha_1 = 0
+posorient.alpha_2 = 0
 scene = cyberbee.scene(posorient)
 
 
diff --git a/navipy/rendering/bee_sampling.py b/navipy/rendering/bee_sampling.py
index bdcc0e0..2181034 100644
--- a/navipy/rendering/bee_sampling.py
+++ b/navipy/rendering/bee_sampling.py
@@ -1,6 +1,6 @@
 """
 .. literalinclude:: example/rendering/blenddemo_beesampling.py
-   :lines: 30-31
+   :lines: 6
 
 With the toolbox at disposition we just need to configure the \
 BeeSampling to render images on a regular 3D grid.
@@ -107,29 +107,40 @@ class BeeSampling(Cyberbee):
 
     @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)
+        """Position orientations to be rendered
 
+        :getter: position-orientations of the grid
+        :setter: set a list of locations to be randered
+        :type: pandas array
         """
         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
+    @grid_posorients.setter
+    def grid_posorients(self, grid_df):
+        """Position orientation to be rendered
+        """
+        columns = ['x', 'y', 'z',
+                   'alpha_0',
+                   'alpha_1',
+                   'alpha_2']
+        for col in grid_df.columns:
+            if col not in columns:
+                raise KeyError(
+                    'Grid dataframe should contains {} column'.format(col))
+        self.__grid_posorients = grid_df.copy()
 
-        .. todo: use @property.setter
-                     def blacklist_indeces(self,indeces)
+    @property
+    def blacklist_indeces(self):
+        """ Blacklist certain indeces of the grid posorients\
+so that they are not rendered
 
+        :getter: return a list of blacklisted indeces
+        :setter: set a blacklisted indeces
+        :type: list
+        """
+    @blacklist_indeces.setter
+    def blacklist_indeces(self, indeces):
+        """ Blacklist certain indeces
         """
         if not isinstance(indeces, list):
             raise TypeError('indeces must be a list')
@@ -138,6 +149,12 @@ class BeeSampling(Cyberbee):
         self.__grid_posorients.loc[indeces, :] = np.nan
 
     def render(self, database_filename):
+        """ Render all images at position specified by grid_posorients.
+
+        :param database_filename: path to the database
+        :type database_filename: str
+
+        """
         if not isinstance(database_filename, str):
             raise TypeError('filename must be a string')
         database_folder = os.path.dirname(database_filename)
@@ -167,25 +184,3 @@ class BeeSampling(Cyberbee):
             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/navipy/rendering/cyber_bee.py b/navipy/rendering/cyber_bee.py
index f3c1def..6b6181d 100644
--- a/navipy/rendering/cyber_bee.py
+++ b/navipy/rendering/cyber_bee.py
@@ -1,17 +1,17 @@
 """
 .. literalinclude:: example/rendering/blenddemo_cyberbee.py
-   :lines: 4
+   :lines: 5
 
 With the toolbox at disposition we just need to configure the \
 Cyberbee to render images at desired positions.
 
 .. literalinclude:: example/rendering/blenddemo_cyberbee.py
-   :lines: 7-8
+   :lines: 8-13
 
 To render a scene at a given positions we just have to do:
 
 .. literalinclude:: example/rendering/blenddemo_cyberbee.py
-   :lines: 9-10
+   :lines: 14-22
 
 """
 import warnings
@@ -98,17 +98,16 @@ class Cyberbee():
 
         :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):
         """change the camera rotation mode
+
         :param mode: the mode of rotation for the camera see blender do
         :type mode: a string
+
         .. seealso: blender bpy.data.scenes["Scene"].camera.rotation_mode
         """
         if not isinstance(mode, str):
@@ -123,8 +122,6 @@ class Cyberbee():
         :returns: the number of samples used for the rendering
         :rtype: int
 
-        ..todo use @property
-                   def cycle_samples(self)
         """
         return bpy.context.scene.cycles.samples
 
@@ -132,15 +129,9 @@ class Cyberbee():
     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')
@@ -150,28 +141,17 @@ class Cyberbee():
     def camera_fov(self):
         """get fov of camera
 
-
-
-        :returns: the field of view of the camera as min/max,longitude/latitude
-
-               in degrees
-
+        :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'
+        if self.camera.data.type != 'PANO':
+            raise TypeError('Camera is not panoramic')
+        if self.camera.cycles.panorama_type == 'EQUIRECTANGULAR':
+            raise TypeError('The panoramic 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)
@@ -182,29 +162,22 @@ class Cyberbee():
         return fov
 
     @camera_fov.setter
-    def camera_fov(self, resolution=[[-90, 90], [-180, 180]]):
+    def camera_fov(self, resolution):
         """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')
+            raise TypeError('Camera is not panoramic')
         if not self.camera.data.cycles.panorama_type == 'EQUIRECTANGULAR':
-            raise Exception('Camera is not equirectangular')
+            raise TypeError('Camera is not equirectangular')
         self.camera.data.cycles.latitude_min = np.deg2rad(
             resolution[0][0])
         self.camera.data.cycles.latitude_max = np.deg2rad(
@@ -215,33 +188,22 @@ class Cyberbee():
             resolution[1][1])
 
     @property
-    def camera_gaussian_width(self, gauss_w=1.5):
+    def camera_gaussian_width(self):
         """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):
+    def camera_gaussian_width(self, gauss_w):
         """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)):
@@ -255,36 +217,19 @@ class Cyberbee():
         :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]):
+    def camera_resolution(self, resolution):
         """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)):
@@ -293,45 +238,15 @@ class Cyberbee():
         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)
+        :returns: the image (height,width,nchannel)
         :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
@@ -341,9 +256,9 @@ class Cyberbee():
         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])
+        pixels = pixels.reshape([im_height, im_width, -1])
+        # The last channel is the alpha channel
+        pixels[..., :(pixels.shape[2] - 1)]
         return pixels
 
     @property
@@ -367,12 +282,39 @@ class Cyberbee():
         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.reshape([im_height, im_width, -1])
+        # Distance are channel independent
         distance = distance[:, :, 0]
         return distance
 
+    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()
+
     def scene(self, posorient):
         """ update position orientation and return a RGBD image
 
@@ -381,8 +323,10 @@ class Cyberbee():
              here the angles are euler rotation around the axis
              specified by scene.camera.rotation_mode
         :type posorient: 1x6 double array
+        :returns: a (height,width, channel) array here the last channel \
+is the distance.
+        :rtype: a double numpy array
         """
         self.update(posorient)
-        image = self.image
-        image[:, :, 3] = self.distance
-        return image
+        return np.concatenate((self.image,
+                               self.distance), axis=2)
-- 
GitLab