Skip to content

Commit

Permalink
Add global evaluators support
Browse files Browse the repository at this point in the history
  • Loading branch information
aelmiger committed Apr 5, 2024
1 parent c5a08a0 commit 8038daf
Show file tree
Hide file tree
Showing 7 changed files with 157 additions and 105 deletions.
20 changes: 20 additions & 0 deletions docs/docs/usage/job_description/dynamic_evaluators.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,23 @@ The following table lists all dynamic evaluators and their parameters.
| random_selection | ```[<step1>, <step2>, ...]``` | Samples a value from a list of steps in a random order. |
| selection_asset | ```{library: <library_name>, type: <type>} ``` | Randomly picks an asset from the given library and type. |
| selection_wildcard | ```{library: <library_name>, wildcard: <wildcard_pattern>}``` | Randomly selects an asset from a library that matches the given wildcard pattern. |


## Referencing Global Evaluators

In addition to the dynamic evaluators that are specific to each attribute, you can also reference global evaluators defined in the `global_evaluators` section of the job configuration. Global evaluators are evaluated once per frame and ensure that the same random value is used for all sensors within a single frame.

To reference a global evaluator, use the syntax `$global.<evaluator_name>`. For example:

```yaml
sensor:
syclops_sensor_camera:
- name: "main_camera"
gamma: $global.gamma
# ...
- name: "secondary_camera"
gamma: $global.gamma
# ...
```

In this example, both the `main_camera` and `secondary_camera` will use the same random value for `gamma` in each frame, as defined in the `global_evaluators` section.
19 changes: 18 additions & 1 deletion docs/docs/usage/job_description/job_configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ Each job can be divided into the following sections:
* **sensor**: Configuration of individual sensors and their corresponding outputs.
* **postprocessing** - [*optional]*: Operations that are applied to the generated data after creation.
* **textures** - [*optional]*: Dynamically generated textures that can be used in the scene.
* **global_evaluators**: Defines global evaluators that can be referenced by multiple plugins or sensors.

=== "general"

Expand Down Expand Up @@ -238,4 +239,20 @@ Each job can be divided into the following sections:
res: 8
octaves: 4
```
</details>
</details>

=== "global_evaluators"

This section defines global evaluators that can be referenced by multiple plugins or sensor. These evaluators are evaluated once per frame and ensure that the same random value is used for all plugins/sensors within a single frame. This is useful for multiple cameras that should have the same random exposure value every frame.

!!! tip
See [Dynamic Evaluators](dynamic_evaluators.md) for more information.

**Example**:
```yaml
global_evaluators:
gamma:
uniform: [0.8, 1.2]
exposure:
normal: [0, 1]
```
35 changes: 33 additions & 2 deletions syclops/preprocessing/preprocessor.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
from PIL import Image
from ruamel.yaml import YAML
from syclops.preprocessing.texture_processor import process_texture
from syclops import utility


CATALOG_LIBRARY_KEY = "Preprocessed Assets"

Expand Down Expand Up @@ -140,14 +142,43 @@ def create_textures(config: dict, catalog: dict, output_path: str):
_add_to_catalog(catalog, texture_name, asset_value, output_path)


def _set_seed(config: dict, seed: int):
np.random.seed(config["seeds"]["numpy"] * seed)
def evaluate_global_evaluators(config: dict, global_evaluators: dict, catalog: dict):
"""
Evaluates global evaluators in the config.
"""
evaluated_global_evaluators = {}
for key, eval_value in global_evaluators.items():
is_list = isinstance(eval_value, list) or hasattr(eval_value, "to_list")
if is_list:
raise ValueError("Global evaluators must be dictionaries.")
evaluated_global_evaluators[key] = {
"step": [
utility.apply_sampling(eval_value, step, catalog)
for step in range(config["steps"])
]
}

return evaluated_global_evaluators


def preprocess(config_path: str, catalog_path: str, schema: str, output_path: str):
config = read_yaml(config_path)
catalog = read_yaml(catalog_path)
schema = read_yaml(schema)

# Evaluate global evaluators
global_evaluators = config.get("global_evaluators", {})
evaluated_global_evaluators = evaluate_global_evaluators(
config, global_evaluators, catalog
)

# Replace global evaluator references in sensor configurations
for sensor_config in config["sensor"].values():
for sensor in sensor_config:
for key, value in sensor.items():
if isinstance(value, str) and value.startswith("$global."):
global_key = value[8:]
sensor[key] = evaluated_global_evaluators[global_key]
try:
validate(config, schema)
print("Job Config is valid!")
Expand Down
21 changes: 21 additions & 0 deletions syclops/schema/base_schema.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,27 @@ properties:
- $ref: "#/definitions/keep_overlapp"
- $ref: "#/definitions/random_rectangles"
minItems: 1
global_evaluators:
type: object
additionalProperties:
anyOf:
- type: number
- $ref: "#/definitions/sample_linear_vector"
- $ref: "#/definitions/sample_linear_number"
- $ref: "#/definitions/sample_normal_vector"
- $ref: "#/definitions/sample_normal_number"
- $ref: "#/definitions/sample_uniform_vector"
- $ref: "#/definitions/sample_uniform_number"
- $ref: "#/definitions/sample_step_number"
- $ref: "#/definitions/sample_step_vector"
- $ref: "#/definitions/sample_step_string"
- $ref: "#/definitions/sample_random_selection_number"
- $ref: "#/definitions/sample_random_selection_vector"
- $ref: "#/definitions/sample_random_selection_string"
- $ref: "#/definitions/sample_selection_folder"
- $ref: "#/definitions/sample_selection_asset"
- $ref: "#/definitions/sample_wildcard"

required: [steps, seeds, render_device, denoising_enabled]

allOf:
Expand Down
11 changes: 5 additions & 6 deletions syclops/utility/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,11 @@
load_image, load_img_as_array, merge_objects,
refresh_modifiers, render_visibility,
resize_textures, set_active_collection, set_seeds,
show_all_modifiers)
from .sampling_utils import (eval_param, interpolate_img, sample_linear,
sample_normal, sample_random_selection,
sample_selection_asset, sample_selection_folder,
sample_step, sample_uniform, sample_wildcard)

show_all_modifiers, eval_param)
from .sampling_utils import (sample_linear,
sample_normal, sample_random_selection,
sample_selection_asset, sample_selection_folder,
sample_step, sample_uniform, sample_wildcard, apply_sampling)

from .general_utils import (AtomicYAMLWriter, create_folder,
find_class_id_mapping,get_site_packages_path, get_module_path, hash_vector)
Expand Down
38 changes: 35 additions & 3 deletions syclops/utility/blender_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import coacd
import numpy as np
from mathutils import Matrix
from . import sampling_utils as su


def apply_transform(
Expand Down Expand Up @@ -443,6 +444,7 @@ def convex_decomposition(

return convex_hulls


class DisjointSet:
def __init__(self):
self.parent = {}
Expand All @@ -468,7 +470,7 @@ def get_clusters(self):
clusters[root] = set()
clusters[root].add(item)
return list(clusters.values())

def find_cluster(self, item):
if item not in self.parent:
return None # Item not present in any cluster
Expand All @@ -479,7 +481,8 @@ def find_cluster(self, item):
if self.find(key) == root:
cluster.add(key)
return cluster



def run_coacd(quality: float, coacd_mesh: coacd.Mesh) -> list:
"""Run the COACD algorithm on a mesh with default parameters.
Expand Down Expand Up @@ -638,7 +641,14 @@ def add_volume_attribute(obj: bpy.types.Object):

bm = bmesh.new()
bm.from_mesh(obj.data)
volume = float(bm.calc_volume()) * volume_conversion_factor
# Calculate the raw volume (without considering scaling)
raw_volume = float(bm.calc_volume())

# Calculate the scale factor (product of the scale on all axes)
scale_factor = obj.scale.x * obj.scale.y * obj.scale.z
# Adjust the volume for the object's scaling
volume = raw_volume * scale_factor * volume_conversion_factor

# Remove bmesh
bm.free()
bm = None
Expand Down Expand Up @@ -789,3 +799,25 @@ def merge_objects(obj_list: list) -> bpy.types.Object:
):
bpy.ops.object.join()
return obj_list[0]


def eval_param(eval_params: Union[float, dict, str]) -> Union[float, list, str]:
"""Convert a parameter to a discrete value with the help of a sample function.
Args:
eval_params: Parameter to be evaluated.
Returns:
Union[float, list, str]: Evaluated parameter.
"""
is_list = isinstance(eval_params, list) or hasattr(eval_params, "to_list")
eval_params = eval_params if is_list else [eval_params]

curr_frame = bpy.context.scene.frame_current
catalog_string = bpy.data.scenes["Scene"]["catalog"]
catalog = pickle.loads(bytes(catalog_string, "latin1"))

evaluated_param = list(
map(lambda x: su.apply_sampling(x, curr_frame, catalog), eval_params)
)
return evaluated_param if is_list else evaluated_param[0]
Loading

0 comments on commit 8038daf

Please sign in to comment.