From 82223b7d157895f9d5d02e1c18e6e544274bf0d6 Mon Sep 17 00:00:00 2001 From: "Olivier J.N. Bertrand" <olivier.bertrand@uni-bielefeld.de> Date: Wed, 26 Sep 2018 14:14:15 +0200 Subject: [PATCH] Add viewing dir table to save viewing direction --- navipy/database/__init__.py | 64 ++++++++++++++++++++++------ navipy/sensors/renderer.py | 85 +++++++++++++++++++++++-------------- 2 files changed, 105 insertions(+), 44 deletions(-) diff --git a/navipy/database/__init__.py b/navipy/database/__init__.py index 0083324..73d842d 100644 --- a/navipy/database/__init__.py +++ b/navipy/database/__init__.py @@ -12,6 +12,7 @@ import navipy.maths.constants as mconst from navipy.trajectories import Trajectory from navipy.scene import __spherical_indeces__ import logging +import numbers def adapt_array(arr): @@ -121,7 +122,6 @@ class DataBase(): filename, channels)) self.filename = filename self.channels = channels - self.viewing_directions = None self.normalisation_columns = list() for chan_n in self.channels: self.normalisation_columns.append(str(chan_n) + '_max') @@ -139,11 +139,10 @@ class DataBase(): self.tablecolumns['position_orientation']['q_3'] = 'real' self.tablecolumns['position_orientation']['frame_i'] = 'real' self.tablecolumns['position_orientation']['rotconv_id'] = 'string' + self.tablecolumns['viewing_directions'] = dict() + self.tablecolumns['viewing_directions']['data'] = 'real' self.tablecolumns['image'] = dict() self.tablecolumns['image']['data'] = 'array' - # self.tablecolumns['viewing_directions'] = dict() - # self.tablecolumns['viewing_directions']['elevation'] = 'array' - # self.tablecolumns['viewing_directions']['azimuth'] = 'array' self.tablecolumns['normalisation'] = dict() for col in self.normalisation_columns: self.tablecolumns['normalisation'][col] = 'real' @@ -181,13 +180,52 @@ class DataBase(): self.db_cursor.execute( "create table {} {}".format(key, columns)) self.db.commit() + self.__nbaz = None + self.__nbel = None + self.__viewing_dir = None - azimuth = np.deg2rad(np.linspace(-180, 180, 360)) - elevation = np.deg2rad(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[..., __spherical_indeces__['elevation']] = me - self.viewing_directions[..., __spherical_indeces__['azimuth']] = ma + @property + def viewing_directions(self): + if self.__viewing_dir is None: + rowid = 1 + tablename = 'viewing_directions' + self.db_cursor.execute( + """ + SELECT data + FROM {} + WHERE (rowid=?) + """.format(tablename), (rowid,)) + self.__viewing_dir = self.db_cursor.fetchone()[0] + return self.__viewing_dir.copy() + + @viewing_directions.setter + def viewing_directions(self, viewdir): + """Get the viewing direction from images + + :param az_lim: (min,max) of the azimuth angles + :param el_lim: (min,max) of the elevation angles + :returns: viewing direction of every pixels + :rtype: np.array + + """ + if self.__viewing_dir is None: + msg = 'write viewing direction' + self._logger.info(msg) + if isinstance(viewdir, np.ndarray): + tablename = 'viewing_directions' + params = dict() + params['rowid'] = 1 + params['data'] = viewdir + self.insert_replace(tablename, params) + else: + msg = 'viewdir should be a numpy nd.array' + msg += ' and not {}'.format(type(viewdir)) + self._logger.exception(msg) + raise TypeError(msg) + else: + msg = 'viewing direction has already been set' + self._logger.exception(msg) + raise Exception(msg) def table_exist(self, tablename): """ @@ -293,8 +331,10 @@ class DataBase(): raise Exception(msg) found_convention = False index = posorient.index - if isinstance(posorient.name, int): - msg = 'posorient.name should give the frame #' + if not isinstance(posorient.name, numbers.Number): + msg = 'posorient.name should give the frame #\n' + msg += ' posorient.name: {}\n'.format(posorient.name) + msg += ' type(posorient.name): {}'.format(type(posorient.name)) self._logger.exception(msg) raise Exception(msg) frame_i = posorient.name diff --git a/navipy/sensors/renderer.py b/navipy/sensors/renderer.py index befcae6..59fe438 100644 --- a/navipy/sensors/renderer.py +++ b/navipy/sensors/renderer.py @@ -16,6 +16,7 @@ import yaml # Used to load config files import pkg_resources from navipy.maths.homogeneous_transformations import compose_matrix from navipy.maths.quaternion import matrix as quatmatrix +from navipy.scene import spherical_indeces import navipy.maths.constants as constants from navipy.trajectories import Trajectory from PIL import Image @@ -48,6 +49,11 @@ class AbstractRender(): """ worldlimit""" self.__worldlimit = worldlimit + @property + def viewing_directions(self): + """ Need to be implemented by children classes """ + return None + def render_trajectory(self, outputfile, trajectory, imformat='.jpg'): """ @@ -84,6 +90,8 @@ class AbstractRender(): mode='a', channels=['R', 'G', 'B', 'D'], arr_dtype=np.uint8) + self._logger.debug('database created') + dataloger.viewing_directions = self.viewing_directions # We now can render self._logger.info('Start rendering') for frame_i, posorient in trajectory.iterrows(): @@ -96,6 +104,7 @@ class AbstractRender(): # Avoid rerendering by checking if data already # exist if mode == 'database': + self._logger.warning(posorient) rowid = dataloger.get_posid(posorient) if dataloger.check_data_validity(rowid): msg = 'frame_i: {} data is valid rowid {}' @@ -356,8 +365,8 @@ class BlenderRender(AbstractRender): def cycle_samples(self): """get the samples for rendering with cycle - :returns: the number of samples used for the rendering - :rtype: int + : returns: the number of samples used for the rendering + : rtype: int """ return bpy.context.scene.cycles.samples @@ -366,8 +375,8 @@ class BlenderRender(AbstractRender): 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 + : param samples: the number of samples to use when rendering images + : type samples: int """ if not isinstance(samples, int): @@ -378,11 +387,11 @@ class BlenderRender(AbstractRender): def camera_fov(self): """get fov of camera - :returns: the field of view of the camera as \ + : returns: the field of view of the camera as \ [[minimum latitude, maximum latitude], [minimum longitude, maximum longitude]] (in deg) - :rtype: np.array + : rtype: np.array ..todo Change assert to if -> raise TypeError/KeyError @@ -402,10 +411,10 @@ class BlenderRender(AbstractRender): def camera_fov(self, resolution): """change the field of view of the panoramic camera - :param resolution: [[minimum latitude, maximum latitude], + : param resolution: [[minimum latitude, maximum latitude], [minimum longitude, maximum longitude]] (in deg) - :type latmin: 2x2 float array or list + : type latmin: 2x2 float array or list """ if not (isinstance(resolution, tuple) or isinstance(resolution, list) or @@ -428,8 +437,8 @@ class BlenderRender(AbstractRender): def camera_gaussian_width(self): """get width of the gaussian spatial filter - :returns: the width of the gaussian filter - :rtype: float + : returns: the width of the gaussian filter + : rtype: float """ return bpy.context.scene.cycles.filter_width @@ -438,9 +447,8 @@ class BlenderRender(AbstractRender): 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 - + : param gauss_w: width of the gaussian filter + : type gauss_w: float """ if not (isinstance(gauss_w, int) or isinstance(gauss_w, float)): @@ -449,10 +457,10 @@ class BlenderRender(AbstractRender): @property def camera_resolution(self): - """return camera resolution (x,y) + """return camera resolution(x, y) - :returns: the resolution of the camera along (x-axis,y-axis) - :rtype: (int,int) + : returns: the resolution of the camera along(x-axis, y-axis) + : rtype: (int, int) """ resolution_x = bpy.context.scene.render.resolution_x @@ -461,12 +469,12 @@ class BlenderRender(AbstractRender): @camera_resolution.setter def camera_resolution(self, resolution): - """change the camera resolution (nb of pixels) + """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 + : 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 """ if not (isinstance(resolution, list) or isinstance(resolution, np.ndarray)): @@ -475,12 +483,25 @@ class BlenderRender(AbstractRender): bpy.context.scene.render.resolution_y = resolution[1] bpy.context.scene.render.resolution_percentage = 100 + @property + def viewing_directions(self): + self._logger.info('get viewdir') + rx, ry = self.camera_resolution + fov = self.camera_fov + az = np.linspace(fov[1, 0], fov[1, 1], rx) + el = np.linspace(fov[0, 0], fov[0, 1], ry) + [ma, me] = np.meshgrid(az, el) + view_dir = np.zeros((ma.shape[0], ma.shape[1], 2)) + view_dir[..., spherical_indeces()['elevation']] = me + view_dir[..., spherical_indeces()['azimuth']] = ma + return view_dir + @property def image(self): """return the last rendered image as a numpy array - :returns: the image (height,width,nchannel) - :rtype: a double numpy array + : 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 @@ -509,8 +530,8 @@ class BlenderRender(AbstractRender): def distance(self): """return the last rendered distance map as a numpy array - :returns: the distance map (height, width) - :rtype: a double 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 @@ -543,7 +564,7 @@ class BlenderRender(AbstractRender): def update(self, posorient): """assign the position and the orientation of the camera. - :param posorient: is a 1x6 vector containing: + : param posorient: is a 1x6 vector containing: *in case of euler angeles the index should be ['location']['x'] ['location']['y'] @@ -565,7 +586,7 @@ class BlenderRender(AbstractRender): quaternion here the angles are euler rotation around the axis specified by scene.camera.rotation_mode - :type posorient: pandas Series with multi-index + : type posorient: pandas Series with multi-index """ self._logger.info('update posorient to {}'.format(posorient)) if isinstance(posorient, pd.Series): @@ -613,14 +634,14 @@ class BlenderRender(AbstractRender): def scene(self, posorient): """ update position orientation and return a RGBD image - :param posorient: is a 1x6 vector containing: - x,y,z, angle_1, angle_2, angle_3, + : 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 - :returns: a (height,width, channel) array here the last channel \ + : type posorient: 1x6 double array + : returns: a(height, width, channel) array here the last channel \ is the distance. - :rtype: a double numpy array + : rtype: a double numpy array """ self._logger.info('get a scene') self.update(posorient) -- GitLab