Hello @Turowicz, tere are a few options for better compatibility depending on your setup and what you wan to achieve. What kind of randomization are you trying to do together with the utilities?
Here are some options for integrating the tools with Replicator:
Option A: Full Python workflow + Replicator
It’s possible to mix and match a python workflow with replicator randomization. The friction often comes from needing to get prim paths out of replicator functions. This can be done through:
boxes = rep.get.prims(semantics=[["class", "box"]])
box_prims = .get_output_prims()["prims"] # where "prims" is the node output attribute that we want prims from
Option B: Extend Replicator with new functionality
If you are creating your own Kit Extension, you can create new OmniGraph nodes that implement novel randomization functionality and use the usd_scene_construction_utils as needed. Otherwise, it’s still possible to use the usd scene construction utilities at prim creation time and to assemble existing Replicator functionality in new ways. Here’s a larger example doing this where we register a new box_stacks
randomizer:
import random
import omni.replicator.core as rep
import omni.usd
import os
import sys
from typing import Tuple, Union, List
from pxr import Usd, Sdf, Tf
sys.path.append("/home/jlafleche/Projects/usd_scene_construction_utils/")
from usd_scene_construction_utils import (
add_usd_ref,
add_xform,
compute_bbox,
compute_bbox_center,
rotate_x,
rotate_y,
rotate_z,
scale,
translate,
)
# Create a box stack creation function
@rep.utils.ReplicatorWrapper
def box_stack(
count: int = 1,
name: str = None,
parent: Union[rep.utils.ReplicatorItem, str, Sdf.Path, Usd.Prim] = None,
semantics: List[Tuple[str, str]] = None,
):
if count < 1 or not isinstance(count, int):
raise ValueError("`count` must be a positive integer")
if isinstance(parent, str) and not Tf.IsValidIdentifier(parent):
raise ValueError(f"`parent` must be a valid prim path")
stage = omni.usd.get_context().get_stage()
if parent and not stage.GetPrimAtPath(str(parent)):
raise ValueError(f"Unable to find `parent` prim: {parent}")
# Create a random stack of boxes
box_asset_url = "http://omniverse-content-production.s3-us-west-2.amazonaws.com/Assets/DigitalTwin/Assets/Warehouse/Shipping/Cardboard_Boxes/Flat_A/FlatBox_A02_15x21x8cm_PR_NVD_01.usd"
def add_box_of_size(
stage,
path: str,
size: Tuple[float, float, float]
):
"""Adds a box and re-scales it to match the specified dimensions
"""
# Add USD box
prim = add_usd_ref(stage, path, usd_path=box_asset_url)
rotate_x(prim, random.choice([-90, 0, 90, 180]))
rotate_y(prim, random.choice([-90, 0, 90, 180]))
# Scale USD box to fit dimensions
usd_min, usd_max = compute_bbox(prim)
usd_size = (
usd_max[0] - usd_min[0],
usd_max[1] - usd_min[1],
usd_max[2] - usd_min[2]
)
required_scale = (
size[0] / usd_size[0],
size[1] / usd_size[1],
size[2] / usd_size[2]
)
scale(prim, required_scale)
return prim
def add_random_box_stack(
stage,
path: str,
count_range=(1, 5),
size_range=((30, 30, 10), (50, 50, 25)),
angle_range=(-5, 5),
jitter_range=(-3,3)
):
container = add_xform(stage, path)
count = random.randint(*count_range)
# get sizes and sort
sizes = [
(
random.uniform(size_range[0][0], size_range[1][0]),
random.uniform(size_range[0][1], size_range[1][1]),
random.uniform(size_range[0][2], size_range[1][2])
)
for i in range(count)
]
sizes = sorted(sizes, key=lambda x: x[0]**2 + x[1]**2, reverse=True)
boxes = []
for i in range(count):
box_i = add_box_of_size(stage, os.path.join(path, f"box_{i}"), sizes[i])
boxes.append(box_i)
if count > 0:
center = compute_bbox_center(boxes[0])
for i in range(1, count):
prev_box, cur_box = boxes[i - 1], boxes[i]
cur_bbox = compute_bbox(cur_box)
cur_center = compute_bbox_center(cur_box)
prev_bbox = compute_bbox(prev_box)
offset = (
center[0] - cur_center[0],
center[1] - cur_center[1],
prev_bbox[1][2] - cur_bbox[0][2]
)
translate(cur_box, offset)
# add some noise
for i in range(count):
rotate_z(boxes[i], random.uniform(*angle_range))
translate(boxes[i], (
random.uniform(*jitter_range),
random.uniform(*jitter_range),
0
))
if semantics:
rep.utils._set_semantics(container, semantics)
return container, boxes
def add_random_box_stacks(
stage,
path: str,
count_range=(0, 3),
):
container = add_xform(stage, path)
stacks = []
count = random.randint(*count_range)
for i in range(count):
stack, items = add_random_box_stack(stage, os.path.join(path, f"stack_{i}"))
stacks.append(stack)
for i in range(count):
cur_stack = stacks[i]
cur_bbox = compute_bbox(cur_stack)
cur_center = compute_bbox_center(cur_stack)
translate(cur_stack, (0, -cur_center[1], -cur_bbox[0][2]))
if i > 0:
prev_bbox = compute_bbox(stacks[i - 1])
translate(cur_stack, (prev_bbox[1][0] - cur_bbox[0][0], 0, 0))
return container, stacks
stacks = []
suffix = ""
if parent is None:
parent = rep.create.REPLICATOR_SCOPE
elif isinstance(parent, Usd.Prim):
parent = str(parent.GetPath())
elif isinstance(parent, rep.utils.ReplicatorItem):
# Use first prim as parent
parent = parent.get_output("prims")[0]
if name is None:
name = "BoxStack"
for c in range(count):
box_stacks_path = omni.usd.get_stage_next_free_path(stage, f"{parent}/{name}{suffix}", False)
add_random_box_stacks(stage, box_stacks_path, count_range=(1,4))
stacks.append(box_stacks_path)
suffix = f"_{c}"
return rep.create.group(stacks)
# Register box stack function so it's now part of replicator
rep.create.register(box_stack)
# Create box stack randomizer by combining replicator nodes
@rep.utils.ReplicatorWrapper
def box_stacks(surface_prims: rep.utils.ReplicatorItem, count: int = 1):
stacks = rep.create.box_stack(count=count, semantics=[("class", "box_stack")])
with stacks:
rep.randomizer.scatter_2d(surface_prims=surface_prims)
rep.modify.pose(rotation=rep.distribution.uniform((0, 0, -180), (0, 0, 180)))
# Register randomizer
rep.randomizer.register(box_stacks)
# Set stage defaults
rep.settings.set_stage_up_axis("Z")
rep.settings.set_stage_meters_per_unit(1.0)
# Get main scene
warehouse = rep.create.from_usd("omniverse://localhost/NVIDIA/Assets/Isaac/4.2/Isaac/Environments/Simple_Warehouse/full_warehouse.usd")
with warehouse:
rep.modify.pose(scale=100)
# Use Replicator to create and randomize stack placement on the floor
floor = rep.get.prims(semantics=[("class", "floor")])
with rep.trigger.on_frame():
rep.randomizer.box_stacks(surface_prims=floor, count=100)