Skip to content
Snippets Groups Projects
Commit 94d9131c authored by Olivier Bertrand's avatar Olivier Bertrand
Browse files

Update rendering doc

parent 4f0d24a0
No related branches found
No related tags found
No related merge requests found
......@@ -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()
......
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)
......
"""
.. 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')
"""
.. 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)
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment