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