Each module used in BlenderProc is defined in here. The folders structure the modules according to their use-case.
As mentioned before, all of the source code relevant to the key features (i.e. modules) of BlenderProc is presented here. The main folder contains the Module base class and the Pipeline class, which gets executed by the run.py & debug.py
Existing modules are placed in use-case-dependent folders:
- camera: camera loading and camera pose sampling.
- composite: complex (composite, duh) modules that are using other existing modules.
- constructor: constructing scenery and adding objects.
- lighting: light source loading, light source pose sampling, dataset-specific light loaders.
- loader: .obj, .ply, etc. object loading, dataset-specific object loading.
- manipulators: manipulating of the World and different entities present in the scene.
- materials: manipulating materials.
- object: object pose manipulation, physics between-object interaction, sampling and geometry manipulation.
- postprocessing: changing the pipeline output inside of a .hdf5 container.
- provider: samplers and getters used for sampling various values, selecting objects, etc.
- renderer: RGB, distance, etc. rendering.
- utility: an assortment of variuos utility functions and classes for any taste.
- writer: world-to-.hdf5-container state writers.
If our modules lack some specific functionality, you always can modify it, or create your own module. When deciding, where to place your module, think about it's use-case. If you are working on something, that isn't really fitting anywhere, use your best judgement and create a new folder with a clear and short name.
A module is a class executing one step in the pipeline. Here is the basic structure of such a module:
from src.main.Module import Module
class CameraLoader(Module):
def __init__(self, config):
Module.__init__(self, config)
[...]
def run(self):
[...]
The constructor of all modules is called before running any module, also in the order specified in the config file.
Nevertheless it should only be used for small preparation work, while most of the module`s work should be done inside run()
The module`s configuration can be accessed via self.config
.
This configuration object has the methods get_int
, get_float
, get_bool
, get_string
, get_list
, get_raw_dict
, etc. each working in the same way.
- The first parameter specifies the key/name of the parameter to get. By using
/
it is also possible access values nested inside additional dicts (see example below). - The second parameter specifies the default value which is returned, if the requested parameter has not been specified inside the config file. If
None
is given, an error is thrown instead.
Example:
Config file:
{
"modules": [
{
"module": "main.Initializer",
"config": {
"global": {
"output_dir": "/tmp/",
"auto_tile_size": False,
"pixel_aspect_x": 1.333333333
}
}
},
{
"module": "renderer.NewRenderer",
"config": {
"auto_tile_size": True,
"cycles": {
"samples": 255
}
}
}
]
}
Inside the renderer.NewRenderer
module:
self.get_int("cycles/samples", 42)
# -> 255
self.get_float("pixel_aspect_x")
# -> 1.333333333 this value is drawn from the GlobalStorage
self.get_string("output_dir", "output/")
# -> /tmp/ this value is drawn from the GlobalStorage
self.get_bool("auto_tile_size")
# -> True
self.config.get_int("resolution_x", 512)
# -> 512
self.config.get_int("tile_x")
# -> throws an error
In some modules it makes sense to revert changes made inside the module to not disturb modules coming afterwards (For example renderer modules should not change the state of the scene).
This often required functionality can be easily done via the Utility.UndoAfterExecution()
with-statement:
Example:
def run(self):
bpy.context.scene.cycles.samples = 50
with Utility.UndoAfterExecution():
bpy.context.scene.cycles.samples = 320
print(bpy.context.scene.cycles.samples)
# Outputs: 320
print(bpy.context.scene.cycles.samples)
# Outputs: 50
All changes inside the with-block are undone which could also be undone via CTRL+Z
inside Blender.
To exchange information between modules the blender's custom properties are used (Blender allows to assign arbitrary information to scenes and objects). So modules can read out custom properties set by earlier modules and change their behaviour accordingly.
Example
- The module
loader.SuncgLoader
adds the custom propertycategory_id
to every object - The module
renderer.SegMapRenderer
reads out this property and sets the segmentation color of every object correspondingly
In this way the renderer.SegMapRenderer
can also be used without using the loader.SuncgLoader
.
The loader used instead just has to also set the category_id
.